servicios rest - experto javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016....

159
1 Servicios Rest María Isabel Alfonso Galipienso <<[email protected]>> Table of Contents 1. Introducción a REST. Diseño y creación de servicios RESTful ........................................ 4 1.1. ¿Qué es un servicio Web? .................................................................................... 4 Servicios Web RESTful ......................................................................................... 5 1.2. Fundamentos de REST .......................................................................................... 5 Recursos ................................................................................................................ 6 Representación de los recursos ............................................................................ 7 Direccionabilidad de los recursos: URI .................................................................. 8 Uniformidad y restricciones de las interfaces ........................................................ 9 1.3. Diseño de servicios Web RESTful ....................................................................... 11 1.4. Un primer servicio JAX-RS .................................................................................. 12 Modelo de objetos ............................................................................................... 12 Modelado de URIs ............................................................................................... 13 Definición del formato de datos ........................................................................... 13 Asignación de métodos HTTP ............................................................................. 15 Implementación del servicio: Creación del proyecto Maven ................................ 20 Implementación del servicio: Recursos JAX-RS .................................................. 23 Construcción y despliegue del servicio ............................................................... 28 Probando nuestro servicio ................................................................................... 29 1.5. Ejercicios .............................................................................................................. 33 Servicio REST ejemplo (0 puntos) ...................................................................... 33 Servicio REST saludo (1 punto) .......................................................................... 33 Servicio REST foro (1 punto) .............................................................................. 35 2. Anotaciones básicas JAX-RS. El modelo de despliegue. ............................................... 38 2.1. ¿Cómo funciona el enlazado de métodos HTTP? ............................................... 38 2.2. La anotación @Path ............................................................................................. 39 Expresiones @Path ............................................................................................. 40 Parámetros matrix (Matrix parameters) ............................................................... 44 Subrecursos (Subresource Locators) .................................................................. 44 2.3. Usos de las anotaciones @Produces y @Consumes .......................................... 49 Anotación @Consumes ....................................................................................... 49 Anotación @Produces ......................................................................................... 51 2.4. Inyección de parámetros JAX-RS ........................................................................ 51 @javax.ws.rs.PathParam ..................................................................................... 52 Interfaz UriInfo ..................................................................................................... 53 @javax.ws.rs.MatrixParam ................................................................................... 55 @javax.ws.rs.QueryParam ................................................................................... 55 @javax.ws.rs.FormParam .................................................................................... 56 @javax.ws.rs.HeaderParam ................................................................................. 56 @javax.ws.rs.core.Context ................................................................................... 57 @javax.ws.rs.BeanParam .................................................................................... 58 Conversión automática de tipos .......................................................................... 59 Valores por defecto (@DefaultValue) .................................................................. 60 2.5. Configuración y despliegue de aplicaciones JAX-RS ........................................... 60 Configuración mediante la clase Application ....................................................... 60 Configuración mediante un fichero web.xml ........................................................ 63

Upload: others

Post on 19-Aug-2021

2 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones

1

Servicios RestMariacutea Isabel Alfonso Galipienso ltlteliuaesgtgt

Table of Contents1 Introduccioacuten a REST Disentildeo y creacioacuten de servicios RESTful 4

11 iquestQueacute es un servicio Web 4Servicios Web RESTful 5

12 Fundamentos de REST 5Recursos 6Representacioacuten de los recursos 7Direccionabilidad de los recursos URI 8Uniformidad y restricciones de las interfaces 9

13 Disentildeo de servicios Web RESTful 1114 Un primer servicio JAX-RS 12

Modelo de objetos 12Modelado de URIs 13Definicioacuten del formato de datos 13Asignacioacuten de meacutetodos HTTP 15Implementacioacuten del servicio Creacioacuten del proyecto Maven 20Implementacioacuten del servicio Recursos JAX-RS 23Construccioacuten y despliegue del servicio 28Probando nuestro servicio 29

15 Ejercicios 33Servicio REST ejemplo (0 puntos) 33Servicio REST saludo (1 punto) 33Servicio REST foro (1 punto) 35

2 Anotaciones baacutesicas JAX-RS El modelo de despliegue 3821 iquestCoacutemo funciona el enlazado de meacutetodos HTTP 3822 La anotacioacuten Path 39

Expresiones Path 40Paraacutemetros matrix (Matrix parameters) 44Subrecursos (Subresource Locators) 44

23 Usos de las anotaciones Produces y Consumes 49Anotacioacuten Consumes 49Anotacioacuten Produces 51

24 Inyeccioacuten de paraacutemetros JAX-RS 51javaxwsrsPathParam 52Interfaz UriInfo 53javaxwsrsMatrixParam 55javaxwsrsQueryParam 55javaxwsrsFormParam 56javaxwsrsHeaderParam 56javaxwsrscoreContext 57javaxwsrsBeanParam 58Conversioacuten automaacutetica de tipos 59Valores por defecto (DefaultValue) 60

25 Configuracioacuten y despliegue de aplicaciones JAX-RS 60Configuracioacuten mediante la clase Application 60Configuracioacuten mediante un fichero webxml 63

Servicios Rest

2

Configuracioacuten en un contenedor que no disponga de una implementacioacuten JAX-RS 63

26 Ejercicios 65Creacioacuten de un recurso creacioacuten y consulta de temas en el foro (05 puntos) 65Despliegue y pruebas del recurso (05 puntos) 67Muacuteltiples consultas de los temas del foro (05 puntos) 68Creacioacuten de subrecursos (05 puntos) 69

3 Manejadores de contenidos Respuestas del servidor y manejo de excepciones 7231 Proveedores de entidades 72

Interfaz javaxwsrsextMessageBodyReader 72Interfaz javaxwsrsextMessageBodyWriter 73

32 Proveedores de entidad estaacutendar incluidos en JAX-RS 73javaxwsrscoreStreamingOutput 74javaioInputStream javaioReader 75javaioFile 76byte[] 77String char[] 78MultivaluedMapltString Stringgt y formularios de entrada 78

33 Muacuteltiples representaciones de recursos 7934 Introduccioacuten a JAXB 80

Clase JAXBContext 87Manejadores JAX-RS para JAXB 88JAXB y JSON 89

35 Respuestas del servidor 91Coacutedigos de respuesta por defecto 91Elaboracioacuten de respuestas con la clase Response 93

36 Manejadores de excepciones 98La clase javaxwsrsWebApplicationException 99Mapeado de excepciones 100Jerarquiacutea de excepciones 101

37 Ejercicios 104Servicio REST ejemplo 104Plantillas que se proporcionan 104Uso de JAXB (05 puntos) 104Uso de manejadores de contenidos y clase Response (075 puntos) 105Manejo de excepciones (075 puntos) 106

4 HATEOAS y Seguridad 10741 iquestQueacute es HATEOAS 10742 HATEOAS y Servicios Web 108

Enlaces Atom 108Ventajas de utilizar HATEOAS con Servicios Web 108Enlaces en cabeceras frente a enlaces Atom 111

43 HATEOAS y JAX-RS 111Construccioacuten de URIs con UriBuilder 112URIs relativas mediante el uso de UriInfo 115Construccioacuten de enlaces (Links) en documentos XML y en cabeceras HTTP 116

44 Seguridad 118Autentificacioacuten en JAX-RS 119Autorizacioacuten en JAX-RS 121Encriptacioacuten 122Seguridad programada 124

45 Ejercicios 126Uso de Hateoas (1 puntos) 126

Servicios Rest

3

Ejercicio seguridad (1 punto) 1275 Api cliente Procesamiento JSON y Pruebas 128

51 API cliente Visioacuten general 128Obtenemos una instancia Client 129Configuramos el target del cliente (URI) 131Construimos y Realizamos la peticioacuten 133Manejo de excepciones 138

52 Procesamiento JSON 14053 Modelo de procesamiento basado en el modelo de objetos 141

Creacioacuten de un modelos de objetos desde el coacutedigo de la aplicacioacuten 142Navegando por el modelo de objetos 143Escritura de un modelo de objetos en un stream 144Modelo de procesamiento basado en streaming 145

54 Pruebas de servicios REST 147Ciclo de vida de Maven y tests JUnit 147Anotaciones JUnit y aserciones AssertThat 151Observaciones sobre los tests y algunos ejemplos de tests 154

55 Ejercicios 158Tests utilizando el API cliente y un mapeador de excepciones (1 punto) 158Tests utilizando el API Json y JUnit (1 punto) 158Implementacioacuten de los tests 159

Servicios Rest

4

1 Introduccioacuten a REST Disentildeo y creacioacuten de serviciosRESTful

En esta sesioacuten vamos a introducir los conceptos de servicio Web y servicio Web RESTfulque es el tipo de servicios con los que vamos a trabajar Explicaremos el proceso de disentildeodel API de un servicio Web RESTful y definiremos las URIs que constituiraacuten los puntos deentrada de nuestra aplicacioacuten REST Finalmente ilustraremos los pasos para implementardesplegar y probar un servicio REST utilizando Maven IntelliJ y el servidor de aplicacionesWildfly Tambieacuten nos familiarizaremos con Postman una herramienta para poder probar deforma sencilla los servicios web directamente desde el navegador

11 iquestQueacute es un servicio Web

El disentildeo del software tiende a ser cada vez maacutes modular Las aplicaciones estaacuten formadaspor una serie de componentes reutilizables (servicios) que pueden encontrarse distribuidosa lo largo de una serie de maacutequinas conectadas en red

El WC3 (World Wide Web Consortium) define un servicio Web como un sistema softwaredisentildeado para soportar interacciones maacutequina a maacutequina a traveacutes de la red Dicho de otromodo los servicios Web proporcionan una forma estaacutendar de interoperar entre aplicacionessoftware que se ejecutan en diferentes plataformas Por lo tanto su principal caracteriacutesticasu gran interoperabilidad y extensibilidad asiacute como por proporcionar informacioacuten faacutecilmenteprocesable por las maacutequinas gracias al uso de XML Los servicios Web pueden combinarsecon muy bajo acoplamiento para conseguir la realizacioacuten de operaciones complejas De estaforma las aplicaciones que proporcionan servicios simples pueden interactuar con otras paraentregar servicios sofisticados antildeadidos

Historia de los servicios Web

Los servicios Web fueron inventados para solucionar el problema de lainteroperabilidad entre las aplicaciones Al principio de los 90 con el desarrollo deInternetLANWAN aparecioacute el gran problema de integrar aplicaciones diferentes Unaaplicacioacuten podiacutea haber sido desarrollada en C++ o Java y ejecutarse bajo Unix unPC o un computador mainframe No habiacutea una forma faacutecil de intercomunicar dichasaplicaciones Fueacute el desarrollo de XML el que hizo posible compartir datos entreaplicaciones con diferentes plataformas hardware a traveacutes de la red o incluso a traveacutesde Internet La razoacuten de que se llamasen servicios Web es que fueron disentildeadospara residir en un servidor Web y ser llamados a traveacutes de Internet tiacutepicamente viaprotocolos HTTP o HTTPS De esta forma se asegura que un servicio puede serllamado por cualquier aplicacioacuten usando cualquier lenguaje de programacioacuten y bajocualquier sistema operativo siempre y cuaacutendo por supuesto la conexioacuten a Internetesteacute activa y tenga un puerto abierto HTTPHTTPS lo cual es cierto para casi cualquiercomputador que disponga de acceso a Internet

A nivel conceptual un servicio es un componente software proporcionado a traveacutes de unendpoint accesible a traveacutes de la red Los servicios productores y consumidores utilizanmensajes para intercambiar informacioacuten de invocaciones de peticioacuten y respuesta en formade documentos auto-contenidos que hacen muy pocas asunciones sobre las capacidadestecnoloacutegicas de cada uno de los receptores

Servicios Rest

5

iquestQueacute es un endpointLos servicios pueden interconectarse a traveacutes de la red En unaarquitectura orientada a servicios cualquier interaccioacuten punto a puntoimplica dos endpoints uno que proporciona un servicio y otro de loconsume Es decir que un endpoint es cada uno de los elementosen nuestro caso nos referimos a servicios que se situacutean en ambosextremos de la red que sirve de canal de comunicacioacuten entre ellosCuando hablamos de servicios Web un endpoint se especifica medianteuna URI

A nivel teacutecnico los servicios pueden implementarse de varias formas En estesentido podemos distinguir dos tipos de servicios Web los denominados servicios Webgrandes (big Web Services) los llamaremos servicios Web SOAP y servicios ligeros oservicios Web RESTful

Los servicios Web SOAP se caracterizan por utilizar mensajes XML que siguen el estaacutendarSOAP (Simple Object Access Protocol) Ademaacutes contienen una descripcioacuten de las operacionesproporcionadas por el servicio escritas en WSDL (Web Services Description Language) unlenguaje basado en XML

Los servicios Web RESTful por el contrario pueden intercambiar mensajes escritos endiferentes formatos y no requieren el publicar una descripcioacuten de las operaciones queproporcionan por lo que necesitan una menor infraestructura para su implementacioacutenNosotros vamos a centrarnos en el uso de estos servicios

Servicios Web RESTful

Son un tipo de Servicios Web que se adhieren a una serie de restricciones arquitectoacutenicasenglobadas bajo las siglas de REST y que utilizan estaacutendares Web tales como URIs HTTPXML y JSON

El API Java para servicios Web RESTful (JAX-RS) permite desarrollar servicios Web RESTfulde forma sencilla La versioacuten maacutes reciente del API es la 20 cuya especificacioacuten estaacute publicadaen el documento JSR-339 y que podemos descargar desde httpsjcporgenjsrdetailid=339 A lo largo de estas sesiones veremos coacutemo utilizar JAX-RS para desarrollar serviciosWeb RESTful Dicho API utiliza anotaciones Java para reducir los esfuerzos de programacioacutende los servicios

12 Fundamentos de REST

El teacutermino REST proviene de la tesis doctoral de Roy Fielding publicada en el antildeo 2000y significa REpresentational State Transfer (podemos acceder a la tesis original en httpwwwicsuciedu~fieldingpubsdissertationtophtm) REST es un conjunto de restriccionesque cuando son aplicadas al disentildeo de un sistema crean un estilo arquitectoacutenico de softwareDicho estilo arquitectoacutenico se caracteriza por seguir los siguientes principios

bull Debe ser un sistema cliente-servidor

bull Tiene que ser sin estado es decir no hay necesidad de que los servicios guarden lassesiones de los usuarios (cada peticioacuten al servicio tiene que ser independiente de lasdemaacutes)

bull Debe soportar un sistema de cacheacutes la infraestructura de la red deberiacutea soportar cacheacuteen diferentes niveles

Servicios Rest

6

bull Debe ser un sistema uniformemente accesible (con una interfaz uniforme) Estarestriccioacuten define coacutemo debe ser la interfaz entre clientes y servidores La idea es simplificary desacoplar la arquitectura permitiendo que cada una de sus partes puede evolucionarde forma independiente Una interfaz uniforme se debe caracterizar por

Estar basada en recursos La abstraccioacuten utilizada para representar la informacioacuten ylos datos en REST es el recurso y cada recurso debe poder ser accedido medianteuna URI (Uniform Resource Identifier)

Orientada a representaciones La interaccioacuten con los servicios tiene lugar atraveacutes de las representaciones de los recursos que conforman dicho servicio Unrecurso referenciado por una URI puede tener diferentes formatos (representaciones)Diferentes plataformas requieren formatos diferentes Por ejemplo los navegadoresnecesitan HTML JavaScript requiere JSON (JavaScript Object Notation) y unaaplicacioacuten Java puede necesitar XML

Interfaz restringida Se utiliza un pequentildeo conjunto de meacutetodos bien definidos paramanipular los recursos

Uso de mensajes auto-descriptivos cada mensaje debe incluir la suficienteinformacioacuten como para describir coacutemo procesar el mensaje Por ejemplo se puedeindicar coacutemo parsear el mensaje indicando el tipo de contenido del mismo (xml htmltextohellip)

Uso de Hipermedia como maacutequina de estados de la aplicacion (HATEOAS) Lospropios formatos de los datos son los que dirigen las transiciones entre estados dela aplicacioacuten Como veremos maacutes adelante con maacutes detalle el uso de HATEOAS(Hypermedia As The Engine Of Application State) va a permitir transferir de formaexpliacutecita el estado de la aplicacion en los mensajes intercambiados y por lo tantorealizar interacciones con estado

bull Tiene que ser un sistema por capas un cliente no puede discernir si estaacute accediendodirectamente al servidor o a alguacuten intermediario Las capas intermedias van a permitirsoportar la escalabilidad asiacute como reforzar las poliacuteticas de seguridad

A continuacioacuten analizaremos algunas de las abstracciones que constituyen un sistemaRESTful recursos representaciones URIs y los tipos de peticiones HTTP que constituyen lainterfaz uniforme utilizada en las transferencias clienteservidor

Recursos

Un recurso REST es cualquier cosa que sea direccionable (y por lo tanto accesible) a traveacutesde la Web Por direccionable nos referimos a recursos que puedan ser accedidos y transferidosentre clientes y servidores Por lo tanto un recurso es una correspondencia loacutegica ytemporal con un concepto en el dominio del problema para el cual estamos implementandouna solucioacuten

Algunos ejemplos de recursos REST son

bull Una noticia de un perioacutedico

bull La temperatura de Alicante a las 400pm

bull Un valor de IVA almacenado en una base de datos

bull Una lista con el historial de las revisiones de coacutedigo en un sistema CVS

bull Un estudiante en alguna aula de alguna universidad

Servicios Rest

7

bull El resultado de una buacutesqueda de un iacutetem particular en Google

Aun cuando el mapeado de un recurso es uacutenico diferentes peticiones a un recursopueden devolver la misma representacioacuten binaria almacenada en el servidor Por ejemploconsideremos un recurso en el contexto de un sistema de publicaciones En este casouna peticioacuten de la uacuteltima revisioacuten publicada y la peticioacuten de la revisioacuten nuacutemero 12 enalguacuten momento de tiempo pueden devolver la misma representacioacuten del recurso cuandola uacuteltima revisioacuten sea efectivamente la 12 Por lo tanto cuando la uacuteltima revisioacuten publicadase incremente a la versioacuten 13 una peticioacuten a la uacuteltima revisioacuten devolveraacute la versioacuten 13 yuna peticioacuten de la revisioacuten 12 continuaraacute devolviendo la versioacuten 12 En definitiva cada unode los recursos puede ser accedido directamente y de forma independiente pero diferentespeticiones podriacutean apuntar al mismo dato

Debido a que estamos utilizando HTTP para comunicarnos podemos transferir cualquier tipode informacioacuten que pueda transportarse entre clientes y servidores Por ejemplo si realizamosuna peticioacuten de un fichero de texto de la CNN nuestro navegador mostraraacute un fichero de textoSi solicitamos una peliacutecula flash a YouTube nuestro navegador recibiraacute una peliacutecula flash Enambos casos los datos son transferidos sobre TCPIP y el navegador conoce coacutemo interpretarlos streams binarios debido a la cabecera de respuesta del protocolo HTTP Content-Type Porlo tanto en un sistema RESTful la representacioacuten de un recurso depende del tipo deseado porel cliente (tipo MIME) el cual estaacute especificado en la peticioacuten del protocolo de comunicaciones

Representacioacuten de los recursos

La representacioacuten de los recursos es lo que se enviacutea entre los servidores y clientes Unarepresentacioacuten muestra el estado temporal del dato real almacenado en alguacuten dispositivo dealmacenamiento en el momento de la peticioacuten En teacuterminos generales es un stream binariojuntamente con los metadatos que describen coacutemo dicho stream debe ser consumido por elcliente yo servidor (los metadatos tambieacuten puden contener informacioacuten extra sobre el recursocomo por ejemplo informacioacuten de validacioacuten y encriptacioacuten o coacutedigo extra para ser ejecutadodinaacutemicamente)

A traveacutes del ciclo de vida de un servicio web pueden haber varios clientes solicitando recursosClientes diferentes son capaces de consumir diferentes representaciones del mismo recursoPor lo tanto una representacioacuten puede tener varias formas como por ejemplo una imagen untexto un fichero XML o un fichero JSON pero tienen que estar disponibles en la misma URL

Para respuestas generadas para humanos a traveacutes de un navegador una representacioacutentiacutepica tiene la forma de paacutegina HTML Para respuestas automaacuteticas de otros servicios web lalegibilidad no es importante y puede utilizarse una representacioacuten mucho maacutes eficiente comopor ejemplo XML

El lenguaje para el intercambio de informacioacuten con el servicio queda a eleccioacuten deldesarrollador A continuacioacuten mostramos algunos formatos comunes que podemos utilizarpara intercambiar esta informacioacuten

Table 1 Ejemplos de formatos utilizados por los servicios REST

Formato Tipo MIME

Texto plano textplain

HTML texthtml

XML applicationxml

JSON applicationjson

Servicios Rest

8

De especial intereacutes es el formato JSON Se trata de un lenguaje ligero de intercambio deinformacioacuten que puede utilizarse en lugar de XML (que resulta considerablemente maacutespesado) para aplicaciones AJAX De hecho en Javascript puede leerse este tipo de formatosimplemente utilizando el meacutetodo eval()

Direccionabilidad de los recursos URI

Una URI o Uniform Resource Identifier en un servicio web RESTful es un hiper-enlace aun recurso y es la uacutenica forma de intercambiar representaciones entre clientes y servidoresUn servicio web RESTful expone un conjunto de recursos que identifican los objetivos de lainteraccioacuten con sus clientes

El conjunto de restricciones REST no impone que las URIs deban ser hiper-enlacesSimplemente hablamos de hiper-enlaces porque estamos utilizando la Web para crearservicios web Si estuvieacutesemos utilizando un conjunto diferente de tecnologiacuteas soportadasuna URI RESTful podriacutea ser algo completamente diferente Sin embargo la idea dedireccionabilidad debe permanecer

En un sistema REST la URI no cambia a lo largo del tiempo ya que la implementacioacutende la arquitectura es la que gestiona los servicios localiza los recursos negocia lasrepresentaciones y enviacutea respuestas con los recursos solicitados Y lo que es maacutes importantesi hubiese un cambio en la estructura del dispositivo de almacenamiento en el lado del servidor(por ejemplo un cambio de servidores de bases de datos) nuestras URIs seguiraacuten siendo lasmismas y seraacuten vaacutelidas mientras el servicio web siga estando en marcha o el contexto delrecurso no cambie

Sin las restricciones REST los recursos se acceden por su localizacioacutenlas direcciones web tiacutepicas son URIs fijas Si por ejemplo renombramosun fichero en el servidor la URI seraacute diferente si movemos el fichero a undirectorio diferente la URI tambieacuten seraacute diferente

El formato de una URI se estandariza como sigue

schemehostportpathqueryStringfragment

En donde

bull scheme es el protocolo que estamos utilizando para comunicarnos con el servidor Paraservicios REST normalmente el protocolo seraacute http o https

bull El teacutermino host es un nombre DNS o una direccioacuten IP

bull A continuacioacuten se puede indicar de forma opcional un puerto (mediante port ) que es unvalor numeacuterico El host y el port representan la localizacioacuten de nuestro recurso en la red

bull Seguidamente aparece una expresioacuten path que es un conjunto de segmentos de textodelimitados por el caraacutecter (pensemos en la expresioacuten path como en una lista dedirectorios de un fichero en nuestra maacutequina)

bull Esta expresioacuten puede ir seguida opcionalmente por una queryString El caraacutecter )separa el path de la queryString Esta uacuteltima es una lista de paraacutemetros representadoscomo pares nombrevalor Cada par estaacute delimitado por el caraacutecter amp

La uacuteltima parte de la URI es el fragment delimitado por el caraacutecter Normalmente seutiliza para apuntar a cierto lugar del documento al que estamos accediendo

Servicios Rest

9

En una URI no todos los caracteres estaacuten permitidos de forma que algunos caracteres secodificaraacuten de acuerdo a las siguientes reglas

bull Los caracteres a-z A-Z 0-9 - y _ permanecen igual

bull El caracter espacio se convierte en el caraacutecter +

bull El resto de caracteres se codifican como una secuencia de bits siguiendo un esquema decodificacioacuten hexadecimal de forma que cada dos diacutegitos hexadecimales van precedidospor el caraacutecter

Un ejemplo de URI podriacutea ser eacuteste

httpexpertojavauaesrecursosclientesapellido=MartinezampcodPostal=02115

En el ejemplo anterior el host viene dado por expertojavauaes el path o ruta de acceso alrecurso es recursosclientes y hemos especificado los paraacutemetros apellido y codPostal conlos valores Martinez y 02115 respectivamente

Si por ejemplo en nuestra aplicacioacuten tenemos informacioacuten de clientes podriacuteamos acceder ala lista correspondiente mediante una URL como la siguiente

httpexpertojavauaesrecursosclientes

Esto nos devolveraacute la lista de clientes en el formato que el desarrollador del servicio hayadecidido Hay que destacar por lo tanto que en este caso debe haber un entendimiento entreel consumidor y el productor del servicio de forma que el primero comprenda el lenguajeutilizado por el segundo

La URL anterior nos podriacutea devolver un documento como el siguiente

ltxml version=10gtltclientesgt ltclientegthttpexpertojavauaesrecursosclientes1ltclientegt ltclientegthttpexpertojavauaesrecursosclientes2ltclientegt ltclientegthttpexpertojavauaesrecursosclientes4ltclientegt ltclientegthttpexpertojavauaesrecursosclientes6ltclientegtltclientesgt

En este documento se muestra la lista de clientes registrados en la aplicacioacuten cada uno deellos representado tambieacuten por una URL Accediendo a estas URLs a su vez podremosobtener informacioacuten sobre cada curso concreto o bien modificarlo

Uniformidad y restricciones de las interfaces

Ya hemos introducido los conceptos de recursos y sus representaciones Hemos dichoque los recursos son correspondencias (mappings) de los estados reales de las entidadesque son intercambiados entre los clientes y servidores Tambieacuten hemos dicho que lasrepresentaciones son negociadas entre los clientes y servidores a traveacutes del protocolo decomunicacioacuten en tiempo de ejecucioacuten (a traveacutes de HTTP) A continuacioacuten veremos con detalle

Servicios Rest

10

lo que significa el intercambio de estas representaciones y lo que implica para los clientes yservidores el realizar acciones sobre dichos recursos

El desarrollo de servicios web REST es similar al desarrollo de aplicaciones web Sin embargola diferencia fundamental entre el desarrollo de aplicaciones web tradicionales y las maacutesmodernas es coacutemo pensamos sobre las acciones a realizar sobre nuestras abstraccionesde datos De forma maacutes concreta el desarrollo moderno estaacute centrado en el concepto denombres (intercambio de recursos) el desarrollo tradicional estaacute centrado en el conceptode verbos (acciones remotas a realizar sobre los datos) Con la primera forma estamosimplementando un servicio web RESTful con la segunda un servicio similar a una llamada aprocedimiento remoto- RPC) Y lo que es maacutes un servicio RESTful modifica el estado delos datos a traveacutes de la representacioacuten de los recursos (por el contrario una llamada aun servicio RPC oculta la representacioacuten de los datos y en su lugar enviacutea comandos paramodificar el estado de los datos en el lado del servidor) Finalmente en el desarrollo modernode aplicaciones web limitamos la ambiguumledad en el disentildeo y la implementacioacuten debido aque tenemos cuatro acciones especiacuteficas que podemos realizar sobre los recursos CreateRetrieve Update y Delete (CRUD) Por otro lado en el desarrollo tradicional de aplicacionesweb podemos tener otras acciones con nombres o implementaciones no estaacutendar

A continuacioacuten indicamos la correspondencia entre las acciones CRUD sobre los datos y losmeacutetodos HTTP asociados

Table 2 Operaciones REST sobre los recursos

Accioacuten sobre los datos Protocolo HTTP equivalente

CREATE POST

RETRIEVE GET

UPDATE PUT

DELETE DELETE

El principio de uniformidad de la interfaz de acceso a recursos es fundamental y quizaacute el maacutesdifiacutecil de seguir por los programadores acostumbrados al modelo RPC (Remote ProcedureCall) La idea subyacente es utilizar uacutenicamente un conjunto finito y claramente establecidode operaciones para la interaccioacuten con los servicios Esto significa que no tendremos unparaacutemetro accioacuten en nuestra URI y que soacutelo utilizaremos los meacutetodos HTTP para accedera nuestros servicios Cada uno de los meacutetodos tiene un propoacutesito y significado especiacuteficosque mostramos a continuacioacuten

GETGET es una operacioacuten soacutelo de lectura Se utiliza para recuperar informacioacuten especiacuteficadel servidor Tambieacuten se trata de una operacioacuten idempotente y segura Idempotentesignifica que no importa cuaacutentas veces invoquemos esta operacioacuten el resultado (queobservaremos como usuarios) debe ser siempre el mismo Segura significa que unaoperacioacuten GET no cambia el estado del servidor en modo alguno es decir no debe exhibirninguacuten efecto lateral en el servidor Por ejemplo el hecho de leer un documento HTMLno deberiacutea cambiar el estado de dicho documento

PUTLa operacioacuten PUT solicita al servidor el almacenar el cuerpo del mensaje enviado condicha operacioacuten en la direccioacuten proporcionada en el mensaje HTTP Normalmente semodela como una insercioacuten o actualizacioacuten (nosotros la utilizaremos solamente comoactualizacioacuten) Es una propiedad idempotente Cuando se utiliza PUT el cliente conoce

Servicios Rest

11

la identidad del recurso que estaacute creando o actualizando Es idempotente porque enviar elmismo mensaje PUT maacutes de una vez no tiene ninguacuten efecto sobre el servicio subyacenteUna analogiacutea podriacutea ser un documento de texto que estemos editando No importa cuaacutentasveces pulsemos el botoacuten de grabar el fichero que contiene el documento loacutegicamenteseraacute el mismo documento

DELETEEsta operacioacuten se utiliza para eliminar recursos Tambieacuten es idempotente

POSTPost es la uacutenica operacioacuten HTTP que no es idempotente ni segura Cada peticioacuten POSTpuede modificar el servicio de forma exclusiva Se puede enviar o no informacioacutencon la peticioacuten Tambieacuten podemos recibir o no informacioacuten con la respuesta Paraimplementar servicios REST es deseable enviar informacioacuten con la peticioacuten y tambieacutenrecibir informacioacuten con la respuesta

Adicionalmente podemos utilizar otras dos operaciones HTTP

HEADEs una operacioacuten exactamente igual que GET excepto que en lugar de devolver un cuerpode mensaje solamente devuelve un coacutedigo de respuesta y alguna cabecera asociada conla peticioacuten

OPTIONSSe utiliza para solicitar informacioacuten sobre las opciones disponibles sobre un recurso en elque estamos interesados Esto permite al cliente determinar las capacidades del servidory del recurso sin tener que realizar ninguna peticioacuten que provoque una accioacuten sobre elrecurso o la recuperacioacuten del mismo

PATCHSe utiliza para para realiza reemplazos (actualizaciones) parciales de un documento yaque la operacioacuten PUT soacutelo permite una actualizacioacuten completa del recurso (y requiereindicar una representacioacuten completa del mismo) Es uacutetil cuando el recurso a modificares complejo y solamente queremos actualizar parte de su contenido En este caso solonecesitamos indicar la parte que queremos cambiar

13 Disentildeo de servicios Web RESTful

El disentildeo de servicios RESTful no es muy diferente del disentildeo de aplicaciones webtradicionales tenemos requerimientos de negocio tenemos usuarios que quieren realizaroperaciones sobre los datos y tenemos restricciones hardware que van a condicionar nuestraarquitectura software La principal diferencia reside en el hecho de que tenemos que buscara partir de los requerimientos cuaacuteles van a ser los recursos que van a ser accedidos a traveacutesde los servicios sin preocuparnos de queacute operaciones o acciones especiacuteficas van a poderserealizar sobre dichos recursos (el proceso de disentildeo depende de los nombres no de losverbos)

Podemos resumir los principios de disentildeo de servicios web RESTful en los siguientes cuatropasos

1 Elicitacioacuten de requerimientos y creacioacuten del modelo de objetos Este paso es similar aldisentildeo orientado a objetos El resultado del proceso puede ser un modelo de clases UML

2 Identificacioacuten de recursos Este paso consiste en identificar los objetos de nuestromodelo sin preocuparnos de las operaciones concretas a realizar sobre dichos objetos

Servicios Rest

12

3 Definicioacuten de las URIs Para satisfacer el principio de direccionabilidad de los recursostendremos que definir las URIs que representaraacuten los endpoints de nuestros servicios yque constituiraacuten los puntos de entrada de los mismos

4 Definicioacuten de la representacioacuten de los recursos Puesto que los sistemas REST estaacutenorientados a la representacioacuten tendremos que definir el formato de los datos queutilizaremos para intercambiar informacioacuten entre nuestros servicios y clientes

5 Definicioacuten de los meacutetodos de acceso a los recursos Finalmente tendremos que decidirqueacute meacutetodos HTTP nos permitiraacuten acceder a las URIs que queremos exponer asiacutecomo queacute haraacute cada meacutetodo Es muy importante que en este paso nos cintildeamos alas restricciones que definen los principios RESTful que hemos indicado en apartadosanteriores

14 Un primer servicio JAX-RS

Vamos a ilustrar los pasos anteriores con un ejemplo concretamente definiremos una interfazRESTful para un sistema sencillo de gestioacuten de pedidos de un hipoteacutetico comercio por internetLos potenciales clientes de nuestro sistema podraacuten realizar compras modificar pedidosexistentes en nuestro sistema asiacute como visualizar sus datos personales o la informacioacuten sobrelos productos que son ofertados por el comercio

Modelo de objetos

A partir de los requerimientos del sistema obtenemos el modelo de objetos El modelode objetos de nuestro sistema de ventas por internet es bastante sencillo Cada pedidoen el sistema representa una uacutenica transaccioacuten de compra y estaacute asociada con un clienteparticular Los pedidos estaraacuten formados por una o maacutes liacuteneas de pedido Las liacuteneas de pedidorepresentan el tipo y el nuacutemero de unidades del producto adquirido

Basaacutendonos en esta descripcioacuten de nuestro sistema podemos extraer que los objetos denuestro modelo son Pedido Cliente LineaPedido y Producto Cada objeto de nuestromodelo tiene un identificador uacutenico representado por la propiedad id dada por un valor detipo entero La siguiente figura muestra un diagrama UML de nuestro modelo

Estamos interesados en consultar todos los pedidos realizados asiacute como cada pedido deforma individual Tambieacuten queremos poder realizar nuevos pedidos asiacute como actualizar

Servicios Rest

13

pedidos existentes El objeto ServicioPedidos representa las operaciones que queremosrealizar sobre nuestos objetos Pedido Cliente LineaPedido y Producto

Modelado de URIs

Lo primero que haremos para crear nuestra interfaz distribuida es definir y poner nombre acada uno de los endpoints de nuestro sistema En un sistemam RESTful los endpoints seraacutenlos recursos del sistema que identificaremos mediante URIs

En nuestro modelo de objetos queremos poder interactuar con Pedidos Clientes y ProductosEacutestos seraacuten por lo tanto nuestros recursos de nivel maacutes alto Por otro lado estamosinteresados en obtener una lista de cada uno de estos elementos de alto nivel asiacute comointeractuar con los elementos indiduales de cada tipo El objeto LineaPedido es un objetoagregado del objeto Pedido por lo que no lo consideraremos com un recurso de nivel superiorMaacutes adelante veremos que podremos exponerlo como un subrecurso de un Pedido particularpero por ahora asumiremos que estaacute oculto por el formato de nuestros datos Seguacuten estouna posible lista de URIs que expondraacute nuestro sistema podriacutea ser

bull pedidos

bull pedidosid

bull productos

bull productosid

bull clientes

bull clientesid

Fiacutejate que hemos representado como URIs los nombres en nuestromodelo de objetos Recuerda que las URIS no deberiacutean utilizarse comomini-mecanismos de RPC ni deberiacutean identificar operaciones En vez deeso tenemos que utilizar una combinacioacuten de meacutetodos HTTP y de datos(recursos) para modelar las operaciones de nuestro sistema RESTful

Definicioacuten del formato de datos

Una de las cosas maacutes importantes que tenemos que hacer cuando definimos la interfazRESTful es determinar coacutemo se representaraacuten los recursos que seraacuten accedidos por losusuarios de nuestro API REST Quizaacute XML sea el formato maacutes popular de la web y puede serprocesado por la mayor parte de los lenguajes de programacioacuten Como veremos maacutes adelanteJSON es otro formato popular menos verboso que XML y que puede ser interpretadodirectamente por JavaScript (lo cual es perfecto para aplicaciones Ajax por ejemplo) Porahora utilizaremos el formato XML en nuestro ejemplo

Generalmente tendriacuteamos que definir un esquema XML para cada representacioacuten quequeramos transimitir a traves de la red Un esquema XML define la gramaacutetica del formato dedatos Por simplicidad vamos a omitir la creacioacuten de esquemas asumiendo que los ejemplosque proporcionamos se adhieren a sus correspondientes esquemas

A continuacioacuten distinguiremos entre dos formatos de datos uno para las operaciones delectura y actualizacioacuten y otro para la operacioacuten de creacioacuten de recursos

Formato de datos para operaciones de lectura y modificacioacuten de los recursos

Las representaciones de los recursos Pedido Cliente y Producto tendraacuten un elemento XMLen comuacuten al que denominaremos link

Servicios Rest

14

ltlink rel=self href=httporgexpertojavagt

El elemento (o etiqueta) link indica a los clientes que obtengan un documento XML comorepresentacioacuten del recurso doacutende pueden interaccionar en la red con dicho recurso enparticular El atributo self le indica al cliente queacute relacioacuten tiene dicho enlace con la URIdel recurso al que apunta (informacioacuten contenida en el atributo href ) El valor self indicaque estaacute apuntando a siacute mismo Maacutes adelante veremos la utilidad del elemento link cuandoagreguemos informacioacuten en documentos XML maacutes grandes

El formato de representacioacuten del recurso Cliente podriacutea ser

ltcliente id=8gt ltlink rel=self href=httporgexpertojavaclientes8gt ltnombregtPedroltnombregt ltapellidosgtGarcia Perezltapellidosgt ltdirecciongtCalle del Pino 5ltdirecciongt ltcodPostalgt08888ltcodPostalgt ltciudadgtMadridltciudadgtltclientegt

El formato de representacioacuten del recurso Producto podriacutea ser

ltproducto id=34gt ltlink rel=self href=httporgexpertojavaproductos34gt ltnombregtiPhone 6ltnombregt ltpreciogt800ltpreciogt ltcantidadgt1ltcantidadgtltproductogt

Finalmente el formato de la representacioacuten del recurso Pedido podriacutea ser

ltpedido id=233gt ltlink rel=self href=httporgexpertojavapedidos233gt lttotalgt800lttotalgt ltfechagtDecember 22 2014 0656ltfechagt ltcliente id=8gt ltlink rel=self href=httporgexpertojavaclientes8gt ltnombregtPedroltnombregt ltapellidosgtGarcia Perezltapellidosgt ltdirecciongtCalle del Pino 5ltdirecciongt ltcodPostalgt08888ltcodPostalgt ltciudadgtMadridltciudadgt ltclientegt ltlineasPedidogt ltlineaPedido id=1gt ltproducto id=34gt ltlink rel=self href=httporgexpertojavaproductos34gt ltnombregtiPhone 6ltnombregt

Servicios Rest

15

ltpreciogt800ltpreciogt ltcantidadgt1ltcantidadgt ltproductogt ltlineaPedidogt ltlineasPedidogtltpedidogt

El formato de datos de un Pedido tiene en un primer nivel la informacioacuten del total conel importe total del pedido asiacute como la fecha en la que se hizo dicho pedido Pedido es unbuen ejemplo de composicioacuten de datos ya que un pedido incluye informacioacuten sobre el Clientey el Productos Aquiacute es donde el elemento ltlinkgt puede resultar particularmente uacutetil Si elusuario estaacute interesado en interaccionar con el Cliente que ha realizado el pedido o en uno delos productos del mismo se proporciona la URI necesaria para interactuar con cada uno dedichos recursos De esta forma cuando el usuario del API consulte un pedido podraacute ademaacutesacceder a informacioacuten adicional relacionada con la consulta realizada

Formato de datos para operaciones de creacioacuten de los recursos

Cuando estamos creando nuevos Pedidos Clientes o Productos no tiene mucho sentidoincluir un atributo id y un elemento link en nuestro documento XML El servidor seraacute elencargado de crear los ids cuando inserte nuestro nuevo objeto en la base de datos Tampococonocemos la URI del nuevo objeto creado ya que seraacute el servidor el encargado de generarloPor lo tanto para crear un nuevo Producto el formato de la informacioacuten podriacutea ser el siguiente

ltproductogt ltlink rel=self href=httporgexpertojavaclientes8gt ltnombregtiPhoneltnombregt ltpreciogt800ltpreciogtltproductogt

Asignacioacuten de meacutetodos HTTP

Finalmente tendremos que decidir queacute meacutetodos HTTP expondremos en nuestro servicio paracada uno de los recursos asiacute como definir queacute haraacuten dichos meacutetodos Es muy importanteno asignar funcionaldad a un meacutetodo HTTP que sobrepase los liacutemites impuestos por laespecificacioacuten de dicho meacutetodo Por ejemplo una operacioacuten GET sobre un recurso concretodeberiacutea ser de soacutelo lectura No deberiacutea cambiar el estado del recurso cuando invoquemos laoperacioacuten GET sobre eacutel Si no seguimos de forma estricta la semaacutentica de los meacutetodos HTTPlos clientes asiacute como cualquier otra herramienta administrativa no pueden hacer asuncionessobre nuestros servicios de forma que nuestro sistema se vuelve maacutes complejo

Veamos para cada uno de los meacutetodos de nuestro modelo de objetos cuales seraacuten las URIsy meacutetodos HTTP que usaremos para representarlos

Visualizacioacuten de todos los Pedidos Clientes o Productos

Los tres objetos de nuestro modelo Pedidos Clientes y Productos son accedidos ymanipulados de forma similar Los usuarios pueden estar interesados en ver todos losPedidos Clientes o Productos en el sistema Las siguientes URIs representan dichos objetoscomo un grupo

bull pedidos

Servicios Rest

16

bull productos

bull clientes

Para obtener una lista de Pedidos Clientes o Productos el cliente remoto realizara unallamada al meacutetodo HTTP GET sobre la URI que representa el grupo de objetos Un ejemplode peticioacuten podriacutea ser la siguiente

GET productos HTTP11

Nuestro servicio responderaacute con los datos que representan todos los Pedidos de nuestrosistema Una respuesta podriacutea ser eacutesta

HTTP11 200 OKContent-Type applicationxml

ltproductosgt ltproducto id=111gt ltlink rel=self href=httporgexpertojavaproductos111gt ltnombregtiPhoneltnombregt ltpreciogt64899ltpreciogt ltproductogt ltproducto id=222gt ltlink rel=self href=httporgexpertojavaproductos222gt ltnombregtMacbookltnombregt ltpreciogt159999ltpreciogt ltproductogt ltproductosgt

Un problema que puede darse con esta peticioacuten es que tengamos miles de Pedidos Clienteso Productos en nuestro sistema por lo que podemos sobrecargar a nuestro cliente y afectarnegativamente a los tiempos de respuesta Para mitigar esta problema permitiremos que elusuario especifique unos paraacutemetros en la URI para limitar el tamantildeo del conjunto de datosque se va a devolver

GET pedidosstartIndex=0ampsize=5 HTTP11GET productosstartIndex=0ampsize=5 HTTP11GET clientesstartIndex=0ampsize=5 HTTP11

En las oacuterdenes anteriores hemos definido dos paraacutemetros de peticioacuten startIndex ysize El primero de ellos es un iacutendice numeacuterico que representa a partir de queacute posicioacuten enla lista de Pedidos Clientes o Productos comenzaremos a enviar la informacioacuten al clienteEl paraacutemetro size especifica cuaacutentos de estos objetos de la lista queremos que nos seandevueltos

Estos paraacutemetros seraacuten opcionales de forma que el cliente no tiene que especificarlos en suURI

Obtencioacuten de Pedidos Clientes o Productos individuales

Ya hemos comentado previamente que podriacuteamos utilizar las siguientes URIs para obtenerPedidos Clientes o Productos

Servicios Rest

17

bull pedidosid

bull productosid

bull clientesid

En este caso usaremos el meacutetodo HTTP GET para recuperar objetos individuales en elsistema Cada invocacioacuten GET devolveraacute la informacioacuten del correspondiente objeto Porejemplo

GET pedidos233 HTTP11

Para esta peticioacuten el cliente estaacute interesado en obtener una representacioacuten del Pedido conidentificador 233 Las peticiones GET para Productos y Clientes podriacutean funcionar de formasimilar El mensaje de respuesta podriacutea parecerse a algo como esto

HTTP11 200 OKContent-Type applicationxml

ltpedido id=233gtltpedidogt

El coacutedigo de respuesta es 200 OK indicando que la peticioacuten ha tenido eacutexito La cabeceraContent-Type especifica el formato del cuerpo de nuestro mensaje como XML y finalmenteobtenemos la representacioacuten del Pedido solicitado

Creacioacuten de un Pedido Cliente o Producto

Para crear un Pedido Cliente o Producto utilizaremos el meacutetodo POST En este caso el clienteenviacutea una representacioacuten del nuevo objeto que se prentende crear a la URI padre de surepresentacioacuten y por lo tanto podremos omitir el identificador del recurso Por ejemplo

Peticioacuten POST para crear un pedidio

POST pedidos HTTP11Content-Type applicationxml

ltpedidogt lttotalgt19902lttotalgt ltfechagtDecember 22 2008 0656ltfechagt ltpedidogt

El servicio recibe el mensaje POST procesa la XML y crea un nuevo pedido en la basede datos utilizando un identificador generado de forma uacutenica Si bien esta aproximacioacutenfunciona perfectamente se le pueden plantear varias cuestiones al usuario iquestQueacute ocurre siel usuario quiere visualizar modificar o eliminar el pedido que acaba de crear iquestCuaacutel es elidentificador del nuevo recurso iquestCuaacutel es la URI que podemos utilizar para interactuar con elnuevo recurso Para resolver estas cuestiones antildeadiremos alguna informacioacuten al mensajede respuesta HTTP El cliente podriacutea recibir un mensaje similar a eacuteste

Respuesta de una peticioacuten POST para crear un pedido

HTTP11 201 Created

Servicios Rest

18

Content-Type applicationxmlLocation httporgexpertojavapedidos233

ltpedido id=233gt ltlink rel=self href=httporgexpertojavapedidos233gt lttotalgt19902lttotalgt ltfechagtDecember 22 2008 0656ltfechagt ltpedidogt

HTTP requiere que si POST crea un nuevo recurso se debe responder con un coacutedigo 201Created Tambieacuten se requieer que la cabecera Location en el mensaje de respuestaproporcione una URI al usuario que ha hecho la peticioacuten para que eacuteste pueda interactuar conla Peticioacuten que acaba de crear (por ejemplo para modificar dicho Pedido) Es opcional porparte del servidor devolver en la respuesta la representacioacuten del nuevo recurso creado Ennuestro ejemplo optamos por devolver una representacioacuten XML de la Peticion creada con elidentificador del atributo asiacute como el elemento link

Actualizacioacuten de un Pedido Cliente o Producto

Para realizar modificaciones sobre los recursos que ya hemos creado utilizaremos el meacutetodoPUT En este caso un ejemplo de peticioacuten podriacutea ser eacutesta

Peticioacuten PUT para modificar un pedidio

PUT pedidos233 HTTP11Content-Type applicationxml

ltproducto id=111gt ltnombregtiPhoneltnombregt ltpreciogt64999ltpreciogtltproductogt

Tal y como he hemos indicado anteriormente la operacioacuten PUT es idempotente Lo quesignifica que no importa cuaacutentas veces solicitemos la peticioacuten PUT el producto subyacentesigue permaneciendo con el mismo estado final

Cuando un recurso se modifica mediante PUT la especificacioacuten HTTP requiere que el servidorenviacutee un coacutedigo de respuesta 200 OK y un cuerpo de mensaje de respuesta o bien el coacutedigo204 No Content sin ninguacuten cuerpo de mensaje en la respuesta

En nuestro caso devolveremos un coacutedigo de estado 204 y un mensaje sin cuerpo derespuesta

RECUERDA Es importante NO confundir POST con PUTMuchas veces se confunden los meacutetodos PUT y POST El significado deestos meacutetodos es el siguiente

bull POST Publica datos en un determinado recurso El recurso debe existirpreviamente y los datos enviados son antildeadidos a eacutel Por ejemplo paraantildeadir nuevos pedidos con POST hemos visto que debiacuteamos hacerlocon el recurso lista de pedidos (pedidos) ya que la URI del nuevopedido todaviacutea no existe La operacioacuten NO es idempotente es decir si

Servicios Rest

19

antildeadimos varias veces el mismo alumno apareceraacute repetido en nuestralista de pedidos con URIs distintas

bull PUT Hace que el recurso indicado tome como contenido los datosenviados El recurso podriacutea no existir previamente y en caso de queexistiese seriacutea sobrescrito con la nueva informacioacuten A diferencia dePOST PUT es idempotente Muacuteltiples llamadas ideacutenticas a la mismaaccioacuten PUT siempre dejaraacuten el recurso en el mismo estado Laaccioacuten se realiza sobre la URI concreta que queremos establecer (porejemplo pedidos215) de forma que varias llamadas consecutivas conlos mismos datos tendraacuten el mismo efecto que realizar soacutelo una deellas

Borrado de un Pedido Cliente o Producto

Modelaremos el borrado de los recursos utilizando el meacutetodo HTTP DELETE El usuariosimplemente invocaraacute el meacutetodo DELETE sobre la URI que representa el objeto que queremoseliminar Este meacutetodo haraacute que dicho recurso ya no exista en nuestro sistema

Cuando eliminamos un recurso con DELETE la especificacioacuten requiere que se enviacutee un coacutedigode respuesta 200 OK y un cuerpo de mensaje de respuesta o bien un coacutedigo de respuesta204 No Content sin un cuerpo de mensaje de respuesta

En nuestro caso devolveremos un coacutedigo de estado 204 y un mensaje sin cuerpo derespuesta

Cancelacioacuten de un Pedido

Hasta ahora las operaciones de nuestro modelo de objetos encajan bastante bien enla especificacioacuten de los correspondientes meacutetodos HTTP Hemos utilzado GET para leerdatos PUT para realizar modificaciones POST para crear nuevos recursos y DELETE paraeliminarlos En nuestro sistema los Pedidos pueden eliminarse o tambieacuten cancelarse Yahemos comentado que el borrado de un recurso lo elimina completamente de nuestra basede datos La operacioacuten de cancelacioacuten solamente cambia el estado del Pedido y lo siguemanteniendo en el sistema iquestCoacutemo podriacuteamos modelar esta operacioacuten

Cuando modelamos una interfaz RESTful para las operaciones de nuestro modelo de objetosdeberiacuteamos plantearnos la siguiente pregunta iquestla operacioacuten es un estado del recurso Sila respuesta es siacute entonces deberiacuteamos modelar esta operacioacuten dentro del formato de losdatos

La cancelacioacuten de un pedido es un ejemplo perfecto de esto que acabamos de decir La claveestaacute en que esta operacioacuten en realidad es un estado especiacutefico del Pedido eacuteste puede estarcancelado o no Cuando un usuario accede a un Pedido puede desear conocer si el Pedidoha sido o no cancelado Por lo tanto la informacioacuten sobre la cancelacioacuten deberiacutea formar partedel formato de datos de un Pedido Asiacute antildeadiremos un nuevo elemento a la informacioacuten delPedido

ltpedido id=233gt ltlink rel=self href=httporgexpertojavapedidos233gt lttotalgt19902lttotalgt ltfechagtDecember 22 2008 0656ltfechagt ltcanceladogtfalseltcanceladogt

Servicios Rest

20

ltpedidogt

Ya que el estado cancelado se modela en el propio formato de datos modelaremos la accioacutende cancelacioacuten con una operacioacuten HTTP PUT que ya conocemos

PUT pedidos233 HTTP11Content-Type applicationxml

ltpedido id=233gt ltlink rel=self href=httporgexpertojavapedidos233gt lttotalgt19902lttotalgt ltfechagtDecember 22 2008 0656ltfechagt ltcanceladogttrueltcanceladogt ltpedidogt

En este ejemplo modificamos la representacioacuten del Pedido con el elemento ltcanceladogtcon valor true

Este patroacuten de modelado no siempre sirve en todos los casos Por ejemplo imaginemosque queremos ampliar el sistema de forma que borremos del sistema todos los pedidoscancelados No podemos modelar esta operacioacuten de la misma manera que la de cancelacioacutenya que esta operacioacuten no cambia el estado de nuestra aplicacioacuten (no es en siacute misma un estadode la aplicacioacuten)

Para resolver este problema podemos modelar esta nueva operacioacuten como un subrecursode pedidos y realizar un borrado de los pedidos cancelados mediante el meacutetodo HTTP POSTde dicho subrecurso de la siguiente forma

POST pedidoseliminacion HTTP11

Un efecto interesante de lo que acabamos de hacer es que puesto que ahora eliminaciones una URI podemos hacer que la interfaz de nuestro servicios RESTful evolucionen conel tiempo Por ejemplo la orden GET pedidoseliminacion podriacutea devolver la uacuteltima fechaen la que se procedioacute a eliminar todos los pedidos cancelados asiacute como queacute pedidosfueron cancelados iquestY si queremos antildeadir alguacuten criterio a la hora de realizar el borradode pedidos cancelados Podriacuteamos introducir paraacutemetros para indicar que soacutelo queremoseliminar aquellos pedidos que esteacuten cancelados en una fecha anterior a una dada Comovemos podemos mantener una interfaz uniforme y centildeirnos a las operaciones HTTP tal ycomo estaacuten especificadas y a la vez dotar de una gran flexiblidad a la interfaz de nuestrosistema RESTful Hablaremos con maacutes detalle de los subrecursos en la siguiente sesioacuten

Implementacioacuten del servicio Creacioacuten del proyecto Maven

Vamos a utilizar Maven para crear la estructura del proyecto que contendraacute la implementacioacutende nuestro servicio Rest Inicialmente podemos utilizar el mismo arquetipo con el que habeacuteistrabajado en sesiones anteriores Y a continuacioacuten modificaremos la configuracioacuten del ficheropomxml para implementar nuestros servicios

Una opcioacuten es generar la estructura del proyecto directamente desde liacutenea de comandos Elcomando es el siguiente (recuerda que debes escribirlo en una misma liacutenea Los caracteres

Servicios Rest

21

que aparecen en el comando no forman parte del mismo simplemente indican que no sedebe pulsar el retorno de carro)

mvn --batch-mode archetypegenerate -DarchetypeGroupId=orgcodehausmojoarchetypes -DarchetypeArtifactId=webapp-javaee7 -DgroupId=orgexpertojava -DartifactId=ejemplo-rest

En donde

bull archetypeGroupId y archetypeArtifactId son los nombres del groupId yartifactId del arquetipo Maven que nos va a generar la plantilla para nuestro proyecto

bull groupId y artifactId son los nombres que asignamos como groupId y artifactId denuestro proyecto En este caso hemos elegido los valores orgexpertojava y ejemplo-restrespectivamente

Si utilizamos IntelliJ para crear el proyecto tenemos que

1 Crear un nuevo proyecto (New Project)

2 Elegir el tipo de proyecto Maven

3 Crear el proyecto Maven a partir de un arquetipo con las siguientes coordenadas

bull GroupId orgcodehausmojoarchetypes

bull ArtifactId webapp-javaee7

bull Version 11

4 Indicar las coordenadas de nuestro proyecto

bull GroupId orgexpertojava

bull ArtifactId ejemplo-rest

bull Version 10-SNAPSHOT

5 Confirmamos los datos introducidos

6 Para finalizar especificamos el nombre de nuestro proyecto en IntelliJ

bull Project Name ejemplo-rest (este valor tambieacuten identificaraacute el nombre del moacutedulo enIntelliJ)

7 Por comodidad marcaremos Enable autoimport para importar automaacuteticamente cualquiercambio en el proyecto

Una vez que hemos creado el proyecto con IntelliJ el paso siguiente es cambiar laconfiguracioacuten del pommxl que nos ha generado el arquetipo para incluir las propiedadesdependencias pluginshellip que necesitaremos para implementar nuestros recursos REST

Como ya sabemos el fichero pomxml contiene la configuracioacuten que utiliza Maven paraconstruir el proyecto A continuacioacuten indicamos las modificaciones en el fichero pomxmlgenerado inicialmente para adecuarlo a nuestras necesidades particulares

bull Cambiamos las propiedades del proyecto (etiqueta ltpropertiesgt ) por

Servicios Rest

22

Propiedades del proyecto

ltpropertiesgt ltprojectbuildsourceEncodinggtUTF-8ltprojectbuildsourceEncodinggtltpropertiesgt

bull Indicamos las dependencias del proyecto (etiqueta ltdependenciesgt en donde seincluyen las libreriacuteas necesarias para la construccioacuten del proyecto) En nuestro casonecesitamos incluir la libreriacutea javaxjavaee-web-api70 que contiene el apiestaacutendar de javaee 7 Marcamos el aacutembito de la libreriacutea (etiqueta ltscopegt ) comoprovided Con esto estamos indicando que soacutelo necesitaremos el jar correspondientepara compilar el proyecto y por lo tanto no incluiremos dicho jar en el fichero wargenerado para nuestra aplicacioacuten ya que dicha libreriacutea ya estaraacute disponible desde elservidor de aplicaciones en el que residiraacute nuestra aplicacioacuten

Libreriacuteas utilizadas para construir el proyecto (dependencias)

ltdependenciesgt ltdependencygt ltgroupIdgtjavaxltgroupIdgt ltartifactIdgtjavaee-web-apiltartifactIdgt ltversiongt70ltversiongt ltscopegtprovidedltscopegt ltdependencygtltdependenciesgt

bull A continuacioacuten configuramos la construccioacuten del proyecto (etiqueta ltbuildgt ) de lasiguiente forma (cambiamos la configuracioacuten original por la que mostramos a continuacioacuten)

Configuracioacuten de la construccioacuten del proyecto

ltbuildgt lt-- Especificamos el nombre del war que seraacute usado como context root cuando despleguemos la aplicacioacuten --gt ltfinalNamegt$projectartifactIdltfinalNamegt

ltpluginsgt lt-- Compilador de java Utilizaremos la versioacuten 17 --gt ltplugingt ltgroupIdgtorgapachemavenpluginsltgroupIdgt ltartifactIdgtmaven-compiler-pluginltartifactIdgt ltversiongt31ltversiongt ltconfigurationgt ltsourcegt17ltsourcegt lttargetgt17lttargetgt ltconfigurationgt ltplugingt

lt-- Servidor de aplicaciones wildfly --gt ltplugingt ltgroupIdgtorgwildflypluginsltgroupIdgt ltartifactIdgtwildfly-maven-pluginltartifactIdgt ltversiongt102Finalltversiongt ltconfigurationgt lthostnamegtlocalhostlthostnamegt

Servicios Rest

23

ltportgt9990ltportgt ltconfigurationgt ltplugingt

lt-- Cuando generamos el war no es necesario que el fichero webxml esteacute presente --gt ltplugingt ltgroupIdgtorgapachemavenpluginsltgroupIdgt ltartifactIdgtmaven-war-pluginltartifactIdgt ltversiongt23ltversiongt ltconfigurationgt ltfailOnMissingWebXmlgtfalseltfailOnMissingWebXmlgt ltconfigurationgt ltplugingt ltpluginsgtltbuildgt

Implementacioacuten del servicio Recursos JAX-RS

Una vez que tenemos la estructura del proyecto implementaremos los recursos de nuestraaplicacioacuten que seraacuten clases Java que utilizaraacuten anotaciones JAX-RS para enlazar y mapearpeticiones HTTP especiacuteficas a meacutetodos java los cuales serviraacuten dichas peticiones Eneste caso vamos a ilustrar con un ejemplo una posible implementacioacuten para el recursoCliente Tenemos que diferenciar entre las clases java que representaraacuten entidades denuestro dominio (objetos java que representan elementos de nuestro negocio y que seraacutenalmacenados tiacutepicamente en una base de datos) de nuestros recursos JAX-RS que tambieacutenseraacuten clases java anotadas y que utilizaraacuten objetos de nuestro dominio para llevar a cabo lasoperaciones expuestas en el API RESTful que hemos disentildeado

Asiacute por ejemplo implementaremos las clases

bull Clientejava representa una entidad del dominio Contiene atributos y suscorrespondientes getters y setters

bull ClienteResourcejava representa las operaciones RESTful sobre nuestro recurso Clienteque hemos definido en esta sesioacuten Es una clase java con anotaciones JAX-RS que nospermitiraacute insertar modificar borrar consultar un cliente asiacute como consultar la lista declientes de nuestro sistema

Clases de nuestro dominio (entidades) Clientejava

La clase que representa nuestra entidad del dominio Cliente es una clase java plana con suscorrespondientes atributos y getters y setters

Implementacioacuten del dominio clientejava

package orgexpertojavadomain

XmlRootElement(name=cliente)XmlAccessorType(XmlAccessTypeFIELD)public class Cliente private int id private String nombre private String apellidos private String direccion

Servicios Rest

24

private String codPostal private String ciudad

public int getId() return id public void setId(int id) thisid = id

public String getNombre() return nombre public void setNombre(String nom) thisnombre = nom

public String getApellidos() return apellidos public void setApellidos(String apellidos) thisapellidos = apellidos

public String getDireccion() return direccion public void setDireccion(String dir) thisdireccion = dir

public String getCodPostal() return codPostal public void setCodPostal(String cp) thiscodPostal = cp

public String getCiudad() return ciudad public void setCiudad(String ciudad) thisciudad = ciudad

Hemos anotado la clase Cliente con XmlRootElement y XmlAccessorType Hablaremos de estas anotaciones en sesiones posteriores las cuales se encargan delserializadodeserializado del cuerpo del mensaje (en formato xml o json) a nuestra clase javaCliente

Clases de nuestro servicio RESTful ClienteResourcejava

Una vez definido el objeto de nuestro dominio que representaraacute un objeto Cliente vamos a vercoacutemo implementar nuestros servicio JAX-RS para que diferentes usuarios de forma remotapuedan interactuar con nuestra base de datos de clientes

La implementacioacuten del servicio es lo que se denomina una resource class que no es maacutesque una clase java que utiliza anotaciones JAX-RS

Por defecto una nueva instancia de nuestra clase de recursos se crea para cada peticioacuten aese recurso Es lo que se conoce como un objeto per-request Esto implica que se crea unobjeto Java para procesar cada peticioacuten de entrada y se destruye automaacuteticamente cuandola peticioacuten se ha servido Per-request tambieacuten implica sin estado ya que no se guarda elestado del servicio entre peticiones

Comencemos con la implementacioacuten del servicio

package orgexpertojavaservices

import

Path(clientes)public class ClienteResource

private static MapltInteger Clientegt clienteDB = new ConcurrentHashMapltInteger Clientegt()

Servicios Rest

25

private static AtomicInteger idContador = new AtomicInteger()

Podemos observar que ClientResource es una clase java planay que no implementaninguna interfaz JAX-RS particular La anotacioacuten javaxwsrsPath indica que laclase ClienteResource es un servicio JAX-RS Todas las clases que queramos que seanreconocidas como servicios JAX-RS tienen que tener esta anotacioacuten Fiacutejate que estaanotacioacuten tiene el valor clientes Este valor representa la raiacutez relativa de la URI de nuestroservicio RESTful Si la URI absoluta de nuestro servidor es por ejemplo httpexpertojavaorglos meacutetodos expuestos por nuestra clase ClienteResource estariacutean disponibles bajo la URIhttpexpertojavaorgclientes

En nuestra clase definimos un Mapa para el campo ClienteDB quealmacenaraacute en memoria a los objetos Cliente de nuestro sistema Utilizamos unjavautilconcurrentConcurrentHashMap como tipo de clienteDB ya que nuestro recursoseraacute accedido concurrentemente por los usuarios de nuestro servicio rest El campoidContador lo utilizaremos para generar nuevos identificadores de nuestros objetos Clientecreados El tipo de este campo es javautilconcurrentatomicAtomicInteger para garantizarque siempre generaremos un identificador uacutenico aunque tengamos peticiones concurrentes

Justificacioacuten del caracter static de los atributosComo nuestros objetos seraacuten de tipo per-request el runtime de JAX-RScrearaacute una instancia de ClienteResource para cada pecioacuten que se realicesobre nuestro servicio La maacutequina virtual de java ejecutaraacute cada peticioacutena nuestro servicio en un hilo (thread) diferente permitiendo asiacute el accesoconcurrente a nuestro recurso Puesto que hemos decidido almacenaren memoria la informacioacuten de los clientes necesitamos que los atributosclienteDB y idContador sean static para que todas las instancias deClienteResource tengan acceso a la lista de clientes en memoria y nohaya problemas de concurrencia En realidad lo que estamos haciendocon eacutesto es permitir que el servicio guarde el estado entre peticiones Enun sistema real ClienteResource probablemente interactuacutee con una basede datos para almacenar y recuperar la informacioacuten de los clientes y porlo tanto no necesitaremos guardar el estado entre peticiones

Una mejor solucioacuten seriacutea no utilizar variables estaacuteticas y definir nuestroservicio como singleton Si hacemos eacutesto solamente se creariacutea unainstancia de clienteResource y estariacuteamos manteniendo el estado delas peticiones En la siguiente sesioacuten explicaremos coacutemo configurar unservicio como singleton Por simplicidad de momento optaremos por laopcioacuten de que los objetos RESTful sean per-request

Creacioacuten de clientes

Para implementar la creacioacuten de un nuevo cliente utilizamos el mismo modelo que hemosdisentildeado previamente Una peticioacuten HTTP POST enviacutea un documento XML que representaal cliente que queremos crear

El coacutedigo para crear nuevos clientes en nuestro sistema podriacutea ser eacuteste

POST

Consumes(applicationxml)

public Response crearCliente(Cliente cli)

Servicios Rest

26

el paraacutemetro cli se instancia con los datos del cliente del body del mensaje HTTP idContador++ clisetId(idContadorincrementAndGet())

clienteDBput(cligetId() cli)

Systemoutprintln(Cliente creado + cligetId()) return Responsecreated(URIcreate(clientes

+ cligetId()))build()

se recibe una peticioacuten POSTel cuerpo de la peticioacuten debe tener formato xmlcontiene la informacioacuten del documento xml del cuerpo de la peticioacuten de entradase antildeade el nuevo objeto Cliente a nuestro mapa de clientes (clienteDB)este meacutetodo se ejectua en el servidor por lo que el mensaje soacutelo seraacute visible por ejemplosi consultamos los mensajes generados por el servidor durante la ejecucioacuten el meacutetododevuelve un coacutedigo de respuesta 201 Created junto con una cabecera Locationapuntando a la URI absoluta del cliente que acabamos de crear

Vamos a explicar la implementacioacuten con maacutes detalle

Para enlazar peticiones HTTP POST con el meacutetodo crearCliente() lo anotamos conla anotacioacuten javaxwsrsPOST La anotacioacuten Path combinada con la anotacioacutenPOST enlaza todas las peticiones POST dirigidas a la URI relativa clientes al meacutetodo JavacrearCliente()

La anotacioacuten javaxwsrsConsumes aplicada a crearCliente() especifica queacute media typeespera el meacutetodo en el cuerpo del mensaje HTTP de entrada Si el cliente incluye en su peticioacutenPOST un media type diferente de XML se enviacutea un coacutedigo de error al cliente

El meacutetodo crearCliente() tiene un paraacutemetro de tipo Cliente En JAX-RS cualquier paraacutemetrono anotado con anotaciones JAX-RS se considera que es una representacioacuten del cuerpodel mensaje de la peticioacuten de entrada HTTP Las anotaciones que hemos introducido en laclase Cliente de nuestro dominio realizan el trabajo de convertir el documento xml contenidoen el cuerpo de la peticioacuten htpp de entrada en una instancia de nuestra clase Cliente

Solamente UNO de los paraacutemetros del meacutetodo Java puede representar elcuerpo del mensaje de la peticioacuten HTTP Esto significa que el resto deparaacutemetros deben anotarse con alguna anotacioacuten JAX-RS que veremosmaacutes adelante

El meacutetodo crearCliente() devuelve una respuesta de tipo javaxwsrscoreResponse El meacutetodo estaacutetico Responsecreated() crea un objeto Response que contiene un coacutedigode estado 201 Created Tambieacuten antildeade una cabecera Location con un valor similar ahttpexpertojavaorgclientes123 dependiendo del valor del valor de base de la raiacutez dela URI del servidor y el identificador generado para el objeto Cliente (en este caso se habriacuteagenerado el identificador 123) Maacutes adelante explicaremos con detalle el uso de la claseResponse

Consulta de clientes

A continuacioacuten mostramos un posible coacutedigo para consultar la informacioacuten de un cliente

GET

Servicios Rest

27

Path(id)Produces(applicationxml)public Cliente recuperarClienteId(PathParam(id) int id) final Cliente cli = clienteDBget(id) if (cli == null) throw new WebApplicationException(ResponseStatusNOT_FOUND) return new Cliente(cligetId() cligetNombre() cligetApellidos() cligetDireccion() cligetCodPostal() cligetCiudad())

En este caso anotamos el meacutetodo recuperarClienteId() con la anotacioacutenjavaxwsrsGET para enlazar las operaciones HTTP GET con este meacutetodo Java

Tambieacuten anotamos recuperarClienteId() con la anotacioacuten javaxwsrsPRODUCES Estaanotacioacuten indica a JAX-RS que valor tiene la cabecera HTTP Content-Type en la respuestaproporcionada por la operacioacuten GET En este caso estamos indicando que seraacute de tipoapplicationxml

En la implementacioacuten del meacutetodo utilizamos el paraacutemetro id para consultar si existe unobjeto Cliente en nuestro mapa clienteDB Si dicho cliente no existe lanzaremos la excepcioacutenjavaxwsrsWebApplictionException Esta excepcioacuten provocaraacute que el coacutedigo derespuesta HTTP sea 404 Not Found y significa que el recurso cliente requerido no existeDiscutiremos maacutes adelante el tema del manejo de excepciones

Modificacioacuten de clientes

Vamos a mostrar coacutemo seriacutea el coacutedigo para modificar un cliente

PUTPath(id)Consumes(applicationxml)public void modificarCliente(PathParam(id) int id Cliente nuevoCli)

Cliente actual = clienteDBget(id) if (actual == null) throw new WebApplicationException(ResponseStatusNOT_FOUND)

actualsetNombre(nuevoCligetNombre()) actualsetApellidos(nuevoCligetApellidos()) actualsetDireccion(nuevoCligetDireccion()) actualsetCodPostal(nuevoCligetCodPostal()) actualsetCiudad(nuevoCligetCiudad())

Anotamos el meacutetodo modificarCliente() con javaxwsrsPUT para enlazar laspeticiones HTTP PUT a este meacutetodo Al igual que hemos hecho con recuperarClienteId() elmeacutetodo modificarCliente() estaacute anotado adicionalmente con Path de forma que podamosatender peticiones a traveacutes de las URIs clientesid

El meacutetodo modificarCliente() tiene dos paraacutemetros El primero es un paraacutemetro id querepresenta el objeto Cliente que estamos modificando Al igual que ocurriacutea con el meacutetodorecuperarClienteId() utilizamos la anotacioacuten PathParam para extraer el identificador a

Servicios Rest

28

partir de la URI de la peticioacuten de entrada El segundo paraacutemetro es un objeto Cliente querepresenta el cuerpo del mensaje de entrada ya que no tiene ninguna anotacioacuten JAX-RS

El meacutetodo intenta encontrar un objeto Cliente en nuestro mapa clienteDB Si no existeprovocamos una WebApplicationException que enviaraacute una respuesta al usuario con elcoacutedigo 404 Not Found Si el objeto Cliente existe modificamos nuestro objeto Clienteexistente con los nuevos valores que obtenemos de la peticioacuten de entrada

Construccioacuten y despliegue del servicio

Una vez implementado nuestro servicio RESTful necesitamos poner en marcha el procesode construccioacuten El proceso de construccioacuten compilaraacutehellip empaquetaraacute hellip y finalmente nospermitiraacute desplegar nuestro servicio en el servidor de aplicaciones

Para poder empaquetar nuestro servicio RESTful como un war que se desplegaraacute en elservidor de aplicaciones vamos a incluir un proveedor de servicios JAX-RS en el descriptorde despliegue de nuestra aplicacioacuten (fichero webxml) En la siguiente sesioacuten justificaremos laexistencia de dicho proveedor (que seraacute un servlet) y explicaremos el modelo de desplieguede los servicios JAX-RS Los pasos a seguir desde IntelliJ para configurar el despliegue denuestro servicio son

bull Antildeadimos el directorio WEB-INF como subdirectorio de webapp

bull Nos vamos a FileProject StructurehellipFacetsWeb y antildeadimos el fichero webxml (enel panel Deployment descriptors pulsamos sobre + y seleccionamos webxml) Editamoseste fichero para antildeadir el servlet que serviraacute las peticiones de nuestros servicios RESTindicando cuaacutel seraacute la ruta en la que estaraacuten disponibles dichos servicios (en nuestroejemplo indicaremos la ruta rest) Dicha ruta es relativa a la ruta del contexto de nuestraaplicacioacuten y que por defecto es el nombre del artefacto war desplegado que hemosindicado en la etiqueta ltfinalNamegt dentro del ltbuildgt del fichero de configuracioacuten deMaven (pomxml)

Contenido del fichero webxml

ltweb-app version=30 xmlns=httpjavasuncomxmlnsjavaee xmlnsxsi=httpwwww3org2001XMLSchema-instance xsischemaLocation=httpjavasuncomxmlnsjavaee httpjavasuncomxmlnsjavaeeweb-app_3_0xsdgt lt-- One of the way of activating REST Services is adding these lines the server is responsible for adding the corresponding servlet automatically if the src folder has the Annotations to receive REST invocation--gt ltservlet-mappinggt ltservlet-namegtjavaxwsrscoreApplicationltservlet-namegt lturl-patterngtrestlturl-patterngt ltservlet-mappinggtltweb-appgt

A continuacioacuten ya estamos en disposicioacuten de iniciar la construccioacuten del proyecto con Mavenpara compilar empaquetar y desplegar nuestro servicio en Wildfly

Si utilizamos el terminal la secuencia de pasos para empaquetar y desplegar nuestroproyecto seriacutean

cd ejemplo-rest

Servicios Rest

29

mvn package

usrlocalwildfly-821Finalbinstandalonesh

mvn wildflydeploy

Nos situamos en el directorio que contiene el pomxml de nuestro proyectoEmpaquetamos el proyecto (obtendremos el war)Arrancamos el servidor wildflyDesplegamos el war generado en el servidor wildfly

Secuencia correcta de acciones

En ejecuciones posteriores despueacutes de realizar modificaciones en nuestro coacutedigo esrecomendable ejecutar mvn clean previamente al empaquetado del proyecto

Por lo tanto y suponiendo que el servidor de aplicaciones ya estaacute en marcha lasecuencia de acciones (comandos maven) que deberiacuteamos realizar para asegurarnosde que vamos a ejecutar exactamente la aplicacioacuten con los uacuteltimos cambios quehayamos introducido son

bull mvn wildflyundeploy

bull mvn clean

bull mvn package

bull mvn wildflydeploy

Tambieacuten podriacuteamos realizar todas estas acciones con un uacutenico comando maven

bull mvn wildflyundeploy clean package wildflydeploy

Si utilizamos IntelliJ antildeadiremos un nuevo elemento de configuracioacuten de ejecucioacuten desdeRunEdit Configurations Pulsamos el icono + y antildeadimos la configuracioacuten de tipo JBosssServerLocal Podemos ponerle por ejemplo como nombre Wilfdly start A continuacioacutenconfiguramos la ruta del servidor wildfly como usrlocalwildfly-821Final

Cuando lancemos este elemento de ejecucioacuten desde IntelliJ automaacuteticamente seconstruiraacute el proyecto (obtendremos el war) y arrancaremos wildfly Para desplegarel war utlizaremos la ventana Maven Projects y haremos doble click sobre ejemplo-restPluginswildflywildflydeploy

Probando nuestro servicio

Podemos probar nuestro servicio de varias formas Vamos a mostrar como hacerlodirectamente desde liacutenea de comandos utilizando IntelliJ o bien utilizando la herramientaPostman (que teneacuteis disponible desde el navegador Chrome)

Invocacioacuten del servicio desde liacutenea de comandosUtilizaremos la herramienta curl Por ejemplo para realizar una insercioacuten de un cliente elcomando seriacutea

Servicios Rest

30

curl -i -H Accept applicationxml -H Content-Type applicationxml -X POST -d clientexml httplocalhost8080ejemplo-restrestclientes

En donde

-iTambieacuten se puede utilizar la opcioacuten equivalente --include Indica que se debe incluirlas cabeceras HTTP en la respuesta recibida Recuerda que la peticioacuten POST devuelveen la cabecera Location el enlace del nuevo recurso creado (puede hacerlo en unacabedera Location o como un campo ltlinkgt del elemento creado en el cuerpo delmensaje lo veremos maacutes adelante) Esta informacioacuten seraacute necesaria para poder consultarla informacioacuten del nuevo cliente creado

-HIndica un par cabecera_valor_ En nuestro caso lo utilizamos para especificar los valoresde las cabeceras HTTP Accept y Content-Type

-XIndica el meacutetodo a invocar (GET POST PUThellip)

-dTambieacuten se puede utilizar --data Indica cuaacuteles son los datos enviados en el mensajede entrada en una peticioacuten POST Si los datos especificados van precedidos por estamos indicando que dichos datos estaacuten en un fichero Por ejemplo en la orden anteriorescribimos en el fichero clientexml los datos del cliente que queremos antildeadir en nuestrosistema

El contenido del fichero clientexml podriacutea ser eacuteste

ltxml version=10 encoding=UTF-8gtltclientesgt ltclientegt ltnombregtPepe ltnombregt ltapellidosgtGarcia Lopezltapellido1gt ltdirecciongtCalle del pino 3ltapellido2gt ltcodPostalgt0001ltcodPostalgt ltciudadgtAlicanteltciudadgt ltclientegtltclientesgt

Finalmente en la orden indicamos la URI a la que queremos acceder en este caso

httplocalhost8080ejemplo-restrestclientes

Una vez insertado el cliente podemos recuperar el cliente utilizando el enlace que se incluyeen la cabecera de respuesta Location

curl -i -H Accept applicationxml -H Content-Type applicationxml -X GET httplocalhost8080ejemplo-restrestclientes1

Servicios Rest

31

Invocacioacuten del servicio desde IntelliJIntelliJ nos proporciona una herramienta para probar servicios REST desde ToolsTestRESTful Web Service Desde esta nueva ventana podremos invocar al servicio RESTindicando el tipo de peticioacuten HTTP asiacute como las cabeceras y cuerpo de la peticioacuten

La siguiente figura muestra la elaboracioacuten de una peticioacuten POST a nuestro servicio REST

A continuacioacuten mostramos la ejecucioacuten de una peticioacuten GET

Cuando realizamos una peticioacuten POST debemos indicar el contenido del cuerpo del mensajeEn la siguiente figura observamos que tenemos varias opciones disponibles como por ejemploteclear directamente dicho contenido (opcioacuten Text) o bien subir dicha informacioacuten desdeun fichero en nuestro disco duro (opcioacuten File Contents) Podemos ver que hemos elegido estauacuteltima opcioacuten para probar nuestro servicio

Invocacioacuten del servicio desde PostmanOtra alternativa sencilla para probar nuestro servicio REST es la herramienta postmanque podemos lanzar desde el navegador en nuestro caso Chrome

Accederemos a la aplicacioacuten desde la barra de marcadores seleccionando Aplicaciones yy a continuacioacuten pulsaremos sobre el icono Postman

El aspecto de la herramienta es el que mostramos a continuacioacuten

Servicios Rest

32

Postman a diferencia de las alternativas anteriores nos permitiraacute guardar un historial depeticiones de forma que podamos repetir la ejecucioacuten de nuestros tests exactamente de lamisma forma aunque no de forma automaacutetica sino que tenemos que lanzar manualmentecada test que queramos volver a ejecutar

Tambieacuten podemos crear colecciones que no son maacutes que carpetas que contienen unconjunto de peticiones de nuestro historial Por ejemplo podemos crear la coleccioacuten s1-rest-ejercicio1 en donde guardaremos todas las peticiones que hayamos hecho sobre el ejercicio1 de la primera sesioacuten de rest

Podeacuteis crearos una cuenta gratuita para almacener y gestionar vuestras peticiones rest Unavez que tengaacuteis creadas varias colecciones Postman nos permite guardarlas en nuestrodisco duro en formato json

Servicios Rest

33

15 Ejercicios

Antes de empezar a crear los proyectos debes descargarte el repositorio git java_uaejercicios-rest-expertojava en el que vas a implementar los ejercicios relativos a laasignatura de Servicios REST El proceso es el mismo que el seguido en sesiones anteriores

1 Accedemos al repositorio y realizamos un Fork en nuestra cuenta personal (asiacute podremostener una copia con permisos de escritura)

2 Realizamos un Clone en nuestra maacutequina

$ git clone httpsbitbucketorgltalumnogtejercicios-rest-expertojava

De esta forma se crea en nuestro ordenador el directorio ejercicios-rest-expertojavay se descarga en eacutel un proyecto IntelliJ en el que iremos antildeadiendo MOacuteDULOS para cada unode los ejercicios Contiene tambieacuten el fichero gitignore asiacute como diferentes moacutedulos conlas plantillas que vayamos a necesitar para realizar los ejercicios

A partir de este momento se puede trabajar con dicho proyecto y realizar Commit y Pushcuando sea oportuno

$ cd ejercicios-rest-expertojava$ git add $ git commit -a -m Mensaje de commit$ git push origin master

Los MOacuteDULOS IntelliJ que iremos antildeadiendo tendraacuten todos el prefijo sx- siendo x elnuacutemero de la sesioacuten correspondiente (por ejemplo s1-ejercicio s2-otroEjerciciohellip)

Servicio REST ejemplo (0 puntos)

Para familiarizarnos con las peticiones http POST PUT GET DELETE se proporciona elMOacuteDULO s1-ejemplo-rest con la implementacioacuten de un servicio rest que podeacuteis probar biendesde liacutenea de comandos con la utilidad curl desde el navegador con la herramienta postman o bien desde IntelliJ con el cliente REST incluido en el IDE

En el directorio srcmainresources de dicho moacutedulo teneacuteis un fichero de texto(instruccionestxt) con las instrucciones para construir desplegar y probar la aplicacioacuten deejemplo

Servicio REST saludo (1 punto)

Vamos a implementar un primer servicio RESTful muy sencillo Para ello seguiremos lassiguientes indicaciones

bull Creamos un MOacuteDULO Maven con IntelliJ (desde el directorio ejercicios-rest-expertojava ) con el arquetipo webapp-javaee7 tal y como hemos visto en losapuntes de la sesioacuten Las coordenadas del artefacto Maven seraacuten

GroupId orgexpertojava

ArtifactId s1-saludo-rest

Servicios Rest

34

version 10-SNAPSHOT

bull Configuramos el pommxl del proyecto para poder compilar empaquetar y desplegarnuestro servicio Consulta los apuntes para ver cuaacutel debe ser el contenido de las etiquetasltpropertiesgt ltdependenciesgt y ltbuildgt

bull Creamos la carpeta WEB-INF y antildeadimos el fichero de configuracioacuten webxml tal y comohemos visto en los apuntes (esto seraacute necesario para configurar el despliegue) En estecaso queremos mapear los servicios REST contenidos en el paquete orgexpertojavaal directorio recursos dentro de nuestro contexto (recuerda que el contexto de nuestraaplicacioacuten web vendraacute dado por el valor de la etiqueta ltfinalNamegt anidada dentro deltbuildgt)

bull Creamos un recurso de nombre HolaMundoResource que se mapee a la direccioacuten holamundo Implementar un meacutetodo de forma que al acceder a eacutel por GET nos devuelvaen texto plano (textplain) el mensaje Hola mundo Una vez desplegada la aplicacioacutenen el servidor WildFly prueba el servicio mediante la utilidad Postman desde ChromeComprobar que la invocacioacuten

GET httplocalhost8080saludo-restholamundo

Devuelve como cuerpo del mensaje Hola mundo

bull Vamos a antildeadir un segmento variable a la ruta Implementa un meacutetodo GET nuevode forma que si accedemos a holamundonombre antildeade el nombre indicado al saludo(separado por un espacio en blanco y seguido por )

Una vez desplegada la aplicacioacuten en el servidor WildFly prueba el servicio mediante la utilidadTest RESTFul Web Service de IntelliJ o con Postman Comprobar que la invocacioacuten

GET httplocalhost8080saludo-restholamundopepe

Devuelve como cuerpo del mensaje Hola mundo pepe

bull Hacer que se pueda cambiar el saludo mediante un meacutetodo PUT El nuevo saludo llegaraacutetambieacuten como texto plano en el cuerpo de la peticioacuten y posteriores invocaciones a losmeacutetodos GET utilizaraacuten el nuevo saludo Almacenaremos el nuevo saludo en una variableestaacutetica de nuestro recurso iquestQueacute pasa si no lo es (lo hemos explicado en los apuntespuedes hacer la prueba para ver queacute ocurre si la variable no es estaacutetica)

Una vez desplegada la aplicacioacuten en el servidor WildFly prueba el servicio con Postmano bien mediante la utilidad Test RESTFul Web Service de IntelliJ Realizar las siguientesinvocaciones (en este orden)

PUT httplocalhost8080saludo-restholamundoy en el cuerpo del mensaje Buenos diacuteas

GET httplocalhost8080saludo-restholamundoGET httplocalhost8080saludo-restholamundopepe

Servicios Rest

35

La segunda invocacioacuten debe devolver como cuerpo del mensaje Buenos dias Al ejecutarla tercera invocacioacuten el cuerpo del mensaje de respuesta deberiacutea ser Buenos diasPepe

Servicio REST foro (1 punto)

Vamos a implementar un servicio RESTful que contemple las cuatro operaciones baacutesicas(GET PUT POST y DELETE) Se trata de un foro con en el que los usuarios pueden intervenirde forma anoacutenima en diferentes conversaciones

Primero debes crear un nuevo moacutedulo Maven configurar el pomxml asiacute como el ficherowebxml de la misma forma que hemos hecho en el ejercicio anterior pero para esteejercicio

bull Las coordenadas del moacutedulo Maven seraacuten

GroupId orgexpertojava

ArtifactId s1-foro-rest

version 10-SNAPSHOT

bull Nuestros servicios REST estaraacuten disponibles en la URI httplocalhost8080s1-foro-rest

El foro estaraacute formado por diferentes mensajes Por lo tanto el modelo del dominio de nuestraaplicacioacuten estaraacute formado por la clase Mensaje que contendraacute un identificador y una cadenade caracteres que representaraacute el contenido del mensaje (recuerda que debes implementarlos correspondientes getters y setters)

Por simplicidad vamos a almacenar los mensajes de nuestro foro en memoria Estos estaraacutendisponibles desde la clase DatosEnMemoria que contendraacute la variable estaacutetica

static MapltInteger Mensajegt datos = new HashMapltInteger Mensajegt()

Los servicios que proporcionaraacute el foro estaraacuten implementados en la claseMensajeResource Se accederaacute a ellos traveacutes de la ruta relativa a la raiacutez de nuestrosservicios mensajes Concretamente podremos realizar las siguientes operaciones

bull Antildeadir un nuevo mensaje al foro con la URI relativa a la raiacutez de nuestros servicios mensajes El texto del mensaje estaraacute en el cuerpo de la peticioacuten y el tipo MIME asociadoseraacute textplain (contenido de la cabecera Content-type de la peticioacuten HTTP)Nuestra respuesta debe incluir en la cabecera Location de la respuesta HTTP la URIdel nuevo recurso creado Utiliza para ello la clase Response tal y como hemos mostradoen el coacutedigo de ejemplo proporcionado para el ejercicio anterior Hablaremos con detallesobre esta clase en sesiones posteriores

Recuerda que para acceder al cuerpo de la peticioacuten basta con definir unparaacutemetro de tipo String JAX-RS automaacuteticamente lo instanciaraacute a partirdel cuerpo de la peticioacuten y lo convertiraacute en un objeto de tipo String

bull Modificar un mensaje determinado con un identificador con valor id a traveacutes de la URIrelativa a la raiacutez de nuestros servicios mensajesid ( id debe ser por tanto unsegmento de ruta variable) Si no existe ninguacuten mensaje con el identificador id se lanzaraacutela excepcioacuten WebApplicationException(ResponseStatusNOT_FOUND)

Servicios Rest

36

bull Borrar un mensaje determinado con un identificador con valor id atraveacutes de la URI relativa a la raiacutez de nuestros servicios mensajesid Igual que en el caso anterior si el identificador proporcionado no secorresponde con el de ninguacuten mensaje del foro se lanzaraacute la excepcioacutenWebApplicationException(ResponseStatusNOT_FOUND)

bull Consultar todos los mensajes del foro (la URI relativa seraacute mensajes ) El resultado semostraraacute en tantas liacuteneas como mensajes Cada mensaje iraacute precedido de su identificadorTambieacuten se informaraacute del nuacutemero total de mensajes en el foro (La respuesta seraacute unacadena de caracteres Al final del ejercicio mostramos un ejemplo de mensaje de respuestapara esta operacioacuten)

bull Consultar un mensaje determinado con un identificador con valor id a traveacutes dela URI relativa a la raiacutez de nuestros servicios mensajesid Si el identificadorproporcionado no se corresponde con el de ninguacuten mensaje del foro se lanzaraacute laexcepcioacuten WebApplicationException(ResponseStatusNOT_FOUND)

Prueba el servicio utilizando Postman o el cliente de IntelliJ para servicios REST con lassiguientes entradas

bull Crea los mensajes Mensaje numero 1 Mensaje numero 2 Mensaje numero 3 en esteorden

bull Consulta los mensajes del foro El resultado debe ser

1 Mensaje numero 1

2 Mensaje numero 2

3 Mensaje numero 3

Numero total de mensajes = 3

bull Cambia el mensaje con identificador 2 por Nuevo mensaje numero 2

bull Consulta los mensajes del foro El resultado debe ser

1 Mensaje numero 1

2 Nuevo Mensaje numero 2

3 Mensaje numero 3

Numero total de mensajes = 3

bull Borra el mensaje con identificador 3

bull Consulta el mensaje con el identificador 3 Se debe obtener una respuesta 404 NotFound

bull Consulta los mensajes del foro El resultado debe ser

1 Mensaje numero 1

2 Nuevo Mensaje numero 2

Numero total de mensajes = 2

bull Antildeade el mensaje Mensaje final Vuelve a consultar los mensajes el resultado debe ser

1 Mensaje numero 1

Servicios Rest

37

2 Nuevo Mensaje numero 2

4 Mensaje final

Numero total de mensajes = 3

Para evitar problemas con el id generado si hemos borrado mensajeslo maacutes sencillo es que el identificador vaya incrementaacutendose siemprecon cada nuevo mensaje Esto puede hacer que queden huecos en lanumeracioacuten como en el ejemplo anterior

Servicios Rest

38

2 Anotaciones baacutesicas JAX-RS El modelo de despliegue

Ya hemos visto como crear un servicio REST baacutesico Ahora se trata de analizar con maacutesdetalle aspectos fundamentales sobre la implementacioacuten de los servicios Comenzaremos pordetallar los usos de la anotacioacuten Path que es la que nos permite etiquetar una clase Javacomo un recurso REST sobre el que podremos realizar las operaciones que hemos identificadoen la sesioacuten anterior Tambieacuten hablaremos algo maacutes sobre las anotaciones Produces yConsumes que ya hemos utilizado para implementar nuestro primer servicio

En segundo lugar hablaremos sobre la extraccioacuten de informacioacuten de las peticiones HTTP ycoacutemo podemos inyectar esa informacioacuten en nuestro coacutedigo java Esto nos permitiraacute servir laspeticiones sin tener que escribir demasiado coacutedigo adicional

Finalmente explicaremos maacutes detenidamente coacutemo configurar el despliegue de nuestraaplicacioacuten REST de forma que sea portable

21 iquestCoacutemo funciona el enlazado de meacutetodos HTTP

JAX-RS define cinco anotaciones que se corresponden con operaciones HTTP especiacuteficas

bull javaxwsrsGET

bull javaxwsrsPUT

bull javaxwsrsPOST

bull javaxwsrsDELETE

bull javaxwsrsHEAD

En la sesioacuten anterior ya hemos utilizado estas anotaciones para hacer corresponder (enlazar)peticiones HTTP GET con un meacutetodo Java concreto

Por ejemplo

Path(clientes)public class ServicioCliente

GET Produces(applicationxml) public String getTodosLosClientes()

En este coacutedigo la anotacioacuten GET indica al runtime JAX-RS que el meacutetodo javagetTodosLosClientes() atiende peticiones HTTP GET dirigidas a la URI clientes

Soacutelamente se puede utilizar una de las anotaciones anteriores para unmismo meacutetodo Si se aplica maacutes de uno se produce un error durante eldespliegue de la aplicacioacuten

Es interesante conocer que cada una de estas anotaciones a su vez estaacute anotada con otrasanotaciones (podriacuteamos llamarlas meta anotaciones) Por ejemplo la implementacioacuten de laanotacioacuten GET tiene este aspecto

package javaxwsrsimport

Servicios Rest

39

Target(ElementTypeMETHOD)Retention(RetentionPolicyRUNTIME)HttpMethod(HttpMethodGET)public interface GET

GET en siacute mismo no tiene ninguacuten significado especial para el proveedor JAX-RS (runtimede JAX-RS) Lo que hace que la anotacioacuten GET sea significativo para el runtime de JAX-RSes el valor de la meta anotacioacuten javaxwsrsHttpMethod (en este caso HttpMethodGET )Este valor es el que realmente decide que un determinado meacutetodo Java se enlace con undeterminado meacutetodo HTTP

iquestCuaacuteles son las implicaciones de eacutesto Pues que podemos crear nuevas anotaciones quepodemos enlazar a otros meacutetodos HTTP que no sean GET POST PUT DELETE o HEAD Deesta forma podriacuteamos permitir que diferentes tipos de clientes que hacen uso de la operacioacutenHTTP LOCK puedan ser atendidos por nuestro servicio REST (como por ejemplo un clienteWebDAV )

22 La anotacioacuten Path

La anotacioacuten Path identifica la plantilla de path para la URI del recurso al que se accede yse puede especificar a nivel de clase o a nivel de meacutetodo de dicho recurso

El valor de una anotacioacuten Path es una expresioacuten que denota una URI relativa a la URIbase del servidor en el que se despliega el recurso a la raiz del contexto de la aplicacioacuten yal patroacuten URL al que responde el runtime de JAX-RS

Un segmento de la URI es cada una de las subcadenas delimitadas por que aparecenen dicha URI Por ejemplo la URI httpejemploclientescomclientesviprecientes contiene 4segmentos de ruta ejemploclientescom clientes vip y recientes

La anotacioacuten Path no es necesario que contenga una ruta que empieceo termine con el caraacutecter El runtime de JAX-RS analiza igualmente laexpresioacuten indicada como valor de Path

Para que una clase Java sea identificada como una clase que puede atender peticiones HTTPeacutesta tiene que estar anotada con al menos la expresioacuten Path() Este tipo de clasesse denominan recursos JAX-RS raiacutez

Para recibir una peticioacuten un meacutetodo Java debe tener al menos una anotacioacuten de meacutetodoHTTP como por ejemplo javaxwsrsGET Este meacutetodo no requiere tener ninguna anotacioacutenPath adicional Por ejemplo

Path(pedidos)public class PedidoResource GET public String getTodosLosPedidos()

Una peticioacuten HTTP GET pedidos se delegaraacute en el meacutetodo getTodosLosPedidos()

Servicios Rest

40

Podemos aplicar tambieacuten Path a un meacutetodo Java Si hacemos esto la expresioacuten de laanotacioacuten Path de la clase se concatenaraacute con la expresioacuten de la anotacioacuten Path delmeacutetodo Por ejemplo

Path(pedidos)public class PedidoResource

GET Path(noPagados) public String getPedidosNoPagados()

De esta forma una peticioacuten GET pedidosnoPagados se delegaraacute en el meacutetodogetPedidosNoPagados()

Podemos tener anotaciones Path para cada meacutetodo que seraacuten relativos a la ruta indicadaen la anotacioacuten Path de la definicioacuten de la clase Por ejemplo la siguiente clase de recursosirve peticiones a la URI pedidos

Path(pedidos)public class PedidoResource

GET public String getPedidos()

Si quisieacuteramos proporcionar el servicio en la URI pedidosincidencias por ejemplono necesitamos una nueva definicioacuten de clase y podriacuteamos anotar un nuevo meacutetodogetIncidenciasPedidos() de la siguiente forma

Path(pedidos)public class PedidoResource

GET public String getPedidos()

GET Path(incidencias) public String getIncidenciasPedidos()

Ahora tenemos una clase de recurso que gestiona peticiones para pedidos y para pedidosincidencias

Expresiones Path

El valor de una anotacioacuten Path puede ser una cadena de caracteres o tambieacutenpuede contener expresiones maacutes complejas si es necesario nos referiremos a ellas comoexpresiones Path

Servicios Rest

41

Una expresioacuten Path puede incluir variables que se indican entre llaves que seraacuten sustituidasen tiempo de ejecucioacuten dependiendo del valor que se indique en la llamada al recurso Asiacutepor ejemplo si tenemos la siguiente anotacioacuten

GETPath(clientesid)

y el usuario realiza la llamada

GET httporgexpertojavacontextorestclientesPedro

la peticioacuten se delegaraacute en el meacutetodo que esteacute anotado con las anotaciones anteriores y el valorde id seraacute instanciado en tiempo de ejecucioacuten a Pedro

Para obtener el valor del nombre del cliente utilizaremos la anotacioacuten PathParam en losparaacutemetros del meacutetodo de la siguiente forma

GETPath(clientesnombre)public String getClientePorNombre(PathParam(nombre) String nombre)

Una expresioacuten Path puede tener maacutes de una variable cada una figuraraacute entre llaves Porejemplo si utilizamos la siguiente expresioacuten Path

Path(nombre1nombre2)public class MiResource

podremos atender peticiones dirigidas a URIs que respondan a la plantilla

httporgexpertojavacontextorecursosnombre1nombre2

como por ejemplo

httporgexpertojavacontextorecursosPedroLopez

Las expresiones Path pueden incluir maacutes de una variable para referenciar un segmento deruta Por ejemplo

Path()public class ClienteResource GET Path(clientesapellido1-apellido2) public String getCliente(PathParam(apellido1) String ape1 PathParam(apellido2) String ape2)

Servicios Rest

42

Una peticioacuten del tipo

GET httporgexpertojavacontextoclientesPedro-Lopez

seraacute procesada por el meacutetodo getCliente()

Expresiones regulares

Las anotaciones Path pueden contener expresiones regulares (asociadas a las variables)Por ejemplo si nuestro meacutetodo getClienteId() tiene un paraacutemetro de tipo entero podemosrestringir las peticiones para tratar solamente aquellas URIs que contengan diacutegitos en elsegmento de ruta que nos interese

Path(clientes)public class ClienteResource GET Path(id d+) solo soporta diacutegitos public String getClienteId(PathParam(id) int id)

Si la URI de la peticioacuten de entrada no satisface ninguna expresioacuten regular de ninguno de losmetodos del recurso entonces se devolveraacute el coacutedigo de error 404 Not Found

El formato para especificar expresiones regulares para las variables del path es

nombre-variable [ expresion-regular ]

El uso de expresiones regulares es opcional Si no se proporciona una expresioacuten regularpor defecto se admite cualquier caraacutecter En teacuterminos de una expresioacuten regular la expresioacutenregular por defecto seriacutea

[^]+

Por ejemplo si queremos aceptar solamente nombres que comiencen por una letra y acontinuacioacuten puedan contener una letra o un diacutegito lo expresariacuteamos como

Path(clientes)public class ClienteResource GET Path(nombre [a-zA-Z][a-zA-Z_0-9]) public String getClienteNombre(PathParam(nombre) string nom)

Servicios Rest

43

De esta forma la URI clientesaaa no seriacutea vaacutelida la URI clientesa9 activariacutea elmeacutetodo getClienteNombre() y la URI clientes89 activariacutea el meacutetodo getClienteId()

Las expresiones regulares no se limitan a un soacutelo segmento de la URI Por ejemplo

Path(clientes)public class ClienteResource GET Path(id +) public String getCliente(PathParam(id) String id)

GET Path(id +direccion) public String getDireccion(PathParam(id) String id)

La expresioacuten regular + indica que estaacuten permitidos cualquier nuacutemero de caracteres Asiacutepor ejemplo la peticioacuten GET clientespedrolopez podriacutea delegarse en el meacutetodogetClientes()

El meacutetodo getDireccion() tiene asociada una expresioacuten maacutes especiacutefica la cual puedemapearse con cualquier cadena de caracteres que termine con direccion Seguacuten eacutestola peticioacuten GET clientespedrolopezdireccion podriacutea delegarse en el meacutetodogetDireccion()

Reglas de precedencia

En el ejemplo anterior acabamos de ver que las expresiones Path para getCliente() ygetDireccion() son ambiguas Una peticioacuten GET clientespedrolopezdireccionpodriacutea mapearse con cualquiera de los dos meacutetodos La especificacioacuten JAX-RS define lassiguientes reglas para priorizar el mapeado de expresiones regulares

bull El primer criterio para ordenar las acciones de mapeado es el nuacutemero de caracteres literalesque contiene la expresioacuten Path teniendo prioridad aquellas con un mayor nuacutemero decaracteres literales El patroacuten de la URI para el meacutetodo getCliente() tiene 10 caraacutecteresliterales clientes El patroacuten para el meacutetodo getDireccion() tiene 19 clientesdireccion Por lo tanto se elegiriacutea primero el meacutetodo getDireccion()

bull El segundo criterio es el nuacutemero de variables en expresiones Path (por ejemplo id oid +) Teniendo precedencia las patrones con un mayor nuacutemero de variables

bull El tercer criterio es el nuacutemero de variables que tienen asociadas expresiones regulares(tambieacuten en orden descendente)

A continuacioacuten mostramos una lista de expresiones Path ordenadas en orden descendentede prioridad

1 clientesidnombredireccion

2 clientesid +direccion

3 clientesiddireccion

4 clientesid +

Servicios Rest

44

Las expresiones 13 se analizariacutean primero ya que tienen maacutes caracteres literales que laexpresioacuten nuacutemero 4 Si bien las expresiones 13 tienen el mismo nuacutemero de caracteresliterales La expresioacuten 1 se analizariacutea antes que las otras dos debido a la segunda regla (tienemaacutes variables) Las expresiones 2 y 3 tienen el mismo nuacutemero de caracteres literales y elmismo nuacutemero de variables pero la expresioacuten 2 tiene una variable con una expresioacuten regularasociada

Estas reglas de ordenacioacuten no son perfectas Es posible que siga habiendo ambiguumledadespero cubren el 90 de los casos Si el disentildeo de nuestra aplicacioacuten presenta ambiguumledadesaplicando estas reglas es bastante probable que hayamos complicado dicho disentildeo y seriacuteaconveniente revisarlo y refactorizar nuestro esquema de URIs

Paraacutemetros matrix (Matrix parameters)

Los paraacutemetros matrix con pares nombre-valor incluidos como parte de la URI Aparecen alfinal de un segmento de la URI (segmento de ruta) y estaacuten delimitados por el caraacutecter Por ejemplo

httpejemplocochescomseatibizacolor=black2006

En la ruta anterior el paraacutemetro matrix aparece despueacutes del segmento de ruta ibiza Su nombrees color y el valor asociado es black

Un paraacutemetro matrix es diferente de lo que denominamos paraacutemetro de consulta (queryparameter) ya que los paraacutemetros matrix representan atributos de ciertos segmentos de laURI y se utilizan para propoacutesitos de identificacioacuten Pensemos en ellos como adjetivos Losparaacutemetros de consulta por otro lado siempre aparecen al final de la URI y siemprepertenecen al recurso completo que estemos referenciando

Los paraacutemetros matrix son ignorados cuando el runtime de JAX-RS realiza el matching de laspeticiones de entrada a meacutetodos de recursos REST De hecho es ilegal incluir paraacutemetrosmatrix en las expresiones Path Por ejemplo

Path(seat)public class SeatService

GET Path(ibizaanyo) Produces(imagejpeg) public Response getIbizaImagen(PathParam(anyo) String anyo)

Si la peticioacuten de entrada es GET seatibizacolor=black2009 el meacutetodo getIbizaImagen()seriacutea elegido por el proveedor de JAX-RS para servir la peticioacuten de entrada y seriacutea invocadoLos paraacutemetros matrix NO se consideran parte del proceso de matching debido a quenormalmente son atributos variables de la peticioacuten

Subrecursos (Subresource Locators)

Acabamos de ver la capacidad de JAX-RS para hacer corresponder de forma estaacuteticaa traveacutes de la anotacioacuten Path URIs especificadas en la entrada de la peticioacuten conmeacutetodos Java especiacuteficos JAX-RS tambieacuten nos permitiraacute de forma dinaacutemica servir nosotros

Servicios Rest

45

mismos las peticiones a traveacutes de los denominados subresource locators (localizadores desubrecursos)

Los subresource locators son meacutetodos Java anotados con Path pero sin anotacionesGET PUT hellip Este tipo de meacutetodos devuelven un objeto que es en siacute mismo un servicioJAX-RS que sabe coacutemo servir el resto de la peticioacuten Vamos a describir mejor este conceptocon un ejemplo

Supongamos que queremos extender nuestro servicio que proporciona informacioacuten sobre losclientes Disponemos de diferentes bases de datos de clientes seguacuten regiones geograacuteficasQueremos antildeadir esta informacioacuten en nuestro esquema de URIs pero desacoplando labuacutesqueda del servidor de base de datos de la consulta particular de un cliente en concretoAntildeadiremos la informacioacuten de la zona geograacutefica en la siguiente expresioacuten Path

clienteszona-dbclienteId

A continuacioacuten definimos la clase ZonasClienteResource que delegaraacute en la claseClienteResource que ya teniacuteamos definida

Path(clientes)public class ZonasClienteResource

Path(zona-db) public ClienteResource getBaseDeDatos(PathParam(zona) String db) devuelve una instancia dependiendo del paraacutemetro db ClienteResource resource = localizaClienteResource(db) return resource

protected ClienteResource localizaClienteResource(String db)

La clase ZonasClienteResource es nuestro recurso raiacutez Dicha clase no atiende ningunapeticioacuten HTTP directamente Nuestro recurso raiacutez procesa el segmento de URI que hacereferencia a la base de datos en donde buscar a nuestro cliente y devuelve una instancia dedicha base de datos (o maacutes propiamente dicho del objeto con en que accederemos a dichabase de datos) El proveedor de JAX-RS utiliza dicha instancia para servir el resto de lapeticioacuten

public class ClienteResource private MapltInteger Clientegt clienteDB = new ConcurrentHashMapltInteger Clientegt() private AtomicInteger idContador = new AtomicInteger()

public ClienteResource(MapltInteger Clientegt clienteDB) thisclienteDB = clienteDB

POST Consumes(applicationxml)

Servicios Rest

46

public Response crearCliente(InputStream is)

GET Path(id) Produces(applicationxml) public Cliente recuperarClienteId(PathParam(id) int id)

PUT Path(id) Consumes(applicationxml) public void modificarCliente(PathParam(id) int id Cliente cli)

Si un usuario enviacutea la peticioacuten GET clientesnorteamerica-db333 el proveedorJAX-RS primero realizaraacute un matching de la expresioacuten sobre el meacutetodoZonasClienteResourcegetBaseDeDatos() A continuacioacuten procesaraacute el resto de la peticioacuten(333) a traveacutes del meacutetodo ClienteResourcerecuperarClienteId()

Podemos observar que la nueva clase ClienteResource ademaacutes de tener un nuevoconstructor ya no estaacute anotada con Path Esto implica que ya no es un recurso de nuestrosistema es un subrecurso y no debe ser registrada en el runtime de JAX-RS a traveacutes de laclase Application (como veremos maacutes adelante)

Veamos otro ejemplo Supongamos que tenemos un conjunto de alumnos del que podemosobtener el listado completo de alumnos y antildeadir nuevos alumnos pero ademaacutes queremos quecada alumno individual pueda consultarse modificarse o borrarse Una forma sencilla de trataresto es dividir el coacutedigo en un recurso (lista de alumnos) y un subrecurso (alumno individual)de la siguiente forma

Path(alumnos)public class AlumnosResource

Context UriInfo uriInfo

GET Produces(MediaTypeAPPLICATION_XML MediaTypeAPPLICATION_JSON) public ListltAlumnoBeangt getAlumnos() return FactoriaDaosgetAlumnoDao()getAlumnos()

POST Consumes(MediaTypeAPPLICATION_JSON) public void addAlumno(AlumnoBean alumno) throws IOException String dni = FactoriaDaosgetAlumnoDao()addAlumno(alumno) URI uri = uriInfogetAbsolutePathBuilder()path(dni)build(dni) Responsecreated(uri)build()

Path(alumno) public AlumnoResource getAlumno( PathParam(alumno) String dni)

Servicios Rest

47

return new AlumnoResource(uriInfo dni)

Vemos que en este recurso inyectamos informacioacuten sobre la URI solicitada como variable deinstancia (utilizando la anotacioacuten Context de la que hablaremos maacutes adelante) Para elconjunto de alumnos ofrecemos dos operaciones obtener la lista de alumnos y antildeadir unnuevo alumno a la lista la cual devuelve como respuesta la URI que nos da acceso al recursoque acabamos de antildeadir

Sin embargo lo maacutes destacable es el uacuteltimo meacutetodo Eacuteste se ejecutaraacute cuando antildeadamos a laruta el identificador de un alumno (por ejemplo alumnos15 ) En este caso lo que hace esdevolver un subrecurso (AlumnoResource) para asiacute tratar un alumno individual (destacamosque el nombre estaacute en singular para distinguirlo del recurso anterior que representa elconjunto)

Cuando hacemos esto estamos delegando en el nuevo Recurso para tratar la peticioacuten

public class AlumnoResource

UriInfo uriInfo

String dni

public AlumnoResource(UriInfo uriInfo String dni) thisuriInfo = uriInfo thisdni = dni

GET Produces(MediaTypeAPPLICATION_XMLMediaTypeAPPLICATION_JSON) public AlumnoBean getAlumno() AlumnoBean alumno = FactoriaDaosgetAlumnoDao()getAlumno(dni) if(alumno==null) throw new WebApplicationException(StatusNOT_FOUND) return alumno

PUT Consumes(MediaTypeAPPLICATION_XML) public Response setAlumno(AlumnoBean alumno) El DNI del alumno debe coincidir con el de la URI alumnosetDni(dni)

if(FactoriaDaosgetAlumnoDao()getAlumno(dni) = null) FactoriaDaosgetAlumnoDao()updateAlumno(alumno) return ResponsenoContent()build() else FactoriaDaosgetAlumnoDao()addAlumno(alumno) return Responsecreated(uriInfogetAbsolutePath())build()

Servicios Rest

48

DELETE public void deleteAlumno() FactoriaDaosgetAlumnoDao()deleteAlumno(dni)

Este recurso ya no es un recurso raiacutez mapeado a una ruta determinada (podemos ver que laclase no lleva la anotacioacuten Path) sino que es creado desde otro recurso Es por lo tantoun subrecurso

Como ya hemos visto los subrecursos nos permiten simplificar la forma de trabajar conconjuntos de recursos definiendo en un uacutenico meacutetodo la ruta de acceso a un recurso individualen lugar de tenerlo que hacer de forma independiente para cada operacioacuten

Ademaacutes este disentildeo modular de los recursos nos va a permitir reutilizar determinadosrecursos dentro de otros Por ejemplo dentro del recurso de un alumno podriacuteamos ver la listade asignaturas en las que se ha matriculado y reutilizar el subrecurso encargado de acceder alas asignaturas para poder acceder a sus datos a partir del recurso del alumno No deberemosabusar de esta caracteriacutestica ya que si creamos relaciones ciacuteclicas perdemos la caracteriacutesticadeseable de los servicios REST de que cada recurso estaacute asignado a una uacutenica URI

En un subrecurso NO podemos inyectar objetos de contexto mediantela anotacioacuten Context ya que no estamos en un recurso raiacutez Porese motivo en el ejemplo hemos proporcionado el objeto UriInfo enel constructor del subrecurso De forma alternativa tambieacuten podriacuteamoshaber inyectado este objeto como paraacutemetro de sus meacutetodos en ese casosi que habriacutea sido posible la inyeccioacuten

Caraacutecter dinaacutemico del dispatching de peticiones

En los ejemplos anteriores hemos ilustrado el concepto de subresource locator aunque nohemos mostrado completamente su caraacutecter dinaacutemico Asiacute si volvemos al primero de ellosel meacutetodo ZonasClienteResourcegetBaseDeDatos() puede devolver cualquier instancia decualquier clase En tiempo de ejecucioacuten el proveedor JAX-RS buscaraacute el interior de estainstancia meacutetodos de recurso que puedan gestionar la peticioacuten

Supongamos que tenemos dos bases de datos de clientes con diferentes tipos deidentificadores Una de ellas utiliza una clave numeacuterica La otra utiliza una clave formada porel nombre y apellidos Necesitamos tener dos clases diferentes para extraer la informacioacutenadecuada de la URI de la peticioacuten Cambiaremos la implementacioacuten de la siguiente forma

Path(clientes)public class ZonasClienteResourceResource protected ClienteResource europa = new ClienteResource() protected OtraClaveClienteResource norteamerica = new OtraClaveClienteResource()

Path(zona-db) public Object getBaseDeDatos(PathParam(zona) String db) if (dbequals(europa)) return europa else if (dbequals(norteamerica)) return northamerica

Servicios Rest

49

else return null

En lugar de devolver una instancia de ClienteResource el meacutetodo getBaseDeDatos() devuelveuna instancia de javalangObject JAX-RS analizaraacute la instancia devuelta para ver coacutemoprocesar el resto de la peticioacuten

Ahora si un usuario enviacutea la peticioacuten GET clienteseuropa-db333 se utilizaraacute la claseClienteResource para servir el resto de la peticioacuten Si la peticioacuten es GET clientesnorteamerica-dbjohn-smith utilizaremos el nuevo subrecurso OtraClaveClienteResource

public class OtraClaveClienteResource private MapltString Clientegt clienteDB = new ConcurrentHashMapltString Clientegt() GET Path(nombre-apellidos) Produces(applicationxml) public Cliente getCliente(PathParam(nombre) String nombre PathParam(apellidos) String apelllidos)

PUT Path(nombre-apellidos) Consumes(applicationxml) public void actualizaCliente()PathParam(nombre) String nombre PathParam(apellidos) String apelllidos Cliente cli)

23 Usos de las anotaciones Produces y Consumes

La informacioacuten enviada a un recurso y posteriormente devuelta al cliente que realizoacute la peticioacutense especifica con la cabecera HTTP Media-Type tanto en la peticioacuten como en la respuestaComo ya hemos visto podemos especificar que representaciones de los recursos (valor deMedia_Type) son capaces de aceptar yo producir nuestros servicios mediante las siguientesanotaciones

bull javaxwsrsConsumes

bull javaxwsrsProduces

La ausencia de dichas anotaciones es equivalente a incluirlas con el valor de media type es decir su ausencia implica que se soporta (acepta) cualquier tipo de representacioacuten

Anotacioacuten Consumes

Esta anotacioacuten funciona conjuntamente con POST y PUT Le indica al framework (libreriacuteasJAX-RS) a queacute meacutetodo se debe delegar la peticioacuten de entrada Especiacuteficamente el clientefija la cabecera HTTP Content-Type y el framework delega la peticioacuten al correspondientemeacutetodo capaz de manejar dicho contenido Un ejemplo de anotacioacuten con PUT es lasiguiente

Servicios Rest

50

Path(pedidos)public class PedidoResource PUT Consumes(applicationxml) public void modificarPedido(Pedido representation)

Si Consumes se aplica a la clase por defecto los meacutetodos correspondientes aceptan lostipos especificados de tipo MIME Si se aplica a nivel de meacutetodo se ignora cualquier anotacioacutenConsumes a nivel de clase para dicho meacutetodo

En este ejemplo le estamos indicando al framework que el meacutetodo modificarPedido() aceptaun recurso cuya representacioacuten (tipo MIME) es applicationxml (y que se almacenaraacute en lavariable representation hablaremos de ello en la siguiente sesioacuten) Por lo tanto un clienteque se conecte al servicio web a traveacutes de la URI pedidos debe enviar una peticioacuten HTTPPUT conteniendo el valor de applicationxml como tipo MIME de la cabecera HTTPContent-Type y el cuerpo (body) del mensaje HTTP debe ser por tanto un documentoxml vaacutelido

Si no hay meacutetodos de recurso que puedan responder al tipo MIME solicitado (tipo MIMEespecificado en la anotacioacuten Consumes del servicio) se le devolveraacute al cliente un coacutedigoHTTP 415 (Unsupported Media Type) Si el meacutetodo que consume la representacioacutenindicada como tipo MIME no devuelve ninguna representacioacuten se enviaraacute un el coacutedigo HTTP204 (No content) A continuacioacuten mostramos un ejemplo en el que sucede eacutesto

POSTConsumes(applicationxml)public void creaPedido(Pedido pedido) Crea y almacena un nuevo _Pedido_

Podemos ver que el meacutetodo consume una representacioacuten en texto plano pero devuelvevoid es decir no devuelve ninguna representacioacuten En este caso se enviacutea el coacutedigo de estadoHTTP 204 No content en la respuesta

Un recurso puede aceptar diferentes tipos de entradas Asiacute podemos utilizar la anotacioacutenPUT con maacutes de un meacutetodo para gestionar las repuestas con tipos MIME diferentes Porejemplo podriacuteamos tener un meacutetodo para aceptar estructuras XML y otro para aceptarestructuras JSON

Path(pedidos)public class PedidoResource PUT Consumes(applicationxml) public void modificarPedidoXML(InputStream pedido) PUT Consumes(applicationjson) public void modificarPedidoJson(InputStream pedido)

Servicios Rest

51

Anotacioacuten Produces

Esta anotacioacuten funciona conjuntamente con GET POST y PUT Indica al frameworkqueacute tipo de representacioacuten se enviacutea de vuelta al cliente

De forma maacutes especiacutefica el cliente enviacutea una peticioacuten HTTP junto con una cabecera HTTPAccept que se mapea directamente con el Content-Type que el meacutetodo produce Por lo tantosi el valor de la cabecera Accept HTTP es applicationxml el meacutetodo que gestiona la peticioacutendevuelve un stream de tipo MIME applicationxml Esta anotacioacuten tambieacuten puede utilizarse enmaacutes de un meacutetodo en la misma clase de recurso Un ejemplo que devuelve representacionesXML y JSON seriacutea el siguiente

Path(pedidos)public class PedidoResource

GET Produces(applicationxml) public String getPedidoXml()

GET Produces(applicationjson) public String getPedidoJson()

Si un cliente solicita una peticioacuten a una URI con un tipo MIME no soportadopor el recurso el framework JAX-RS lanza la excepcioacuten adecuadaconcretamente el runtime de JAX-RS enviacutea de vuelta un error HTTP 406Not acceptable

Se puede declarar maacutes de un tipo en la misma declaracioacuten Produces como por ejemplo

Produces(applicationxml applicationjson)public String getPedidosXmlOJson()

El meacutetodo getPedidosXmlOJson() seraacute invocado si cualquiera de los dos tipos MIMEespecificados en la anotacioacuten Produces son aceptables (la cabecera Accept de la peticioacutenHTTP indica queacute representacioacuten es aceptable) Si ambas representaciones son igualmenteaceptables se elegiraacute la primera

En lugar de especificar los tipos MIME como cadenas detexto en Consumes y Produces podemos utilizar lasconstantes definidas en la clase javaxwsrscoreMediaTypecomo por ejemplo MediaTypeAPPLICATION_XML oMediaTypeAPPLICATION_JSON en lugar de applicationxml yapplicationjson

24 Inyeccioacuten de paraacutemetros JAX-RS

Buena parte del trabajo de JAX-RS es el extraer informacioacuten de una peticioacuten HTTP einyectarla en un meacutetodo Java Podemos estar interesados en un fragmento de la URI de

Servicios Rest

52

entrada en los paraacutemetros de peticioacutenhellip El cliente tambieacuten podriacutea enviar informacioacuten en lascabeceras de la peticioacuten A continuacioacuten indicamos una lista con algunas de las anotacionesque podemos utilizar para inyectar informacioacuten de las peticiones HTTP

bull javaxwsrsPathParam

bull javaxwsrsMatrixParam

bull javaxwsrsQueryParam

bull javaxwsrsFormParam

bull javaxwsrsHeaderParam

bull javaxwsrsContext

bull javaxwsrsBeanParam

Habitualmente estas anotaciones se utilizan en los paraacutemetros de un meacutetodo de recurso JAX-RX Cuando el proveedor de JAX-RS recibe una peticioacuten HTTP busca un meacutetodo Java quepueda servir dicha peticioacuten Si el meacutetodo Java tiene paraacutemetros anotados con alguna de estasanotaciones extraeraacute la informacioacuten de la peticioacuten HTTP y la pasaraacute como un paraacutemetrocuando se invoque el meacutetodo

javaxwsrsPathParam

Ya la hemos utilizado en la sesioacuten anterior PathParam nos permite inyectar el valor de losparaacutemetros de la URI definidos en expresiones Path Recordemos el ejemplo

Path(clientes)public class ClienteResource

GET Path(id) Produces(applicationxml) public Cliente recuperarClienteId(PathParam(id) int id)

Podemos referenciar maacutes de un paraacutemetro en el path de la URI en nuestros meacutetodo javaPor ejemplo supongamos que estamos utilizando el nombre y apellidos para identificar a uncliente en nuestra clase de recurso

Path(clientes)public class ClienteResource

GET Path(nombre-apellidos) Produces(applicationxml) public Cliente recuperarClienteId(PathParam(nombre) String nom PathParam(apellidos) String ape)

Servicios Rest

53

En ocasiones un parameacutetro de path de la URI puede repetirse en diferentes expresionesPath que conforman el patroacuten de matching completo para un meacutetodo de un recurso (porejemplo puede repetirse en la expresioacuten Path de la clase y de un meacutetodo) En estos casos laanotacioacuten PathParam siempre referencia el paraacutemetro path final Asiacute en el siguiente coacutedigo

Path(clientesid)public class ClienteResource

GET Path(direccionid) Produces(textplain) public String getDireccion(PathParam(id) String direccionId)

Si nuestra peticioacuten HTTP es GET clientes123direccion456 el paraacutemetro direccionIddel meacutetodo getDireccion() tendriacutea el valor inyectado de 456

Interfaz UriInfo

Podemos disponer ademaacutes de un API maacutes general para consultar y extraer informacioacuten sobrelas peticiones URI de entrada Se trata de la interfaz javaxwsrscoreUriInfo

public interface UriInfo public javanetURI getAbsolutePath() public UriBuilder getAbsolutePathBuilder()

public javanetURI getBaseUri() public UriBuilder getBaseUriBuilder()

public String getPath() public ListltPathSegmentgt getPathSegments() public MultivaluedMapltString Stringgt getPathParameters()

Los meacutetodos getAbsolutePathBuilder() y getAbsolutePath() devuelven la ruta absoluta dela peticioacuten HTTP en forma de UriBuilder y URI respectivamente

Los meacutetodos getBaseUri() y getBaseUriBuilder() devuelven la ruta base de la aplicacioacuten(ruta raiz de nuestros servicios rest) en forma de UriBuilder y URI respectivamente

El meacutetodo UriInfogetPath() permite obtener la ruta relativa de nuestros servicios RESTutilizada para realizar el matching con nuestra peticioacuten de entrada (es la ruta de la peticioacutenactual relativa a la ruta base de la peticioacuten rest)

El meacutetodo UriInfogetPathSegments() divide la ruta relativa de nuestro servicio REST enuna serie de objetos PathSegment (segmentos de ruta delimitados por )

El meacutetodo UriInfogetPathParameters() devuelve un objeto de tipo MultivaluedMap con todoslos paraacutemetros del path definidos en todas las expresiones Path de nuestra peticioacuten rest

Servicios Rest

54

Por ejemplo si la ruta de nuestra petcioacuten http es httplocalhost8080contextorestclientes2(siendo contexto la ruta raiacutez del war desplegado y rest la ruta de servicio de jax-rs)

bull la ruta absoluta (meacutetodo getAbsolutePath()) seriacutea httplocalhost8080contextorestclientes2

bull la ruta base (meacutetodo getBaseUri) seriacutea httplocalhost8080contextorest

bull la ruta relativa a la ruta base (meacutetodo getPath()) seriacutea clientes2

bull el nuacutemero de segmentos de la peticioacuten rest (meacutetodo getPathSegments()) seriacutean 2 clientesy 2

Podemos inyectar una instancia de la interfaz UriInfo utilizando la anotacioacutenjavaxwsrscoreContext A continuacioacuten mostramos un ejemplo

Path(cochesmarca)public class CarResource

GET Path(modeloanyo) Produces(imagejpeg) public Response getImagen(Context UriInfo info) String fabricado = infogetPathParameters()getFirst(marca) PathSegment modelo = infogetPathSegments()get(2) String color = modelogetMatrixParameteres()getFirst(color)

En este ejemplo inyectamos una instancia de UriInfo como paraacutemetro del meacutetodo getImagen()A continuacioacuten hacemos uso de dicha instancia para extraer informacioacuten de la URI

Recuerda que tambieacuten podriacuteamos inyectar una instancia de UriInfo en unavariable de instancia de la clase raiacutez de nuestro recurso

El meacutetodo CarResourcegetImagen() utiliza la interfazjavaxwsrscorePathSegment que como ya hemos indicado representa unsegmento de ruta

package javaxwsrscorepublic interface PathSegment String getPath() MultivaluedMapltString Stringgt getMatrixParameters()

El meacutetodo PathSegmentgetPath() devuelve el valor de la cadena de caracteres del segmentode ruta actual sin considerar niguacuten paraacutemetro matrix que pudiese contener

El meacutetodo PathSegmentgetMatrixParameters() devuelve un mapa con todos losparaacutemetros matrix aplicados a un segmento de ruta

Supongamos que realizamos la siguiente peticioacuten http para el coacutedigo anterior (claseCarResource)

Servicios Rest

55

GET cochesseatleoncolor=rojo2015

Esta peticioacuten es delegada en el meacutetodo ClarResourcegetImagen() La ruta contiene 4segmentos coches seat leon y 2015 La variable _modelo tomaraacute el valor leon y la variablecolor se instanciaraacute con el valor rojo

javaxwsrsMatrixParam

La especificacioacuten JAX-RS nos permite inyectar una matriz de valores de paraacutemetros a traveacutesde la anotacioacuten javaxwsrsMatrixParam

Path(cochesmarca)public class CarResource

GET Path(modeloanyo) Produces(imagejpeg) public Response getImagen(PathParam(marca) String marca PathParam(modelo) String modelo MatrixParam(color) String color)

El uso de la anotacioacuten MatrixParam simplifica nuestro coacutedigo y lo hace algo maacutes legibleSi por ejemplo la peticioacuten de entrada es

GET cochesseatibizacolor=black2009

entonces el paraacutemetro color del meacutetodo CarResourcegetImagen() tomariacutea el valor black

javaxwsrsQueryParam

La anotacioacuten javaxwsrsQueryParam nos permite inyectar paraacutemetros de consulta(query parameters) de la URI en los valores de los paraacutemetros de los meacutetodos java denuestros recursos Por ejemplo supongamos que queremos consultar informacioacuten de nuestrosclientes y queremos recuperar un subconjunto de clientes de nuestra base de datos NuestraURI de peticioacuten podriacutea ser algo asiacute

GET clientesinicio=0amptotal=10

El paraacutemetro de consulta inicio representa el iacutendice (o posicioacuten) del primer cliente quequeremos consultar y el paraacutemetro total representa cuaacutentos clientes en total queremosobtener como respuesta Una implementacioacuten del servicio RESTful podriacutea contener elsiguiente coacutedigo

Path(clientes)public class ClienteResource GET Produces(applicationxml) public String getClientes(QueryParam(inicio) int inicio

Servicios Rest

56

QueryParam(total) int total)

En este ejemplo el paraacutemetro inicio tomariacutea el valor 0 y el paraacutemetro total tomariacutea elvalor 10 (JAX-RS convierte automaacuteticamente las cadenas de caracteres de los paraacutemetrosde consulta en enteros)

javaxwsrsFormParam

La anotacioacuten javaxwsrsFormParam se utiliza para acceder al cuerpo del mensajede la peticioacuten HTTP de entrada cuyo valor de Content-Type es applicationx-www-form-urlencoded Es decir se utiliza para acceder a entradas individuales de un formulario HTMLPor ejemplo supongamos que para registrar a nuevos clientes en el sistema tenemos querellenar el siguiente formulario

ltFORM action=httpejemplocomclientes method=postgt ltPgt Nombre ltINPUT type=text name=nombregtltBRgt Apellido ltINPUT type=text name=apellidogtltBRgt ltINPUT type=submit value=Sendgt ltPgtltFORMgt

La ejecucioacuten de este coacutedigo inyectaraacute los valores del formulario como paraacutemetros de nuestromeacutetodo Java que representa el servicio de la siguiente forma

Path(clientes)public class ClienteResource POST public void crearCliente(FormParam(nombre) String nom FormParam(apellido) String ape)

Aquiacute estamos inyectando los valores de nombre y apellidos del formulario HTML en losparaacutemetors nom y ape del meacutetodo java crearCliente() Los datos del formulario viajan atraveacutes de la red codificados como URL-encoded Cuando se utiliza la anotacioacuten FormParamJAX-RS decodifica de forma automaacutetica las entradas del fomulario antes de inyectar susvalores

Asiacute por ejemplo si tecleamos los valores Maria Luisa y_Perlado_ como valores en loscampos de texto nombre y apellido del formulario el cuerpo de nuestro mensaje HTTP seraacutenombre=Maria20Luisaapellido=Perlado Este mensaje seraacute recibido por nuestro meacutetodoque extraeraacute los valores correspondientes y los instanciaraacute en los paraacutemetros nom y ape delmeacutetodo _ClienteResourcecrearCliente()

javaxwsrsHeaderParam

La anotacioacuten javaxwsrsHeaderParam se utiliza para inyectar valores de lascabeceras de las peticiones HTTP Por ejemplo si estamos interesados en la paacutegina web quenos ha referenciado o enlazado con nuestro servicio web podriacuteamos acceder a la cabeceraHTTP Referer utilizando la anotacioacuten HeaderParam de la siguiente forma

Servicios Rest

57

Path(miservicio)public class MiServicio GET Produces(texthtml) public String get(HeaderParam(Referer) String referer)

De forma alternativa podemos acceder de forma programativa a todas las cabeceras de lapeticioacuten de entrada utilizando la interfaz javaxwsrscoreHttpHeaders

public interface HttpHeaders public ListltStringgt getRequestHeader(String name) public MultivaluedMapltString Stringgt getRequestHeaders()

El meacutetodo getRequestHeader() permite acceder a una cabecera en concreto y el meacutetodogetRequestHeaders() nos proporciona un objeto de tipo Map que representa todas lascabeceras A continuacioacuten mostramos un ejemplo que accede a todas las cabeceras de lapeticioacuten HTTP de entrada

Path(miservicio)public class MiServicio GET Produces(texthtml) public String get(Context HttpHeaders cabeceras) String referer = headersgetRequestHeader(Referer)get(0) for (String header headersgetRequestHeaders()keySet()) Systemoutprintln(Se ha utilizado esta cabecera + header)

javaxwsrscoreContext

Dentro de nuestros recursos JAX-RS podemos inyectar determinados objetos coninformacioacuten sobre el contexto de JAX-RS sobre el contexto de servlets o sobreelementos de la peticioacuten recibida desde el cliente Para ello utilizaremos la anotacioacutenjavaxwsrscoreContext

En los ejemplos de esta sesioacuten ya hemos visto como utilizarla para inyectar objetos de tipoUriInfo y HttpHeaders

A continuacioacuten mostramos un ejemplo en el que podos obtener detalles sobre el contextodel despliegue de la aplicacion asi como del contexto de peticiones individuales utilizando laanotacion Context

Implementacioacuten de un servicio que muestra informacioacuten sobre el contexto de la peticioacuten

Path(orders)

Servicios Rest

58

public class PedidoResource

Context Application app

Context UriInfo uri

Context HttpHeaders headers

Context Request request

Context SecurityContext security

Context Providers providers

GET Produces(applicationxml) public ListltOrdergt getAll(QueryParam(start)int from QueryParam(end)int to) (appgetClasses()) (urigetPath()) (headersgetRequestHeader(HttpHeadersACCEPT)) (headersgetCookies()) (requestgetMethod()) (securityisSecure())

Application proporciona acceso a la informacioacuten de la configuracioacuten de la aplicacioacuten(clase Application)UriInfo proporciona acceso a la URI de la peticioacutenHttpHeaders proporciona acceso a las cabeceras de la peticioacuten HTTP La anotacioacutenHeaderParam puede tambieacuten utilizarse para enlazar una cabecera HTTP a unparaacutemetro de un meacutetodo de nuestro recurso a un campo del mismo o a una propiedadde un beanRequest se utiliza para procesar la respuestas tiacutepicamente se usa juntamente con laclase Response para construir la respuesta de forma dinaacutemicaSecurityContext proporciona acceso a la informacioacuten de la peticioacuten actual relacionadacon la seguridadProviders proporciona informacioacuten sobre la buacutesqueda del runtime de las instancias deproveedores utilizando un conjunto de criterios de buacutesqueda

Con respecto a contexto de servlets podremos inyectar informacioacuten de ServletContextServletConfig HttpServletRequest y HttpServletResponse Debemos recordar que losrecursos JAX-RS son invocados por un servlet dentro de una aplicacioacuten web por lo quepodemos necesitar tener acceso a la informacioacuten del contexto de servlets Por ejemplo sinecesitamos acceder a la ruta en disco donde tenemos los datos de nuestra aplicacioacuten webtendremos que inyectar el objeto ServletContext

GETProduces(imagejpeg)public InputStream getImagen(Context ServletContext sc) return scgetResourceAsStream(fotos + nif + jpg)

javaxwsrsBeanParam

La anotacioacuten javaxwsrsBeanParam nos permite inyectar una clase especiacutefica cuyosmeacutetodos o atributos esteacuten anotados con alguna de las anotaciones de inyeccioacuten de paraacutemetrosxxxParam que hemos visto en esta sesioacuten Por ejemplo supongamos esta clase

Servicios Rest

59

public class ClienteInput FormParam(nombre) String nombre

FormParam(apellido) String apellido

HeaderParam(Content-Type) String contentType

public String getFirstName()

La clase ClienteInput es un simple POJO (Plain Old Java Object) que contiene el nombrey apellidos de un cliente asiacute como el tipo de contenido del mismo Podemos dejar que JAX-RScree inicialice e inyecte esta clase usando la anotacioacuten BeanParam de la siguiente forma

Path(clientes)public class ClienteResource POST public void crearCliente(BeanParam ClienteInput newCust)

El runtime de JAX-RS analizaraacute los paraacutemetros anotados con BeanParam para inyectarlas anotaciones correspondientes y asignar el valor que corresponda En este ejemplo la claseClienteInput contendraacute dos valores de un formulario de entrada y uno de los valores de lacabecera de la peticioacuten De esta forma nos podemos evitar una larga lista de paraacutemetros enel meacutetodo crearCliente() (en este caso son soacutelo tres pero podriacutean ser muchos maacutes)

Conversioacuten automaacutetica de tipos

Todas las anotaciones que hemos visto referencian varias partes de la peticioacuten HTTP Todasellas se representan como una cadena de caracteres en dicha peticioacuten HTTP JAX-RS puedeconvertir esta cadena de caracteres en cualquier tipo Java siempre y cuando se cumpla almenos uno de estos casos

1 Se trata de un tipo primitivo Los tipos int short float double byte char y booleanpertenecen a esta categoriacutea

2 Se trata de una clase Java que tiene un constructor con un uacutenico paraacutemetro de tipo String

3 Se trata de una clase Java que tiene un meacutetodo estaacutetico denominado valueOf() que tomaun uacutenico String como argumento y devuelve una instancia de la clase

4 Es una clase de tipo javautilListltTgt javautilSetltTgt o javautilSortedSetltTgt en dondeT es un tipo que satisface los criterios 2 oacute 3 o es un String Por ejemplo ListltDoublegtSetltStringgt o SortedSetltIntegergt

Si el runtime JAX-RS falla al convertir una cadena de caracteres en el tipo Java especificadose considera un error del cliente Si se produce este fallo durante el procesamiento de unainyeccioacuten de tipo MatrixParam QueryParam o PathParam se devuelve al clienteun error 404 Not found Si el fallo tiene lugar con el procesamiento de las inyecciones

Servicios Rest

60

HeaderParam o CookieParam (esta uacuteltima no la hemos visto) entonces se enviacutea al clienteel eror 400 Bad Request

Valores por defecto (DefaultValue)

Suele ser habitual que algunos de los paraacutemetros proporcionados en las peticiones a serviciosRESTful sean opcionales Cuando un cliente no proporciona esta informacioacuten opcional en lapeticioacuten JAX-RS inyectaraacute por defecto un valor null si se trata de un objeto o un valor ceroen el caso de tipos primitivos

Estos valores por defecto no siempre son los que necesitamos para nuestro servicioPara solucionar este problema podemos definir nuestro propio valor por defecto para losparaacutemetros que sean opcionales utilizando la anotacioacuten javaxwsrsDefaultValue

Consideremos el ejemplo anterior relativo a la recuperacioacuten de la informacioacuten de unsubconjunto de clientes de nuestra base de datos Para ello utilizaacutebamos dos paraacutemetros deconsulta para indicar el iacutendice del primer elemento asiacute como el nuacutemero total de elementosque estamos interesados en recuperar En este caso no queremos que el cliente tengaque especificar siempre estos paraacutemetros al realizar la peticion Usaremos la anotacioacutenDefaultValue para indicar los valores por defecto que nos interese

Path(clientes)public class ClienteResource GET Produces(applicationxml) public String getClientes( DefaultValue(0) QueryParam(inicio) int inicio DefaultValue(10) QueryParam(total) int total)

Hemos usado DefaultValue para especificar un iacutendice de comienzo con valor cero y untamantildeo del subconjunto de los datos de la respuesta JAX-RS utilizaraacute las reglas de conversioacutende cadenas de caracteres que acabamos de indicar para convertir el valor del paraacutemetro enel tipo Java que especifiquemos

25 Configuracioacuten y despliegue de aplicaciones JAX-RS

Como ya hemos visto en la sesioacuten anterior implementamos nuestros servicios REST utilizandoel API de Java JAX-RS (especificacioacuten JSR-339) Una aplicacioacuten JAX-RS consiste en unoo maacutes recursos y cero o maacutes proveedores En este apartado vamos a describir ciertosaspectos aplicados a las aplicaciones JAX-RS como un todo concretamente a la configuracioacuteny tambieacuten a la publicacioacuten de las mismas cuando utilizamos un servidor de aplicacionesJavaEE 7 o bien un contenedor de servlets 30 que incluyan una implementacioacuten del APIJAX-RS Tambieacuten indicaremos coacutemo configurar el despliegue en el caso de no disponer comomiacutenimo de un contenedor de servlets 30

Configuracioacuten mediante la clase Application

Tanto los recursos (clases anotadas con Path) como los proveedores que conforman nuestraaplicacioacuten JAX-RS pueden configurarse utilizando una subclase de Application Cuandohablamos de configuracioacuten nos estamos refiriendo en este caso a definir los mecanismospara localizar las clases que representan los recursos asiacute como a los proveedores

Servicios Rest

61

Un proveedor es una clase que implementa una o algunade las siguientes interfaces JAX-RS MesssageBodyReaderMessageBodyWriter ContextResolverltTgt y ExceptionMapperltTgt Lasdos primeras permiten crear proveedores de entidades (entity providers)la tercera es un proveedor de contexto (context provider) y la uacuteltima unproveedor de mapeado de excepciones (exception mapping provider) Lasclases que actuacutean como proveedores estaacuten anotadas con Providerpara que puedan ser identificadas automaacuteticamente por el runtime JAX-RS

El uso de una subclase de Application para configurar nuestros servicios REST constituyela forma maacutes sencilla de desplegar los servicios JAX-RS en un servidor de aplicacionescertificado como Java EE (en este caso Wildfly cumple con este requisito) o un contenedorstandalone de Servlet 3 (como por ejemplo Tomcat)

Pasemos a conocer la clase javaxwsrscoreApplication El uso de la claseApplication es la uacutenica forma portable de decirle a JAX-RS queacute servicios web (clasesanotadas con Path) asiacute como queacute otros elementos como filtros interceptoreshellip queremospublicar (desplegar)

La clase Application se define como

package javaxwsrscore

import javautilCollectionsimport javautilSet

public abstract class Application private static final SetltObjectgt emptySet = CollectionsemptySet()

public abstract SetltClassltgtgt getClasses()

public SetltObjectgt getSingletons() return emptySet

La clase Application es muy simple Como ya hemos indicado su propoacutesito es proporcionaruna lista de clases y objetos que queremos desplegar

El meacutetodo getClasses() devuelve una lista de clases de servicios web y proveedores JAX-RS Cualquier servicio JAX-RS devuelto por este meacutetodo sigue el modelo per-request queya hemos introducido en la sesioacuten anterior Cuando la implementacioacuten de JAX-RS determinaque una peticioacuten HTTP necesita ser procesada por un meacutetodo de una de estas clases secrearaacute una instancia de dicha clase durante la peticioacuten y se destruiraacute al finalizar la mismaEn este caso estamos delegando en el runtime JAX-RS la creacioacuten de los objetos Lasclases proveedoras son instanciadas por el contenedor JAX-RS y registradas una uacutenica vezpor aplicacioacuten

El meacutetodo getSingletons() devuelve una lista de servicios y proveedores web JAX-RSya instanciados Nosotros como programadores de las aplicaciones somos responsablesde crear estos objetos El runtime JAX-RS iteraraacute a traveacutes de la lista de objetos y los registraraacuteinternamente

Servicios Rest

62

Un ejemplo de uso de una subclase de Application podriacutea ser eacuteste

package orgexpertojava

import javaxwsrscoreApplicationimport javaxwsrsApplicationPath

ApplicationPath(rest)public class ComercioApplication extends Application

public SetltClassltgtgt getClasses() HashSetltClassltgtgt set = new HashSetltClassltgtgt() setadd(ClienteResourceclass) setadd(PedidoResourceclass) return set

public SetltObjectgt getSingletons() JsonWriter json = new JsonWriter() TarjetaCreditoResource servicio = new TarjetaCreditoResource()

HashSetltObjectgt set = new HashSet() setadd(json) setadd(servicio) return set

La anotacioacuten ApplicationPath define la base URL de la ruta para todos nuestrosservicios JAX-RS desplegados Asiacute por ejemplo accederemos a todos nuestros serviciosJAX-RS seraacuten desde la ruta rest cuando los ejecutemos En el ejemplo anterior estamosindicando que ClienteResource y PedidoResource son servicios per-request El meacutetodogetSingletons() devuelve el servicio de tipo TarjetaCreditoResource asiacute como el proveedorJsonWriter (que implementa la interfaz MessageBodyWriter)

Si tenemos al menos una implementacioacuten de la clase Application anotada conApplicationPath esta seraacute detectada y desplegada automaacuteticamente por el servidor deaplicaciones

Podemos aprovechar completamente esta capacidad para escanear y detectarautomaacuteticamente nuestros servicios si tenemos implementada una subclase de Applicationpero dejamos que getSingletons() devuelva el conjunto vaciacuteo y no indicamos nada en elmeacutetodo getClasses() de esta forma

package orgexpertojava

import javaxwsrsApplicationPathimport javaxwsrscoreApplication

ApplicationPath(rest)public class ComercioApplication extends Application

Servicios Rest

63

En este caso el servidor de aplicaciones se encargaraacute de buscar en el directorio WEB-INFclasses y en cualquier fichero jar dentro del directorio WEB-INFlib A continuacioacuten antildeadiraacutecualquier clase anotada con Path o Provider a la lista de cosas que necesitan serdesplegadas y registradas en el runtime JAX-RS

Los servicios REST son atendidos por un servlet que es especiacutefico de la implementacioacutenJAX-RS utilizada por el servidor de aplicaciones El servidor wildfly utiliza la implementacioacutende JAX-RS 20 denomindada resteasy (otra implementacioacuten muy utilizada es jersey porejemplo con el servidor de aplicaciones Glassfish) El runtime de JAX-RS contiene un servletinicializado con un paraacutemetro de inicializacioacuten de tipo javaxwsrsApplication cuyo valor seraacuteinstanciado automaacuteticamente por el servidor de aplicaciones con el nombre de la subclasede Application que sea detectada en el war de nuestra aplicacioacuten

Configuracioacuten mediante un fichero webxml

En la sesioacuten anterior no hemos utilizado de forma expliacutecita la clase Application para configurarel despliegue En su lugar hemos indicado esta informacioacuten en el fichero webxml

ltweb-app version=30 xmlns=httpjavasuncomxmlnsjavaee xmlnsxsi=httpwwww3org2001XMLSchema-instance xsischemaLocation=httpjavasuncomxmlnsjavaee httpjavasuncomxmlnsjavaeeweb-app_3_0xsdgt lt-- Con estas liacuteneas el servidor es el responsable de antildeadir el servlet correspondiente de forma automaacutetica Si en nuestro war tenemos clases anotadas con anotaciones JAX-RS para recibir invocaciones REST eacutestas seraacuten detectadas y registradas--gt ltservlet-mappinggt ltservlet-namegtjavaxwsrscoreApplicationltservlet-namegt lturl-patterngtrestlturl-patterngt ltservlet-mappinggtltweb-appgt

Esta configuracioacuten es equivalente a incluir una subclase de Application sin sobreescribir losmeacutetodos correspondientes En este caso se antildeade de forma dinaacutemica el servlet que sirvelas peticiones REST con el nombre javaxwsrscoreApplication de forma que se detectenautomaacuteticamente todas las clases de recursos y clases proveedoras empaquetadas en el warde la aplicacioacuten

Configuracioacuten en un contenedor que no disponga de una implementacioacuten JAX-RS

Si queremos hacer el despliegue sobre servidores de aplicaciones o servidores web queden soporte a una especificacioacuten de servlets con una versioacuten inferior a la 30 tendremosque configurar MANUALMENTE el fichero webxml para que cargue el servlet de nuestraimplementacioacuten propietaria de JAX-RS (cuyos ficheros jar deberemos incluir en el directorioWEB-INFlib de nuestro war) Un ejemplo de configuracioacuten podriacutea ser eacuteste

Configuracioacuten del fichero webxml (directorio de fuentes webappWEB-INFwebxml)

ltxml version=10gtltweb-appgt ltservletgt

Servicios Rest

64

ltservlet-namegtJAXRSltservlet-namegt ltservlet-classgt orgjbossresteasypluginsserverservletHttpServletDispatcher ltservlet-classgt ltinit-paramgt ltparam-namegt javaxwsrsApplication ltparam-namegt ltparam-valuegt orgexpertoJavaComercioApplication ltparam-valuegt ltinit-paramgt ltservletgt

ltservlet-mappinggt ltservlet-namegtJAXRSltservlet-namegt lturl-patterngtrestlturl-patterngt ltservlet-mappinggtltweb-appgt

En la configuracioacuten anterior estamos indicando de forma expliacutecita el servlet JAX-RS que recibelas peticiones REST que a su vez utilizaraacute la clase Application para detectar queacute servicios yproveedores REST seraacuten desplegados en el servidor

Tambieacuten seraacute necesario incluir la libreriacutea con la implementacioacuten JAX-RS 20 de formaexpliacutecita en el war generado (recordemos que para ello tendremos que utilizar la etiquetaltscopegtcompileltscopegt para que se antildeadan los jar correspondientes)

Libreriacutea con la implementacioacuten de JAX-RS 20

ltdependencygt ltgroupIdgtjavaxltgroupIdgt ltartifactIdgtjavaee-web-apiltartifactIdgt ltversiongt70ltversiongt ltscopegtcompileltscopegtltdependencygt

Servicios Rest

65

26 Ejercicios

Para esta sesioacuten antildeadiremos un nuevo moacutedulo en el que implementaremos un servicio restincorporando los conceptos que hemos explicado durante la sesioacuten En concreto

bull Creamos un moacutedulo Maven con IntelliJ (desde el directorio ejercicios-rest-expertojava ) con el arquetipo webapp-javaee7 tal y como hemos visto en losapuntes de la sesioacuten Las coordenadas del artefacto Maven seraacuten

GroupId orgexpertojava

ArtifactId s2-foro-nuevo

version 10-SNAPSHOT

bull Configuramos el pommxl del proyecto para poder compilar empaquetar y desplegarnuestro servicio en el servidor de aplicaciones Wildfly Consulta los apuntes para ver cuaacuteldebe ser el contenido de las etiquetas ltpropertiesgt ltdependenciesgt y ltbuildgt

bull Vamos a estructurar los fuentes (directorio srcmainjava) de nuestro proyecto en lossiguientes paquetes

orgexpertojavadatos contendraacute clases relacionadas con los datos a los que accedenuestra aplicacioacuten rest Por simplicidad almacenaremos en memoria los datos denuestra aplicacioacuten

orgexpertojavamodelo contiene las clases de nuestro modelo de objetos que seraacutenclases java con atributos y sus correspondientes getters y setters

orgexpertojavarest contiene los recursos JAX-RS que implementan nuestrosservicios rest asiacute como las clases necesarias para automatizar el despliegue de dichosrecursos

Creacioacuten de un recurso creacioacuten y consulta de temas en el foro (05 puntos)

Vamos a crear un recurso JAX-RS al que denominaremos TemasResource (en el paqueteorgexpertojavarest ) En el siguiente ejercicio al configurar la aplicacioacuten haremos que esterecurso sea un singleton Nuestro recurso gestionaraacute sus propios datos en memoria Porejemplo podemos utilizar un atributo private de tipo HashMap en el que almacenaremos lostemas cada uno con un identificador numeacuterico como clave Tambieacuten necesitaremos un atributopara generar las claves para cada uno de los temas Por ejemplo

private MapltInteger Temagt temasDB = new HashMapltInteger Temagt()private int contadorTemas = 0

Fiacutejate que si utilizamos los tipos HashMap e int podemos tener problemasde concurrencia si muacuteltiples usuarios estaacuten realizando peticiones paracrear yo consultar los temas del foro En una situacioacuten real deberiacuteamosutilizar en su lugar los tipos ConcurrentHasMap y AtomicInteger paraevitar el que dos usuarios intentaran crear un nuevo tema con la mismaclave perdieacutendose asiacute uno de los dos temas creados Al tratarse de unejercicio en el que solamente tendremos un cliente no nos plantearaacuteninguacuten problema el trabajar con HashMap e int por lo que podeacuteis elegircualquiera de las dos opciones para realizar el ejercicio

Servicios Rest

66

bull Nuestro recurso estaraacute accesible en el servidor en la ruta temas (relativa a la raiacutez delcontexto de nuestra aplicacioacuten y a la ruta de nuestro servlet JAX-RS que determinaremoscon la anotacioacuten ApplicationPath de nuestra clase Application)

bull En el paquete orgexpertojavamodelo crearemos la clase Tema con los atributosprivados

int idString nombre

y sus correspondientes getters y setters

setId() getId()setNombre() getNombre()

bull Implementamos un primer meacutetodo en el recurso TemasResource denominadocreaTema() para poder crear un nuevo tema en el foro Dicho meacutetodo atenderaacutepeticiones POST a nuestro servicio Los datos de entrada (cadena de caracteres querespresenta el nombre del tema) se pasan a traveacutes de un formulario html en el que tenemosuna uacutenica entrada denominada nombre

Puedes incluir el siguiente contenido en el fichero indexhtml para introducir los datosdesde el navegador

ltDOCTYPE htmlgtlthtmlgtltheadgt lttitlegtStart Pagelttitlegt ltmeta http-equiv=Content-Type content=texthtml charset=UTF-8gtltheadgtltbodygtlth1gtAlta de temas en el foro lth1gtltform action=s2-foro-nuevoresttemas method=postgt Nombre del tema ltinput type=text name=nombre gtltbr gt

ltinput type=submit value=Enviar gtltformgtltbodygtlthtmlgt

Cada nuevo Tema creado se antildeadiraacute a nuestra base de datos en memoria temasDB juntocon un identificador numeacuterico (que se iraacute incrementando para cada nueva instancia creada)

bull Implementamos un segundo meacutetodo para consultar los temas creados en el foro Elmeacutetodo se denominaraacute verTemasTodos() y devuelve (en formato texto) todos los temasactualmente creados Dado que puede haber un gran nuacutemero de ellos vamos a permitirque el usuario decida cuaacutentos elementos como maacuteximo quiere consultar a partir de unaposicioacuten determinada Por defecto si no se indica esta informacioacuten se mostraraacuten comomaacuteximo los primeros 8 temas registrados en el foro Si el identificador a partir del cualqueremos iniciar la consulta es mayor que el nuacutemero de temas almacenados entoncesdevolveremos la cadena No es posible atender la consulta Ejemplos de URIs que aceptadicho meacutetodo son

Servicios Rest

67

temas

en este caso y suponiendo que hayamos creado solamente los tres temas del apartadoanterior el resultado seriacutea

Listado de temas del 1 al 81 animales2 plantas3 ab

temasinicio=2amptotal=2

el resultado seriacutea

Listado de temas del 2 al 32 plantas3 ab

temasinicio=7amptotal=1

el resultado seriacutea

No es posible atender la consulta

Como ya hemos comentado las URIs indicadas en este ejercicio sonrelativas a la raiacutez del contexto de nuestra aplicacioacuten y a la ruta especificadapara nuestros servicios rest Recuerda que si has configurado el pomxmlcomo en la sesioacuten anterior la raiacutez del contexto de la aplicacioacuten vendraacutedada por el valor de la etiqueta ltfinalNamegt anidada en ltbuildgt Ennuestro caso deberiacutea ser s2-foro-nuevo Maacutes adelante fijaremos la rutade nuestros servicios rest como rest Por ejemplo la URI completa parael uacuteltimo apartado seriacutea httplocalhost8080s2-foro-nuevoresttemasinicio=7amptotal=1

Despliegue y pruebas del recurso (05 puntos)

Vamos a construir y desplegar nuestro servicio en el servidor de aplicaciones Para ello vamosa utilizar una subclase de Application que antildeadiremos en el paquete orgexpertojavarestLa ruta en la que se van a servir nuestras peticiones rest seraacute rest Fiacutejate que el recursoque hemos creado es el encargado de gestionar (crear modificarhellip) sus propios datosPor lo tanto necesitamos que nuestro recurso REST sea un singleton Implementa la claseForoApplication y realiza la construccioacuten y despliegue del proyecto A continuacioacutenprueba el servicio utilizando postman Puedes probar la insercioacuten de temas utilizando tambieacutenel formulario a traveacutes de la URI httplocalhost8080s2-foro-nuevo Podemos utilizar lasentradas del apartado anterior de forma que comprobemos que se crean correctamentelos temas animales plantas y ab y que obtenemos los listados correctos tanto si noindicamos el inicio y total de elementos como si decidimos mostrar los temas desde el 2 hastael 3

Servicios Rest

68

Cuando utilices el cliente IntelliJ para probar meacutetodos POST debesproporcionar un Request Body no vaciacuteo En este caso como en lapropia URI incluimos el contenido del mensaje que es el nombre del temaque queremos antildeadir al foro tendraacutes que seleccionar Text aunque norellenemos el campo correspondiente De no hacerlo asiacute obtendremoscomo respuesta un cuerpo de mensaje vaciacuteo y la cabecera de respuestaHTTP11 415 Unsupported Media Type

Muacuteltiples consultas de los temas del foro (05 puntos)

Implementa tres nuevas consultas de los temas del foro de forma que

bull Se pueda realizar una consulta de un tema concreto a partir de su identificador numeacuterico(el meacutetodo solamente debe admitir identificadores formados por uno o maacutes diacutegitos) Si eltema consultado no existe se debe devolver una excepcioacuten con la cabecera de respuestaHTTP11 404 Not Found Por ejemplo

temas2

Debe devolver lo siguiente

Ver el tema 2plantas

temas4

Obtenemos como respuesta un cuerpo de mensaje vaciacuteo y la cabecera de respuestaHTTP11 404 Not Found

bull Se pueda realizar una consulta de los temas que comiencen por uno de los siguientescaracteres a b c oacute d Por ejemplo teniendo en cuenta que hemos introducido los temasanteriores

temasa

Debe devolver lo siguiente

Listado de temas que comienzan por aanimales

temasd

Debe devolver Listado de temas que comienzan por d

bull Se pueda realizar una consulta de los temas que contengan una subcadena de caracteresPor ejemplo teniendo en cuenta que hemos introducido los temas anteriores

temasma + Debe devolver lo siguiente

Listado de temas que contienen la subcadena maanimales

Servicios Rest

69

Creacioacuten de subrecursos (05 puntos)

Vamos a crear el subrecurso MensajesResource (en el paquete orgexpertojavarest)de forma que este recurso gestione la creacioacuten y consulta de mensajes para cada unode los temas del foro Este subrecurso debe atender peticiones desde rutas del tipotemasidentificadorTemamensajes siendo identificadorTema la clave numeacutericaasociada a uno de los temas almacenados

bull En este caso nuestro subrecurso no seraacute un singleton por lo que necesitaremos almacenarlos mensajes en otra clase diferente (ya que crearemos una nueva instancia del recursopara cada peticioacuten) La clase DatosEnMemoria (en el paquete orgexpertojavadatos)seraacute la encargada de almacenar en memoria la informacioacuten de los mensajes publicadospara cada tema Por ejemplo puedes utilizar los siguientes campos estaacuteticos paragestionar los mensajes

public static MapltMensaje Stringgt mensajesDB = new HashMapltMensaje Stringgt()

La clave seraacute el propio mensaje (objeto Mensaje que se asociaraacute al tema correspondiente)

public static int contadorMen = 0

Como ya hemos comentado puedes usar ConcurrentHashMap yAtomicInteger en lugar de los tipos anteriores para evitar problemas deconcurrencia

bull En el paquete orgexpertojavadatos crearemos la clase Mensaje con los atributosprivados

int idString textoString autor=anonimo

y sus correspondientes getters y setters

setId() getId()setTexto() getTexto()setAutor() getAutor()

bull Vamos a crear un meacutetodo para poder realizar la publicacioacuten de un mensaje de texto en elforo en uno de los temas ya creados Independientemente del tipo de peticioacuten realizadasobre los mensajes si el tema indicado en la URI no existe lanzaremos la excepcioacutenWebApplicationException(ResponseStatusNOT_FOUND) Veamos alguacuten ejemplo

Deberemos poder realizar una peticioacuten POST a temas1mensajes con el cuerpo demensaje = Mensaje numero 1 El mensaje creado por defecto tendraacute asociado el autoranonimo

Servicios Rest

70

Si realizamos una peticioacuten para antildeadir un mensaje a la URI temas9mensajesdeberiacuteamos obtener como cabecera de respuesta HTTP11 404 Not Foundindependientemente del cuerpo del mensaje

bull Vamos a crear un meacutetodo para realizar una consulta de todos los mensajes publicados enun tema concreto Por ejemplo

Una peticioacuten GET a temas1mensajes deberiacutea dar como resultado

Lista de mensajes para el tema animales1 Mensaje anonimo

Si realizamos una peticioacuten GET a la URI temas9mensajes deberiacuteamos obtenercomo cabecera de respuesta HTTP11 404 Not Found independientemente delcuerpo del mensaje

bull Finalmente vamos a antildeadir dos nuevos meacutetodos para (a) antildeadir un nuevo mensajeen un tema concreto indicando el autor del mensaje Como restriccioacuten el nombre delautor deberaacute estar formado solamente por caracteres alfabeacuteticos utilizando mayuacutesculas ominuacutesculas y como miacutenimo tiene que tener un caracter y (b) consultar todos los mensajesque un determinado autor ha publicado en el foro en un tema determinado

Una peticioacuten POST a la URI temas1mensajespepe con el cuerpo de mensaje convalor mensaje de pepe deberiacutea crear un nuevo mensaje para el tema con identificador2 y devolver como resultado el nuevo id (yo la URI del nuevo recurso en la cabecerade respuesta Location si seguimos la ortodoxia REST) En caso de que devolvamos laURI del nuevo recurso podemos utilizar la orden

return Responsecreated(uriInfogetAbsolutePathBuilder()

segment(StringvalueOf(id))

build())

build()

Obtenemos el path absoluto de la uri que nos ha invocadoAntildeadimos el identificador id del nuevo recurso creadoConstruimos la nueva URIConstruimos el objeto Response

Veremos coacutemo manipular objetos de tipo Response en sesiones posteriores

Recuerda que para acceder al cuerpo de la peticioacuten basta con definir unparaacutemetro de tipo String JAX-RS automaacuteticamente lo instanciaraacute con elcuerpo de la peticioacuten como una cadena

bull Una peticioacuten GET a la URI temas1mensajesanonimo dariacutea como resultado

Lista de mensajes tema= animales y autor= anonimo

1 Mensaje anonimo

bull Una peticioacuten GET a la URI temas1mensajes dariacutea como resultado

Lista de mensajes para el tema animales

Servicios Rest

71

1 Mensaje anonimo2 mensaje de pepe

bull Una peticioacuten GET a la URI temas1mensajesroberto dariacutea como resultado

Lista de mensajes tema= animales y autor= roberto

Servicios Rest

72

3 Manejadores de contenidos Respuestas del servidor ymanejo de excepciones

En la sesioacuten anterior hemos hablado de coacutemo inyectar informacioacuten contenida en las cabecerasde las peticiones HTTP ahora nos detendremos en el cuerpo del mensaje tanto de la peticioacutencomo de la respuesta En el caso de las peticiones explicaremos el proceso de transformarlos datos de entrada en objetos Java para poder ser procesados por nuestros serviciosCon respecto a las respuestas proporcionadas por nuestros servicios analizaremos tanto loscoacutedigos de respuesta por defecto como la elaboracioacuten de respuestas complejas y manejo deexcepciones

31 Proveedores de entidades

JAX-RS define lo que se denominan proveedores de entidades que son clases queproporcionan servicios de mapeado entre las representaciones del cuerpo del mensaje HTTPy los correspondientes tipos java que utilizaremos en nuestros recursos (paraacutemetros en losmeacutetodos o bien como tipo de la respuesta de los mismos) Las entidades tambieacuten se conocencon el nombre de message payload o simplemente como payload y representan elcontenido del cuerpo del mensaje HTTP

ProvidersEl runtime de JAX-RS puede extenderse (ampliarse) utilizandoclases proveedoras (providers) suministradas por nuestra aplicacioacutenConcretamente JAX-RS nos proporciona un conjunto de interfaces quepodemos implementar en nuestra aplicacioacuten creando asiacute dichas clasesproveedoras de entidades (entity providers) La especificacioacuten de JAX-RS define un proveedor como una clase que implementa una o maacutesinterfaces JAX-RS (de entre un conjunto determinado) y que puedenanotarse con provider para ser descubiertas de forma automaacuteticapor el runtime de JAX-RS

Nuestra aplicacioacuten puede proporcionar su propio mapeado entre representaciones(tipos MIME) del mensaje de entrada y tipos Java implementando las interfacesMessageBodyWriter y MessageBodyReader convirtieacutendose asiacute en clases proveedorasde entidades (entity providers) Por ejemplo podemos tener nuestro propio proveedor deentidades para el formato XML o JSON de forma que utilizando las libreriacuteas de java paraprocesamiento XML o JSON (Java API for XML Processing JAXP1 y Java API for JSONProcessing JSON-P2) implementemos el serializadodeserializado del cuerpo del mensajeHTTP de entrada cuando eacuteste presente los tipos MIME applicationxml o application_jsonLas clases que realizan dichos mapeados son clases entity provider

Interfaz javaxwsrsextMessageBodyReader

La interfaz MessageBodyReader define el contrato entre el runtime de JAX-RS y loscomponentes que proporcionan servicios de mapeado desde diferentes representaciones(indicadas como tipos mime) al tipo Java correspondiente Cualquier clase que quieraproporcionar dicho servicio debe implementar la interfaz MessageBodyReader y debeanotarse con Provider para poder ser detectada de forma automaacutetica por el runtime deJAX-RS

1 httpswwwjcporgenjsrdetailsummaryid=2062 httpsjcporgenjsrdetailid=353

Servicios Rest

73

La secuencia loacutegica de pasos seguidos por una implementacioacuten de JAX-RS cuando se mapeael cuerpo de un mensaje HTTP de entrada a un paraacutemetro de un meacutetodo Java es la siguiente

1 Se obtiene el media type de la peticioacuten (valor de la cabecera HTTP Content-Type ) Sila peticioacuten no contiene una cabecera Content-Type se usaraacute applicationoctet-stream

2 Se identifica el tipo java del paraacutemetro cuyo valor seraacute mapeado desde el cuerpo delmensaje

3 Se localiza la clase MessageBodyReader que soporta el media type de la peticioacuten y seusa su meacutetodo readFrom() para mapear el contenido del cuerpo del mensaje HTTP enel tipo Java que corresponda

4 Si no es posible encontrar el MessageBodyReader adecuado se genera la excepcioacutenNotSupportedException con el coacutedigo 405

Interfaz javaxwsrsextMessageBodyWriter

La interfaz MessageBodyWriter define el contrato entre el runtime de JAX-RS ylos componentes que proporcionan servicios de mapeado desde un tipo Java a unarepresentacioacuten determinada Cualquier clase que quiera proporcionar dicho servicio debeimplementar la interfaz MessageBodyWriter y debe anotarse con Provider para poderser detectada de forma automaacutetica por el runtime de JAX-RS

La secuencia loacutegica de pasos seguidos por una implementacioacuten de JAX-RS cuando se mapeaun valor de retorno de un meacutetodo del recurso a una entidad del cuerpo de un mensaje HTTPes la siguiente

1 Se obtiene el objeto que seraacute mapeado a la entidad del cuerpo del mensaje

2 Se determina el media type de la respuesta

3 Se localiza la clase MessageBodyWriter que soporta el objeto que seraacute mapeado a laentidad del cuerpo del mensaje HTTP y se utiliza su meacutetodo writeTo() para realizardicho mapeado

4 Si no es posible encontrar el MessageBodyWriter adecuado se generala excepcioacuten InternalServerErrorException (que es una subclase deWebApplicationException ) con el coacutedigo 500

32 Proveedores de entidad estaacutendar incluidos en JAX-RS

Cualquier implementacioacuten de JAX-RS debe incluir un conjunto de implementaciones deMessageBodyReader y MessageBodyWriter de forma predeterminada para ciertascombinaciones de tipos Java y media types

Table 3 Proveedores de entidades estaacutendar de una implementacioacuten JAX-RS

Tipo Java Media Type

byte[] (Cualquier media type)

javalangString (Cualquier media type)

javaioInputStream (Cualquier media type)

Servicios Rest

74

Tipo Java Media Type

javaioReader (Cualquier media type)

javaioFile (Cualquier media type)

javaxactivationDataSource (Cualquier media type)

javaxxmltransformSource textxml applicationxml application+xml(tipos basados en xml)

javaxxmlbindJAXBElement andapplication-supplied JAXB classes

textxml applicationxml application+xml(tipos basados en xml)

MultivaluedMapltStringStringgt applicationx-www-form-urlencoded(Contenido de formularios)

StreamingOutput (Cualquier media type) (SoacuteloMessageBodyWriter )

javalangBoolean javalangCharacterjavalangNumber

textplain

A continuacioacuten comentaremos algunos de estos proveedores de entidades estaacutendar oconversores por defecto que permiten convertir el cuerpo del mensaje HTTP a objetos Javade diferentes tipos y viceversa

javaxwsrscoreStreamingOutput

StreamingOutput es una interfaz callback que implementamos cuando queremos tratar comoun flujo continuo (streaming) el cuerpo de la respuesta Constituye una alternativa ligera aluso de MessageBodyWriter

public interface StreamingOutput void write(OutputStream output) throws IOException WebApplicationException

Implementamos una instancia de esta interfaz y la utilizamos como tipo de retorno denuestros meacutetodos de recursos Cuando el runtime de JAX-RS estaacute listo para escribir elcuerpo de respuesta del mensaje se invoca al meacutetodo write() de la instancia deStreamingOutput Veamos un ejemplo

Path(miservicio) public class MiServicio GET Produces(textplain) StreamingOutput get() return new StreamingOutput() public void write(OutputStream output) throws IOException WebApplicationException outputwrite(hello worldgetBytes())

Hemos utilizado una clase interna anoacutenima que implementa la interfaz StreamingOutputen lugar de crear una clase puacuteblica separada La razoacuten de utilizar una clase interna es porque

Servicios Rest

75

en este caso al contener tan pocas liacuteneas de coacutedigo resulta beneficioso mantener dicha loacutegicadentro del meacutetodo del recurso JAX-RS de forma que el coacutedigo sea maacutes faacutecil de seguirNormalmente no tendremos necesidad de reutilizar la loacutegica implementada en otros meacutetodospor lo que no tiene demasiado sentido crear otra clase especiacutefica

iquestY por queacute no inyectamos un OutputStream directamente iquestPor queacute necesitamos un objetocallback La razoacuten es que asiacute dejamos que el runtime de JAX-RS maneje la salida de lamanera que quiera Por ejemplo por razones de rendimiento puede ser conveniente que JAX-RS utilice un thread para responder diferente del thread de peticioacuten

javaioInputStream javaioReader

Para leer el cuerpo de un mensaje de entrada podemos utilizar las clases InputStream oReader Por ejemplo

Path()public class MiServicio PUT Path(dato) public void modificaDato(InputStream is) byte[] bytes = readFromStream(is) String input = new String(bytes) Systemoutprintln(input)

private byte[] readFromStream(InputStream stream) throws IOException ByteArrayOutputStream baos = new ByteArrayOutputStream() byte[] buffer = new byte[1000] int wasRead = 0 do wasRead = streamread(buffer) if (wasRead gt 0) baoswrite(buffer 0 wasRead) while (wasRead gt -1) return baostoByteArray()

En este caso estamos leyendo bytes a partir de un javaioInputStream para convertirloen una cadena de caracteres que mostramos por pantalla

En el siguiente ejemplo creamos un javaioLineNumberReader a partir de un objetoReader e imprimimos cada liacutenea del cuerpo del mensaje de entrada

PUTPath(maslineas)public void putMasLineas(Reader reader) LineNumberReader lineReader = new LineNumberReader(reader) do String line = lineReaderreadLine() if (line = null) Systemoutprintln(line)

Servicios Rest

76

while (line = null)

No estamos limitados solamente a utilizar instancias de InputStream yo Reader paraleer el cuerpo de los mensajes de entrada Tambieacuten podemos devolver dichos objetos comorespuesta Por ejemplo

Path(fichero)public class FicheroServicio private static final String basePath =

GET Path(rutafichero ) Produces(textplain) public InputStream getFichero(PathParam(rutafichero) String path) FileInputStream is = new FileInputStream(basePath + path) return is

Aquiacute estamos inyectando un valor PathParam para crear una referencia a un fichero realde nuestro disco duro Creamos una instancia de javaioFileInputStream a partir delvalor de la ruta inyectada como paraacutemetro y la devolvemos como cuerpo de nuestro mensajede respuesta La implementacioacuten de JAX-RS leeraacute la respuesta de este stream de entrada y laalmacenaraacute en un buffer para posteriormente escribirla de forma incremental en el stream desalida de la respuesta En este caso debemos especificar la anotacioacuten Produces para quela implementacioacuten de JAX-RS conozca el valor que debe asignar a la cabecera Content-Type en la respuesta

javaioFile

Se pueden utilizar instancias de la clase javaioFile para entrada y salida decualquier MIME-TYPE (especificado en Content-Type yo Accept y en las anotacionesProduces yo Consumes ) El siguiente coacutedigo por ejemplo devuelve una referencia aun fichero en nuestro disco

Path(fichero)public class FicheroServicio private static final String baseRuta =

GET Path(rutafichero ) Produces(textplain) public File getFichero(PathParam(rutafichero) String ruta) return new File(baseRuta + ruta)

En este caso inyectamos el valor de la ruta del fichero con la anotacioacuten PathParam A partirde dicha ruta creamos un objeto javaioFile y lo devolvemos como cuerpo del mensaje

Servicios Rest

77

de respuesta La implementacioacuten JAX-RS leeraacute la informacioacuten abriendo un InputStreambasado en esta referencia al fichero y la escribiraacute en un buffer Posteriormente y de formaincremental volveraacute a escribir el contenido del buffer en el stream de salida de la respuesta Aligual que en el ejemplo anterior debemos especificar la anotacioacuten Produces para que JAX-RS sepa coacutemo rellenar la cabecera Content-Type de la respuesta

Tambieacuten podemos inyectar instancias de javaioFile a partir del cuerpo del mensaje dela peticioacuten Por ejemplo

POSTPath(masdatos)public void post(File fichero) Reader reader = new Reader(new FileInputStream(fichero)) LineNumberReader lineReader = new LineNumberReader(reader) do String line = lineReaderreadLine() if (line = null) Systemoutprintln(line) while (line = null)

En este caso la implementacioacuten de JAX-RS crea un fichero temporal en el disco para laentrada Lee la informacioacuten desde el buffer de la red y guarda los bytes leiacutedos en este ficherotemporal En el ejemplo los datos leiacutedos desde la red estaacuten representados por el el objetoFile inyectado por el runtime de JAX-RS (recuerda que soacutelo puede haber un paraacutemetro sinanotaciones en los meacutetodos del recurso y que eacuteste representa el cuerpo del mensaje de lapeticioacuten HTTP) A continuacioacuten el meacutetodo post() crea un javaioFileInputStreama partir del objeto File inyectado Finalmente utilizamos eacuteste stream de entrada para crearun objeto LineNumberReader y mostrar los datos por la consola

byte[]

Podemos utilizar un array de bytes como entrada y salida para cualquier tipo especificadocomo media-type A continuacioacuten mostramos un ejemplo

Path()public class MiServicio GET Produces(textplain) public byte[] get() return hello worldgetBytes()

POST Consumes(textplain) public void post(byte[] bytes) Systemoutprintln(new String(bytes))

Para cualquier meacutetodo de recurso JAX-RS que devuelva un array de bytes debemosespecificar la anotacioacuten Produces para que JAX-RS sepa queacute valor asignar a la cabeceraContent-Type

Servicios Rest

78

String char[]

La mayor parte de formatos en internet estaacuten basados en texto JAX-RS puede convertircualquier formato basado en texto a un String o a cualquier array de caracteres Porejemplo

Path()public class MiServicio GET Produces(applicationxml) public String get() return ltcustomergtltnamegtSergio Garcialtnamegtltcustomergt

POST Consumes(textplain) public void post(String str) Systemoutprintln(str)

Para cualquier meacutetodo de recurso JAX-RS que devuelva un Sring o un array de caracteresdebemos especificar la anotacioacuten Produces para que JAX-RS sepa que valor asignar a lacabecera Content-Type

MultivaluedMapltString Stringgt y formularios de entrada

Los formularios HTML son usados habitualmente para enviar datos a servidores web Losdatos del formulario estaacuten codificados con el media type applicationx-www-form-urlencoded Ya hemos visto como utilizar la anotacioacuten FormParam para inyectarparaacutemetros individuales de un formulario de las peticiones de entrada Tambieacuten podremosinyectar una instancia de MultivaluedMapltString Stringgt que representa todos losdatos del formulario enviado en la peticioacuten Por ejemplo

Path() public class MiServicio POST Consumes(applicationx-www-form-urlencoded) Produces(applicationx-www-form-urlencoded) public MultivaluedMapltStringStringgt post( MultivaluedMapltString Stringgt form) el formulario tiene los campos fieldName1 y fieldName2 Systemoutprintln(formgetFirst(fieldName1)) Systemoutprintln(formgetFirst(fieldName2)) return form

En este coacutedigo nuestro meacutetodo post() acepta peticiones POST y recibe unMultivaluedMapltStringStringgt que contiene todos los datos de nuestro formularioEn este caso tambieacuten devolvemos una instancia de un formulario como respuesta

Los datos del formulario pueden representarse en el cuerpo de la petcioacuten como paresnombre=valor separados por amp Por ejemplo

Servicios Rest

79

fieldName1=valor20con20espaciosampfielName2=otroValor

Los espacios en blanco se codifican como 20 No es necesario poner comillas

33 Muacuteltiples representaciones de recursos

Por defecto un recurso RESTful se produce o consume con el tipo MIME Un recursoRESTful puede restringir los media types que soporta tanto en la peticioacuten como en larespuesta utilizando las anotaciones Consumes y Produces respectivamente Estasanotaciones pueden especificarse como ya hemos visto a nivel de clase o de meacutetodo derecurso Las anotaciones especificadas sobre el meacutetodo prevalecen sobre las de la clase Laausencia de estas anotaciones es equivalente a su inclusioacuten con el tipo MIME () es decirsu ausencia implica que se soporta cualquier tipo

A continuacioacuten mostramos un ejemplo en el que un Pedido puede producirse tanto en formatoxml como en formato json

GETPath(id)Produces(applicationxml applicationjson)public Pedido getPedido(PathParam(id)int id)

El meacutetodo getPedido() puede generar ambas representaciones para el pedido El tipo exactode la respuesta viene determinado por la cabecera HTTP Accept de la peticioacuten

Otro ejemplo en el que pueden consumirse varios tipos MIME puede ser el siguiente

POSTPath(id)Consumes(applicationxml applicationjson)public Pedido addPedido(PathParam(id)int id)

En este caso el formato consumido vendraacute dado por el valor de la cabecera HTTP Content-Type de la peticioacuten

JAX-RS 20 nos permite indicar la preferencia por un media type en el lado del servidorutilizando el paraacutemetro qs (quality on service) qs toma valores entre 0000 y 1000 eindica la calidad relativa de una representacioacuten comparado con el resto de representacionesdisponibles Una representacioacuten con un valor de qs de 0000 nunca seraacute elegido Unarepresentacioacuten sin valor para el paraacutemetro qs se asume que dicho valor es 1000

Ejemplo

POSTPath(id)Consumes(applicationxml qs=075 applicationjson qs=1)public Pedido addPedido(PathParam(id)int id)

Si un cliente realiza una peticioacuten y no manifiesta ninguna preferencia por ningunarepresentacioacuten en particular o con una cabecera Accept con valor application entonces el

Servicios Rest

80

servidor seleccionaraacute la representacioacuten con el valor de qs maacutes alto (en este caso applicationjson) Los valores de qs son relativos y como tales solamente son comparables con otrosvalores qs dentro de la misma instancia de la anotacioacuten Consumes (o Produces)

Los clientes pueden indicar tambieacuten sus preferencias utilizando otro factor relativo de calidaden forma de paraacutemetro denominado q El valor del paraacutemetro q se utiliza para ordenar elconjunto de tipos aceptados q toma valores entre 0000 y 1000 (maacutexima preferencia) Al igualque antes Los valores de q son relativos y como tales solamente son comparables con otrosvalores q dentro de la misma cabecera Accept o Content-type

Las preferencias del servidor (valores de los paraacutemetros qs) soacutelo se tienenen cuenta si el cliente acepta muacuteltiples media types con el mismo valorde q

Veamos un ejemplo

GETPath(id)Produces(applicationxml qs=1 applicationjson qs=075)public Pedido getPedido(PathParam(id)int id)

Supongamos que un cliente lanza una petcioacuten GET con una valor para la cabecera Acceptde application q=05 texthtml En este caso el servidor determina que los tipos MIMEapplicationxml y applicationjson tienen la misma preferencia por parte del cliente (con valorde 05) por lo tanto el servidor elegiraacute la representacioacuten applicationjson ya que tiene un valorde qs mayor

34 Introduccioacuten a JAXB

JAXB (Java Architecture for XML Binding) es una especificacioacuten Java antigua (JSR 2223) yno estaacute definida por JAX-RS JAXB es un framework de anotaciones que mapea clases Java aXML y esquemas XML Es extremadamente uacutetil debido a que en lugar de interactuar con unarepresentacioacuten abstracta de un documento XML podemos trabajar con objetos Java realesque estaacuten maacutes cercanos al dominio que estamos modelando JAX-RS proporciona soportepara JAXB pero antes de revisar los manejadores de contenidos JAXB incuidos con JAX-RSveamos una pequentildea introduccioacuten al framework JAXB

Como ya hemos dicho si queremos mapear una clase Java existente a XML podemos utilizarJAXB a traveacutes de un conjunto de anotaciones Veaacutemoslo mejor con un ejemplo

XmlRootElement(name=cliente)XmlAccessorType(XmlAccessTypeFIELD)public class Cliente XmlAttribute protected int id

XmlElement protected String nombre

public Customer()

3 httpsjcporgaboutJavacommunityprocessmreljsr222index2html

Servicios Rest

81

public int getId() return thisid public void setId(int id) thisid = id

public String getNombre() return thisnombre public void setNombre(String nombre thisnombre = nombre

La anotacioacuten javaxxmlbindannotationXmlRootElement se utiliza en clasesjava para denotar que representan elementos XML (etiqueta XML raiacutez) En este caso estamosdiciendo que la clase Java representa un documento XML que tiene como etiqueta raiacutezltclientegt Las clases java anotadas con XmlRootElement se denomina beansJAXB

La anotacioacuten javaxxmlbindannotationXmlAttribute la hemos asociado alcampo id de nuestra clase Cliente Esta anotacioacuten indica que el campo id de la clasedebe mapearse como el atributo id del elemento raiacutez ltclientegt del documento XML Laanotacioacuten XmlAttribute tiene un atributo name de forma que podemos especificar elnombre exacto del atributo XML dentro del documento Por defecto tiene el mismo nombreque el campo anotado

Hemos utilizado la anotacioacuten javaxxmlbindannotationXmlElement en el camponombre de la clase Cliente Esta anotacioacuten indica a JAXB que debe mapearse el camponombre como el elemento ltnombregt anidado en la etiqueta raiacutez ltclientegt Igual queantes podemos especificar el nombre concreto del elememto XML Por defecto toma el mismonombre que el correspondiente campo anotado

La anotacioacuten javaxxmlbindannotationXmlAccessorType permite controlar laserializacioacuten por defecto de los atributos de la clase Esta anotacioacuten soacutelo puede ser usadaconjuntamente con XmlRootElement (y alguna otra anotacioacuten que no mostramos aquiacute)Hemos usado como valor XmlAccessTypeFIELD lo que significa que por defecto se debenserializar todos los campos (fields) de la clase (esteacuten anotados o no) y las propiedades(properties) de la clase que tengan anotaciones JAXB (a menos que la anotacioacuten seaXMLTransient)

Si alguno de los campos de la clase no tiene anotaciones JAXB asociadas por defecto seserializaraacuten como elementos (etiquetas) en el documento XML correspondiente Seguacuten ladocumentacioacuten4 de JAXB un campo es una variable de instancia no estaacutetica (normalmenteprivada)

Las propiedades de la clase vienen dadas por las combinaciones gettersetter de los atributosde la clase El coacutedigo anterior tiene dos propiedades nombre (dado por el par getNombresetNombre_) e id (par getIdsetId) Normalmente se anotan los meacutetodos getter Dichaspropiedades no estaacuten anotadas por lo que JAXB no las serializaraacute

Al proceso de serializar (convertir) un objeto Java en un documento XMLse le denomina marshalling El proceso inverso la conversioacuten de XML aobjetos Java se denomina unmarshalling

Con las anotaciones anteriores un ejemplo de una instancia de nuestra clase Cliente conun id de 42 y el valor de nombre Pablo Martinez tendriacutea el siguiente aspecto

ltcliente id=42gt

4 httpsjaxbjavanetnonav226docsapi

Servicios Rest

82

ltnombreCompletogtPablo MartinezltnombreCompletogtltclientegt

Observamos que se han serializado los campos (variables de instancia de la clase)

Si no especificamos la anotacioacuten XmlAccessorType por defecto se utilizaraacute

XmlAccessorType(XmlAccessTypePUBLIC_MEMBER)

El valor XmlAccessTypePUBLIC_MEMBER indica a JAXB que se deben serializar todoslos campos puacuteblicos de la clase todos los campos anotados y todas las propiedades (paresgettersetter) a menos que esteacuten anotadas con XMLTransient

Tambieacuten podriacuteamos utilizar

XmlAccessorType(XmlAccessTypeNONE)

En este caso la anotacioacuten XmlAccessTypeNONE indica soacutelo se deben serializar aquellaspropiedades yo campos de la clase que esteacuten anotados

A continuacioacuten indicamos en forma de tabla el uso de los diferentes XmlAccessType

Table 4 Valores utilizados para XmlAccessorType() conjuntamentecon XmlRootElement()

Valor Significado

XmlAccessTypePUBLIC_MEMBER Serializacioacuten por defecto si no se especificaXmlAccessorType() Serializa laspropiedades (pares gettersetter) y campospuacuteblicos a menos que esteacuten anotados conXMLTransient Si alguacuten campo nopuacuteblico estaacute anotado tambieacuten se serializa

XmlAccessTypeFIELD Serializa todos los campos (puacuteblicos oprivados) a menos que esteacuten anotados conXMLTransient

XmlAccessTypeNONE Solamente serializa aquellos camposy propiedades que esteacuten anotadas conanotaciones JAXB

XmlAccessTypePROPERTY Serializa cada par gettersetter a menosque esteacuten anotados con XMLTransient Si alguacuten campo (no puacuteblico) estaacute anotadotambieacuten se serializa

Podemos utilizar la anotacioacuten XmlElement para anidar otras clases anotadas con JAXBPor ejemplo supongamos que queremos antildeadir una clase Direccion a nuestra claseCliente

XmlRootElement(name=direccion)

Servicios Rest

83

XmlAccessorType(XmlAccessTypeFIELD)public class Direccion XmlElement protected String calle

XmlElement protected String cludad

XmlElement protected String codPostal

getters y setters

Simplemente tendriacuteamos que antildeadir el campo de tipo Direccion a nuestra clase Clientede la siguiente forma

XmlRootElement(name=cliente)XmlAccessorType(XmlAccessTypeFIELD)public class Cliente XmlAttribute protected int id

XmlElement protected String nombreCompleto

XmlElement protected Direccion direccion

public Customer()

getters y setters

En este caso una instancia de un Cliente con valores id =56 nombre =RicardoLopez calle =calle del oso 35 ciudad =Alicante y coacutedigo_postal =01010 seriacuteaserializado como

ltcliente id=56gt ltnombregtRicardo Lopezltnombregt ltdirecciongt ltcallegtcalle del oso 35ltcallegt ltciudadgtAlicanteltciudadgt ltcodPostalgt01010ltcodPostalgt ltdirecciongt ltclientegt

Veamos otro ejemplo Supongamos que tenemos el recurso EstadoResource con meacutetodosque responden a peticiones http GET y PUT

Path(estado)

Servicios Rest

84

public class EstadoResource

private static EstadoBean estadoBean = new EstadoBean()

GET Produces(applicationxml) public EstadoBean getEstado() return estadoBean

PUT Consumes(applicationxml) public void setEstado(EstadoBean estado) thisestadoBean = estado

En este caso la clase EstadoBean debe utilizar anotaciones JAXB para poder ser convertidaautomaacuteticamente por el runtime de JAX-RS en un documento XML y viceversa

XmlRootElement(name = estadoImpresora)public class EstadoBean

public String estado = Idle public int tonerRestante = 25 public ListltTareaBeangt tareas = new ArrayListltTareaBeangt()

Por defecto la anotacioacuten XmlRootElement realiza la serializacioacuten de la claseEstadoBean en formato xml utilizando los campos puacuteblicos y propiedades definidas en laclase ( estado tonerRestante y tareas ) Vemos que el campo tareas a su vez esuna coleccioacuten de elementos de tipo TareaBean que tambieacuten necesitan ser serializados Acontinuacioacuten mostramos la implementacioacuten de la clase TareaBeanjava

XmlRootElement(name = tarea)public class TareaBean public String nombre public String estado public int paginas

public TareaBean()

public TareaBean(String nombre String estado int paginas) thisnombre = nombre thisestado = estado thispaginas = paginas

Para serializar la clase es necesario que la clase tenga un constructor sin paraacutemetros

Servicios Rest

85

Si accedemos al servicio anterior nos devolveraacute la informacioacuten sobre el estado de la siguienteforma (resultado devuelto por el meacutetodo getEstado() anotado con GET)

ltxml version=10 encoding=UTF-8 standalone=yesgtltestadoImpresoragt ltestadogtIdleltestadogt lttonerRestantegt25lttonerRestantegt lttareasgt ltnombregttextodocltnombregt ltestadogtimprimiendoltestadogt ltpaginasgt13ltpaginasgt lttareasgt lttareasgt ltnombregttexto2docltnombregt ltestadogten esperaltestadogt ltpaginasgt5ltpaginasgt lttareasgtltestadoImpresoragt

Podemos observar que se utiliza el elemento xml lttareasgt para representar cada una de lasinstancias de TareaBean las cuales forman parte de de la lista del campo tareas de nuestraclase EstadoBean

Vamos a usar las anotaciones XmlAttribute y XmlElement de la siguiente forma

XmlRootElement(name=estadoImpresora)public class EstadoBean XmlAttribute(name=valor) public String estado = Idle XmlAttribute(name=toner) public int tonerRestante = 25 XmlElement(name=tarea) public ListltTareaBeangt tareas = new ArrayListltTareaBeangt()

En este caso el XML resultante quedariacutea de la siguiente forma

ltestadoImpresora valor=Idle toner=25gt lttareagt ltnombregttextodocltnombregt ltestadogtimprimiendoltestadogt ltpaginasgt13ltpaginasgt lttareagt lttareagt ltnombregttexto2docltnombregt ltestadogten esperaltestadogt ltpaginasgt5ltpaginasgt lttareagtltestadoImpresoragt

Servicios Rest

86

Si no se indica lo contrario por defecto se convierten los campos a elementos del XML

En caso de que los campos no sean puacuteblicos etiquetaremos los getterscorrespondiente (propiedades de la clase)

Hemos visto que para las listas el nombre que especificamos en XmlElement se utilizapara nombrar cada elemento de la lista Si queremos que ademaacutes se incluya un elemento queenvuelva a toda la lista podemos utilizar la etiqueta XmlElementWrapper

XmlRootElement(name=estadoImpresora)public class EstadoBean

XmlAttribute(name=valor) public String estado = Idle

XmlAttribute(name=toner) public int tonerRestante = 25

XmlElementWrapper(name=tareas) XmlElement(name=tarea) public ListltTareaBeangt tareas = new ArrayListltTareaBeangt()

En este caso tendremos un XML como el que se muestra a continuacioacuten

ltestadoImpresora valor=Idle toner=25gt lttareasgt lttareagt ltnombregttextodocltnombregt ltestadogtimprimiendoltestadogt ltpaginasgt13ltpaginasgt lttareagt lttareagt ltnombregttexto2docltnombregt ltestadogten esperaltestadogt ltpaginasgt5ltpaginasgt lttareagt lttareasgtltestadoImpresoragt

Para etiquetar una lista tambieacuten podemos especificar distintos tipos de elemento seguacutenel tipo de objeto contenido en la lista Por ejemplo supongamos que en el ejemploanterior la clase TareaBean fuese una clase abstracta que tiene dos posible subclasesTareaSistemaBean y TareaUsuarioBean Podriacuteamos especificar una etiqueta distintapara cada elemento de la lista seguacuten el tipo de objeto del que se trate con la etiquetaXmlElements de la siguiente forma

XmlElementWrapper(name=tareas) XmlElements( XmlElement(name=usuariotype=TareaUsuarioBeanclass

Servicios Rest

87

XmlElement(name=sistematype=TareaSistemaBeanclass) public ListltTareaBeangt tareas = new ArrayListltTareaBeangt()

De esta forma podriacuteamos tener un XML como el siguiente

ltestadoImpresora valor=Idle toner=25gt lttareasgt ltusuariogt ltnombregttextodocltnombregt ltestadogtimprimiendoltestadogt ltpaginasgt13ltpaginasgt ltusuariogt ltsistemagt ltnombregttexto2docltnombregt ltestadogten esperaltestadogt ltpaginasgt5ltpaginasgt ltsistemagt lttareasgtltestadoImpresoragt

Hemos visto que por defecto se serializan todos los campos Si queremos excluir algunode ellos de la serializacioacuten podemos hacerlo anotaacutendolo con XmlTransient Comoalternativa podemos cambiar el comportamiento por defecto de la serializacioacuten de la claseetiquetaacutendola con XmlAccessorType Por ejemplo

XmlAccessorType(NONE)XmlRootElement(name=estadoImpresora)public class EstadoBean

En este uacuteltimo caso especificando como tipo NONE no se serializaraacute por defectoninguacuten campo soacutelo aquellos que hayamos anotado expliacutecitamente con XmlElement oXmlAttribute Los campos y propiedades (getters) anotados con estas etiquetas seserializaraacuten siempre Ademaacutes de ellos tambieacuten podriacuteamos especificar que se serialicenpor defecto todos los campos puacuteblicos y los getters ( PUBLIC_MEMBER ) todos los getters( PROPERTY ) o todos los campos ya sean puacuteblicos o privados ( FIELD ) Todos los que seserialicen por defecto sin especificar ninguna etiqueta lo haraacuten como elemento

Por uacuteltimo si nos interesa que toda la representacioacuten del objeto venga dada uacutenicamente porel valor de uno de sus campos podemos etiquetar dicho campo con XmlValue

Clase JAXBContext

Para serializar clases Java a y desde XML es necesario interactuar con la clasejavaxxmlbindJAXBContext Esta clase nos permite inspeccionar las clasesJava para comprender la estructura de nuestras clases anotadas Dichas clasesse utilizan como factoriacuteas para las interfaces javaxxmlbindMarshaller yjavaxxmlbindUnmarshaller Las instancias de Marshaller se utilizan para creardocumentos XML a partir de objetos Java Las instancias de Unmarshaller se utilizan para crearobjetos Java a partir de documentos XML A continuacioacuten mostramos un ejemplo de uso de

Servicios Rest

88

JAXB para convertir una instancia de la clase Cliente que hemos definido anteriormentea formato XML para posteriormente volver a crear el objeto de tipo Cliente

Cliente cliente = new Cliente()clientesetId(42)

clientesetNombre(Lucia Arg)

JAXBContext ctx = JAXBContextnewInstance(Clienteclass) StringWriter writer = new StringWriter()

ctxcreateMarshaller()marshal(cliente writer)

String modString = writertoString()

cliente = (Cliente)ctxcreateUnmarshaller()

unmarshal(new StringReader(modString))

Creamos e inicializamos una instancia de tipo ClienteInicializamos JAXBContext para que pueda analizar la clase `ClienteUtilizamos una instancia de Marshaller para escribir el objeto Cliente como unString de Java ( StringWriter es un stream de caracteres que utilizaremos paraconstruir un String )Utilizamos una instancia de Unmarshaller para recrear el objeto Cliente a partir delString que hemos obtenido en el paso anterior

La clase JAXBContext constituye un punto en entrada al API JAXB paralos clientes de nuestros servicios RESTful Proporciona una abstraccioacutenpara gestionar el enlazado (binding) de informacioacuten XMLJava necesariapara implementar las operaciones de marshalling y unmarshalling

bull Unmarshalling La clase Unmarshaller proporciona a la aplicacioacutencliente la capacidad para convertir datos XML en un aacuterbol de objetosJava

bull Marshalling La clase Marshaller proporciona a la aplicacioacutencliente la capacidad para convertir un aacuterbol con contenidos Java denuevo en datos XML

Una vez que hemos proporcionado una visioacuten general sobre coacutemo funciona JAXB vamos aver coacutemo se integra con JAX-RS

Manejadores JAX-RS para JAXB

La especificacioacuten de JAX-RS indica que cualquier implementacioacuten debe soportar deforma automaacutetica el proceso de marshalling y unmarshalling de clases anotadas conXmlRootElement A continuacioacuten mostramos un ejemplo de la implementacioacuten de unservicio que hace uso de dichos manejadores Para ello utilizamos la clase Cliente que hemosanotado previamente con XmlRootElement y que hemos mostrado en apartados anteriores

Path(clientes)public class ClienteResource GET Path(id)

Servicios Rest

89

Produces(applicationxml) public Cliente getCliente(PathParam(id) int id) Cliente cli = findCliente(id) return cust

POST Consumes(applicationxml) public void crearCliente(Cliente cli)

Como podemos ver una vez que aplicamos las anotaciones JAXB a nuestras clases Java ( eneste caso a la clase Cliente ) es muy sencillo intercambiar documentos XML entre el clientey nuestro servicio web Los manejadores JAXB incluidos en la implementacioacuten de JAX-RSgestionaraacuten el marshallingunmarshalling de cualquier clase con anotaciones JAXB para losvalores de Content-Type applicationxml textxml o application+xml Por defecto tambieacuten se encargan de la creacioacuten e inicializacioacuten de instancias JAXBContext Debido a que la creacioacuten de las instancias JAXBContext puede ser cara la implementacioacutende JAX-RS normalmente las guarda despueacutes de la primera inicializacioacuten

JAXB y JSON

JAXB es lo suficientemente flexible como para soportar otros formatos ademaacutes de XMLAunque la especificacioacuten de JAX-RS no lo requiere muchas implementaciones de JAX-RSincluyen adaptadores de JAXB para soportar el formato JSON ademaacutes de XML JSON es unformato basado en texto que puede ser interpretado directamente por Javascript De hechoes el formato de intercambio preferido por las aplicaciones Ajax

JSON es un formato mucho maacutes simple que XML Los objetos estaacuten entre llaves ycontienen pares de clavevalor separados por comas Los valores pueden ser cadenas decaracteres booleanos ( true o false ) valores numeacutericos o arrays de los tipos anteriores

Supongamos que tenemos la siguiente descripcioacuten de un producto en formato XML

ltxml version=10 encoding=UTF-8gtltproductogt ltidgt1ltidgt ltnombregtiPadltnombregt ltdescripciongtDispositivo moacutevilltdescripciongt ltpreciogt500ltpreciogtltproductogt

La representacioacuten JSON equivalente seriacutea

id1 nombreiPad descripcionDispositivo moacutevil precio500

Servicios Rest

90

El formato JSON asociado por ejemplo al siguiente objeto Cliente

ltcliente id=56gt ltnombregtRicardo Lopezltnombregt ltdirecciongt ltcallegtcalle del oso 35ltcallegt ltciudadgtAlicanteltciudadgt ltcodPostalgt01010ltcodPostalgt ltdirecciongt ltclientegt

quedariacutea como sigue

nombre Ricardo Lopez direccion calle calle del oso 35 ciudad Alicante codPostal 01010

Como vemos en el ejemplo el formato JSON consiste baacutesicamente en objetos situados entrellaves los cuales estaacuten formados por pares clavevalor separadas por comas Cada clavey valor estaacute separado por Los valores pueden cadenas de caracteres booleanos (true ofalse) valores numeacutericos o vectores de los tipos anteriores (los vectores estaacuten delimitadospor corchetes)

Podemos antildeadir el formato applicationjson o bienMediaTypeAPPLICATION_JSON a la anotacioacuten Produces en nuestros meacutetodos derecurso para generar respuestas en formato JSON

GETPath(get)Produces(applicationxmlapplicationjson)public Producto getProducto()

En este ejemplo se elegiraacute el formato JSON en la respuesta si el cliente realiza una peticioacutenGET que incluye en la cabecera

Accept applicationjson

El tipo de respuesta es de tipo Producto En este caso Producto debe ser un bean JAXBes decir una clase anotada con XmlRootElement

Los meacutetodos de recurso pueden aceptar tambieacuten datos JSON para clases con anotacionesJAXB

POSTPath(producto)Consumes(applicationxmlapplicationjson)

Servicios Rest

91

public Response crearProducto(Producto prod)

En este caso el cliente deberiacutea incluir la siguiente cabecera cuando realice la peticioacuten POSTque incluya los datos JSON anteriores en el cuerpo del mensaje

Content-Type applicationjson

Hablaremos con maacutes detalle del formato JSON en una sesioacuten posterior

Finalmente y como resumen de lo anterior

JAXB

Para dar soporte al serializado y deserializado XML se utilizan beans JAXBPorejemplo las clases anteriores EstadoBean TareaBean Cliente y Productoson ejemplos de beans JAXB La clase que se serializaraacute como un recurso XMLo JSON se tiene que anotar con XmlRootElement Si en alguacuten elemento deun recurso se retorna un elemento de esa clase y se etiqueta con los tiposProduces(MediaTypeAPPLICATION_XMLMediatypeAPPLICATION_JSON )eacuteste se serializa automaacuteticamente utilizando el tipo de representacioacuten aceptada por elcliente Se puede consultar el artiacuteculo de Lars Vogel5 para maacutes informacioacuten

35 Respuestas del servidor

Vamos a explicar cuaacutel es el comportamiento por defecto de los meacutetodos de recursos JAX-RS en particular veremos cuaacuteles son los coacutedigos de respuesta HTTP por defecto teniendo encuenta situaciones de eacutexito asiacute como de fallo

Dado que en ocasiones vamos a tener que enviar cabeceras de respuesta especiacuteficas antecondiciones de error complejas tambieacuten vamos a explicar coacutemo podemos elaborar respuestascomplejas utilizando el API JAX-RS

Coacutedigos de respuesta por defecto

Los coacutedigos de respuesta por defecto se corresponden con el comportamiento indicado en laespecificacioacuten6 de la definicioacuten de los meacutetodos HTTP 11 Vamos a examinar dichos coacutedigosde respuesta con el siguiente ejemplo de recurso JAX-RS

Path(clientes)public class ClienteResource

Path(id) GET Produces(applicationxml) public Cliente getCliente(PathParam(id) int id)

5 httpwwwvogelladearticlesJAXBarticlehtml6 httpwwww3orgProtocolsrfc2616rfc2616-sec9html

Servicios Rest

92

POST Produces(applicationxml) Consumes(applicationxml) public Cliente crearCliente(Cliente nuevoCli)

PUT Path(id) Consumes(applicationxml) public void updateCliente(PathParam(id) int id Cliente cli)

Path(id) DELETE public void borrarCliente(PathParam(id) int id)

Respuestas que indican eacutexito

Los nuacutemeros de coacutedigo de respuestas HTTP con eacutexito se situacutean en el rango de 200 a 399

bull Para los meacutetodos crearCliente() y getCliente() se devolveraacute una respuestaHTTP con el coacutedigo 200 OK si el objeto Cliente que devuelven dichos meacutetodos noes null

bull Para los meacutetodos crearCliente() y getCliente() se devolveraacute una respuestaHTTP con el coacutedigo 204 No Content si el objeto Cliente que devuelven dichosmeacutetodos es null El coacutedigo de respuesta 204 no indica una condicioacuten de error Solamenteavisa al cliente de que todo ha ido bien pero que el mensaje de respuesta no contienenada en el cuerpo de la misma Seguacuten eacutesto si un meacutetodo de un recurso devuelve void por defecto se devuelve el coacutedigo de respuesta 204 No content Este es el caso para losmeacutetodos updateCliente() y borrarCliente() de nuestro ejemplo

La especificacioacuten HTTP es bastante consistente para los meacutetodos PUT POST GET yDELETE Si una respuesta exitosa HTTP contiene informacioacuten en el cuerpo del mensaje derespuesta entonces el coacutedigo de respuesta es 200 OK Si el cuerpo del mensaje estaacute vaciacuteoentonces se debe devolver 204 No Content

Respuestas que indican una situacioacuten de fallo

Es habitual que las respuestas fallidas se programen de forma que se lance una excepcioacutenLo veremos en un apartado posterior Aquiacute comentaremos algunas condiciones de error pordefecto

Los nuacutemeros de coacutedigo de error de respuesta estaacutendar en HTTP se situacutean en el rango entre400 y 599 En nuestro ejemplo si un cliente se equivoca tecleando la URI y eacutesta queda porejemplo como httphellipcliente entonces el servidor no encontraraacute ninguacuten meacutetodo delrecurso que pueda servir dicha peticioacuten (la URI correcta seriacutea httphellipclientes ) Eneste caso se enviaraacute como respuesta el coacutedigo 404 Not Found

Para los meacutetodos getCliente() y crearCliente() de nuestro ejemplo si el clientesolicita una respuesta con el tipo MIME texthtml entonces la implementacioacuten de JAX-RS devolveraacute automaacuteticamente 406 Not Acceptable con un mensaje de respuesta con elcuerpo de dicho mensaje vaciacuteo Esta respuesta indica que JAX-RS puede encontrar una rutade URI relativa que coincide con la peticioacuten pero no encuentra ninguacuten meacutetodo del recursoque devuelva la respuesta con ese tipo MIME

Servicios Rest

93

Si el cliente invoca una peticioacuten HTTP sobre una URI vaacutelida para la que no se puedeencontrar un meacutetodo de recurso asociado entonces el runtime de JAX-RS devolveraacute el coacutedigo405 Method Not Allowed Asiacute en nuestro ejemplo si el cliente solicita una operacioacuten PUTGET o DELETE sobre la URI clientes obtendraacute como respuesta 405 Method NotAllowed puesto que POST es el uacutenico meacutetodo HTTP que puede dar soporte a dicha URILa implementacioacuten de JAX-RS tambieacuten devolveraacute una cabecera de respuesta Allow con lalista de meacutetodos HTTP que pueden dar soporte a dicha URI Por lo tanto si nuestra aplicacioacutencliente realiza la siguiente peticioacuten de entrada

GET clientes

el servidor devolveraacute la siguiente respuesta

HTTP11 405 Method Not AllowedAllow POST

Elaboracioacuten de respuestas con la clase Response

Como ya hemos visto por ejemplo para peticiones GET si todo va bien se estaraacute devolviendoun coacutedigo de respuesta 200 (Ok) junto con el contenido especificado en el tipo de datosutilizado en cada caso Si devolvemos void el coacutedigo de respuesta seraacute 204 (No Content)

Sin embargo en ocasiones el servicio web que estamos disentildeando no puede implementarseutilizando el comportamiento por defecto de peticioacutenrespuesta inherente a JAX-RS En estoscasos necesitaremos controlar de forma expliacutecita la respuesta que se le enviacutea al cliente (cuerpodel mensaje de la respuesta HTTP) Por ejemplo cuando creamos un nuevo recurso con POSTdeberiacuteamos devolver 201 (Created) Para tener control sobre este coacutedigo nuestros recursosJAX-RS podraacuten devolver instancias de javaxwsrscoreResponse

GETProduces(MediaTypeAPPLICATION_XML)public Response getClientes() ClientesBean clientes = obtenerClientes()

return Responseok(clientes)build()

POSTConsumes(MediaTypeAPPLICATION_XML)public Response addCliente(ClienteBean cliente Context UriInfo uriInfo)

String id = insertarCliente(cliente) URI uri = uriInfo getAbsolutePathBuilder() path(id)

build(id)

return Responsecreated(uri)build()

Al crear una respuesta con Response podemos especificar una entidad que podraacute serun objeto de cualquiera de los tipos vistos anteriormente y que representa los datos a

Servicios Rest

94

devolver como contenido Por ejemplo cuando indicamos ok(clientes) estamos creandouna respuesta con coacutedigo 200 (Ok) y con el contenido generado por nuestro beanJAXB clientes Esto seraacute equivalente a haber devuelto directamente ClientesBean comorespuesta pero con la ventaja de que en este caso podemos controlar el coacutedigo de estadode la respuestaInsertamos una instancia de ClienteBean en la base de datos y obtenemos la claveasociada a dicho clienteEn la sesioacuten anterior hemos hablado de la interfaz uriInfo Es una interfaz inyectableque proporciona acceso a informacioacuten sobre la URI de la aplicacioacuten o la URI de laspeticiones recibidas En este caso estamos construyendo una nueva URI formada porla ruta absoluta de la peticioacuten de entrada http POST antildeadieacutendole la plantilla id yfinalmente sustituyendo el paraacutemetro de la plantilla por el valor id Supongamos quela peticioacuten POST contiene la uri httplocalhost8080recursosclientes Y que el valor de id es 8 El valor de la variable uri seraacute por tanto httplocalhost8080recursosclientes8Devolvemos la respuesta incluyendo la URI anterior en la cabecera HTTP `Location

Veamos con maacutes detalle la clase Response

public abstract class Response

public abstract Object getEntity()

public abstract int getStatus()

public abstract MultivaluedMapltString Objectgt getMetadata()

public abstract URI getLocation()

public abstract MediaType getMediaType()

public abstract void close()

El meacutetodo getEntity() devuelve el objeto Java correspondiente al cuerpo delmensaje HTTPEl meacutetodo getStatus() devuelve el coacutedigo de respuesta HTTPEl meacutetodo getMetadata() devuelve una instancia de tipo MultivaluedMap con lascabeceras de la respuestaEl meacutetodo getLocation() devuelve la URI de la cabecera Location de la respuestaEl meacutetodo getMediaType() devuelve el mediaType del cuerpo de la respuestaEl meacutetodo close() cierra el input stream correspondiente a la entidad asociada delcuerpo del mensaje (en el caso de que esteacute disponible y abierto) Tambieacuten liberacualquier otro recurso asociado con la respuesta (como por ejemplo datos posiblementealmacenados en un buffer)

Estos meacutetodos tiacutepicamente seraacuten invocados dese el cliente tal y como veremos maacutes adelantecuando expliquemos el API cliente

Los objetos Response no pueden crearse directamente Tienen que crearse a partir deinstancias de javaxwsrscoreResponseResponseBuilder devueltas por uno delos siguientes meacutetodos estaacuteticos de Response

public abstract class Response public abstract void close() public static ResponseBuilder status(Status status) public static ResponseBuilder status(int status) public static ResponseBuilder ok()

Servicios Rest

95

public static ResponseBuilder ok(Object entity) public static ResponseBuilder ok(Object entity MediaType type) public static ResponseBuilder ok(Object entity String type) public static ResponseBuilder ok(Object entity Variant var) public static ResponseBuilder serverError() public static ResponseBuilder created(URI location) public static ResponseBuilder noContent() public static ResponseBuilder notModified() public static ResponseBuilder notModified(EntityTag tag) public static ResponseBuilder notModified(String tag) public static ResponseBuilder seeOther(URI location) public static ResponseBuilder temporaryRedirect(URI location) public static ResponseBuilder notAcceptable(ListltVariantgt variants) public static ResponseBuilder fromResponse(Response response)

Veamos por ejemplo el meacutetodo ok()

ResponseBuilder ok(Object entity MediaType type)

Este meacutetodo recibe como paraacutemetros un objeto Java que queremos convertir en una respuestaHTTP y el Content-Type de dicha respuesta Como valor de retorno se obtiene unainstancia de tipo ResponseBuilder pre-inicializada con un coacutedigo de estado de 200 OK

El meacutetodo created() devuelve un ResponseBuilder para un recurso creado y asigna a lacabecera Location de la respuesta el valor de la URI proporionado como paraacutemetro

La clase ResponseBuilder es una factoriacutea utilizada para crear instancias individuales detipo Response

public static abstract class ResponseBuilder public abstract Response build() public abstract ResponseBuilder clone()

public abstract ResponseBuilder status(int status) public ResponseBuilder status(Status status)

public abstract ResponseBuilder entity(Object entity) public abstract ResponseBuilder type(MediaType type) public abstract ResponseBuilder type(String type)

public abstract ResponseBuilder variant(Variant variant) public abstract ResponseBuilder variants(ListltVariantgt variants)

public abstract ResponseBuilder language(String language) public abstract ResponseBuilder language(Locale language)

public abstract ResponseBuilder location(URI location) public abstract ResponseResponseBuilder header(String name Object value) public abstract ResponseResponseBuilder link(URI uri String rel)

Servicios Rest

96

public abstract ResponseResponseBuilder link(String uri String rel)

Vamos a mostrar un ejemplo sobre coacutemo crear respuestas utilizando un objeto Response En este caso el meacutetodo getLibro() devuelve un String que representa el libro en el queestaacute interesado nuestro cliente

Path(libro)public class LibroServicio GET Path(restfuljava) Produces(textplain) public Response getLibro()

String libro =

ResponseBuilder builder = Responseok(libro)

builderlanguage(fr)header(Some-Header some value)

return builderbuild()

Recuperamos los datos del libro solicitado En este caso vamos a devolver una cadenade caracteres que representa el libro en el que estamos interesadosInicializamos el cuerpo de la respuesta utilizando el meacutetodo Responseok() El coacutedigode estado de ResponseBuilder se inicializa de forma automaacutetica con 200Usamos el meacutetodo ResponseBuilderlanguage() para asignar el valorde la cabecera Content-Languaje a franceacutes Tambieacuten usamos el meacutetodoResponseBuilderheader() para asignar un valor concreto a otra cabeceraFinalmente creamos y devolvemos el objeto Response usando el meacutetodoResponseBuilderbuild()

Un detalle que es interesante destacar en este coacutedigo es que no indicamos ninguacuten valor parael Content-Type de la respuesta Debido a que ya hemos especificado esta informacioacutenen la anotacioacuten Produces del meacutetodo el runtime de JAX-RS devolveraacute el valor adecuadodel media type de la respuesta por nosotros

Inclusioacuten de cookies en la respuesta

JAX-RS proporciona la clase javaxwsrscoreNewCookie que utilizaremos para crearnuevos valores de cookies y enviarlos en las respuestas

Para poder incluir las cookies en nuestro objeto Response primero crearemoslas instancias correspondientes de tipo NewCookie las pasaremos al meacutetodoResponseBuildercookie() Por ejemplo

Path(myservice)public class MyService GET public Response get()

NewCookie cookie = new NewCookie(nombre pepe)

ResponseBuilder builder = Responseok(hola textplain) return buildercookie(cookie)build()

Servicios Rest

97

Creamos una nueva cookie con el nombre de clave nombre y le asignamos el valorpepeEn este caso al no indicar el tipo mime de la respuesta con la anotacioacuten Producesnecesitamos indicarlo (en este caso como un paraacutemetro del meacutetodo ok() )

El tipo enumerado de coacutedigos de estado

JAX-RS proporciona el tipo enumerado javaxwsrscoreStatus para representarcoacutedigos de respuesta especiacuteficos

public enum Status OK(200 OK) CREATED(201 Created) ACCEPTED(202 Accepted) NO_CONTENT(204 No Content) MOVED_PERMANENTLY(301 Moved Permanently) SEE_OTHER(303 See Other) NOT_MODIFIED(304 Not Modified) TEMPORARY_REDIRECT(307 Temporary Redirect) BAD_REQUEST(400 Bad Request) UNAUTHORIZED(401 Unauthorized) FORBIDDEN(403 Forbidden) NOT_FOUND(404 Not Found) NOT_ACCEPTABLE(406 Not Acceptable) CONFLICT(409 Conflict) GONE(410 Gone) PRECONDITION_FAILED(412 Precondition Failed) UNSUPPORTED_MEDIA_TYPE(415 Unsupported Media Type) INTERNAL_SERVER_ERROR(500 Internal Server Error) NOT_IMPLEMENTED(501 Not Implemented) SERVICE_UNAVAILABLE(503 Service Unavailable) public enum Family INFORMATIONAL SUCCESSFUL REDIRECTION CLIENT_ERROR SERVER_ERROR OTHER

public Family getFamily() public int getStatusCode() public static Status fromStatusCode(final int statusCode)

Cada valor del tipo Status se asocia con una familia especiacutefica de coacutedigos de respuestaHTTP Estas familias se identifican por el enumerado StatusFamily

bull Los coacutedigos en el rango del 100 se consideran informacionales

bull Los coacutedigos en el rango del 200 se consideran exitosos

bull Los coacutedigos en el rango del 300 son coacutedigos con eacutexito pero dentro de la categoriacutearedireccioacuten

bull Los coacutedigos de error pertenecen a los ragos 400 y 500 En el rango de 400 se consideranerrores del cliente y en el rango de 500 son errores del servidor

Servicios Rest

98

Tanto el meacutetodo Responsestatus() como ResponseBuilderstatus() puedenaceptar un valor enumerado de tipo Status Por ejemplo

DELETEResponse delete() return Responsestatus(StatusGONE)build()

En este caso estamos indicando al cliente que lo que queremos borrar ya no existe (410)

La clase javaxwsrscoreGenericEntity

Cuando estamos creando objetos de tipo Response se nos plantea un problema cuandoqueremos devolver tipos geneacutericos ya que el manejador JAXB necesita extraer la informacioacutendel tipo parametrizado de la respuesta en tiempo de ejecucioacuten Para estos casos JAX-RS proporciona la clase javaxwsrscoreGenericEntity Veamos su uso con unejemplo

GETProduces(applicationxml)public Response getListaClientes() ListltClientegt list = new ArrayListltClientegt() listadd(new Cliente())

GenericEntity entity =

new GenericEntityltListltClientegtgt(list)

return Responseok(entity)build()

La clase GenericEntity es tambieacuten una clase geneacuterica Lo que hacemos escrear una clase anoacutenima que extiende GenericEntity inicializando la plantilla deGenericEntity con el tipo geneacuterico que estemos utilizando

36 Manejadores de excepciones

Vamos a explicar coacutemo podemos tratar las excepciones en nuestros servicios RESTful

Los errores pueden enviarse al cliente bien creando y devolviendo el objeto Responseadecuado o lanzando una excepcioacuten Podemos lanzar cualquier tipo de excepcioacutentanto las denominadas checked (clases que heredan de javalangException ) comolas excepciones unchecked (clases que extienden javalangRuntimeException )Las excepciones generadas son manejadas por el runtime de JAX-RS si tenemosregistrado un mapper de excepciones Dichos mappers (o mapeadores) de excepcionespueden convertir una excepcioacuten en una respuesta HTTP Si las excepciones no estaacutengestionadas por un mapper eacutestas se propagan y se gestionan por el contenedor (deservlets) en el que se estaacute ejecutando JAX-RS JAX-RS proporciona tambieacuten la clasejavaxwsrsWebApplicationException Esta excepcioacuten puede lanzarse por elcoacutedigo de nuestra aplicacioacuten y seraacute procesado automaacuteticamente por JAX-RS sin necesidadde disponer de forma expliacutecita de ninguacuten mapper Vamos a ver coacutemo utilizar esta clase

Servicios Rest

99

La clase javaxwsrsWebApplicationException

JAX-RS incluye una excepcioacuten unchecked que podemos lanzar desde nuestraaplicacioacuten RESTful (ver la documentacioacuten del httpdocsoraclecomjavaee7apijavaxwsrsWebApplicationExceptionhtml [API]) Esta excepcioacuten se puede pre-inicializar con un objetoResponse o con un coacutedigo de estado particular

Clase javaxwsrsWebApplicationException

public class WebApplicationException extends RuntimeException Constructores public WebApplicationException()

public WebApplicationException(Response response) public WebApplicationException(int status)

public WebApplicationException(ResponseStatus status)

public WebApplicationException(String message) public WebApplicationException(Throwable cause) public WebApplicationException(Throwable cause Response response) public WebApplicationException(Throwable cause int status) public WebApplicationException(Throwable cause ResponseStatus status)

public Response getResponse() ]

Podemos crear una instancia a partir de un objeto ResponseCreacioacuten de una instancia a partir de un coacutedigo de estadoCreacioacuten de una instancia a partir de un String Por defecto se incluye el coacutedigo de estado500 El String que se pasa como paraacutemetro se almacena para su posterior recuperacioacutena traveacutes del mensaje `getMessage()

Cuando JAX-RS detecta que se ha lanzado la excepcioacuten WebApplicationException lacaptura y realiza una llamada al meacutetodo WebApplicationExceptiongetResponse()para obtener un objeto Response que enviaraacute al cliente Si la aplicacioacuten ha inicializado laexcepcioacuten WebApplicationException con un coacutedigo de estado o un objeto Response dicho coacutedigo o Response se utilizaraacuten para crear la respuesta HTTP real En otro caso laexcepcioacuten WebApplicationException devolveraacute el cliente el coacutedigo de respuesta 500Internal Server Error

Por ejemplo supongamos que tenemos un servicio web que permite a los usuarios solicitarinformacioacuten de nuestros clientes utilizando una representacioacuten XML

Path(clientes)public class ClienteResource GET Path(id) Produces(applicationxml) public Cliente getCliente(PathParam(id) int id) Cliente cli = recuperarCliente(id) if (cli == null) throw new WebApplicationException(ResponseStatusNOT_FOUND) return cli

Servicios Rest

100

En este ejemplo si no encontramos una instancia de Cliente con el id proporcionadolanzaremos una WebApplicationException que provocaraacute que se le enviacutee al clientecomo coacutedigo de respuesta 404 Not Found

Mapeado de excepciones

Normalmente las aplicaciones tienen que tratar con multitud de excepciones lanzadas desdenuestro coacutedigo de aplicacioacuten o por frameworks de terceros Dejar que el servlet JAX-RSque reside en el servidor maneje la excepcioacuten no nos proporciona demasiada flexibilidadSi capturamos y redirigimos todas estas excepciones a WebApplicationExceptionpodriacutea resultar bastante tedioso De forma alternativa podemos implementar y registrarinstancias de javaxwsrsextExceptionMapper Estos objetos saben coacutemo mapearuna excepcioacuten lanzada por la aplicacioacuten a un objeto Response

public interface ExceptionMapperltE extends Throwablegt Response toResponse(E exception)

Las clases que implementan la interfaz ExceptionMapperltTgt son proveedores demappings de excepciones (Exception Mapping Providers) y mapean una excepcioacuten runtimeo checked a una instancia de Response

Cuando un recurso JAX-RS lanza una excepcioacuten para la que existe un proveedor de mappingde excepciones eacuteste uacuteltimo se utiliza para devolver una instancia de Response Estarespuesta resultante se procesa como si hubiese sido generada por el recurso

Por ejemplo una excepcioacuten bastante utilizada por aplicaciones de bases de datos que utilizanJPA (Java Persistence Api) es javaxpersistenceEntityNotFoundException Estaexcepcioacuten se lanza cuando JPA no puede encontrar un objeto particular en la base de datosEn lugar de escribir coacutedigo que maneje dicha excepcioacuten de forma expliacutecita podemos escribirun ExceptionMapper para que gestione dicha excepcioacuten por nosotros Veaacutemos coacutemo

Provider public class EntityNotFoundMapper

implements ExceptionMapperltEntityNotFoundExceptiongt

public Response toResponse(EntityNotFoundException e)

return Responsestatus(ResponseStatusNOT_FOUND)build()

Nuestra implementacioacuten de ExceptionMapper debe anotarse con Provider Estole indica al runtime de JAX-RS que esta clase es un componente RESTLa clase que implementa ExceptionMapper debe proporcionar el tipo que se quiereparametrizar JAX-RS utiliza esta informacioacuten para emparejar las excepciones de tipoEntityNotFoundException con nuestra clase EntityNotFoundMapperEl meacutetodo toResponse() recibe la excepcioacuten lanzada ycrea un objeto Response que se utilizaraacute para construir la respuesta HTTP

Servicios Rest

101

Otro ejemplo es la excepcioacuten EJBException lanzada por aplicaciones que utilizan EJBs

Jerarquiacutea de excepciones

JAX-RS 20 proporciona una jerarquiacutea de excepciones para varias condiciones deerror para las peticiones HTTP La idea es que en lugar de crear una instancia deWebApplicationException e inicializarla con un coacutedigo de estado especiacutefico podemosuilizar en su lugar una de las excepciones de la jeraquiacutea (clases que heredan de la claseWebApplicationException) Por ejemplo podemos cambiar el coacutedigo anterior que utilizabaWebApplicationException y en su lugar usar javaxwsrsNotFoundException

Path(clientes)public class ClienteResource GET Path(id) Produces(applicationxml) public Cliente getCliente(PathParam(id) int id) Cliente cli = recuperarCliente(id) if (cli == null) throw new NotFoundException() return cli

Al igual que el resto de excepciones de la jerarquiacutea la excepcioacuten NotFoundExceptionhereda de WebApplicationException La siguiente tabla muestra algunas excepcionesque podemos utilizar todas ellas del paquete javaxwsrs Las que incluyen un coacutedigo deestado en el rango de 400 son subclases de ClientErrorException y como ya hemosindicado representan errores en las peticiones Las que presentan un coacutedigo de estado enel rango de 500 son subclases de ServerErrorException y representan errores delservidor

Table 5 Jerarquiacutea de excepciones JAX-RS

Excepcioacuten Coacutedigo de estado Descripcioacuten

BadRequestException 400 Mensaje mal formado

NotAuthorizedException 401 Fallo de autenticacioacuten

ForbiddenException 403 Acceso no permitido

NotFoundException 404 No se ha podido encontrar elrecurso

NotAllowedException 405 Meacutetodo HTTP no soportado

NotAcceptableException 406 Media type solicitado porel cliente no soportado(cabecera Accept de lapeticion)

NotSupportedException 415 El cliente ha incluido unMedia type no soportado(cabecera Content-Type dela peticion)

Servicios Rest

102

Excepcioacuten Coacutedigo de estado Descripcioacuten

InternalServerErrorException 500 Error general del servidor

ServiceUnavailableException 503 El servidor estaacute ocupadoo temporalmente fuera deservicio

bull La excepcioacuten BadRequestException se utiliza cuando el cliente enviacutea algo al servidorque eacuteste no puede interpretar Ejemplos de escenarios concretos que provocan que elruntime JAX-RS son cuando una peticioacuten PUT o POST contiene un cuerpo del mensaje conun documento XML o JSON mal formado de forma que falle el parsing del documento ocuando no puede convertir un valor especificado en la cabecera o cookie al tipo deseadoPor ejemplo

HeaderParam(Cabecera-Particular) int cabeceraCookieParam(miCookie) int cookie

Si el valor de la cabecera HTTP de la peticioacuten o el valor miCookie no puede convertirseen un entero se lanzaraacute la excepcioacuten BadRequestException

bull La excepcioacuten NotAuthorizedException (coacutedigo 401) se usa cuando queremosescribir nuestros propios protocolos de autorizacioacuten y queremos indicar al cliente que eacutestenecesita autenticarse con el servidor

bull La excepcioacuten ForbiddenException (coacutedigo 403)se usa generalmente cuando elcliente realiza una invocacioacuten para la que no tiene permisos de acceso Esto ocurrenormalmente debido a que el cliente no tiene el rol requerido

bull La excepcioacuten NotFoundException (coacutedigo 404) se usa cuando queremos comunicar alcliente que el recurso que estaacute solicitando no existe Esta excepcioacuten tambieacuten se generaraacute deforma automaacutetica por el runtime de JAX-RS cuando a eacuteste no le sea posible inyectar un valoren un PathParam QueryParam o MatrixParam Al igual que hemos comentado paraBadRequestException esto puede ocurrir si intentamos convertir el valor del paraacutemetroa un tipo que no admite esta conversioacuten

bull La excepcioacuten NotAllowedException (coacutedigo 405) se usa cuando el meacutetodo HTTP queel cliente estaacute intentando invocar no estaacute soportado por el recurso al que el cliente estaacuteaccediendo El runtime de JAX-RS lanza automaacuteticamente esta excepcioacuten si no encuentraninguacuten meacutetodo que pueda emparejar con el meacutetodo HTTP invocado

bull La excepcioacuten NotAcceptableException (coacutedigo 406) se usa cuando un cliente estaacutesolicitando un formato especiacutefico a traveacutes de la cabecera Accept El runtime de JAX-RSlanza automaacuteticamente esta excepcioacuten si no hay un meacutetodo con una anotacioacuten Producesque sea compatible con la cabecera Accept del cliente

bull La excepcioacuten NotSupportedException (coacutedigo 415)se usa cuando un cliente estaacuteenviando una representacioacuten que el servidor no comprende El runtime de JAX-RS lanzaautomaacuteticamente esta excepcioacuten si no hay un meacutetodo con una anotacioacuten Consumes quecoincida con el valor de la cabecera Content-Type de la peticioacuten

bull La excepcioacuten InternalServerErrorException (coacutedigo 500)es una excepcioacuten depropoacutesito general lanzada por el servidor Si en nuestra aplicacioacuten queremos lanzar estaexcepcioacuten deberiacuteamos hacerlo si se ha producido una condicioacuten de error que realmenteno encaja en ninguna de las situaciones que hemos visto El runtime de JAX-RS lanzaautomaacuteticamente esta excepcioacuten si falla un MessageBodyWriter o si se lanza algunaexcepcioacuten desde alguacuten ExceptionMapper

Servicios Rest

103

bull La excepcioacuten ServiceUnavailableException (coacutedigo 503)se usa cuando el servidorestaacute ocupado o temporalmente fuera de servicio En la mayoriacutea de los casos es suficientecon que el cliente vuelva a intentar realizar la llamada un tiempo maacutes tarde

Servicios Rest

104

37 Ejercicios

Servicio REST ejemplo

Para familiarizarnos con las el uso de diferentes manejadores de contenidos y manejo deexcepciones proporcionamos el moacutedulo el MOacuteDULO s3-ejemplo-rest con la implementacioacutende un servicio rest sencillo que podeacuteis probar con la herramienta postman

En el directorio srcmainresources de dicho moacutedulo teneacuteis un fichero de texto con lasinstrucciones (instruccionestxt) para construir desplegar y probar la aplicacioacuten de ejemplo

Plantillas que se proporcionan

Para esta sesioacuten proporcionamos un proyecto como plantilla con el nombre s3-filmotecaque tendraacutes utilizar como punto de partida para aplicar lo que hemos aprendido en esta sesioacuten

Se trata de una implementacioacuten parcial para gestionar una filmoteca con informacioacuten depeliacuteculas y actores

La estructura loacutegica del proyecto proporcionado es la siguiente

bull Paquete orgexpertojavadomain es la capa que contiene los objetos del dominiode la aplicacioacuten Por simplicidad no usamos una base de datos real sino que trabajamoscon datos en memoria

bull Paquete orgexpertojavaservice contiene la implementacioacuten de los servicios denuestra aplicacioacuten que seraacuten accedidos desde la capa rest

bull Paquete orgexpertojavarest constituye la capa rest de nuestra aplicacioacuten Estacapa es cliente de la capa de servicios

En la carpeta srcmainresources teneacuteis un fichero de texto (instruccionestxt) coninformacioacuten detallada sobre el API rest implementado

Uso de JAXB (05 puntos)

Utiliza las anotaciones JAXB oportunas para realizar el serializado de las entidades java a xmly json de forma que la lista de peliacuteculas de la filmoteca en formato xml sea

Peticioacuten rest GET peliculas

ltxml version=10 encoding=UTF-8 standalone=yesgtltpeliculasgt ltpelicula duracion=120 estreno=2015-12-08T000000+0100 id=1gt ltdirectorgtStanley Kubrickltdirectorgt lttitulogtEl resplandorlttitulogt ltpeliculagt ltpelicula duracion=155 estreno=2015-18-07T000000+0100 id=2gt ltdirectorgtDirector 2ltdirectorgt lttitulogtPelicula 2lttitulogt ltpeliculagtltpeliculasgt

Servicios Rest

105

Por defecto JAXB serializa los tipos Date con el formato ISO 80617

para los contenidos en el cuerpo de la petioacutenrespuesta Dicho formatoes YYYY-MM-DD (seguido de HHMMSS) Por otro lado si antildeadimosactores a las peliacuteculas eacutestos deberaacuten mostrarse bajo la etiquetaltactoresgt tal y como mostramos en el siguiente ejemplo

Los datos mostrados para una peliacutecula en formato xml tienen que presentar el siguienteaspecto

sourcejava] Peticioacuten rest GET peliculas1

ltxml version=10 encoding=UTF-8 standalone=yesgtltpelicula duracion=120 estreno=2015-12-08T000000+0100 id=1gt ltactoresgt ltactor nombre=Jack Nicholson personaje=Jack Torrancegt ltactoresgt ltdirectorgtStanley Kubrickltdirectorgt lttitulogtEl resplandorlttitulogtltpeliculagt

Puedes comprobar si has hecho bien el ejercicio utilizando Postman

Uso de manejadores de contenidos y clase Response (075 puntos)

Implementa una nueva peticioacuten POST que reciba los datos de una nueva peliacutecula desdeun formulario Recuerda que los datos de un formulario pueden ponerse en el cuerpo de lapeticioacuten HTTP como pares nombre=valor separados por amp Los espacios en blanco secodifican como 20 No es necesario poner comillas Por ejemplo

nombre1=valor20con20espaciosampnombre2=valor

Se proporciona el fichero indexhtml con un formulario para utilizarlo como alternativa apostman Para acceder al formulario usaremos httplocalhost8080s3-filmoteca

Modifica las peticiones POST sobre peliacuteculas y actores de forma que devuelvan enla cabecera Location la URI del nuevo recurso creado Por ejemplo

httplocalhost8080s3-filmotecapeliculas3

en el caso de una nueva peliacutecula y

httplocalhost8080s3-filmotecapeliculas3actoresSigourney20Weaver

si por ejemplo hemos antildeadido un nuevo actor a la peliacutecula con id=3

Modifica los meacutetodos GET para que devuelvan el estado 204 No Content en los casos enlos que la peliacutecula yo actor consultado no exista

7 httpseswikipediaorgwikiISO_8601

Servicios Rest

106

Modifica los meacutetodos GET para que devuelvan el estado 404 Not Found en los casos enlos que las listas de peliacuteculas yo actores esteacuten vaciacuteas

Implementa el coacutedigo para antildeadir un nuevo actor Tendraacutes que obtener la informacioacuten de lapeliacutecula y nombre del actor de la URI de la peticioacuten

Puedes comprobar si has hecho bien el ejercicio utilizando Postman

Manejo de excepciones (075 puntos)

Modifica el meacutetodo addPelicula de la capa de servicio (paquete orgexpertojavaservice)para que lance una excepcioacuten de tipo ServiceException con el mensaje El tiacutetulo de la peliacuteculano puede ser nulo ni vaciacuteo cuando se intente antildeadir una peliacutecula con un tiacutetulo con valor nullo vaciacuteo

El meacutetodo addPelicula debe lanzar tambieacuten una excepcioacuten de tipo ServiceException conel mensaje La peliacutecula ya existe cuando se intente antildeadir una peliacutecula con un tiacutetulo que yaexiste

Modifica el meacutetodo addActor de la capa de servicio para que lance las excepciones de tipoServiceException con los mensajes El tiacutetulo de la peliacutecula no puede ser nulo ni vaciacuteo cuandose intente antildeadir un actor a una peliacutecula cuyo tiacutetulo no existe o bien el mensaje EL actor yaexiste si intentamos antildeadir un actor a una peliacutecula que ya habiacuteamos antildeadido previamente

Implementa un mapper para capturar las excepciones de la capa de servicio de forma quese devuelva el estado 500 Internal Server error y como entidad del cuerpo de la respuestael mensaje asociado a las excepciones generadas por el servicio (El tiacutetulo de la peliacutecula nopuede ser nulo ni vaciacuteo EL actor ya existe hellip) La nueva clase pertenceraacute a la capa restPuedes ponerle el nombre ServiceExceptionMapper

Puedes comprobar si has hecho bien el ejercicio utilizando Postman

Servicios Rest

107

4 HATEOAS y Seguridad

En esta sesioacuten trataremos uno de los principios REST de obligado cumplimiento para poderhablar de un servicio RESTful Nos referimos a HATEOAS Hasta ahora hemos visto coacutemo losclientes pueden cambiar el estado de los recursos (el nombre del recurso se especifica en laURI de la peticioacuten) a traveacutes de los contenidos del cuerpo del mensaje o utilizando paraacutemetroso cabeceras de peticioacuten A su vez los servicios comunican el estado resultante de la peticioacuten alos clientes a traveacutes del contenido del cuerpo del mensaje coacutedigos de respuesta y cabecerasde respuesta Pues bien teniendo en cuenta lo anterior HATEOAS hace referencia a quecuando sea necesario tambieacuten deben incluirse los enlaces a los recursos (URI) en el cuerpode la respuesta (o en las cabeceras) para asiacute poder recuperar el recurso en cuestioacuten o losrecursos relacionados

En esta sesioacuten tambieacuten explicaremos algunos conceptos baacutesicos para poder dotar deseguridad a nuestros servicios REST

41 iquestQueacute es HATEOAS

Comuacutenmente se hace referencia a Internet como la Web (web significa red telarantildea) debidoa que la informacioacuten estaacute interconectada mediante una serie de hiperenlaces embebidosdentro de los documentos HTML Estos enlaces crean una especie de hilos o hebras entrelos sitios web relacionados en Internet Una consecuencia de ello es que los humanos puedennavegar por la Web buscando elementos de informacioacuten relacionados de su intereacutes haciendoclick en los diferentes enlaces desde sus navegadores Los motores de buacutesqueda puedentrepar o desplazarse por estos enlaces y crear iacutendices enormes de datos susceptibles deser buscados Sin ellos Internet no podriacutea tener la propiedad de ser escalable No habriacuteaforma de indexar faacutecilmente la informacioacuten y el registro de sitios web seriacutea un proceso manualbastante tedioso

Ademaacutes de los enlaces (links) otra caracteriacutestica fundamental de Internet es HTML Enocasiones un sitio web nos solicita que rellenemos alguna informacioacuten para compraralgo o registrarnos en alguacuten servicio El servidor nos indica a nosotros como clientes queacuteinformacioacuten necesitamos proporcionar para completar una accioacuten descrita en la paacutegina webque estamos viendo El navegador nos muestra la paacutegina web en un formado que podemosentender faacutecilmente Nosotros leemos la paacutegina web y rellenamos y enviamos el formulario Unformulario HTML es un formato de datos interesante debido a que auto-describe la interaccioacutenentre el cliente y el servidor

El principio arquitectoacutenico que describe el proceso de enlazado (linking) y el enviacuteo deformularios se denomina HATEOAS Las siglas del teacutermino HATEOAS significan HypermediaAs The Engine Of Application State (es decir el uso de Hipermedia como mecanismo demaacutequina de estados de la aplicacioacuten) La idea de HATEOAS es que el formato de los datosproporciona informacioacuten extra sobre coacutemo cambiar el estado de nuestra aplicacioacuten En laWeb los enlaces HTML nos permiten cambiar el estado de nuestro navegador Por ejemplocuando estamos leyendo una paacutegina web un enlace nos indica queacute posibles documentos(estados) podemos ver a continuacioacuten Cuando hacemos click sobre un enlace el estado delnavegador cambia al visitar y mostrar una nueva paacutegina web Los formularios HTML por otraparte nos proporcionan una forma de cambiar el estado de un recurso especiacutefico de nuestroservidor Por uacuteltimo cuando compramos algo en Internet por ejemplo estamos creando dosnuevos recursos en el servicio una transaccioacuten con tarjeta de creacutedito y una orden de compra

Servicios Rest

108

42 HATEOAS y Servicios Web

Cuando aplicamos HATEOAS a los servicios web la idea es incluir enlaces en nuestrosdocumentos XML o JSON La mayoriacutea de las aplicaciones RESTful basadas en XML utilizanel formato Atom Syndication Format8 para implementar HATEOAS

Enlaces Atom

Los enlaces Atom constituyen un mecanismo estaacutendar para incluir enlaces (links) en nuestrosdocumentos XML Veamos un ejemplo

ltclientesgt ltlink rel=next href=httpejemplocomclientesinicio=2amptotal=2 type=applicationxmlgt ltcliente id=123gt ltnombregtJuan Garcialtnombregt ltclientegt ltcliente id=332gt ltnombregtPablo Bozoltnombregt ltclientegtltclientesgt

El documento anterior representa una lista de clientes y el elemento ltlinkgt que contieneun enlace indica la forma de obtener los siguientes clientes de la lista

Un enlace Atom es simplemente un elemento XML (elemento ltlinkgt ) con unos atributosespeciacuteficos

bull El atributo rel Se utiliza para indicar la relacioacuten del enlace con el elemento XML en elque anidamos dicho enlace Es el nombre loacutegico utilizado para referenciar el enlace Esteatributo tiene el mismo significado para la URL que estamos enlazando que la etiquetaHTML ltagt tiene para la URL sobre la que estamos haciendo click con el ratoacuten en elnavegador Si el enlace hace referencia al propio elemento XML en el que incluimos elenlace entonces asignaremos el valor del atributo self

bull El atributo href es la URL a la que podemos acceder para obtener nueva informacioacuten ocambiar el estado de nuestra aplicacioacuten

bull El atributo type indica el media type asociado con el recurso al que apunta la URL

Cuando un cliente recibe un documento con enlaces Atom eacuteste busca la relacioacuten en la queestaacute interesado (atributo rel ) e invoca la URI indicada en el atributo href

Ventajas de utilizar HATEOAS con Servicios Web

Resulta bastante obvio por queacute los enlaces y los formularios tienen mucho que ver en laprevalencia de la Web Con un navegador tenemos una ventana a todo un mundo deinformacioacuten y servicios Las maacutequinas de buacutesqueda rastrean Internet e indexan sitios webpara que todos los datos esteacuten al alcance de nuestros dedos Esto es posible debido a quela Web es auto-descriptiva Cuando accedemos a un documento conocemos coacutemo recuperarinformacioacuten adicional siguiendo los enlaces situados en dicho documento Por ejemplo

8 httpwwww3org2005Atom

Servicios Rest

109

conocemos coacutemo realizar una compra en Amazon debido a que los formularios HTML nosindican coacutemo hacerlo

Cuando los clientes son maacutequinas en lugar de personas (los servicios Web tambieacuten seconocen como Web para maacutequinas frente a la Web para humanos proporcionada por elacceso a un servidor web a traveacutes de un navegador) el tema es algo diferente puesto que lasmaacutequinas no pueden tomar decisiones sobre la marcha cosa que los humanos siacute puedenhacer Las maacutequinas requieren que los programadores les digan coacutemo interpretar los datosrecibidos desde un servicio y coacutemo realizar transiciones entre estados como resultado de lasinteracciones entre clientes y servidores

En este sentido HATEOAS proporciona algunas ventajas importantes para contribuir a quelos clientes sepan coacutemo utilizar los servicios a la vez que acceden a los mismos Vamos acomentar algunas de ellas

Transparencia en la localizacioacuten

En un sistema RESTful gracias a HATEOAS soacutelo es necesario hacer puacuteblicas unas pocasURIs Los servicios y la informacioacuten son representados con enlaces que estaacuten embebidosen los formatos de los datos devueltos por las URIs puacuteblicas Los clientes necesitan conocerlos nombres loacutegicos de los enlaces para buscar a traveacutes de ellos pero no necesitan conocerlas ubicaciones reales en la red de los servicios a los que acceden

Los enlaces proporcionan un nivel de indireccioacuten de forma que los servicios subyacentespueden cambiar sus localizaciones en la red sin alterar la loacutegica ni el coacutedigo del cliente

Desacoplamiento de los detalles de la interaccioacuten

Consideremos una peticioacuten que nos devuelve una lista de clientes en una base de datosGET clientes Si nuestra base de datos tiene miles de datos probablemente noquerremos devolver todos ellos de una soacutela vez Lo que podemos hacer es definir una vistaen nuestra base de datos utilizando paraacutemetros de consulta por ejemplo

customersinicio=indiceInicioamptotal=numeroElementosDevueltos

El paraacutemetro inicio identifica el iacutendice inicial de nuestra lista de clientes El paraacutemetrototal especifica cuaacutentos clientes queremos que nos sean devueltos como respuesta

Lo que estamos haciendo en realidad es incrementar la cantidad de conocimiento que elcliente debe tener predefinido para interactuar con el servicio (es decir no soacutelo necesita saberla URI sino ademaacutes conocer la existencia de estos paraacutemetros) Supongamos que en el futuroel servidor decide que necesita cambiar la forma en la que se accede al nuacutemero de datossolicitados por el cliente Si el servidor cambia la interfaz los clientes antiguos dejaraacuten defuncionar a menos que cambien su coacutedigo

En lugar de publicar la interfaz REST anterior para obtener datos de los clientes podemosincluir dicha informacioacuten en el documento de respuesta por ejemplo

ltclientesgt ltlink rel=next href=httpejemplocomclientesinicio=2amptotal=2 type=applicationxmlgt ltcliente id=123gt

Servicios Rest

110

ltnombregtJuan Garcialtnombregt ltclientegt ltcliente id=332gt ltnombregtPablo Bozoltnombregt ltclientegtltclientesgt

Cuando incluimos un enlace Atom en un documento estamos asignando un nombre loacutegicoa una transicioacuten de estados En el ejemplo anterior la transicioacuten de estados es el siguienteconjunto de clientes a los que podemos acceder En lugar de tener que recordar cuaacuteles sonlos paraacutemetros de la URI que tenemos que utilizar en la siguiente invocacioacuten para obtener maacutesclientes lo uacutenico que tenemos que hacer es seguir el enlace proporcionado El cliente notiene que contabilizar en ninguacuten sitio la interaccioacuten ni tiene que recordar queacute seccioacuten de labase de datos estamos consultando actualmente

Ademaacutes el XML devuelto es auto-contenido iquestQueacute pasa si tenemos que pasar estedocumento a un tercero Tendriacuteamos que decirle que se trata de una vista parcial de la basede datos y especificar el iacutedice de inicio Al incluir el enlace en el documento ya no es necesarioproporcionar dicha informacioacuten adicional ya que forma parte del propio documento

Reduccioacuten de errores de transicioacuten de estados

Los enlaces no se utilizan solamente como un mecanismo para agregar informacioacuten denavegacioacuten Tambieacuten se utilizan para cambiar el estado de los recursos Pensemos en unaaplicacioacuten de comercio web a la que podemos acceder con la URI pedidos333

ltpedido id=333gt ltcliente id=123gtltclientegt ltimportegt9999ltimportegt ltlineas-pedidogt ltlineas-pedidogtltpedidogt

Supongamos que un cliente quiere cancelar su pedido Podriacutea simplemente invocar la peticioacutenHTTP DELETE pedidos333 Esta no es siempre la mejor opcioacuten ya que normalmenteel sistema necesitaraacute retener el pedido para propoacutesitos de almacenaje Por ello podriacuteamosconsiderar una nueva representacioacuten del pedido con un elemento cancelado a true

PUT pedidos333 HTTP11Content-Type applicationxmlltpedido id=333gt ltcliente id=123gtltclientegt ltimportegt9999ltimportegt ltcanceladogttrueltcanceladogt ltlineas-pedidogt ltlineas-pedidogtltpedidogt

Pero iquestqueacute ocurre si el pedido no puede cancelarse Podemos tener un cierto estado ennuestro proceso de pedidos en donde esta accioacuten no estaacute permitida Por ejemplo si el pedido

Servicios Rest

111

ya ha sido enviado entonces no puede cancelarse En este caso realmente no hay niguacutencoacutedigo de estado HTTP de respuesta que represente esta situacioacuten Una mejor aproximacioacutenes incluir un enlace para poder realizar la cancelacioacuten

ltpedido id=333gt ltcliente id=123gtltclientegt ltimportegt9999ltimportegt ltcanceladogtfalseltcanceladogt ltlink rel=cancelar href=httpejemplocompedidos333canceladogt ltlineas-pedidogt ltlineas-pedidogtltpedidogt

El cliente podriacutea invocar la orden GET pedidos333 y obtener el documento XMLque representa el pedido Si el documento contiene el enlace cancelar entonces el clientepuede cambiar el estado del pedido a cancelado enviando una orden PUT vaciacutea a la URIreferenciada en el enlace Si el documento no contiene el enlace el cliente sabe que estaoperacioacuten no es posible Esto permite que el servicio web controle en tiempo real la forma enla que el cliente interactua con el sistema

Enlaces en cabeceras frente a enlaces Atom

Una alternativa al uso de enlaces Atom en el cuerpo de la respuesta es utilizar enlaces enlas cabeceras de la respuesta (httptoolsietforghtmlrfc5988) Vamos a explicar eacutesto con unejemplo

Consideremos el ejemplo de cancelacioacuten de un pedido que acabamos de ver En lugar deutilizar un enlace Atom para especificar si se permite o no la cancelacioacuten del pedido podemosutilizar la cabecera Link (es uno de los posibles campos que podemos incluir como cabeceraen una respuesta HTTP)) De esta forma si un usuario enviacutea la peticioacuten GET pedidos333 recibiraacute la siguiente respuesta HTTP

HTTP11 200 OKContent-Type applicationxmlLink lthttpejemplocompedidos333canceladogt rel=cancel

ltpedido id=333gt ltpedidogt

La cabecera Link tiene las mismas caracteriacutesticas que un enlace Atom La URI estaacute entrelos signos lt y gt y estaacute seguida por uno o maacutes atributos delimitados por El atributo reles obligatorio y tiene el mismo significado que el correspondiente atributo Atom com el mismonombre En el ejemplo no se muestra pero podriacuteamos especificar el media type utilizando elatributo type

43 HATEOAS y JAX-RS

JAX-RS no proporciona mucho soporte para implementar HATEOAS HATEOAS se definepor la aplicacioacuten por lo que no hay mucho que pueda aportar ninguacuten framework Lo que siacute

Servicios Rest

112

proporciona JAX-RS son algunas clases que podemos utilizar para construir las URIs de losenlaces HATEOAS

Construccioacuten de URIs con UriBuilder

Una clase que podemos utilizar es javaxwsrscoreUriBuilder Esta clase nospermite construir URIs elemento a elemento y tambieacuten permite incluir plantillas de paraacutemetros(segmentos de ruta variables)

Clase UriBuilder meacutetodos para instanciar objetos de la clase

public abstract class UriBuilder public static UriBuilder fromUri(URI uri) throws IllegalArgumentException public static UriBuilder fromUri(String uri) throws IllegalArgumentException public static UriBuilder fromPath(String path) throws IllegalArgumentException public static UriBuilder fromResource(Classltgt resource) throws IllegalArgumentException public static UriBuilder fromLink(Link link) throws IllegalArgumentException

Las instancias de UriBuilder se obtienen a partir de meacutetodos estaacuteticos con la formafromXXX() Podemos inicializarlas a partir de una URI una cadena de caracteres o laanotacioacuten Path de una clase de recurso

Para extraer modificar yo componer una URI se pueden utilizar meacutetodos como

Clase UriBuilder meacutetodos para manipular las URIs

public abstract UriBuilder clone() crea una copia

crea una copia con la informacioacuten de un objeto URIpublic abstract UriBuilder uri(URI uri) throws IllegalArgumentException

meacutetodos para asignarmodificar valores de los atributos de los objetos UriBuilderpublic abstract UriBuilder scheme(String scheme) throws IllegalArgumentExceptionpublic abstract UriBuilder userInfo(String ui)public abstract UriBuilder host(String host) throws IllegalArgumentExceptionpublic abstract UriBuilder port(int port) throws IllegalArgumentExceptionpublic abstract UriBuilder replacePath(String path)

meacutetodos que antildeaden elementos a nuestra URIpublic abstract UriBuilder path(String path)public abstract UriBuilder segment(String segments)public abstract UriBuilder matrixParam(String name Object values)public abstract UriBuilder queryParam(String name Object values)

Servicios Rest

113

meacutetodo que instancia el valor de una plantilla de la URIpublic abstract UriBuilder resolveTemplate(String name Object value)

Los meacutetodos build() construyen la URI Eacutesta puede contener plantillas de paraacutemetros( segmentos de ruta variables) que deberemos inicializar utilizando pares nombrevalor o bienuna lista de valores que reemplazaraacuten a los paraacutemetros de la plantilla en el orden en el queaparezcan

Clase UriBuilder meacutetodos buildXXX() para construir las URIs

public abstract URI buildFromMap(MapltString extends Objectgt values) throws IllegalArgumentException UriBuilderException

public abstract URI build(Object values) throws IllegalArgumentException UriBuilderException

Veamos alguacuten ejemplo que muestra coacutemo crear inicializar componer y construir una URIutilizando un UriBuilder

UriBuilder builder = UriBuilderfromPath(clientesid)builderscheme(http) host(hostname) queryParam(param=param)

Con este coacutedigo estamos definiendo una URI como

httphostnameclientesidparam=param

Puesto que tenemos plantillas de paraacutemetros necesitamos inicializarlos con valores quepasaremos como argumentos para crear la URI final Si queremos reutilizar la URI quecontiene las plantillas deberiacuteamos realizar una llamada a clone() antes de llamar al meacutetodobuild() ya que eacuteste reemplazaraacute los paraacutemetros de las plantillas en la estructura internadel objeto

UriBuilder clone = builderclone()URI uri = clonebuild(ejemplocom 333 valor)

El coacutedigo anterior dariacutea lugar a la siguiente URI

httpejemplocomclientes333param=valor

Tambieacuten podemos definir un objeto de tipo Map que contenga los valores de las plantillas

MapltString Objectgt map = new HashMapltString Objectgt()

Servicios Rest

114

mapput(hostname ejemplocom)mapput(id 333)mapput(param valor)UriBuilder clone = builderclone()URI uri = clonebuildFromMap(map)

Otro ejemplo interesante es el de crear una URI a partir de las expresiones Path definidasen las clases JAX-RS anotadas A continuacioacuten mostramos el coacutedigo

Path(clientes)public class ServicioClientes

Path(id) public Cliente getCliente(PathParam(id) int id)

Podemos referenciar esta clase y el meacutetodo getCliente() a traveacutes de la claseUriBuilder de la siguiente forma

UriBuilder builder = UriBuilderfromResource(ServicioClientesclass)builderhost(hostname)builderpath(ServicioClientesclass getCliente)

El coacutedigo anterior define la siguiente plantilla para la URI

httphostnameclientesid

A partir de esta plantilla podremos construir la URI utilizando alguno de los meacutetodos`buildXXX()

Tambieacuten podemos querer utilizar UriBuilder para crear URIS a partir de plantillas Para ellodisponemos de meacutetodos resolveTemplateXXX() que nos facilitan el trabajo

Clase UriBuilder meacutetodos resolveTemplateXXX() para crear URIs a partir de plantillas

public abstract UriBuilder resolveTemplate(String name Object value)public abstract UriBuilder resolveTemplate(String name Object value boolean encodeSlashInPath)public abstract UriBuilder resolveTemplateFromEncoded(String nameObject value)public abstract UriBuilder resolveTemplates(MapltString Objectgt templateValues)public abstract UriBuilder resolveTemplates( MapltStringObjectgt templateValues boolean encodeSlashInPath) throws IllegalArgumentExceptionpublic abstract UriBuilder resolveTemplatesFromEncoded( MapltString Objectgt templateValues) Devuelve la URI de la plantilla como una cadena de caracterespublic abstract String toTemplate()

Servicios Rest

115

Funcionan de forma similar a los meacutetodos build() y se utilizan para resolver parcialmentelas plantillas contenidas en la URI Cada uno de los meacutetodos devuelve una nueva instanciade UriBuilder de forma que podemos encadenar varias llamadas para resolver todas lasplantillas de la URI Finalmente usaremos el meacutetodo toTemplate() para obtener la nuevaplantilla en forma de String

String original = httphostidString nuevaPlantilla = UriBuilderfromUri(original) resolveTemplate(host localhost) toTemplate()

El valor de nuevaPlantilla para el coacutedigo anterior seriacutea httplocalhostid

URIs relativas mediante el uso de UriInfo

Cuando estamos escribiendo servicios que distribuyen enlaces hay cierta informacioacuten queprobablemente no conozcamos cuando estamos escribiendo el coacutedigo Por ejemplo podemosno conocer todaviacutea los hostnames de los enlaces o incluso los base paths de las URIs en elcaso de que estemos enlazando con otros servicios REST

JAX-RS proporciona una forma sencilla de solucionar estos problemas utilizando la interfazjavaxwsrscoreUriInfo Ya hemos introducido algunas caracteriacutesticas de estainterfaz en sesiones anteriores Ademaacutes de poder consultar informacioacuten baacutesica de la rutatambieacuten podemos obtener instancias de UriBuilder preinicializadas con la URI base utilizadapara definir los servicios JAX-RS o la URI utilizada para invocar la peticioacuten HTTP actual

public interface UriInfo public URI getRequestUri() public UriBuilder getRequestUriBuilder() public URI getAbsolutePath() public UriBuilder getAbsolutePathBuilder() public URI getBaseUri() public UriBuilder getBaseUriBuilder()

Por ejemplo supongamos que tenemos un servicio que permite acceder a Clientes desdeuna base de datos En lugar de tener una URI base que devuelva todos los clientes en unuacutenico documento podemos incluir los enlaces previo y sigiente de forma que podamosnavegar por los datos Vamos a mostrar coacutemo crear estos enlaces utilizando la URI parainvocar la peticioacuten

Path(clientes)public class ServicioClientes GET Produces(applicationxml)

public String getCustomers(Context UriInfo uriInfo)

UriBuilder nextLinkBuilder = uriInfogetAbsolutePathBuilder() nextLinkBuilderqueryParam(inicio 5) nextLinkBuilderqueryParam(total 10) URI next = nextLinkBuilderbuild() rellenar el resto del documento

Servicios Rest

116

Para acceder a la instancia UriInfo que representa al peticioacuten usamos la anotacioacutenjavaxwsrscoreContext para inyectarla como un paraacutemetro del meacutetodo delrecurso RESTObtenemos un UriBuilder preininicializado con la URI utilizada para acceder alservicio

Para el coacutedigo anterior y dependiendo de coacutemo se despliegue el servicio la URI creada podriacuteaser

httporgexpertojavajaxrsclientesinicio=5amptotal=10

Construccioacuten de enlaces (Links) en documentos XML y en cabeceras HTTP

JAX-RS proporciona cierto soporte para construir los enlaces y devolverlos en las cabecerasde respuesta o bien incluirlos en los documentos XML Para ello podemos utilizar las clasesjavawsrscoreLink y javawsrscoreLinkBuilder

Clase abstracta javaxwsrscoreLink

public abstract class Link public abstract URI getUri() public abstract UriBuilder getUriBuilder() public abstract String getRel() public abstract ListltStringgt getRels() public abstract String getTitle() public abstract String getType() public abstract MapltString Stringgt getParams() public abstract String toString()

Link es una clase abstracta que representa todos los metadatos contenidos en una cabecerao en un enlace Atom El meacutetodo getUri() representa el atributo href del enlace Atom Elmeacutetodo getRel() representa el atributo rel y asiacute sucesivamente Podemos referenciar atodos los atributos a traveacutes del meacutetodo getParams() Finalmente el meacutetodo toString()convertiraacute la instancia Link en una cadena de caracteres con el formato de una cabeceraLink

Para crear instancias de Link utilizaremos un LinkBuilder que crearemos con algunode estos meacutetodos

public abstract class Link public static Builder fromUri(URI uri) public static Builder fromUri(String uri) public static Builder fromUriBuilder(UriBuilder uriBuilder) public static Builder fromLink(Link link) public static Builder fromPath(String path) public static Builder fromResource(Classltgt resource) public static Builder fromMethod(Classltgt resource String method)

Servicios Rest

117

Los meacutetodos fromXXX() funcionan de forma similar a UriBuilderfromXXX() Todosinicializan el UriBuilder subyacente que utilizaremos para construir el atributo href delenlace

Los meacutetodos link() uri() y uriBuilder() nos permiten sobreescribir la URIsubyacente del enlace que estamos creando

public abstract class Link interface Builder public Builder link(Link link) public Builder link(String link) public Builder uri(URI uri) public Builder uri(String uri) public Builder uriBuilder(UriBuilder uriBuilder)

Los siguientes meacutetodos nos permiten asignar valores a varios atributos del enlace que estamosconstruyendo

public Builder rel(String rel) public Builder title(String title) public Builder type(String type) public Builder param(String name String value)

Finalmente eacutel meacutetodo build() nos permitiraacute construir el enlace

public Link build(Object values)

El objeto LinkBuilder tiene asociado una UriBuilder subyacente Los valorespasados como paraacutemetros del meacutetodo build() son utilizados por el UriBuilder paracrear una URI para el enlace Veamos un ejemplo

Link link = LinkfromUri(httphostraizclientesid) rel(update)type(textplain) build(localhost 1234)

Si realizamos una llamada a toString() sobre la instancia del enlace ( link )obtendremos lo siguiente

httplocalhostraizclientes1234gt rel=update type=textplain

A continuacioacuten mostramos dos ejemplos que muestran coacutemo crear instancias Link en lascabeceras y en el cuerpo de la respuesta como un enlace Atom

Escritura de enlaces en cabeceras HTTP

Servicios Rest

118

PathGETResponse get() Link link = LinkfromUri(abc)build() Response response = ResponsenoContent() links(link) build() return response

Inclusioacuten de un enlace Atom en el documento XMl de respuesta

import javaxwsrscoreLink

XmlRootElementpublic class Cliente private String nombre private ListltLinkgt enlaces = new ArrayListltLinkgt()

XmlElement public String getNombre() return nombre

public void setNombre(String nom) thisnombre = nom

XmlElement(name = enlace)

XmlJavaTypeAdapter(LinkJaxbAdapterclass) public ListltLinkgt getEnlaces() return enlaces

La clase Link contiene tambieacuten un JaxbAdapter con una implementacioacuten de la claseJAXB XmlAdapter que mapea los objetos JAX-RS de tipo Link a un valor quepuede ser serializado y deserializado por JAXB

El coacutedigo de este ejemplo permite construir cualquier enlace y antildeadirlo a la clase Clientede nuestro dominio Los enlaces seraacuten convertidos a elementos XML que se incluiraacuten en eldocumento XML de respuesta

44 Seguridad

Es importante que los servicios rest permitan un acceso seguro a los datos y funcionalidadesque proporcionan Especialmente para servicios que permiten la realizacioacuten de actualizacionesen los datos Tambieacuten es interesante asegurarnos de que terceros no lean nuestros mensajese incluso permitir que ciertos usuarios accedan a determinadas funcionalidades pero a otrasno

Ademaacutes de la especificacioacuten JAX-RS podemos aprovechar los servicios de seguridad quenos ofrece la web y Java EE y utilizarla en nuestros servicios REST Estos incluyen

AutentificacioacutenHace referencia a la validacioacuten de la identidad del cliente que accede a los serviciosNormalmente implica la comprobacioacuten de si el cliente ha proporcionado unos credenciales

Servicios Rest

119

vaacutelidos tales como el password En este sentido podemos utilizar los mecanismos quenos proporciona la web y las facilidades del contenedor de servlets de Java EE paraconfigurar los protocolos de autentificacioacuten

AutorizacioacutenUna vea que el cliente se ha autenticado (ha validado su identidad) querraacute interactuarcon nuestro servicio REST La autorizacioacuten hace referencia a decidir si un cierto usuariopuede acceder e invocar un determinado meacutetodo sobre una determinada URI Por ejemplopodemos habilitar el acceso a operaciones PUTPOSTDELETE para ciertos usuarios peropara otros no En este caso utilizaremos las facilidades que nos propociona el contenedorde servlets de Java EE para realizar autorizaciones

EncriptadoCuando un cliente estaacute interaccionando con un servicio REST es posible que alguienintercepte los mensajes y los lea si la conexioacuten HTTP no es segura Los datos sensiblesdeberiacutean protegerse con servicios criptograacuteficos tales como SSL

Autentificacioacuten en JAX-RS

Hay varios protocolos de autentificacioacuten En este caso vamos a ver coacutemo realizar unaautenticacioacuten baacutesica sobre HTTP (y que ya habeacuteis utilizado para servlets) Este tipo deautentificacioacuten requiere enviar un nombre de usuario y password codificados como Base-64en una cabecera de la peticioacuten al servidor El servidor comprueba si existe dicho usuario en elsistema y verifica el password enviado Veaacutemoslo con un ejemplo

Supongamos que un cliente no autorizado quiere acceder a nuestros servicios REST

GET clientes333 HTTP11

Ya que la peticioacuten no contiene informacioacuten de autentificacioacuten el servidor deberiacutea responderla siguiente respuesta

HTTP11 401 UnauthorizedWWW-Autenticate Basic realm=Cliente Realm

La respuesta 401 nos indica que el cliente no estaacute autorizado a acceder a dicha URI Lacabecera WWW-Autenticate especifica queacute protocolo de autentificacioacuten se deberiacutea usarEn este caso Basic significa que se deberiacutea utilizar una autentificacioacuten de tipo Basic Elatributo realm identifica una coleccioacuten de recursos seguros en un sitio web En este ejemploindica que solamente estaacuten autorizados a acceder al meacutetodo GET a traveacutes de la URI anteriortodos aquellos uarios que pertenezcan al realm Cliente Realm y seraacuten autentificados porel servidor mediante una autentificacioacuten baacutesica

Para poder realizar la autentificacioacuten el cliente debe enviar una peticioacuten que incluya lacabecera Authorization cuyo valor sea Basic seguido de la siguiente cadena decaracteres loginpassword codificada en Base64 (el valor de login y password representael login y password del usuario) Por ejemplo supongamos que el nombre del usuario esfelipe y el password es locking la cadena felipelocking codificada como Base64es ZmVsaXBlOmxvY2tpbmc= Por lo tanto nuestra peticioacuten deberiacutea ser la siguiente

GET clientes333 HTTP11

Servicios Rest

120

Authorization Basic ZmVsaXBlOmxvY2tpbmc=

El cliente deberiacutea enviar esta cabecera con todas y cada una de las peticiones que haga alservidor

El inconveniente de esta aproximacioacuten es que si la peticioacuten es interceptada por alguna entidadhostil en la red el hacker puede obtner faacutecilmente el usuario y el passwork y utilizarlos parahacer sus propias peticiones Utilizando una conexioacuten HTTP encriptada (HTTPS) se solucionaeste problema

Creacioacuten de usuarios y roles

Para poder utilizar la autentificacioacuten baacutesica necesitamos tener creados previamente los realmsen el servidor de aplicaciones Wildfly y registrar los usuarios que pertenecen a dichos realmsLa forma de hacerlo es ideacutentica a lo que ya habeacuteis visto en la asignatura de ComponentesWeb (a traveacutes del comando add-usersh )

Utilizaremos el realm por defecto ApplicationRealm de Wildfly que nos permitiraacute ademaacutescontrolar la autorizacioacuten mediante la asignacioacuten de roles a usuarios

Lo uacutenico que tendremos que hacer es antildeadir los usuarios a dicho realm a traveacutes de laherramienta $WILDFLY_HOMEbinadd-usersh

Al ejecutarla desde liacutenea de comandos deberemos elegir el ream ApplicationRealm eintroducir los datos para cada nuevo usuario que queramos antildeadir indicando su loginpassword y el grupo (rol) al que queremos que pertenezca dicho usuario

Los datos sobre los nuevos usuarios creados se almacenan en los ficheros application-usersproperties y application-rolesproperties tanto en el directorio$WILDFLY_HOMEstandaloneconfiguration como en $WILDFLY_HOMEdomainconfiguration

Una vez creados los usuarios tendremos que incluir en el fichero de configuracioacuten webxml la siguiente informacioacuten

ltweb-appgt

ltlogin-configgt ltauth-methodgtBASICltauth-methodgt

ltrealm-namegtApplicationRealmltrealm-namegt ltlogin-configgt

ltsecurity-constraintgt ltweb-resource-collectiongt ltweb-resource-namegtcustomer creationltweb-resource-namegt

lturl-patterngtrestresourceslturl-patterngt

lthttp-methodgtPOSTlthttp-methodgt ltweb-resource-collectiongt ltsecurity-constraintgt ltweb-appgt

Servicios Rest

121

El elemento ltlogin-configgt define coacutemo queremos autentificar nuestro despliegueEl subelemento ltauth-methodgt puede tomar los valores BASIC DIGEST orCLIENT_CERT correspondieacutendose con la autentificacioacuten Basic Digest y ClientCertificate respectivamenteEl valor de la etiqueta ltrealm-namegt es el que se mostraraacute como valor del atributorealm de la cabecera WWW-Autenticate si intentamos acceder al recurso sin incluirnuestras credenciales en la peticioacutenEl elemento ltlogin-configgt realmente NO activa la autentificacioacuten Por defectocualquier cliente puede acceder a cualquier URL proporcionada por nuestra aplicacioacutenweb sin restricciones Para forzar la autentificacioacuten debemos especificar el patroacuten URLque queremos asegurar (elemento lturl-patterngt )El elemento lthttp-methodgt nos indica que solamente queremos asegurar laspeticiones POST sobre esta URL Si no incluimos el elemento lthttp-methodgt todoslos meacutetodos HTTP seraacuten seguros En este ejemplo solamente queremos asegurar losmeacutetodos POST dirigidos a la URL restresources

Autorizacioacuten en JAX-RS

Mientras que la autentificacioacuten hacer referencia a establecer y verificar la identidad del usuariola autorizacioacuten tiene que ver con los permisos iquestEl usuario X estaacute autorizado para acceder aun determinado recurso REST

JAX-RS se basa en las especificaciones Java EE y de servlets para definir la forma de autorizara los usuarios En Java EE la autorizacioacuten se realiza asociando uno o maacutes roles con un usuariodado y a continuacioacuten asignando permisos basados en dicho rol Ejemplos de roles puedenser administrador empleado Cada rol tiene asignando unos permisos de acceso adeterminados recursos por lo que asignaremos los permisos utilizando cada uno de los roles

Para poder realizar la autorizacioacuten tendremos que incluir determinadas etiquetas en el ficherode configuracioacuten webxml (tal y como ya habeacuteis visto en la asignatura de ComponentesWeb) Veaacutemoslo con un ejemplo (en el que tambieacuten incluiremos autentificacioacuten)

Volvamos a nuestra aplicacioacuten de venta de productos por internet En esta aplicacioacutenes posible crear nuevos clientes enviando la informacioacuten en formato XML a un recursoJAX-RS localizado por la anotacioacuten Path(clientes) El servicio REST esdesplegado y escaneado por la clase Application anotada con ApplicationPath(servicios) de forma que la URI completa es serviciosclientes Queremosproporcionar seguridad a nuestro servicio de clientes de forma que solamente losadministradores puedan crear nuevos clientes Veamos cuaacutel seriacutea el contenido del ficherowebxml

ltxml version=10gtltweb-appgt ltsecurity-constraintgt ltweb-resource-collectiongt ltweb-resource-namegtcreacion de clientesltweb-resource-namegt lturl-patterngtserviciosclienteslturl-patterngt lthttp-methodgtPOSTlthttp-methodgt ltweb-resource-collectiongt

ltauth-constraintgt ltrole-namegtadminltrole-namegt ltauth-constraintgt ltsecurity-constraintgt

Servicios Rest

122

ltlogin-configgt ltauth-methodgtBASICltauth-methodgt ltrealm-namegtApplicationRealmltrealm-namegt ltlogin-configgt

ltsecurity-rolegt ltrole-namegtadminltrole-namegt ltsecurity-rolegtltweb-appgt

Especificamos queacute roles tienen permiso para acceder mediante POST a la URLservicescustomers Para ello utilizamos el elemento ltauth-constraintgtdentro de ltsecurity-constraintgt Este elemento tiene uno o maacutes subelementosltrole-namegt que definen queacute roles tienen permisos de acceso definidos porltsecurity-constraintgt En nuestro ejemplo estamos dando al rol adminpermisos para acceder a la URL servicescustomers con el meacutetodo POST Si ensu lugar indicamos un ltrole-namegt con el valor cualquier usuario podriacutea accedera dicha URL En otras palabras un ltrole-namegt con el valor significa que cualquierusuario que sea capaz de autentificarse puede acceder al recursoPara cada ltrole-namegt que usemos en nuestras declaraciones ltauth-constraintsgt debemos definir el correspondiente ltsecurity-rolegt en eldescriptor de despliegue

Una limitacioacuten cuando estamos declarando las ltsecurity-contraintsgt para los recursosJAX-RS es que el elemento lturl-patterngt solamente soporta el uso de en el patroacutenurl especificado Por ejemplo rest txt

Encriptacioacuten

Por defecto la especificacioacuten de servlets no requiere un acceso a traveacutes de HTTPSSi queremos forzar un acceso HTTPS podemos especificar un elemento ltuser-data-constraintgt como parte de nuestra definicioacuten de restricciones de seguridad ( ltsecurity-constraintgt ) Vamos a modificar nuestro ejemplo anterior para forzar un acceso a traveacutesde HTTPS

ltweb-appgt ltsecurity-constraintgt

ltweb-resource-collectiongt ltweb-resource-namegtcreacion de clientesltweb-resource-namegt lturl-patterngtserviciosclienteslturl-patterngt lthttp-methodgtPOSTlthttp-methodgt ltweb-resource-collectiongt

ltauth-constraintgt ltrole-namegtadminltrole-namegt ltauth-constraintgt

ltuser-data-constraintgt

lttransport-guaranteegtCONFIDENTIALlttransport-guaranteegt ltuser-data-constraintgt ltsecurity-constraintgt

Servicios Rest

123

ltweb-appgt

Todo lo que tenemos que hacer es declarar un elemento lttransport-guaranteegtdentro de ltuser-data-constraintgt con el valor CONFIDENTIAL Si un usuariointenta acceder a una URL con el patroacuten especificado a traveacutes de HTTP seraacute redirigidoa una URL basada en HTTPS

Anotaciones JAX-RS para autorizacioacuten

Java EE define un conjunto de anotaciones para definir metadatos de autorizacioacuten Laespecificacioacuten JAX-RS sugiere aunque no es obligatorio que las implementaciones pordiferentes vendedores den soporte a dichas anotaciones Eacutestas se encuentran en elpaquete javaxannotationsecurity y son RolesAllowed DenyAll PermitAll yRunAs

La anotacioacuten RolesAllowed define los roles permitidos para ejecutar una determinadaoperacioacuten Si anotamos una clase JAX-RS define el acceso para todas las operacionesHTTP definidas en la clase JAX-RS Si anotamos un meacutetodo JAX-RS la restriccioacuten se aplicasolamente al meacutetodo que se estaacute anotando

La anotacioacuten PermitAll especifica que cualquier usuario autentificado puede invocar anuestras operaciones Al igual que RolesAllowed esta anotacioacuten puede usarse en la clasepara definir el comportamiento por defecto de toda la clase o podemos usarla en cada unode los meacutetodos Veamos un ejemplo

Path(clientes)

RolesAllowed(ADMIN CLIENTE) public class ClienteResource

GET Path(id) Produces(applicationxml) public Cliente getClienter(PathParam(id) int id)

RolesAllowed(ADMIN) POST Consumes(applicationxml) public void crearCliente(Customer cust)

PermitAll GET Produces(applicationxml) public Customer[] getClientes()

Por defecto solamente los usuarios con rol ADMIN y CLIENTE pueden ejecutar losmeacutetodos HTTP definidos en la clase ClienteResourceSobreescribimos el comportamiento por defecto Para el meacutetodo crearCliente()solamente permitimos peticiones de usuarios con rol ADMINSobreescribimos el comportamiento por defecto Para el meacutetodo getClientes() deforma que cualquier usuario autentificado puede acceder a esta operacioacuten a traveacutes de laURI correspondiente con el meacutetodo GET

Servicios Rest

124

La ventaja de utilizar anotaciones es que nos permite una mayor flexibilidad que el usodel fichero de configuracioacuten webxml pudiendo definir diferentes autorizaciones a nivel demeacutetodo

Seguridad programada

Hemos visto como utilizar una seguridad declarativa es decir basaacutendonos en meta-datosdefinidos estaacuteticamente antes de que la aplicacioacuten se ejecute JAX-RS proporciona unaforma de obtener informacioacuten de seguridad que nos permite implementar seguridad de formaprogramada en nuestras aplicaciones

Podemos utilizar la interfaz javaxwsrscoreSecurityContext para determinar laidentidad del usuario que realiza la invocacioacuten al meacutetodo proporcionando sus credencialesTambieacuten podemos comprobar si el usuario pertenece o no a un determinado rol Esto nospermite implementar seguridad de forma programada en nuestras aplicaciones

public interface SecurityContext public Principal getUserPrincipal() public boolean isUserInRole(String role) public boolean isSecure() public String getAuthenticationScheme()

El meacutetodo getUserPrincipal() devuelve un objeto de tipojavaxsecurityPrincipal que representa al usuario que actualmente estaacute realizandola peticioacuten HTTP

El meacutetodo isUserInRole() nos permite determinar si el usuario que realiza la llamadaactual pertenece a un determinado rol

El meacutetodo isSecure() devuelve cierto si la peticioacuten actual es una conexioacuten segura

El meacutetodo getAuthenticationScheme() nos indica queacute mecanismo de autentificacioacuten seha utilizado para asegurar la peticioacuten (valores tiacutepicos devueltos por el meacutetodo son BASIC DIGEST CLIENT_CERT y FORM )

Podemos acceder a una instancia de SecurityContext inyectaacutendola en un campo meacutetodosetter o un paraacutemetro de un recurso utilizando la anotacioacuten Context Veamos un ejemploSupongamos que queremos obtener un fichero de log con todos los accesos a nuestra basede datos de clientes hechas por usuarios que no son administradores

Path(clientes)public class CustomerService GET Produces(applicationxml) public Cliente[] getClientes(Context SecurityContext sec) if (secisSecure() ampamp secisUserInRole(ADMIN)) loggerlog(secgetUserPrincipal() + ha accedido a la base de datos de clientes)

Servicios Rest

125

En este ejemplo inyectamos una instancia de SecurityContext como un paraacutemetro delmeacutetodo getClientes() Utilizamos el meacutetodo SecurityContextisSecure() paradeterminar si se trata de una peticioacuten realizada a traveacutes de un canal seguro (como HTTPS) Acontinuacioacuten utilizamos el meacutetodo SecurityContextisUserInRole() para determinarsi el usuario que realiza la llamada tiene el rol ADMIN o no Finalmente imprimimos el resultadoen nuestro fichero de logs

Con la introduccioacuten del API de filtros en JAX-RS 20 podemos implementar la interfazSecurityContext y sobreescribir la peticioacuten actual sobre SecurityContext utilizandoel meacutetodo ContainerRequestContextsetSecurityContext() Lo interesante deesto es que podemos implementar nuestros propios protocolos de seguridad Por ejemplo

import javaxwsrscontainerContainerRequestContextimport javaxwsrscontainerContainerRequestFilterimport javaxwsrscontainerPreMatchingimport javaxwsrscoreSecurityContextimport javaxwsrscoreHttpHeaders

PreMatchingpublic class CustomAuth implements ContainerRequestFilter protected MyCustomerProtocolHandler customProtocol =

public void filter(ContainerRequestContext requestContext) throws IOException String authHeader = requestgetHeaderString(HttpHeadersAUTHORIZATION) SecurityContext newSecurityContext = customProtocolvalidate(authHeader) requestContextsetSecurityContext(authHeader)

Este filtro no muestra todos los detalles pero siacute la idea Extrae la cabecera Authorizationde la peticioacuten y la pasa a nuestro propio servicio customerProtocol Eacuteste devuelve unaimplementacioacuten de SecurityContext Finalmente sobreescribimos el SecurityContext pordefecto utilizando la nueva implementacioacuten

No vamos a explicar el API de filtros de JAS-RS 20 Como ya habeacuteis visto en la asignaturade Componentes Web los filtros son objetos que se interponen entre el procesamiento delas peticiones tanto del servidor como del cliente

El filtro mostrado en el ejemplo es un filtro de peticioacuten en la parte del servidor Este tipo defiltros se ejecuta antes de que se invoque a un meacutetodo JAX-RS

Servicios Rest

126

45 Ejercicios

Para los ejercicios de esta sesioacuten proporcionamos el MOacuteDULO s4-foroAvanzado quetendreacuteis que usar como plantilla para realizar las tareas planteadas

El proyecto estaacute estructurado loacutegicamente en los siguientes paquetes

bull orgexpertojavanegocio

bull orgexpertojavarest

A su vez cada uno de ellos contiene los subpaquetes api y modelo con las clasesrelacionadas con los servicios proporcionados y los datos utilizados por los serviciosrespectivamente

El API rest implementado es el siguiente

bull Recurso UsuariosResourcejava

GET usuarios proporciona un listado con los usuarios del foro

bull Subrecurso UsuarioResourcejava

GET usuarioslogin proporciona informacioacuten sobre el usuario cuyo login es login

PUT usuarioslogin actualiza los datos de un usuario

DELETE usuarioslogin borra los datos de un usuario

GET usuariosloginmensajes obtiene un listado de los mensajes de un usuario

bull Recurso MensajesResourcejava

GET mensajes proporciona un listado con los mensajes del foro

POST mensajes antildeade un mensaje nuevo en el foro

GET mensajesid proporciona informacioacuten sobre el mensaje cuyo id es id

PUT mensajesid modifica un mensaje

DELETE mensajesid borra un mensaje

Una vez desplegada la aplicacioacuten podeacuteis antildeadir datos a la base de datos del foro utilizandolos datos del fichero srcmainresourcesforosql Para ello simplemente tendreacuteis que invocarla goal Maven correspondiente desde la ventana Maven Projects gt s4-foroAvanzado gt Pluginsgt sql gt sqlexecute

En el directorio srcmainresources teneacuteis un fichero de texto ( instruccionestxt )con informacioacuten adicional sobre la implementacioacuten proporcionada

A partir de las plantillas se pide

Uso de Hateoas (1 puntos)

Vamos a antildeadir a los servicios enlaces a las operaciones que podemos realizar con cadarecurso siguiendo el estilo Hateoas

a Para los usuarios

Servicios Rest

127

bull En el listado de usuarios antildeadir a cada usuario un enlace con relacioacuten self que apuntea la direccioacuten a la que estaacute mapeado el usuario individual

bull En la operacioacuten de obtencioacuten de un usuario individual incluir los enlaces para ver elpropio usuario (self) modificarlo (usuariomodificar) borrarlo (usuarioborrar) o ver losmensajes que envioacute el usuario (usuariomensajes)

b Para los mensajes

bull En el listado de mensajes antildeadir a cada mensaje un enlace con relacioacuten self queapunte a la direccioacuten a la que estaacute mapeado el mensaje individual

bull En la operacioacuten de obtencioacuten de un mensaje individual incluir los enlaces para ver elpropio mensaje (self) modificarlo (mensajemodificar) borrarlo (mensajeborrar) o verlos datos del usuario que envioacute el mensaje (mensajeusuario)

Utiliza postman para comprobar las modificaciones realizadas

Ejercicio seguridad (1 punto)

Vamos ahora a restringir el acceso al servicio para que soacutelo usuarios registrados puedanrealizar modificaciones Se pide

a Antildeadir al usuario pepe en el ApplicationRealm de wildfly con la contrasentildea pepe yperteneciente al grupo (rol) registrado

b Configurar mediante seguridad declarativa para que las operaciones de modificacioacuten(POST PUT y DELETE) soacutelo la puedan realizar los usuarios con rol registrado Utilizarautentificacioacuten de tipo BASIC

c Ahora vamos a hacer que la modificacioacuten o borrado de usuarios soacutelo puedarealizarlas el mismo usuario que va a modificarse o borrarse Para ello utilizaremosseguridad programada En el caso de que el usuario que va a realizar lamodificacioacuten o borrado quiera borrarmodificar otros usuarios lanzaremos la excepcioacutenWebApplicationException(StatusFORBIDDEN)

d Vamos a hacer lo mismo con los mensajes Soacutelo podraacute modificar y borrar mensajes elmismo usuario que los creoacute y al publicar un nuevo mensaje forzaremos que el login delmensaje sea el del usuario que hay autentificado en el sistema

Utiliza postman para comprobar las modificaciones realizadas

Servicios Rest

128

5 Api cliente Procesamiento JSON y Pruebas

Hasta ahora hemos hablado sobre la creacioacuten de servicios web RESTful y hemos probadonuestros servicios utilizando el cliente que nos proporciona IntelliJ curl o Postman pararealizar peticiones y observar las respuestas JAX-RS 20 proporciona un API cliente parafacilitar la programacioacuten de clientes REST que presentaremos en esta sesioacuten

En sesiones anteriores hemos trabajado con representaciones de texto y xmlfundamentalmente Aquiacute hablaremos con maacutes detalle de JSON que constituye otra forma derepresentar los datos de las peticiones y respuestas de servicios REST muy extendida

Finalmente veremos coacutemo implementar pruebas sobre nuestro servicio utilizando el APIcliente y el framework junit

51 API cliente Visioacuten general

La especificacioacuten JAX-RS 20 incorpora un API cliente HTTP que facilita enormemente laimplementacioacuten de nuestros clientes RESTful y constituye una clara alternativa al uso declases Java como javanetURL libreriacuteas externas (como la de Apache) u otras solucionespropietarias

El API cliente estaacute contenido en el paquete javaxwsrsclient y estaacute disentildeado paraque se pueda utilizar de forma fluida (fluent) Esto significa como ya hemos visto que loutilizaremos encadenando una sucesioacuten de llamadas a meacutetodos del API permitieacutendonos asiacuteescribir menos liacuteneas de coacutedigo Baacutesicamente estaacute formado por tres clases principales ClientWebTarget y Response (ya hemos hablado de esta uacuteltima en sesiones anteriores)

Para acceder a un recurso REST mediante el API cliente es necesario seguir los siguientespasos

1 Obtener una instancia de la interfaz Client

2 Configurar la instancia Client a traveacutes de un target (instancia de WebTarget )

3 Crear una peticioacuten basada en el target anterior

4 Invocar la peticioacuten

Vamos a mostrar un coacutedigo ejemplo para ilustrar los pasos anteriores En este caso vamos ainvocar peticiones POST y GET sobre una URL (target) para crear y posteriormente consultarun objeto Cliente que representaremos en formato XML

Client client = ClientBuildernewClient()

WebTarget target =

clienttarget(httpexpertojavaorgclientes)

Response response = target

request()

post(Entityxml(new Cliente(Alvaro Gomez)))

responseclose()

Servicios Rest

129

Cliente cliente = targetqueryParam(nombre Alvaro Gomez) request()

get(Clienteclass) clientclose()

Obtenemos una instancia javaxwsrsclientClientCreamos un WebTargetCreamos la peticioacutenRealizamos una invocacioacuten POSTCerramos (liberamos) el input stream para esta respuesta (en el caso de que esteacutedisponible y abierto) Es una operacioacuten idempotente es decir podemos invocarlamuacuteltiples veces con el mismo efectoA partir de un WebTarget establecemos los valores de los queryParams de la URIde la peticioacuten creamos la peticioacuten y realizamos una invocacioacuten GET

A continuacioacuten explicaremos con detalle los pasos a seguir para implementar un clienteutilizando el API de JAX-RS

Obtenemos una instancia Client

La interfaz javaxwsrsclientClient es el principal punto de entrada del API ClienteDicha interfaz define las acciones e infraestructura necesarias requeridas por un cliente RESTpara consumir un servicio web RESTful Los objetos Client se crean a partir de la claseClientBuilder

Clase ClientBuilder utilizada para crear objetos Client

public abstract class ClientBuilder implements ConfigurableltClientBuildergt public static Client newClient() public static Client newClient(final Configuration configuration)

public static ClientBuilder newBuilder()

public abstract ClientBuilder sslContext(final SSLContext sslContext) public abstract ClientBuilder keyStore(final KeyStore keyStore final char[] password) public ClientBuilder keyStore(final KeyStore keyStore final String password) public abstract ClientBuilder trustStore(final KeyStore trustStore) public abstract ClientBuilder hostnameVerifier(final HostnameVerifier verifier)

public abstract Client build()

La forma maacutes sencilla de crear un objeto Client es medianteClientBuildernewClient() Este meacutetodo proporciona una instancia pre-inicializada detipo Client lista para ser usada La clase ClientBuilder nos proporciona meacutetodos adicionalescon los que podremos configurar diferentes propiedades del objeto

Veamos un ejemplo de uso de ClientBuildernewBuilder() utilizando ademaacutes algunode los meacutetodos proporcionados para configurar nuestra instancia de tipo Client que vamos

Servicios Rest

130

a crear Los meacutetodos register() y property() son meacutetodos de la interfaz Configurable(y que son implementados por ClientBuilder)

Ejemplo de uso de ClientBuilder

Client cliente = ClientBuildernewBuilder()

property(connectiontimeout 100)

sslContext(sslContext)

register(JacksonJsonProviderclass)

build()

Creamos un ClientBuilder invocando al meacutetodo estaacuteticoClientBuildernewBuilder()Asignamos una propiedad especiacutefica de la implementacioacuten concreta de JAX-RS queestemos utilizando que controla el timeout de las conexiones de los socketsEspecificamos el sslContext que queremos utilizar para gestionar las conexiones HTTPRegistramos a traveacutes del meacutetodo register() (de la interfaz Configurable) una claseanotada con Provider Dicha clase conoce coacutemo serializar objetos Java a JSONy viceversaFinalmente realizamos una llamada a build() para crear la instancia Client

Las instancias de Client gestionan conexiones con el cliente utilizando sockets y sonobjetos bastante pesados Se deberiacutean reutilizar las instancias de esta interfaz en la medida delo posible ya que la inicializacioacuten y destruccioacuten de dichas instancias consume mucho tiempoPor lo tanto por razones de rendimiento debemos limitar el nuacutemero de instancias Clienten nuestra aplicacioacuten

Client client = ClientBuildernewClient()

clientclose()

Obtenemos una instancia de tipo Client invocando al meacutetodoClientBuildernewClient()Utilizamos el meacutetodo close() para cerrar la instancia Client despueacutes de realizartodas las invocaciones sobre el target del recurso De esta forma cerramos la conexioacutende forma que se liberan sus recursos y ya no podremos seguir usaacutendola

Recuerda siempre invocar el meacutetodo close() sobre nuestros objetosClient despueacutes de que hayamos realizado todas las invocacionessobre el target dellos recursos REST A menudo los objetos Clientreutilizan conexiones por razones de rendimiento Si no los cerramosdespueacutes de utilizarlos estaremos desaprovechando recursos del sistemamuy valiosos Cerrar la conexioacuten implica cerrar el socket

Igualmente si el resultado de una invocacioacuten a un servicio rest es unainstancia de Response debemos invocar el meacutetodo close() sobredichos objetos Response para liberar la conexioacuten Liberar una conexionsignifica permitir que eacutesta esteacute disponible para otro uso por una instanciaClient Liberar la conexioacuten no implica cerrar el socket

La interfaz Client es una sub-interfaz de Configurable Esto nos permitiraacute utililizarlos meacutetodos property() y register() para cambiar la configuracioacuten y registrarcomponentes en la parte del cliente en tiempo de ejecucioacuten

Servicios Rest

131

Interfaz Client (es una subinterfaz de Configurable)

public interface Client extends ConfigurableltClientgt

public void close()

public WebTarget target(String uri) public WebTarget target(URI uri) public WebTarget target(UriBuilder uriBuilder) public WebTarget target(Link link)

Sin embargo el principal propoacutesito de Client es crear instancias de WebTarget comoveremos a continuacioacuten

Configuramos el target del cliente (URI)

La interfaz javaxwsrsclientWebTarget representa la URI especiacutefica quequeremos invocar para acceder a un recurso REST particular

Interfaz WebTarget (es una subinterfaz de Configurable)

public interface WebTarget extends ConfigurableltWebTargetgt

public URI getUri() public UriBuilder getUriBuilder()

public WebTarget path(String path) public WebTarget resolveTemplate(String name Object value) public WebTarget resolveTemplates(MapltString Objectgt templateValues) public WebTarget matrixParam(String name Object values) public WebTarget queryParam(String name Object values)

La interfaz WebTarget tiene meacutetodos para extender la URI inicial que hayamosconstruido Podemos antildeadir por ejemplo segmentos de path o paraacutemetros deconsulta invocando a los meacutetodos WebTargetpath() o WebTargetqueryParam() respectivamente Si la instancia de WebTarget contiene plantillas de paraacutemetros losmeacutetodos WebTargetresolveTemplate() pueden asignar valores a las variablescorrespondientes Por ejemplo

Ejemplo para crear la URI httpejemplocomclientes123verboso=true

WebTarget target = client

target(httpejemplocomclientesid)

resolveTemplate(id 123)

queryParam(verboso true)

Servicios Rest

132

Inicializamos un WebTarget con una URI que contiene una plantilla con un paraacutemetroid El objeto client es una instancia de la clase `ClientEl meacutetodo resolveTemplate() rellena la expresioacuten id con el valor 123Finalmente antildeadimos a la URI un paraacutemetro de consulta verboso=true

Las instancias de WebTarget son inmutables con respecto a la URI que contienen Estosignifica que los meacutetodos para especificar segmentos de path adicionales y paraacutemetrosdevuelven una nueva instancia de WebTarget Sin embargo las instancias de WebTargetson mutables respecto a su configuracioacuten Por lo tanto la configuracioacuten de objetosWebTarget no crea nuevas instancias

Veamos otro ejemplo

WebTarget base = clientetarget(httpexpertojavaorg)

WebTarget clienteURI = basepath(cliente)

clienteURIregister(MyProviderclass)

base es una instancia de WebTarget con el valor de URI httpexertojavaorgclienteURI es una instancia de WebTarget con el valor de URI httpexertojavaorgclienteConfiguramos clienteURI registrando la clase MyProvider

En este ejemplo creamos dos instancias de WebTarget La instancia clienteURI heredala configuracioacuten de base y posteriormente modificamos la configuramos registrando unaclase Provider Los cambios sobre la configuracioacuten de clienteURI no afectan a laconfiguracioacuten de base ni tampoco se crea una nueva instancia de WebTarget

Los beneficios del uso de WebTarget se hacen evidentes cuando construimos URIscomplejas por ejemplo cuando extendemos nuestra URI base con segmentos de pathadicionales o plantillas El siguiente ejemplo ilustra estas situaciones

WebTarget base = clientetarget(httpexpertojavaorg)

WebTarget saludo = basepath(hola)path(quien) Response res = saludoresolveTemplate(quien mundo)request()get()

base representa la URI httpexpertojavaorgsaludo representa la URI httpexpertojavaholaquien

En el siguiente ejemplo utilizamos una URI base y a partir de ella construimos otras URIs querepresentan servicios diferentes proporcionados por nuestro recurso REST

Client cli = ClientBuildernewClient()WebTarget base = clienttarget(httpejemplowebapi)

WebTarget lectura = basepath(leer)

WebTarget escritura = basepath(escribir)

lectura representa la uri httpejemplowebapileerescritura representa la uri httpejemplowebapiescribir

El meacutetodo WebTargetpath() crea una nueva instancia de WebTarget antildeadiendo a laURI actual el segmento de ruta que se pasa como paraacutemetro

Servicios Rest

133

Construimos y Realizamos la peticioacuten

Una vez que hemos creado y configurado convenientemente el WebTarget que representala URI que queremos invocar tenemos que construir la peticioacuten y finalmente realizarla

Para construir la peticioacuten podemos Utilizar uno de los meacutetodos WebTargetrequest() que mostramos a continuacioacuten

Interfaz WebTarget meacutetodos para comenzar a construir la peticioacuten

public interface WebTarget extends ConfigurableltWebTargetgt public InvocationBuilder request() public InvocationBuilder request(String acceptedResponseTypes) public InvocationBuilder request(MediaType acceptedResponseTypes)

Normalmente invocaremos WebTargetrequest() pasando como paraacutemetro el mediatype aceptado como respuesta en forma de String o utilizando una de las constantes dejavaxwsrscoreMediaType Los meacutetodos WebTargetrequest() devuelven unainstancia de InvocationBuilder una interfaz que proporciona meacutetodos para prepararla peticioacuten del cliente y tambieacuten para invocarla

La interface InvocationBuilder Contiene un conjunto de meacutetodos que nos permitenconstruir diferentes tipos de cabeceras de peticiones Asiacute por ejemplo proporciona variosmeacutetodos acceptXXX() para indicar diferentes tipos MIME lenguajes o encodingaceptados Tambieacuten proporciona meacutetodos cookie() para especificar cookies para enviaral servidor Finalmente proporciona meacutetodos header() para especificar diferentes valoresde cabeceras

Ejemplos de uso de esta interfaz para construir la peticioacuten

Client cli = ClientBuildernewClient()cliinvocation(LinkvalueOf(httpejemplorest))accept(applicationjson)get()si no utilizamos el meacutetodo invocation podemos hacerlo asiacuteclitarget(httpejemplorest)request(applicationjson)get()

Client cliente = ClientBuildernewClient()WebTarget miRecurso = clienttarget(httpejemplowebapimensaje) request(MediaTypeTEXT_PLAIN)

El uso de una constante MediaType es equivalente a utilizar el String que define el tipoMIME

InvocationBuilder builder = miRecursorequest(textplain)

Hemos visto que WebTarget implementa meacutetodos request() cuyosparaacutemetros especifican el tipo MIME de la cabecera Accept de la peticioacutenEl coacutedigo puede resultar maacutes legible si usamos en su lugar el meacutetodo

Servicios Rest

134

InvocationBuilderaccept() En cualquier caso es una cuestioacuten de gustospersonales

Despueacutes de determinar el media type de la respuesta invocamos la peticioacuten realizando unallamada a uno de los meacutetodos de la instancia de InvocationBuilder que se correspondecon el tipo de peticioacuten HTTP esperado por el recurso REST al que va dirigido dicha peticioacutenEstos meacutetodos son

bull get()

bull post()

bull delete()

bull put()

bull head()

bull options()

La interfaz InvocationBuilder es una subinterfaz de la interfaz SyncInvoker y es la queespecifica los meacutetodos anteriores (get post hellip) para realizar peticiones siacutencronas es decirque hasta que no nos conteste el servidor no podremos continuar procesando el coacutedigo enla parte del cliente

Las peticiones GET tienen los siguientes prototipos

Interface SyncInvoker peticiones GET siacutencronas

public interface SyncInvoker ltTgt T get(ClassltTgt responseType) ltTgt T get(GenericTypeltTgt responseType) Response get()

Los primeros dos meacutetodos geneacutericos convertiraacuten una respuesta HTTP con eacutexito a tipos Javaespeciacuteficos indicados como paraacutemetros del meacutetodo El tercero devuelve una instancia de tipoResponse Por ejemplo

Ejemplos de peticiones GET utilizando el API cliente jasx-rx 20

Client cli = ClientBuildernewClient()

peticioacuten get que devuelve una instancia de ClienteCliente cliRespuesta = clitarget(httpejemploclientes123) request(applicationjson)

get(Clienteclass)

peticioacuten get que devuelve una lista de objetos ClienteListltClientegt cliRespuesta2 = clitarget(httpejemploclientes) request(applicationxml)

get(new GenericTypeltListltClientegtgt() )

peticioacuten get que devuelve un objeto de tipo ResponseResponse respuesta = clitarget(httpejemploclientes245)

Servicios Rest

135

request(applicationjson)

get() try if (respuestagetStatus() == 200) Cliente cliRespuesta =

respuestareadEntity(Clienteclass) finally respuestaclose()

En la primera peticioacuten queremos que el servidor nos devuelva la respuesta en formatoJSON y posteriormente la convertiremos en el tipo Cliente utilizando un de loscomponentes MessageBodyReader registradosEn la segunda peticioacuten utilizamos la clase javaxwsrscoreGenericType parainformar al correspondiente MessageBodyReader del tipo de objetos de nuestra ListaPara ello creamos una clase anoacutenima a la que le pasamos como paraacutemetro el tipogeneacuterico que queremos obtenerEn la tercera peticioacuten obtenemos una instancia de Response a partir de la cual podemosobtener el cuerpo del mensaje de respuesta del servidorEl meacutetodo readEntity() asocia el tipo Java solicitado (en este caso el tipo java Cliente) yel contenido de la respuesta recibida con el correspondiente proveedor de entidades (detipo MessageBodyReader) para obtener dicho tipo Java a partir de la respuesta HTTPrecibida En sesiones anteriores hemos utilizado la clase Response desde el servicioREST para construir la respuesta que se enviacutea al cliente

Recordemos algunos de los meacutetodos que podemos utilizar desde el cliente para analizar larespuesta que hemos obtenido

Meacutetodos de la clase Response que utilizaremos desde el cliente

public abstract class Response

public abstract Object getEntity()

public abstract int getStatus()

public abstract ResponseStatusType getStatusInfo()

public abstract MultivaluedMapltString Objectgt getMetadata()

public abstract URI getLocation()

public abstract MediaType getMediaType()

public MultivaluedMapltStringObjectgt getHeaders()

public abstract ltTgt T readEntity(ClassltTgt entityType)

public abstract ltTgt T readEntity(GenericTypeltTgt entityType)

public abstract void close()

El meacutetodo getEntity() devuelve el objeto Java correspondiente al cuerpo delmensaje HTTPEl meacutetodo getStatus() devuelve el coacutedigo de respuesta HTTPEl meacutetodo getStatusInfo() devuelve la informacioacuten de estado asociada con larespuestaEl meacutetodo getMetadata() devuelve una instancia de tipo MultivaluedMap con lascabeceras de la respuesta

Servicios Rest

136

El meacutetodo getLocation() devuelve la URI de la cabecera Location de la respuestaEl meacutetodo getMediaType() devuelve el mediaType del cuerpo de la respuestaEl meacutetodo getHeaders() devuelve las cabeceras de respuesta con sus valorescorrespondientesEl meacutetodo readEntity() devuelve la entidad del cuerpo del mensaje utilizando unMessageBodyReader que soporte el mapeado del inputStream de la entidad a la claseJava especificada como paraacutemetroEl meacutetodo readEntity() tambieacuten puede devolver una clase geneacuterica si se disponedel MessageBodyReader correspondienteEl meacutetodo close() cierra el input stream correspondiente a la entidad asociada delcuerpo del mensaje (en el caso de que esteacute disponible y abierto) Tambieacuten liberacualquier otro recurso asociado con la respuesta (como por ejemplo datos posiblementealmacenados en un buffer)

Veamos otro ejemmplo Si el recurso REST espera una peticioacuten HTTP GET invocaremosel meacutetodo InvocationBuilderget() El tipo de retorno del meacutetodo deberiacuteacorresponderse con la entidad devuelta por el recurso REST que atenderaacute la peticioacuten

Client cliente = ClientBuildernewClient()WebTarget miRecurso = clientetarget(httpejemplowebapilectura)String respuesta = miRecursorequest(MediaTypeTEXT_PLAIN) get(Stringclass)

O tambieacuten podriacuteamos codificarlo como

Client cliente = ClientBuildernewClient()String respuesta = cliente target(httpejemplowebapilectura) request(MediaTypeTEXT_PLAIN) get(Stringclass)

Si el tipo de retorno de la peticioacuten GET es una coleccioacuten usaremosjavaxwsrscoreGenericTypeltTgt como paraacutemetro del meacutetodo en donde T es eltipo de la coleccioacuten

ListltPedidoAlmacengt pedidos = client target(httpejemplowebapilectura) path(pedidos) request(MediaTypeAPPLICATION_XML) get(new GenericTypeltListltPedidoAlmacengtgt() )

Si el recurso REST destinatario de la peticioacuten espera una peticioacuten de tipo HTTP POSTinvocaremos el meacutetodo InvocationBuilderpost()

Las peticiones POST tienen los siguientes prototipos

Interface SyncInvoker peticiones POST siacutencronas

public interface SyncInvoker ltTgt T post(Entityltgt entity ClassltTgt responseType)

Servicios Rest

137

ltTgt T post(Entityltgt entity GenericTypeltTgt responseType) Response post(Entityltgt entity)

Los primeros dos meacutetodos geneacutericos enviacutean una entidad (clase java + tipo MIME asociado)indicada como primer paraacutemetro del meacutetodo y como segundo paraacutemetro se indica el tipo javaal que se convertiraacute la respuesta recibida El tercero enviacutea una entidad y devuelve una instanciade tipo Response Por ejemplo

Veamos un ejemplo de invocacioacuten de peticiones POST

Ejemplo de peticioacuten POST utilizando el API cliente jasx-rx 20

Client cli = ClientBuildernewClient()Pedido pe = new PedidoAlmacen()Pedido peRespuesta = cli target() request() post(Entityentity(new Pedido() applicationjson) Pedidoclass)

En este caso estamos realizando una peticioacuten POST Como payload del mensaje enviamosun objeto Pedido representado en formato json La entidad esperada como respuesta debeser de tipo Pedido

Esto implica que en el lado del servidor el meacutetodo que atiende la peticioacuten Post tendraacute unparaacutemetro de tipo Pedido y se deberaacuten serializar los objetos de tipo Pedido a json ya que esel tipo MIME asociado a esta entidad ( especificado en la cabera Content-Type de la peticioacutenHTTP)

La clase Entity encapsula los objetos Java que queremos enviar con las peticiones GET oPOST No tiene un constructor puacuteblico En su lugar tenemos que invocar uno de sus meacutetodosestaacuteticos

Clase javaxwsrsclientEntity

public final class EntityltTgt

public static ltTgt EntityltTgt entity(T entity String mediaType)

public static ltTgt EntityltTgt entity(T entity MediaType mediaType)

public static ltTgt EntityltTgt xml(final T entity)

public static ltTgt EntityltTgt json(final T entity)

public static ltTgt EntityltTgt text(T entity)

public static EntityltFormgt form(final Form form)

El meacutetodo estaacutetico entity() crea una entidad (clase Java) con un tipo MIME asociado dadopor la cadena de caracteres mediaTypeEl meacutetodo estaacutetico entity() crea una entidad (clase Java) con un tipo MIME indicado enmediaTypeEl meacutetodo xml crea una entidad (clase Java) con el tipo MIME applicationxml

Servicios Rest

138

El meacutetodo json crea una entidad (clase Java) con el tipo MIME applicationjsomEl meacutetodo text crea una entidad (clase Java) con el tipo MIME textplainEl meacutetodo form crea una entidad (clase Java) con el tipo MIME applicationx-www-form-urlencoded

Veamos otro ejemplo de invocacioacuten POST que utiliza la clase Entity

Ejemplo de peticioacuten POST y uso de clase Entity

NumSeguimiento numSeg = client target(httpejemplowebapiescritura)

request(MediaTypeAPPLICATION_XML)

post(Entityxml(pedido) NumeroSeguimientoclass)

Especificamos como paraacutemetro de la peticioacuten request() el tipo MIME que aceptamos enla respuesta (cabecera HTTP Accept)Realizamos una peticioacuten POST El cuerpo del mensaje se crea con la llamadaEntityxml(pedido) El tipo Entity encapsula la entidad del mensaje (tipo JavaPedido) y el tipo MIME asociado (tipo MIME applicationxml)

Veamos un ejemplo en el que enviamos paraacutemetros de un formulario en una peticioacuten POST

Ejemplo de enviacuteo de datos de un formulario en una peticioacuten POST

Form form = new Form()param(nombre Pedro) param(apellido Garcia)Response response = clienttarget(httpejemploclientes) request() post(Entityform(form))responseclose()

La peticioacuten POST del coacutedigo anterior enviacutea los datos del formulario y espera recibir comorespuesta una entidad de tipo Response

El coacutedigo en el lado del servidor seraacute similar a eacuteste

Servicio rest que sirve una peticioacuten POST a partir de datos de un formulario

POSTPath(clientes)Produces(texthtml)public Response crearCliente(FormParam(nombre)String nom FormParam(apellido)String ape) creamos el nuevo cliente return Responseok(RESPONSE_OK)build()

Manejo de excepciones

Veamos queacute ocurre si se produce una excepcioacuten cuando utilizamos una forma de invocacioacutenque automaacuteticamente convierte la respuesta en el tipo especificado Supongamos el siguienteejemplo

Servicios Rest

139

Cliente cli = clienttarget(httptiendacomclientes123) request(applicationjson) get(Clienteclass)

En este escenario el framework del cliente convierte cualquier coacutedigo de error HTTP en unade las excepciones que antildeade JAX-RS 20 (BadRequesException ForbiddenExceptionhellip) yque ya hemos visto Podemos capturar dichas excepciones en nuestro coacutedigo para tratarlasadecuadamente

try Cliente cli = clienttarget(httptiendacomclientes123) request(applicationjson) get(Clienteclass) catch (NotAcceptableException notAcceptable) catch (NotFoundException notFound)

Si el servidor responde con un error HTTP no cubierto por alguna excepcioacutenespeciacutefica JAX-RS entonces se lanza una excepcioacuten de propoacutesito general La claseClientErrorException cubre cualquier coacutedigo de error en la franja del 400 La claseServerErrorException cubre cualquier coacutedigo de error en la franja del 500

Si el servidor enviacutea alguna de los coacutedigos de respuesta HTTP 3xx (clasificados como coacutedigosde la categoriacutea redireccioacuten) el API cliente lanza una RedirectionException a partir de lacual podemos obtener la URL para poder tratar la redireccioacuten nosotros mismos Por ejemplo

WebTarget target = clienttarget(httptiendacomclientes123)boolean redirected = false

Cliente cli = nulldo try cli = targetrequest(applicationjson) get(Clienteclass) catch (RedirectionException redirect) if (redirected) throw redirect redirected = true target = clienttarget(redirectgetLocation()) while (cli == null)

En este ejemplo volvemos a iterar si recibimos un coacutedigo de respuesta 3xx El coacutedigo seasegura de que soacutelo permitimos un coacutedigo de este tipo cambiando el valor de la variableredirect en el bloque en el que capturamos la exceptioacuten A continuacioacuten cambiamos elWebTarget (en el bloque catch ) al valor de la cabecera Location de la respuesta delservidor

Los coacutedigos de estado HTTP 3xx indican que es neceario realizar algunaaccioacuten adicional para que el servidor pueda completar la peticioacuten Laaccioacuten requerida puede llevarse a cabo sin necesidad de interactuar con el

Servicios Rest

140

cliente soacutelo si el meacutetodo utilizado en la segunda peticioacuten es GET o HEAD(ver httpwwww3orgProtocolsrfc2616rfc2616-sec10html)

52 Procesamiento JSON

JSON (JavaScript Object Notation) es un formato para el intercambio de datos basado en textoderivado de Javascript (Javascript disponde de una funcioacuten nativa eval() para convertirstreams JSON en objetos con propiedades que son accesibles sin necesidad de manipularninguna cadena de caracteres)

La especificacioacuten JSR 3539 proporciona un API para el procesamiento de datos JSON(parsing transformacioacuten y consultas)

La gramaacutetica de los objetos JSON es bastante simple Soacutelo se requieren dos estructurasobjetos y arrays Un objeto es un conjunto de pares nombre-valor y un array es una lista devalores JSON define siete tipos de valores string number object array true false y null

El siguiente ejemplo muestra datos JSON para un objeto que contiene pares nombre-valor

Ejemplo formato JSON

nombre John apellidos Smith edad 25 direccion calle 21 2nd Street ciudad New York codPostal 10021 telefonos [ tipo fijo numero 212 555-1234 tipo movil numero 646 555-4567 ]

El objeto anterior tiene cinco pares nombre-valor

bull Los dos primeros son nombre y apellidos con el valor de tipo String

bull El tercero es edad con el valor de tipo number

bull El cuarto es direccion con el valor de tipo object

bull El quinto es telefonos cuyo valor es de tipo array con dos objetos

JSON tiene la siguiente sintaxis

bull Los objetos estaacuten rodeados por llaves sus pares de elementos nombre-valor estaacutenseparados por una coma y el nombre y el valor de cada par estaacuten separados por dospuntos Los nombres en un objeto son de tipo String mientras que sus valores

9 httpsjcporgaboutJavacommunityprocessfinaljsr353indexhtml

Servicios Rest

141

pueden ser cualquiera de los siete tipos que ya hemos indicado incluyendo a otro objetou otro array

bull Los arrays estaacuten rodeados por corchetes [] y sus valores estaacuten separados por una coma Cada valor en un array puede ser de un tipo diferente incluyendo a otro objeto o array

bull Cuando los objetos y arrays contienen otros objetos yo arrays los datos adquieren unaestructura de aacuterbol

Los servicios web RESTful utilizan JSON habitualmente tanto en las peticiones como en lasrespuestas La cabecera HTTP utilizada para indicar que el contenido de una peticioacuten o unarespuesta es JSON es la siguiente

Content-Type applicationjson

La representacioacuten JSON es normalmente maacutes compacta que las representaciones XML debidoa que JSON no tiene etiquetas de cierre A diferencia de XML JSON no tiene un esquemade definicioacuten y validacioacuten de datos ampliamente aceptado

Actualmente las aplicaciones Java utilizan diferentes libreriacuteas para producirconsumir JSONque tienen que incluirse junto con el coacutedigo de la aplicacioacuten incrementando asiacute el tamantildeo delarchivo desplegado El API de Java para procesamiento JSON proporciona un API estaacutendarpara analizar y generar JSON de forma que las aplicaciones que utilicen dicho API sean maacutesligeras y portables

Para generar y parsear datos JSON hay dos modelos de programacioacuten que son similares alos usados para documentos XML

bull El modelo de objetos crea un aacuterbol en memoria que representa los datos JSON

bull El modelo basado en streaming utiliza un parser que lee los datos JSON elemento aelemento (uno cada vez)

Java EE incluye soporte para JSR 353 de forma que el API de java para procesamiento JSONse encuentra en los siguientes paquetes

bull El paquete javaxjson contiene interfaces para leer escribir y construir datos JSONseguacuten el modelo de objetos asiacute como otras utilidades

bull El paquete javaxjsonstream contiene una interfaz para parsear y generar datosJSON para el modelo streaming

Vamos a ver coacutemo producir y consumir datos JSON utilizando cada uno de los modelos

53 Modelo de procesamiento basado en el modelo de objetos

En este caso se crea un aacuterbol en memoria que representa los datos JSON (todos losdatos) Una vez construido el aacuterbol se puede navegar por eacutel analizarlo o modificarlo Estaaproximacioacuten es muy flexible y permite un procesamiento que requiera acceder al contenidocompleto del aacuterbol En contrapartida normalmente es maacutes lento que el modelo de streamingy requiere utilizar maacutes memoria El modelo de objetos genera una salida JSON navegandopor el aacuterbol entero de una vez

El siguiente coacutedigo muestra coacutemo crear un modelo de objetos a partir de datos JSON desdeun fichero de texto

Creacioacuten de un modelos de objetos a partir de datos JSON

Servicios Rest

142

import javaioFileReaderimport javaxjsonJsonimport javaxjsonJsonReaderimport javaxjsonJsonStructureJsonReader reader = JsoncreateReader(new FileReader(datosjsontxt))JsonStructure jsonst = readerread()

El objeto jsonst puede ser de tipo JsonObject o de tipo JsonArray dependiendo delos contenidos del fichero JsonObject y JsonArray son subtipos de JsonStructure Este objeto representa la raiacutez del aacuterbol y puede utilizarse para navegar por el aacuterbol o escribirloen un stream como datos JSON

Vamos a mostrar alguacuten ejemplo en el que utilicemos un StringReader

Objeto JSON con dos pares nombre-valor

jsonReader = JsoncreateReader(new StringReader( + manzanaroja + plaacutetanoamarillo + ))JsonObject json = jsonReaderreadObject()

jsongetString(manzana) jsongetString(plaacutetano)

El meacutetodo getString() devuelve el valor del string para la clave especificadacomo paraacutemetro Pueden utilizarse otros meacutetodos getXXX() para acceder al valorcorrespondiente de la clave en funcioacuten del tipo de dicho objeto

Un array con dos objetos cada uno de ellos con un par nombre-valor puede leerse como

Array con dos objetos

jsonReader = JsoncreateReader(new StringReader([ + manzanarojo + plaacutetanoamarillo + ]))

JsonArray jsonArray = jsonReaderreadArray()

La interfaz JsonArray tambieacuten tiene meacutetodos get para valores de tipo boolean integer y String en el iacutendice especificado (esta interfaz hereda de javautilList)

Creacioacuten de un modelos de objetos desde el coacutedigo de la aplicacioacuten

A continuacioacuten mostramos un ejemplo de coacutedigo para crear un modelo de objetos medianteprogramacioacuten

Ejemplo de creacioacuten de un modelo de objetos JSON desde programacioacuten

import javaxjsonJsonimport javaxjsonJsonObjectJsonObject modelo =

JsoncreateObjectBuilder() add(nombre Duke)

Servicios Rest

143

add(apellidos Java) add(edad 18) add(calle 100 Internet Dr) add(ciudad JavaTown) add(codPostal 12345) add(telefonos

JsoncreateArrayBuilder() add(JsoncreateObjectBuilder() add(tipo casa) add(numero 111-111-1111)) add(JsoncreateObjectBuilder() add(tipo movil) add(numero 222-222-2222))) build()

El tipo JsonObject representa un objeto JSON El meacutetodoJsoncreateObjectBuilder() crea un modelo de objetos en memoria antildeadiendoelementos desde el coacutedigo de nuestra aplicacioacutenEl meacutetodo JsoncreateArrayBuilder() crea un modelo de arrays en memoriaantildeadiendo elementos desde el coacutedigo de nuestra aplicacioacuten

El objeto modelo de tipo JsonObject representa la raiacutez del aacuterbol que es creado anidandollamadas a meacutetodos acuteadd()acute y construyendo el aacuterbol a traveacutes del meacutetodo build()

La estructura JSON generada es la siguiente

Ejemplo formato JSON generado mediante programacioacuten

nombre Duke apellidos Java edad 18 calle 100 Internet Dr ciudad JavaTown codPostal 12345 telefonos [ tipo casa numero 111-111-1111 tipo movil numero 222-222-2222 ]

Navegando por el modelo de objetos

A continuacioacuten mostramos un coacutedigo de ejemplo para navegar por el modelo de objetos

import javaxjsonJsonValueimport javaxjsonJsonObjectimport javaxjsonJsonArrayimport javaxjsonJsonNumberimport javaxjsonJsonStringpublic static void navegarPorElArbol(JsonValue arbol String clave)

Servicios Rest

144

if (clave = null) Systemoutprint(Clave + clave + ) switch(arbolgetValueType()) case OBJECT Systemoutprintln(OBJETO) JsonObject objeto = (JsonObject) arbol for (String nombre objectkeySet()) navegarPorElArbol(objectget(nombre) name) break case ARRAY Systemoutprintln(ARRAY) JsonArray array = (JsonArray) arbol for (JsonValue val array) navegarPorElArbol(val null) break case STRING JsonString st = (JsonString) arbol Systemoutprintln(STRING + stgetString()) break case NUMBER JsonNumber num = (JsonNumber) arbol Systemoutprintln(NUMBER + numtoString()) break case TRUE case FALSE case NULL Systemoutprintln(arbolgetValueType()toString()) break

El meacutetodo navegarPorElArbol() podemos usarlo con el ejemplo anterior de la siguienteforma

navegarPorElArbol(modelo OBJECT)

El meacutetodo navegarPorElArbol() tiene dos argumentos un elemento JSON y una claveLa clave se utiliza para imprimir los pares clave-valor dentro de los objetos Los elementos enel aacuterbol se representan por el tipo JsonValue Si el elemento es un objeto o un array serealiza una nueva llamada a este meacutetodo es invocada para cada elemento contenido en elobjeto o el array Si el elemento es un valor eacuteste se imprime en la salida estaacutendar

El meacutetodo JsonValuegetValueType() identifica el elemento como un objeto un arrayo un valor Para los objetos el meacutetodo JsonObjectkeySet() devuelve un conjunto deStrings que contienene las claves de los objetos y el meacutetodo JsonObjectget(Stringnombre) devuelve el valor del elemento cuya clave es nombre Para los arraysJsonArray implementa la interfaz ListltJsonValuegt Podemos utilizar bucles formejorados con el valor de SetltStringgt devuelto por JsonObjectkeySet() y coninstancias de JsonArray tal y como hemos mostrado en el ejemplo

Escritura de un modelo de objetos en un stream

Los modelos de objetos creados en los ejemplos anteriores pueden escribirse en un streamutilizando la clase JsonWriter de la siguiente forma

Servicios Rest

145

import javaioStringWriterimport javaxjsonJsonWriterStringWriter stWriter = new StringWriter()

JsonWriter jsonWriter = JsoncreateWriter(stWriter)

jsonWriterwriteObject(modelo)

jsonWriterclose()

String datosJson = stWritertoString()Systemoutprintln(datosJson)

El meacutetodo JsoncreateWriter() toma como paraacutemetro un OutputStreamEl meacutetodo JsonWriterwriteObject() escribe el objeto JsonObject en elstreamEl meacutetodo JsonWriterclose() cierra el stream de salida

Modelo de procesamiento basado en streaming

El modelo de streaming utiliza un parser basado en eventos que va leyendo los datos JSONde uno en uno El parser genera eventos y detiene el procesamiento cuando un objeto o arraycomienza o termina cuando encuentra una clave o encuentra un valor Cada elemento puedeser procesado o rechazado por el coacutedigo de la aplicacioacuten y a continuacioacuten el parser continuacuteacon el siguiente evento Esta aproximacioacuten es adecuada para un procesamiento local en elcual el procesamiento de un elemento no requiere informacioacuten del resto de los datos El modelode streaming genera una salida JSON para un determinado stream realizando una llamada auna funcioacuten con un elemento cada vez

A continuacioacuten veamos con ejemplos coacutemo utilizar el API para el modelo de streaming

bull Para leer datos JSON utilizando un parser (JsonParser)

bull Para escribir datos JSON utilizando un generador (JsonGenerator)

Lectura de datos JSON

El API para el modelo streaming es la aproximacioacuten maacutes eficiente para parsear datos JSONutilizando eventos

import javaxjsonJsonimport javaxjsonstreamJsonParserJsonParser parser = JsoncreateParser(new StringReader(datosJson))while (parserhasNext()) JsonParserEvent evento = parsernext() switch(evento) case START_ARRAY case END_ARRAY case START_OBJECT case END_OBJECT case VALUE_FALSE case VALUE_NULL case VALUE_TRUE Systemoutprintln(eventotoString())

Servicios Rest

146

break case KEY_NAME Systemoutprint(eventotoString() + + parsergetString() + - ) break case VALUE_STRING case VALUE_NUMBER Systemoutprintln(eventotoString() + + parsergetString()) break

El ejemplo consta de tres pasos

1 Obtener una instancia de un parser invocando el meacutetodo estaacuteticoJsoncreateParser()

2 Iterar sobre los eventos del parser utilizando los meacutetodos JsonParserhasNext() yJsonParsernext()

3 Realizar un procesamiento local para cada elemento

El ejemplo muestra los diez posibles tipos de eventos del parser El meacutetodoJsonParsernext() avanza al siguiente evento Para los tipos de eventos KEY_NAME VALUE_STRING y VALUE_NUMBER podemos obtener el contenido del elemento invocandoal meacutetodo JsonParsergetString() Para los eventos VALUE_NUMBER podemostambieacuten usar los siguientes meacutetodos

bull JsonParserisIntegralNumber

bull JsonParsergetInt

bull JsonParsergetLong

bull JsonParsergetBigDecimal

El parser genera los eventos START_OBJECT y END_OBJECT para un objeto JSON vaciacuteo

Para un objeto con dos pares nombre-valor

manzajaroja plaacutetanoamarillo

Mostramos los eventos generados

START_OBJECT manzajaKEY_NAMErojaVALUE_STRING plaacutetanoKEY_NAMEamarilloVALUE_STRINGEND_OBJECT

Los eventos generados para un array con dos objetos JSON seriacutean los siguientes

[START_ARRAY START_OBJECT manzajaKEY_NAMErojaVALUE_STRING END_OBJECT

Servicios Rest

147

START_OBJECT plaacutetanoKEY_NAMEamarilloVALUE_STRING END_OBJECT]END_ARRAY

Escritura de datos JSON

El siguiente coacutedigo muestra coacutemo escribir datos JSON en un fichero utilizando el API para elmodelo de streaming

Ejemplo de escritura de datos JSON con el modelo de streaming

FileWriter writer = new FileWriter(testtxt)JsonGenerator gen = JsoncreateGenerator(writer)genwriteStartObject() write(nombre Duke) write(apellidos Java) write(edad 18) write(calle 100 Internet Dr) write(ciudad JavaTown) write(codPostal 12345) writeStartArray(telefonos) writeStartObject() write(tipo casa) write(numero 111-111-1111) writeEnd() writeStartObject() write(tipo movil) write(numero 222-222-2222) writeEnd() writeEnd() writeEnd()genclose()

Este ejemplo obtiene un generador JSON invocando al meacutetodo estaacuteticoJsoncreateGenerator() que toma como paraacutemetro un output stream o un writerstream El ejemplo escribe los datos JSON en el fichero texttxt anidando llamadas a losmeacutetodos write() writeStartArray() writeStartObject() and writeEnd() El meacutetodo JsonGeneratorclose() cierra el output stream o writer stream subyacente

54 Pruebas de servicios REST

Hasta ahora hemos visto varias formas de probar nuestros servicios REST desde liacutenea decomandos con Curl desde IntelliJ con la herramienta Test RESTFul Web Service y desde elnavegador Chrome con Postman (siendo esta uacuteltima la que maacutes hemos utilizado)

Vamos a ver coacutemo implementar tests para nuestros servicios REST utilizando Maven y JUnitPara ello repasaremos algunas cuestiones baacutesicas sobre los ciclos de vida de Maven10

Ciclo de vida de Maven y tests JUnit

Un ciclo de vida en Maven es una secuencia de acciones determinada que defineel proceso de construccioacuten de un proyecto en concreto Como resultado del proceso deconstruccioacuten de un proyecto obtendremos un artefacto (fichero) de un cierto tipo (porejemplo jar war earhellip) Por lo tanto podriacuteamos decir que un ciclo de vida estaacute formado por

10 httpsmavenapacheorgref333maven-corelifecycleshtml

Servicios Rest

148

las acciones necesarias para convertir nuestros archivos fuente que constituyen el proyectoen por ejemplo un jar un warhellip

Maven propone 3 ciclos de vida es decir tres posibles secuencias de acciones que podemosutilizar (y modificar a nuestra conveniencia) para construir nuestro proyecto Dichos ciclos devida son clean site y el denominado default-lifecycle

Cada ciclo de vida estaacute formado por fases Una fase es un concepto abstracto y define el tipode acciones que se deberiacutean llevar a cabo Por ejemplo una fase del ciclo de vida por defectoes compile para referirse a las acciones que nos permiten convertir los ficheros java en losficheros class correspondientes

Cada fase estaacute formada por un conjunto de goals que son las acciones que se llevaraacuten a caboen cada una de las fases Las goals no viven de forma independiente sino que cualquiergoal siempre forma parte de un plugin Maven Podriacuteamos decir que un plugin por lo tantoes una agrupacioacuten loacutegica de una serie de goals relacionadas Por ejemplo el plugin wildflycontiene una serie de goals para desplegar re-desplegar deshacer-el-despliegue arrancarel servidor etc es decir agrupa las acciones que podemos realizar sobre el servidor wildflyUna goal se especifica siempre anteponiendo el nombre del plugin al que pertenece seguidode dos puntos por ejemplo wildflydeploy indica que se trata de la goal deploy que perteneceal plugin wildfly de maven

Pues bien por defecto Maven asocia ciertas goals a las fases de los tres ciclos de vidaCuando se ejecuta una fase de un ciclo de vida por ejemplo mvn package se ejecutan todaslas goals asociadas a todas las fases anteriores a la fase package en orden y finalmentelas goals asociadas a la fase package Por supuesto podemos alterar en cualquier momentoeste comportamiento por defecto incluyendo los plugins y goals correspondientes dentro dela etiqueta ltbuildgt en nuestro fichero de configuracioacuten pomxml

Vamos a implementar tests JUnit Los tests como ya habeacuteis visto en sesiones anterioresen el directorio srctest Algunas normas importantes son que los tests pertenezcan almismo paquete loacutegico al que pertencen las clases Java que estamos probando Por ejemplosi estamos haciendo pruebas sobre las clases del paquete orgexpertojavarest los testsdeberiacutean pertenecer al mismo paquete aunque fiacutesicamente el coacutedigo fuente y sus pruebasestaraacuten separados (el coacutedigo fuente estaraacute en srcmain y los tests en srctest)

Para realizar pruebas sobre nuestros servicios REST necesitamos que el servidor Wilfly esteacuteen marcha Tambieacuten necesitamos empaquetar el coacutedigo en un fichero war y desplegarlo enel servidor todo esto ANTES de ejecutar los tests

Las acciones para arrancar el servidor Wilfly y desplegar nuestra aplicacioacuten en eacutel NO formanparte de las acciones (o goals) incluidas por defecto en el ciclo de vida por defecto de Mavencuando nuestro proyecto tiene que empaquetarse como un war (etiqueta ltpackaginggt denuestro pomxml) Podeacuteis consultar aquiacute11 la lista de goals asociadas a las fases del ciclo devida por defecto de Maven

Por otro lado en el ciclo de vida por defecto se incluye una goal para ejecutar los testsasociada a la fase test Dicha goal es surefiretest El problema es que por defecto la fasetest se ejecuta ANTES de la fase package y por lo tanto antes de empaquetar y desplegarnuestra aplicacioacuten en Wildfly

Por lo tanto tendremos que alterar convenientemente este comportamiento por defectopara que se ejecuten las acciones de nuestro proceso de construccioacuten que necesitemos

11 httpsmavenapacheorgref333maven-coredefault-bindingshtmlPlugin_bindings_for_war_packaging

Servicios Rest

149

y en el orden en el que lo necesitemos Como ya hemos indicado antes esto lo haremosincluyendo dichas acciones en la etiqueta ltbuildgt de nuestro pomxml y configurandolasconvenientemente para asegurarnos que el orden en el que se ejecutan es el que queremos

La siguiente figura muestra parte de la secuencia de fases llevadas a cabo por Maven ensu ciclo de vida por defecto Para conseguir nuestros propoacutesitos simplemente antildeadiremosla goals wildflydeploy y la asociaremos a la fase pre-integration-test y cambiaremos lafase a la que estaacute asociada la goal surefiretest para que los tests se ejecuten DESPUEacuteS dehaber desplegado el war en Wildfly

A continuacioacuten mostramos los cambios que tenemos que realizar en el fichero de configuracioacutenpomxml

Adicioacuten de las goals wildflydeploy y surefiretest a las fases pre-integration-test ysurefiretest respectivamente

lt-- forzamos el despliegue del war generado durante la fase pre-integration-test justo despueacutes de obtener dicho war--gtltplugingt ltgroupIdgtorgwildflypluginsltgroupIdgt ltartifactIdgtwildfly-maven-pluginltartifactIdgt ltversiongt102Finalltversiongt ltconfigurationgt lthostnamegtlocalhostlthostnamegt ltportgt9990ltportgt ltconfigurationgt

Servicios Rest

150

ltexecutionsgt ltexecutiongt ltidgtwildfly-deployltidgt ltphasegtpre-integration-testltphasegt ltgoalsgt ltgoalgtdeployltgoalgt ltgoalsgt ltexecutiongt ltexecutionsgtltplugingt

lt--ejecutaremos los test JUnit en la fase integration-test inmediatamente despueacutes de la fase pre-integration-test y antes de la fase verify--gtltplugingt ltgroupIdgtorgapachemavenpluginsltgroupIdgt ltartifactIdgtmaven-surefire-pluginltartifactIdgt ltversiongt218ltversiongt ltconfigurationgt ltskipgttrueltskipgt ltconfigurationgt ltexecutionsgt ltexecutiongt ltidgtsurefire-itltidgt ltphasegtintegration-testltphasegt ltgoalsgt ltgoalgttestltgoalgt ltgoalsgt ltconfigurationgt ltskipgtfalseltskipgt ltconfigurationgt ltexecutiongt ltexecutionsgtltplugingt

Tambieacuten necesitamos incluir en el pomxml las libreriacuteas de las que depende el coacutedigo depruebas de nuestro proyecto (clases XXXXTest situadas en srctest) libreriacutea JUnit JAXB yel API cliente de JAX-RS Por lo que antildeadimos en el las dependencias correspondientes

Dependencias del coacutedigo srctest con JUnit y API cliente de JAX-RS

ltdependencygt ltgroupIdgtjunitltgroupIdgt ltartifactIdgtjunitltartifactIdgt ltversiongt412ltversiongt ltscopegttestltscopegtltdependencygtltdependencygt ltgroupIdgtorgjbossresteasyltgroupIdgt ltartifactIdgtresteasy-clientltartifactIdgt ltversiongt3013Finalltversiongt ltscopegttestltscopegtltdependencygtltdependencygt ltgroupIdgtorgjbossresteasyltgroupIdgt

Servicios Rest

151

ltartifactIdgtresteasy-jaxb-providerltartifactIdgt ltversiongt3013Finalltversiongtltdependencygt

Dado que vamos a trabajar con el API Json y dado que ejecutaremos los tests desde lamaacutequina virtual de Java y no dentro del servidor WildFly necesitamos antildeadir tambieacuten lassiguientes libreriacuteas

Dependencias del coacutedigo srctest con el API Json de jaxrs

lt--Libreriacuteas para serializardeserializar json --gtltdependencygt ltgroupIdgtorgjbossresteasyltgroupIdgt ltartifactIdgtresteasy-jackson-providerltartifactIdgt ltversiongt3013Finalltversiongtltdependencygt

lt--Jaxrs API json --gtltdependencygt ltgroupIdgtorgjbossresteasyltgroupIdgt ltartifactIdgtresteasy-json-p-providerltartifactIdgt ltversiongt3013Finalltversiongtltdependencygt

No hemos incluido en el pomxml la orden para arrancar Wildfly Vamosa hacer esto desde IntelliJ en un perfil de ejecucioacuten como ya habeacuteishecho en sesiones anteriores De esta forma podremos ver desde IntelliJla consola de logs del servidor En este caso podemos crear un perfilsolamente para arrancar el servidor Wildfly (no es necesario que se incluyael despliegue del war generado puesto que lo haremos desde la ventanaMaven Projects) Antes de iniciar el proceso de construccioacuten por lo tantotendremos que asegurarnos de que hemos arrancado Wildlfly

Con estos cambios en el pomxml y ejecutando el comando mvn verify se llevaraacuten a cabolas siguientes acciones en este orden

bull Despueacutes de compilar el proyecto obtenemos el war (fase package )

bull El war generado se despliega en el servidor de aplicaciones Wilfly (fase pre-integration-test )

bull Se ejecutan los test JUnit sobre la aplicacioacuten desplegada en el servidor (faseintegration-test )

Anotaciones JUnit y aserciones AssertThat

JUnit 4 proporciona anotaciones para forzar a que los meacutetodos anotados con Test seejecuten en el orden que nos interese (por defecto no estaacute garantizado que se ejecuten enel orden en el que se escriben)

En principio debemos programar los tests para que sean totalmente independientes unos deotros y por lo tanto el orden de ejecucioacuten no influya para nada en el resultado de su ejecucioacutentanto si se ejecuta el primero como a mitad o el uacuteltimo El no hacer los tests independientes

Servicios Rest

152

hace que el proceso de testing se alargue y complique innecesariamente ya que puede serque unos tests enmascaren en resultado de otros o que no podamos saber si ciertas partesdel coacutedigo estaacuten bien o mal implementadas hasta que los tests de los que dependemos sehayan superado con eacutexito

Auacuten asiacute y dado que muchas veces se obtienen errores por hacer asunciones en el ordende la ejecucioacuten de los tests JUnit nos permite fijar dicho orden Para ello utilizaremosla anotacioacuten FixMethodOrder indicando el tipo de ordenacioacuten como por ejemploMethodSortersNAME_ASCENDING de forma que se ejecutaraacuten los tests por ordenlexicograacutefico

Por ejemplo

Ejemplo para forzar el orden de ejecucioacuten de los test (orden lexicograacutefico)

FixMethodOrder(MethodSortersNAME_ASCENDING)public class TestMethodOrder

Test public void testB() Systemoutprintln(second)

Test public void testA() Systemoutprintln(first)

Test public void testC() Systemoutprintln(third)

En ese caso el orden de ejecucioacuten seraacute testA() a continuacioacuten testB() y finalmentetestC()

Otra aportacioacuten de JUnit 4 es la incorporacioacuten de aserciones de tipoassertThat En sesiones anteriores habeacuteis utilizado aserciones con meacutetodosAssertassertEquals(resultado_esperado resultado_real) Los nuevosmeacutetodos AssertassertThat() permiten una mayor flexibilidad a la hora de expresar lasaserciones realizadas en nuestros tests asiacute como una mayor legibilidad de los mismos Elprototipo general de las aserciones de este tipo es

assertThat([value] [matcher statement])

en donde [value] es el resultado real (valor sobre el que se quiere afirmar algo)y [matcher statement] es un Matcher u objeto que realiza operaciones deemparejamiento sobre una secuencia de caracteres seguacuten un determinado patroacuten

Por ejemplo

Ejemplos de sentencias assertThat

assertThat(x is(not(4)))

Servicios Rest

153

assertThat(responseStringJson

either(containsString(nombre))and(containsString(apellido)))

assertThat(myList hasItem(3))

Aquiacute utilizamos un matcher con el patroacuten 4 esta sentencia devuelve false si x = 4Podemos combinar varios matchers de forma que se tengan que satisfacer maacutes de unoEn este caso aplicamos el matcher sobre un conjunto de elementos

Hay varias libreriacuteas que implementan Matchers JUnit incluye parte de los matchers deHamcrest (Hamcrest es un framework para escribir objetos matcher permitiendo definir reglasde matching de forma declarativa) Otra libreriacutea interesante para realizar testing de serviciosrest que utilizan representaciones Json es la libreriacutea hamcrest-json que podemos utilizarpara realizar aserciones sobre dos objetos Json

Por ejemplo supongamos que nuestro objeto Json contiene una lista de enlaces Hateoas detipo Link Los objetos Link seraacuten serializadosdeserializados (Wildfly utiliza Jackson pararealizar estas tareas) convenientemente Cuando serializamos un objeto Link (obtenemossu representacioacuten Json) veremos ademaacutes de los objetos uri valor type valor y relvalor que son los que baacutesicamente utilizamos al crear los enlaces Hateoas otros comouriBuilder hellip paramshellip que puede que no nos interese consultar o incluso que no leshayamos asignado ninguacuten valor

Si en nuestro test queremos comprobar que el objeto Json que nos devuelve el servicio(resultado real) se corresponde con el valor esperado tendremos que comparar ambasrepresentaciones Ahora bien puede que solamente nos interese comparar ciertos valorescontenidos en el objeto Json no el objeto completo

Hacer esta comprobacioacuten elemento a elemento es bastante tediosoLa libreriacutea hamcrest-json nos proporciona lo que estamos buscandocon los meacutetodos sameJSONAs() allowingExtraUnexpectedFields() yallowingAnyArrayOrdering() de la siguiente forma

Meacutetodo para comparar dos representaciones Json ClaseukcodatumedgehamcrestjsonSameJSONAs

AssertassertThat(age43 friend_ids[16 52 23] sameJSONAs(friend_ids[52 23 16]) allowingExtraUnexpectedFields() allowingAnyArrayOrdering())

En este coacutedigo tenemos una representacioacuten formada por dos objetos uno de los cuales tienecomo valor un array de enteros Si el servicio rest devuelve un objeto Json con maacutes elementoso en otro orden en este caso el resultado de la sentencia assertThat es true Volviendo alejemplo anterior de un objeto Json que contiene enlaces Hatehoas podriacuteamos realizar lasiguiente comparacioacuten

Comparamos dos objetos Json que contienen hiperenlaces Hateoas (objetos Link)

JsonObject json_object = clienttarget(httplocalhost8080forousuarios) request(MediaTypeAPPLICATION_JSON)

get(JsonObjectclass)

String json_string = json_objecttoString()

Servicios Rest

154

JsonObject usuarios = JsoncreateObjectBuilder() add(usuarios JsoncreateArrayBuilder() add(JsoncreateObjectBuilder() add(nombre Pepe Lopez) add(links JsoncreateArrayBuilder() add(JsoncreateObjectBuilder() add(uri httplocalhost8080forousuariospepe) add(type applicationxmlapplicationjson) add(rel self)))) add(JsoncreateObjectBuilder() add(nombre Ana Garcia) add(links JsoncreateArrayBuilder() add(JsoncreateObjectBuilder() add(uri httplocalhost8080forousuariosana) add(type applicationxmlapplicationjson) add(rel self)))))

build()

AssertassertThat(json_string sameJSONAs(usuariostoString()) allowingExtraUnexpectedFields()

allowingAnyArrayOrdering())

Realizamos la llamada al servicio REST y recibimos como respuesta un objeto Json Eneste caso nuestro objeto Json estaacute formado por una lista de objetosObtenemos la representacioacuten de nuestro objeto Json (resultado real) en forma de cadenade caracteresCreamos un nuevo objeto Json con el resultado esperadoComparamos ambos objetos Si el resultado real incluye maacutes elementos que loscontenidos en json_string o en otro orden consideraremos que hemos obtenido larespuesta correcta

Para utilizar esta libreriacutea en nuestro proyecto simplemente tendremos que antildeadirla comodependencia en la configuracioacuten de nuestro pomxml

Libreriacutea para comparar objetos Json en los tests

lt--Hamcrest Json --gtltdependencygt ltgroupIdgtukcodatumedgeltgroupIdgt ltartifactIdgthamcrest-jsonltartifactIdgt ltversiongt02ltversiongtltdependencygt

Observaciones sobre los tests y algunos ejemplos de tests

Recuerda que para utilizar el API cliente necesitas utilizar instanciasjavaxwsrsclientClient que debemos cerrar siempre despueacutes de su uso paracerrar el socket asociado a la conexioacuten

Servicios Rest

155

Para ello podemos optar por Crear una uacutenica instancia Client antes de ejecutar cualquiertest (meacutetodo BeforeClass) y cerrar el socket despueacutes de ejecutar todos los tests (meacutetodoAfterClass) Crear una uacutenica instancia Client antes de ejecutar CADA test (meacutetodoBefore) y cerrar el socket despueacutes de ejecutar CADA tests (meacutetodo After)

Si el resultado de una invocacioacuten sobre la instancia Client es de tipojavaxwsrscoreResponse debemos liberar de forma expliacutecita la conexioacuten para quepueda ser usada de nuevo por dicha instancia Client

Por ejemplo supongamos que queremos realizar un test en el que realizamos una operacioacutenPOST y a continuacioacuten una operacioacuten GET para verificar que el nuevo recurso se ha antildeadididocorrectamente

Ejemplo de Test que utiliza una instancia Client para todos los tests

public class TestRESTServices private static final String BASE_URL = httplocalhost8080rest private static URI uri = UriBuilderfromUri(BASE_URL)build() private static Client client

BeforeClass public static void initClient()

client = ClientBuildernewClient()

AfterClass public static void closeClient()

clientclose()

Test public void createAndRetrieveACustomer()

Customer customer = Creamos un nuevo cliente Response response = clienttarget(uri) request() post(Entityentity(customer MediaTypeAPPLICATION_JSON)) assertEquals(ResponseStatusCREATED responsegetStatusInfo()) URI referenceURI = responsegetLocation()

responseclose()

Obtenemos el recurso que hemos antildeadido response = clienttarget(referenceURI)request()get()

Customer retrieved_customer = responsereadEntity(Customerclass) assertEquals(ResponseStatusOK responsegetStatusInfo()) assertEquals(retreivedRefgetName() rgetName())

responseclose()

Creamos una instancia Client ANTES de ejecutar cualquier testCerramos el socket asociado a la conexioacuten DESPUEacuteS de ejecutar TODOS los testsLiberamos la conexioacuten para poder reutilizarla

Servicios Rest

156

Liberamos la conexioacuten para poder reutilizarlaAhora veamos otro ejemplo en el que utilizamos una instancia Client para cada test

Ejemplo de Test que utiliza una instancia Client para CADA test

public class TestRESTServices

private Client client

Before public void setUp()

thisclient = ClientBuildernewClient()

After public void tearDown()

thisclientclose()

Test public void getAllCustomersAsJson() String uriString = httplocalhost8080restcustomers JsonArray json_array = client target(uriString) request(MediaTypeAPPLICATION_JSON) accept(MediaTypeAPPLICATION_JSON) get(JsonArrayclass)

AssertassertEquals(2 json_arraysize())

Test public void getAllCustomers() String uriString = httplocalhost8080restcustomers Consultamos los datos de todos los customers ListltCustomergt lista_usuarios = clienttarget(uriString) request(applicationjson) get(new GenericTypeltListltCustomergtgt() ) AssertassertEquals(2 lista_usuariossize())

Creamos una instancia Client ANTES de ejecutar CADA testCerramos el socket asociado a la conexioacuten DESPUEacuteS de ejecutar CADA los tests

Podemos ver en este uacuteltimo ejemplo que no es necesario liberar la conexioacuten entre usossucesivos de la instancia Client si no utilizamos la clase Response En este caso el procesose realiza de forma automaacutetica por el sistema

Finalmente comentaremos que debido a un bug en la especificacioacuten JAX-RS eldeserializado del de los objetos Link no se realiza por lo que obendremos una listade Links vaciacutea (ver httpkingsfleetblogspotcomes201405reading-and-writing-jax-rs-link-objectshtml) Podemos comprobar que si obtenemos la representacioacuten en formato texto dela entidad del mensaje dicha lista de objetos tendraacute el valor correcto

Si no utilizamos la solucioacuten propuesta en el enlace anterior deberemos usar la anotacioacutenJsonIgnoreProperties(ignoreUnknown = true) De esta forma ignoraremos el

Servicios Rest

157

deserializado de los objetos Link pero tendremos que utilizar la representacioacuten en formato decadena de caracteres del recurso json en lugar del objeto java Link asociado

Asiacute por ejemplo si nuestro recurso Customer tiene asociado una lista de objetos Link parapoder utilizar el API Cliente y acceder a la lista de enlaces usaremos la anotacioacuten anterior enla implementacioacuten de la clase Customer

JsonIgnoreProperties(ignoreUnknown = true)XmlRootElement(name=customer)public class Customer int id String name ListltLinkgt links

Servicios Rest

158

55 Ejercicios

Tests utilizando el API cliente y un mapeador de excepciones (1 punto)

Se proporciona como plantilla el MOacuteDULO IntelliJ s5-tienda con una implementacioacuten parcialde una tienda de clientes on-line Este proyecto ya contiene varios tests implementados amodo de ejemplo

Los recursos rest implementados lanzan excepciones de tipo RestException si porejemplo se intenta realizar una consulta sobre un producto yo usuario que no existe

Se ha implementado un mapeador de excepciones RestExceptionMapper quecaptura excepciones de tipo RuntimeException y devuelve una respuesta de tipoErrorMensajeBean que seraacute serializada a formato json yo formato xml (dependiendo delvalor de la cabecera Accept de la peticioacuten) con informacioacuten sobre el error producido

Implementa los siguientes dos tests test7recuperarTodosLosUsuarios() en el querealizamos una invocacioacuten GET sobre httplocalhost8080s5-tiendarestclientes Esta URIpodriacutea corresponderse con un meacutetodo anotado con GET y que devolviese una lista de todoslos clientes de la tienda Sin embargo no existe tal meacutetodo en nuestro recursos rest Verificaque dicha invocacioacuten devuelve el coacutedigo de estado 500 (Internal Server Error) y que en elcuerpo del mensaje se recibe Servicio no disponible

bull test8recuperarClienteQueNoExiste() en el que intentamos recuperar lainformacioacuten de un cliente que no exista en nuestra base de datos En este caso debemosverificar que el mensaje obtenido en formato json es el siguiente

status Not Found code 404 message El producto no se encuentra en la base de datos developerMessage error

Tests utilizando el API Json y JUnit (1 punto)

Vamos a seguir usando el proyecto s4-foroAvanzado con el que hemos trabajado en la sesioacutenanterior

Vamos a implementar algunos tests con JUnit en los que utilizaremos ademaacutes del API clienteel API Json que nos proporciona jaxrs

Para ejecutar los tests necesitamos modificar el pomxml antildeadiendo las dependenciascorrespondientes que hemos visto a lo largo de la sesioacuten y antildeadiendo las goals para que seejecuten los tests despueacutes de desplegar la aplicacioacuten en Wildfly

Proporcionamos el contenido del pomxml con las libreriacuteas y plugins que necesitaraacutes(aunque como ejercicio deberiacuteas intentar modificar la configuracioacuten tuacute mismo y luego puedescomprobar el resultado con el pomxml que se proporciona) El contenido del nuevo pomxmllo tienes en srctestresourcesnuevo-pommxl

Inicializacioacuten de los datos para los tests

Vamos a utilizar DBUnit para inicializar la BD para realizar los tests Para ello tendraacutes queantildeadir en el pomxml las dependencias necesarias (ya estaacuten antildeadidas en el fichero de

Servicios Rest

159

configuracioacuten proporcionado) En el fichero srctestresourcesforo-inicialxml encontrareacuteis elconjunto de datos con el que inicializaremos la base de datos para ejecutar nuestros tests

No es necesario (aunque es una muy buena praacutectica) que inicialicemos la BD para cada test

Implementacioacuten de los tests

Vamos a implementar los siguientes tests (que se ejecutaraacuten en en este mismo orden)

bull test1ConsultaTodosUsuarios() recuperamos los datos de todos los usuarios delforo Recuerda que previamente tienes que haber inicializado la BD con los datos del ficheroforo-inicialxml Recupera los datos en forma de JsonObject y comprueba que el nuacutemerode usuarios es el correcto Tambieacuten debes comprobar que tanto el login como los enlaceshatehoas para cada usuario estaacuten bien creados En concreto para cada usuario debesverificar que la uri (uri) el tipo mime (type) y el tipo de enlace (rel) son los correctos

bull test2CreamosMensajeDePepe() crearemos un nuevo mensaje del usuario con loginpepe Recuerda que este usuario tiene el rol registrado El mensaje tendraacute el asuntocena y el texto seraacute Mejor me voy al cine En este caso deberaacutes comprobar el valorde estado (debe ser 201) y debes recuperar (consultar con una peticioacuten REST) el mensajepara comprobar que la operacioacuten de insertar el mensaje ha tenido eacutexito

bull test3CreamosMensajeDeUsuarioNoAutorizado() creamos un nuevo mensaje deun usuario que no estaacute autorizado (por ejemplo de un usuario con login juan) En estecaso el mensaje tendraacute el asunto cena y el mensaje puede ser Pues yo tampoco voyEl resultado debe ser el coacutedigo de estado 401 ( Unauthorized)

bull test4ConsultaUsuario() Consultamos los datos del usuario pepe Recuperaremoslos datos como un JsonObject y comprobaremos que el valor de la uri para el tipo derelacioacuten self del enlace Link asociado es el correcto

  • Servicios Rest
  • Table of Contents
  • 1 Introduccioacuten a REST Disentildeo y creacioacuten de servicios RESTful
    • 11 iquestQueacute es un servicio Web
      • Servicios Web RESTful
        • 12 Fundamentos de REST
          • Recursos
          • Representacioacuten de los recursos
          • Direccionabilidad de los recursos URI
          • Uniformidad y restricciones de las interfaces
            • 13 Disentildeo de servicios Web RESTful
            • 14 Un primer servicio JAX-RS
              • Modelo de objetos
              • Modelado de URIs
              • Definicioacuten del formato de datos
                • Formato de datos para operaciones de lectura y modificacioacuten de los recursos
                • Formato de datos para operaciones de creacioacuten de los recursos
                  • Asignacioacuten de meacutetodos HTTP
                    • Visualizacioacuten de todos los Pedidos Clientes o Productos
                    • Obtencioacuten de Pedidos Clientes o Productos individuales
                    • Creacioacuten de un Pedido Cliente o Producto
                    • Actualizacioacuten de un Pedido Cliente o Producto
                    • Borrado de un Pedido Cliente o Producto
                    • Cancelacioacuten de un Pedido
                      • Implementacioacuten del servicio Creacioacuten del proyecto Maven
                      • Implementacioacuten del servicio Recursos JAX-RS
                        • Clases de nuestro dominio (entidades) Clientejava
                        • Clases de nuestro servicio RESTful ClienteResourcejava
                        • Creacioacuten de clientes
                        • Consulta de clientes
                        • Modificacioacuten de clientes
                          • Construccioacuten y despliegue del servicio
                          • Probando nuestro servicio
                            • 15 Ejercicios
                              • Servicio REST ejemplo (0 puntos)
                              • Servicio REST saludo (1 punto)
                              • Servicio REST foro (1 punto)
                                  • 2 Anotaciones baacutesicas JAX-RS El modelo de despliegue
                                    • 21 iquestCoacutemo funciona el enlazado de meacutetodos HTTP
                                    • 22 La anotacioacuten Path
                                      • Expresiones Path
                                        • Expresiones regulares
                                        • Reglas de precedencia
                                          • Paraacutemetros matrix (Matrix parameters)
                                          • Subrecursos (Subresource Locators)
                                            • Caraacutecter dinaacutemico del dispatching de peticiones
                                                • 23 Usos de las anotaciones Produces y Consumes
                                                  • Anotacioacuten Consumes
                                                  • Anotacioacuten Produces
                                                    • 24 Inyeccioacuten de paraacutemetros JAX-RS
                                                      • javaxwsrsPathParam
                                                      • Interfaz UriInfo
                                                      • javaxwsrsMatrixParam
                                                      • javaxwsrsQueryParam
                                                      • javaxwsrsFormParam
                                                      • javaxwsrsHeaderParam
                                                      • javaxwsrscoreContext
                                                      • javaxwsrsBeanParam
                                                      • Conversioacuten automaacutetica de tipos
                                                      • Valores por defecto (DefaultValue)
                                                        • 25 Configuracioacuten y despliegue de aplicaciones JAX-RS
                                                          • Configuracioacuten mediante la clase Application
                                                          • Configuracioacuten mediante un fichero webxml
                                                          • Configuracioacuten en un contenedor que no disponga de una implementacioacuten JAX-RS
                                                            • 26 Ejercicios
                                                              • Creacioacuten de un recurso creacioacuten y consulta de temas en el foro (05 puntos)
                                                              • Despliegue y pruebas del recurso (05 puntos)
                                                              • Muacuteltiples consultas de los temas del foro (05 puntos)
                                                              • Creacioacuten de subrecursos (05 puntos)
                                                                  • 3 Manejadores de contenidos Respuestas del servidor y manejo de excepciones
                                                                    • 31 Proveedores de entidades
                                                                      • Interfaz javaxwsrsextMessageBodyReader
                                                                      • Interfaz javaxwsrsextMessageBodyWriter
                                                                        • 32 Proveedores de entidad estaacutendar incluidos en JAX-RS
                                                                          • javaxwsrscoreStreamingOutput
                                                                          • javaioInputStream javaioReader
                                                                          • javaioFile
                                                                          • byte[]
                                                                          • String char[]
                                                                          • MultivaluedMapltString Stringgt y formularios de entrada
                                                                            • 33 Muacuteltiples representaciones de recursos
                                                                            • 34 Introduccioacuten a JAXB
                                                                              • Clase JAXBContext
                                                                              • Manejadores JAX-RS para JAXB
                                                                              • JAXB y JSON
                                                                                • 35 Respuestas del servidor
                                                                                  • Coacutedigos de respuesta por defecto
                                                                                    • Respuestas que indican eacutexito
                                                                                    • Respuestas que indican una situacioacuten de fallo
                                                                                      • Elaboracioacuten de respuestas con la clase Response
                                                                                        • Inclusioacuten de cookies en la respuesta
                                                                                        • El tipo enumerado de coacutedigos de estado
                                                                                        • La clase javaxwsrscoreGenericEntity
                                                                                            • 36 Manejadores de excepciones
                                                                                              • La clase javaxwsrsWebApplicationException
                                                                                              • Mapeado de excepciones
                                                                                              • Jerarquiacutea de excepciones
                                                                                                • 37 Ejercicios
                                                                                                  • Servicio REST ejemplo
                                                                                                  • Plantillas que se proporcionan
                                                                                                  • Uso de JAXB (05 puntos)
                                                                                                  • Uso de manejadores de contenidos y clase Response (075 puntos)
                                                                                                  • Manejo de excepciones (075 puntos)
                                                                                                      • 4 HATEOAS y Seguridad
                                                                                                        • 41 iquestQueacute es HATEOAS
                                                                                                        • 42 HATEOAS y Servicios Web
                                                                                                          • Enlaces Atom
                                                                                                          • Ventajas de utilizar HATEOAS con Servicios Web
                                                                                                            • Transparencia en la localizacioacuten
                                                                                                            • Desacoplamiento de los detalles de la interaccioacuten
                                                                                                            • Reduccioacuten de errores de transicioacuten de estados
                                                                                                              • Enlaces en cabeceras frente a enlaces Atom
                                                                                                                • 43 HATEOAS y JAX-RS
                                                                                                                  • Construccioacuten de URIs con UriBuilder
                                                                                                                  • URIs relativas mediante el uso de UriInfo
                                                                                                                  • Construccioacuten de enlaces (Links) en documentos XML y en cabeceras HTTP
                                                                                                                    • 44 Seguridad
                                                                                                                      • Autentificacioacuten en JAX-RS
                                                                                                                        • Creacioacuten de usuarios y roles
                                                                                                                          • Autorizacioacuten en JAX-RS
                                                                                                                          • Encriptacioacuten
                                                                                                                            • Anotaciones JAX-RS para autorizacioacuten
                                                                                                                              • Seguridad programada
                                                                                                                                • 45 Ejercicios
                                                                                                                                  • Uso de Hateoas (1 puntos)
                                                                                                                                  • Ejercicio seguridad (1 punto)
                                                                                                                                      • 5 Api cliente Procesamiento JSON y Pruebas
                                                                                                                                        • 51 API cliente Visioacuten general
                                                                                                                                          • Obtenemos una instancia Client
                                                                                                                                          • Configuramos el target del cliente (URI)
                                                                                                                                          • Construimos y Realizamos la peticioacuten
                                                                                                                                          • Manejo de excepciones
                                                                                                                                            • 52 Procesamiento JSON
                                                                                                                                            • 53 Modelo de procesamiento basado en el modelo de objetos
                                                                                                                                              • Creacioacuten de un modelos de objetos desde el coacutedigo de la aplicacioacuten
                                                                                                                                              • Navegando por el modelo de objetos
                                                                                                                                              • Escritura de un modelo de objetos en un stream
                                                                                                                                              • Modelo de procesamiento basado en streaming
                                                                                                                                                • Lectura de datos JSON
                                                                                                                                                • Escritura de datos JSON
                                                                                                                                                    • 54 Pruebas de servicios REST
                                                                                                                                                      • Ciclo de vida de Maven y tests JUnit
                                                                                                                                                      • Anotaciones JUnit y aserciones AssertThat
                                                                                                                                                      • Observaciones sobre los tests y algunos ejemplos de tests
                                                                                                                                                        • 55 Ejercicios
                                                                                                                                                          • Tests utilizando el API cliente y un mapeador de excepciones (1 punto)
                                                                                                                                                          • Tests utilizando el API Json y JUnit (1 punto)
                                                                                                                                                            • Inicializacioacuten de los datos para los tests
                                                                                                                                                              • Implementacioacuten de los tests
Page 2: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones

Servicios Rest

2

Configuracioacuten en un contenedor que no disponga de una implementacioacuten JAX-RS 63

26 Ejercicios 65Creacioacuten de un recurso creacioacuten y consulta de temas en el foro (05 puntos) 65Despliegue y pruebas del recurso (05 puntos) 67Muacuteltiples consultas de los temas del foro (05 puntos) 68Creacioacuten de subrecursos (05 puntos) 69

3 Manejadores de contenidos Respuestas del servidor y manejo de excepciones 7231 Proveedores de entidades 72

Interfaz javaxwsrsextMessageBodyReader 72Interfaz javaxwsrsextMessageBodyWriter 73

32 Proveedores de entidad estaacutendar incluidos en JAX-RS 73javaxwsrscoreStreamingOutput 74javaioInputStream javaioReader 75javaioFile 76byte[] 77String char[] 78MultivaluedMapltString Stringgt y formularios de entrada 78

33 Muacuteltiples representaciones de recursos 7934 Introduccioacuten a JAXB 80

Clase JAXBContext 87Manejadores JAX-RS para JAXB 88JAXB y JSON 89

35 Respuestas del servidor 91Coacutedigos de respuesta por defecto 91Elaboracioacuten de respuestas con la clase Response 93

36 Manejadores de excepciones 98La clase javaxwsrsWebApplicationException 99Mapeado de excepciones 100Jerarquiacutea de excepciones 101

37 Ejercicios 104Servicio REST ejemplo 104Plantillas que se proporcionan 104Uso de JAXB (05 puntos) 104Uso de manejadores de contenidos y clase Response (075 puntos) 105Manejo de excepciones (075 puntos) 106

4 HATEOAS y Seguridad 10741 iquestQueacute es HATEOAS 10742 HATEOAS y Servicios Web 108

Enlaces Atom 108Ventajas de utilizar HATEOAS con Servicios Web 108Enlaces en cabeceras frente a enlaces Atom 111

43 HATEOAS y JAX-RS 111Construccioacuten de URIs con UriBuilder 112URIs relativas mediante el uso de UriInfo 115Construccioacuten de enlaces (Links) en documentos XML y en cabeceras HTTP 116

44 Seguridad 118Autentificacioacuten en JAX-RS 119Autorizacioacuten en JAX-RS 121Encriptacioacuten 122Seguridad programada 124

45 Ejercicios 126Uso de Hateoas (1 puntos) 126

Servicios Rest

3

Ejercicio seguridad (1 punto) 1275 Api cliente Procesamiento JSON y Pruebas 128

51 API cliente Visioacuten general 128Obtenemos una instancia Client 129Configuramos el target del cliente (URI) 131Construimos y Realizamos la peticioacuten 133Manejo de excepciones 138

52 Procesamiento JSON 14053 Modelo de procesamiento basado en el modelo de objetos 141

Creacioacuten de un modelos de objetos desde el coacutedigo de la aplicacioacuten 142Navegando por el modelo de objetos 143Escritura de un modelo de objetos en un stream 144Modelo de procesamiento basado en streaming 145

54 Pruebas de servicios REST 147Ciclo de vida de Maven y tests JUnit 147Anotaciones JUnit y aserciones AssertThat 151Observaciones sobre los tests y algunos ejemplos de tests 154

55 Ejercicios 158Tests utilizando el API cliente y un mapeador de excepciones (1 punto) 158Tests utilizando el API Json y JUnit (1 punto) 158Implementacioacuten de los tests 159

Servicios Rest

4

1 Introduccioacuten a REST Disentildeo y creacioacuten de serviciosRESTful

En esta sesioacuten vamos a introducir los conceptos de servicio Web y servicio Web RESTfulque es el tipo de servicios con los que vamos a trabajar Explicaremos el proceso de disentildeodel API de un servicio Web RESTful y definiremos las URIs que constituiraacuten los puntos deentrada de nuestra aplicacioacuten REST Finalmente ilustraremos los pasos para implementardesplegar y probar un servicio REST utilizando Maven IntelliJ y el servidor de aplicacionesWildfly Tambieacuten nos familiarizaremos con Postman una herramienta para poder probar deforma sencilla los servicios web directamente desde el navegador

11 iquestQueacute es un servicio Web

El disentildeo del software tiende a ser cada vez maacutes modular Las aplicaciones estaacuten formadaspor una serie de componentes reutilizables (servicios) que pueden encontrarse distribuidosa lo largo de una serie de maacutequinas conectadas en red

El WC3 (World Wide Web Consortium) define un servicio Web como un sistema softwaredisentildeado para soportar interacciones maacutequina a maacutequina a traveacutes de la red Dicho de otromodo los servicios Web proporcionan una forma estaacutendar de interoperar entre aplicacionessoftware que se ejecutan en diferentes plataformas Por lo tanto su principal caracteriacutesticasu gran interoperabilidad y extensibilidad asiacute como por proporcionar informacioacuten faacutecilmenteprocesable por las maacutequinas gracias al uso de XML Los servicios Web pueden combinarsecon muy bajo acoplamiento para conseguir la realizacioacuten de operaciones complejas De estaforma las aplicaciones que proporcionan servicios simples pueden interactuar con otras paraentregar servicios sofisticados antildeadidos

Historia de los servicios Web

Los servicios Web fueron inventados para solucionar el problema de lainteroperabilidad entre las aplicaciones Al principio de los 90 con el desarrollo deInternetLANWAN aparecioacute el gran problema de integrar aplicaciones diferentes Unaaplicacioacuten podiacutea haber sido desarrollada en C++ o Java y ejecutarse bajo Unix unPC o un computador mainframe No habiacutea una forma faacutecil de intercomunicar dichasaplicaciones Fueacute el desarrollo de XML el que hizo posible compartir datos entreaplicaciones con diferentes plataformas hardware a traveacutes de la red o incluso a traveacutesde Internet La razoacuten de que se llamasen servicios Web es que fueron disentildeadospara residir en un servidor Web y ser llamados a traveacutes de Internet tiacutepicamente viaprotocolos HTTP o HTTPS De esta forma se asegura que un servicio puede serllamado por cualquier aplicacioacuten usando cualquier lenguaje de programacioacuten y bajocualquier sistema operativo siempre y cuaacutendo por supuesto la conexioacuten a Internetesteacute activa y tenga un puerto abierto HTTPHTTPS lo cual es cierto para casi cualquiercomputador que disponga de acceso a Internet

A nivel conceptual un servicio es un componente software proporcionado a traveacutes de unendpoint accesible a traveacutes de la red Los servicios productores y consumidores utilizanmensajes para intercambiar informacioacuten de invocaciones de peticioacuten y respuesta en formade documentos auto-contenidos que hacen muy pocas asunciones sobre las capacidadestecnoloacutegicas de cada uno de los receptores

Servicios Rest

5

iquestQueacute es un endpointLos servicios pueden interconectarse a traveacutes de la red En unaarquitectura orientada a servicios cualquier interaccioacuten punto a puntoimplica dos endpoints uno que proporciona un servicio y otro de loconsume Es decir que un endpoint es cada uno de los elementosen nuestro caso nos referimos a servicios que se situacutean en ambosextremos de la red que sirve de canal de comunicacioacuten entre ellosCuando hablamos de servicios Web un endpoint se especifica medianteuna URI

A nivel teacutecnico los servicios pueden implementarse de varias formas En estesentido podemos distinguir dos tipos de servicios Web los denominados servicios Webgrandes (big Web Services) los llamaremos servicios Web SOAP y servicios ligeros oservicios Web RESTful

Los servicios Web SOAP se caracterizan por utilizar mensajes XML que siguen el estaacutendarSOAP (Simple Object Access Protocol) Ademaacutes contienen una descripcioacuten de las operacionesproporcionadas por el servicio escritas en WSDL (Web Services Description Language) unlenguaje basado en XML

Los servicios Web RESTful por el contrario pueden intercambiar mensajes escritos endiferentes formatos y no requieren el publicar una descripcioacuten de las operaciones queproporcionan por lo que necesitan una menor infraestructura para su implementacioacutenNosotros vamos a centrarnos en el uso de estos servicios

Servicios Web RESTful

Son un tipo de Servicios Web que se adhieren a una serie de restricciones arquitectoacutenicasenglobadas bajo las siglas de REST y que utilizan estaacutendares Web tales como URIs HTTPXML y JSON

El API Java para servicios Web RESTful (JAX-RS) permite desarrollar servicios Web RESTfulde forma sencilla La versioacuten maacutes reciente del API es la 20 cuya especificacioacuten estaacute publicadaen el documento JSR-339 y que podemos descargar desde httpsjcporgenjsrdetailid=339 A lo largo de estas sesiones veremos coacutemo utilizar JAX-RS para desarrollar serviciosWeb RESTful Dicho API utiliza anotaciones Java para reducir los esfuerzos de programacioacutende los servicios

12 Fundamentos de REST

El teacutermino REST proviene de la tesis doctoral de Roy Fielding publicada en el antildeo 2000y significa REpresentational State Transfer (podemos acceder a la tesis original en httpwwwicsuciedu~fieldingpubsdissertationtophtm) REST es un conjunto de restriccionesque cuando son aplicadas al disentildeo de un sistema crean un estilo arquitectoacutenico de softwareDicho estilo arquitectoacutenico se caracteriza por seguir los siguientes principios

bull Debe ser un sistema cliente-servidor

bull Tiene que ser sin estado es decir no hay necesidad de que los servicios guarden lassesiones de los usuarios (cada peticioacuten al servicio tiene que ser independiente de lasdemaacutes)

bull Debe soportar un sistema de cacheacutes la infraestructura de la red deberiacutea soportar cacheacuteen diferentes niveles

Servicios Rest

6

bull Debe ser un sistema uniformemente accesible (con una interfaz uniforme) Estarestriccioacuten define coacutemo debe ser la interfaz entre clientes y servidores La idea es simplificary desacoplar la arquitectura permitiendo que cada una de sus partes puede evolucionarde forma independiente Una interfaz uniforme se debe caracterizar por

Estar basada en recursos La abstraccioacuten utilizada para representar la informacioacuten ylos datos en REST es el recurso y cada recurso debe poder ser accedido medianteuna URI (Uniform Resource Identifier)

Orientada a representaciones La interaccioacuten con los servicios tiene lugar atraveacutes de las representaciones de los recursos que conforman dicho servicio Unrecurso referenciado por una URI puede tener diferentes formatos (representaciones)Diferentes plataformas requieren formatos diferentes Por ejemplo los navegadoresnecesitan HTML JavaScript requiere JSON (JavaScript Object Notation) y unaaplicacioacuten Java puede necesitar XML

Interfaz restringida Se utiliza un pequentildeo conjunto de meacutetodos bien definidos paramanipular los recursos

Uso de mensajes auto-descriptivos cada mensaje debe incluir la suficienteinformacioacuten como para describir coacutemo procesar el mensaje Por ejemplo se puedeindicar coacutemo parsear el mensaje indicando el tipo de contenido del mismo (xml htmltextohellip)

Uso de Hipermedia como maacutequina de estados de la aplicacion (HATEOAS) Lospropios formatos de los datos son los que dirigen las transiciones entre estados dela aplicacioacuten Como veremos maacutes adelante con maacutes detalle el uso de HATEOAS(Hypermedia As The Engine Of Application State) va a permitir transferir de formaexpliacutecita el estado de la aplicacion en los mensajes intercambiados y por lo tantorealizar interacciones con estado

bull Tiene que ser un sistema por capas un cliente no puede discernir si estaacute accediendodirectamente al servidor o a alguacuten intermediario Las capas intermedias van a permitirsoportar la escalabilidad asiacute como reforzar las poliacuteticas de seguridad

A continuacioacuten analizaremos algunas de las abstracciones que constituyen un sistemaRESTful recursos representaciones URIs y los tipos de peticiones HTTP que constituyen lainterfaz uniforme utilizada en las transferencias clienteservidor

Recursos

Un recurso REST es cualquier cosa que sea direccionable (y por lo tanto accesible) a traveacutesde la Web Por direccionable nos referimos a recursos que puedan ser accedidos y transferidosentre clientes y servidores Por lo tanto un recurso es una correspondencia loacutegica ytemporal con un concepto en el dominio del problema para el cual estamos implementandouna solucioacuten

Algunos ejemplos de recursos REST son

bull Una noticia de un perioacutedico

bull La temperatura de Alicante a las 400pm

bull Un valor de IVA almacenado en una base de datos

bull Una lista con el historial de las revisiones de coacutedigo en un sistema CVS

bull Un estudiante en alguna aula de alguna universidad

Servicios Rest

7

bull El resultado de una buacutesqueda de un iacutetem particular en Google

Aun cuando el mapeado de un recurso es uacutenico diferentes peticiones a un recursopueden devolver la misma representacioacuten binaria almacenada en el servidor Por ejemploconsideremos un recurso en el contexto de un sistema de publicaciones En este casouna peticioacuten de la uacuteltima revisioacuten publicada y la peticioacuten de la revisioacuten nuacutemero 12 enalguacuten momento de tiempo pueden devolver la misma representacioacuten del recurso cuandola uacuteltima revisioacuten sea efectivamente la 12 Por lo tanto cuando la uacuteltima revisioacuten publicadase incremente a la versioacuten 13 una peticioacuten a la uacuteltima revisioacuten devolveraacute la versioacuten 13 yuna peticioacuten de la revisioacuten 12 continuaraacute devolviendo la versioacuten 12 En definitiva cada unode los recursos puede ser accedido directamente y de forma independiente pero diferentespeticiones podriacutean apuntar al mismo dato

Debido a que estamos utilizando HTTP para comunicarnos podemos transferir cualquier tipode informacioacuten que pueda transportarse entre clientes y servidores Por ejemplo si realizamosuna peticioacuten de un fichero de texto de la CNN nuestro navegador mostraraacute un fichero de textoSi solicitamos una peliacutecula flash a YouTube nuestro navegador recibiraacute una peliacutecula flash Enambos casos los datos son transferidos sobre TCPIP y el navegador conoce coacutemo interpretarlos streams binarios debido a la cabecera de respuesta del protocolo HTTP Content-Type Porlo tanto en un sistema RESTful la representacioacuten de un recurso depende del tipo deseado porel cliente (tipo MIME) el cual estaacute especificado en la peticioacuten del protocolo de comunicaciones

Representacioacuten de los recursos

La representacioacuten de los recursos es lo que se enviacutea entre los servidores y clientes Unarepresentacioacuten muestra el estado temporal del dato real almacenado en alguacuten dispositivo dealmacenamiento en el momento de la peticioacuten En teacuterminos generales es un stream binariojuntamente con los metadatos que describen coacutemo dicho stream debe ser consumido por elcliente yo servidor (los metadatos tambieacuten puden contener informacioacuten extra sobre el recursocomo por ejemplo informacioacuten de validacioacuten y encriptacioacuten o coacutedigo extra para ser ejecutadodinaacutemicamente)

A traveacutes del ciclo de vida de un servicio web pueden haber varios clientes solicitando recursosClientes diferentes son capaces de consumir diferentes representaciones del mismo recursoPor lo tanto una representacioacuten puede tener varias formas como por ejemplo una imagen untexto un fichero XML o un fichero JSON pero tienen que estar disponibles en la misma URL

Para respuestas generadas para humanos a traveacutes de un navegador una representacioacutentiacutepica tiene la forma de paacutegina HTML Para respuestas automaacuteticas de otros servicios web lalegibilidad no es importante y puede utilizarse una representacioacuten mucho maacutes eficiente comopor ejemplo XML

El lenguaje para el intercambio de informacioacuten con el servicio queda a eleccioacuten deldesarrollador A continuacioacuten mostramos algunos formatos comunes que podemos utilizarpara intercambiar esta informacioacuten

Table 1 Ejemplos de formatos utilizados por los servicios REST

Formato Tipo MIME

Texto plano textplain

HTML texthtml

XML applicationxml

JSON applicationjson

Servicios Rest

8

De especial intereacutes es el formato JSON Se trata de un lenguaje ligero de intercambio deinformacioacuten que puede utilizarse en lugar de XML (que resulta considerablemente maacutespesado) para aplicaciones AJAX De hecho en Javascript puede leerse este tipo de formatosimplemente utilizando el meacutetodo eval()

Direccionabilidad de los recursos URI

Una URI o Uniform Resource Identifier en un servicio web RESTful es un hiper-enlace aun recurso y es la uacutenica forma de intercambiar representaciones entre clientes y servidoresUn servicio web RESTful expone un conjunto de recursos que identifican los objetivos de lainteraccioacuten con sus clientes

El conjunto de restricciones REST no impone que las URIs deban ser hiper-enlacesSimplemente hablamos de hiper-enlaces porque estamos utilizando la Web para crearservicios web Si estuvieacutesemos utilizando un conjunto diferente de tecnologiacuteas soportadasuna URI RESTful podriacutea ser algo completamente diferente Sin embargo la idea dedireccionabilidad debe permanecer

En un sistema REST la URI no cambia a lo largo del tiempo ya que la implementacioacutende la arquitectura es la que gestiona los servicios localiza los recursos negocia lasrepresentaciones y enviacutea respuestas con los recursos solicitados Y lo que es maacutes importantesi hubiese un cambio en la estructura del dispositivo de almacenamiento en el lado del servidor(por ejemplo un cambio de servidores de bases de datos) nuestras URIs seguiraacuten siendo lasmismas y seraacuten vaacutelidas mientras el servicio web siga estando en marcha o el contexto delrecurso no cambie

Sin las restricciones REST los recursos se acceden por su localizacioacutenlas direcciones web tiacutepicas son URIs fijas Si por ejemplo renombramosun fichero en el servidor la URI seraacute diferente si movemos el fichero a undirectorio diferente la URI tambieacuten seraacute diferente

El formato de una URI se estandariza como sigue

schemehostportpathqueryStringfragment

En donde

bull scheme es el protocolo que estamos utilizando para comunicarnos con el servidor Paraservicios REST normalmente el protocolo seraacute http o https

bull El teacutermino host es un nombre DNS o una direccioacuten IP

bull A continuacioacuten se puede indicar de forma opcional un puerto (mediante port ) que es unvalor numeacuterico El host y el port representan la localizacioacuten de nuestro recurso en la red

bull Seguidamente aparece una expresioacuten path que es un conjunto de segmentos de textodelimitados por el caraacutecter (pensemos en la expresioacuten path como en una lista dedirectorios de un fichero en nuestra maacutequina)

bull Esta expresioacuten puede ir seguida opcionalmente por una queryString El caraacutecter )separa el path de la queryString Esta uacuteltima es una lista de paraacutemetros representadoscomo pares nombrevalor Cada par estaacute delimitado por el caraacutecter amp

La uacuteltima parte de la URI es el fragment delimitado por el caraacutecter Normalmente seutiliza para apuntar a cierto lugar del documento al que estamos accediendo

Servicios Rest

9

En una URI no todos los caracteres estaacuten permitidos de forma que algunos caracteres secodificaraacuten de acuerdo a las siguientes reglas

bull Los caracteres a-z A-Z 0-9 - y _ permanecen igual

bull El caracter espacio se convierte en el caraacutecter +

bull El resto de caracteres se codifican como una secuencia de bits siguiendo un esquema decodificacioacuten hexadecimal de forma que cada dos diacutegitos hexadecimales van precedidospor el caraacutecter

Un ejemplo de URI podriacutea ser eacuteste

httpexpertojavauaesrecursosclientesapellido=MartinezampcodPostal=02115

En el ejemplo anterior el host viene dado por expertojavauaes el path o ruta de acceso alrecurso es recursosclientes y hemos especificado los paraacutemetros apellido y codPostal conlos valores Martinez y 02115 respectivamente

Si por ejemplo en nuestra aplicacioacuten tenemos informacioacuten de clientes podriacuteamos acceder ala lista correspondiente mediante una URL como la siguiente

httpexpertojavauaesrecursosclientes

Esto nos devolveraacute la lista de clientes en el formato que el desarrollador del servicio hayadecidido Hay que destacar por lo tanto que en este caso debe haber un entendimiento entreel consumidor y el productor del servicio de forma que el primero comprenda el lenguajeutilizado por el segundo

La URL anterior nos podriacutea devolver un documento como el siguiente

ltxml version=10gtltclientesgt ltclientegthttpexpertojavauaesrecursosclientes1ltclientegt ltclientegthttpexpertojavauaesrecursosclientes2ltclientegt ltclientegthttpexpertojavauaesrecursosclientes4ltclientegt ltclientegthttpexpertojavauaesrecursosclientes6ltclientegtltclientesgt

En este documento se muestra la lista de clientes registrados en la aplicacioacuten cada uno deellos representado tambieacuten por una URL Accediendo a estas URLs a su vez podremosobtener informacioacuten sobre cada curso concreto o bien modificarlo

Uniformidad y restricciones de las interfaces

Ya hemos introducido los conceptos de recursos y sus representaciones Hemos dichoque los recursos son correspondencias (mappings) de los estados reales de las entidadesque son intercambiados entre los clientes y servidores Tambieacuten hemos dicho que lasrepresentaciones son negociadas entre los clientes y servidores a traveacutes del protocolo decomunicacioacuten en tiempo de ejecucioacuten (a traveacutes de HTTP) A continuacioacuten veremos con detalle

Servicios Rest

10

lo que significa el intercambio de estas representaciones y lo que implica para los clientes yservidores el realizar acciones sobre dichos recursos

El desarrollo de servicios web REST es similar al desarrollo de aplicaciones web Sin embargola diferencia fundamental entre el desarrollo de aplicaciones web tradicionales y las maacutesmodernas es coacutemo pensamos sobre las acciones a realizar sobre nuestras abstraccionesde datos De forma maacutes concreta el desarrollo moderno estaacute centrado en el concepto denombres (intercambio de recursos) el desarrollo tradicional estaacute centrado en el conceptode verbos (acciones remotas a realizar sobre los datos) Con la primera forma estamosimplementando un servicio web RESTful con la segunda un servicio similar a una llamada aprocedimiento remoto- RPC) Y lo que es maacutes un servicio RESTful modifica el estado delos datos a traveacutes de la representacioacuten de los recursos (por el contrario una llamada aun servicio RPC oculta la representacioacuten de los datos y en su lugar enviacutea comandos paramodificar el estado de los datos en el lado del servidor) Finalmente en el desarrollo modernode aplicaciones web limitamos la ambiguumledad en el disentildeo y la implementacioacuten debido aque tenemos cuatro acciones especiacuteficas que podemos realizar sobre los recursos CreateRetrieve Update y Delete (CRUD) Por otro lado en el desarrollo tradicional de aplicacionesweb podemos tener otras acciones con nombres o implementaciones no estaacutendar

A continuacioacuten indicamos la correspondencia entre las acciones CRUD sobre los datos y losmeacutetodos HTTP asociados

Table 2 Operaciones REST sobre los recursos

Accioacuten sobre los datos Protocolo HTTP equivalente

CREATE POST

RETRIEVE GET

UPDATE PUT

DELETE DELETE

El principio de uniformidad de la interfaz de acceso a recursos es fundamental y quizaacute el maacutesdifiacutecil de seguir por los programadores acostumbrados al modelo RPC (Remote ProcedureCall) La idea subyacente es utilizar uacutenicamente un conjunto finito y claramente establecidode operaciones para la interaccioacuten con los servicios Esto significa que no tendremos unparaacutemetro accioacuten en nuestra URI y que soacutelo utilizaremos los meacutetodos HTTP para accedera nuestros servicios Cada uno de los meacutetodos tiene un propoacutesito y significado especiacuteficosque mostramos a continuacioacuten

GETGET es una operacioacuten soacutelo de lectura Se utiliza para recuperar informacioacuten especiacuteficadel servidor Tambieacuten se trata de una operacioacuten idempotente y segura Idempotentesignifica que no importa cuaacutentas veces invoquemos esta operacioacuten el resultado (queobservaremos como usuarios) debe ser siempre el mismo Segura significa que unaoperacioacuten GET no cambia el estado del servidor en modo alguno es decir no debe exhibirninguacuten efecto lateral en el servidor Por ejemplo el hecho de leer un documento HTMLno deberiacutea cambiar el estado de dicho documento

PUTLa operacioacuten PUT solicita al servidor el almacenar el cuerpo del mensaje enviado condicha operacioacuten en la direccioacuten proporcionada en el mensaje HTTP Normalmente semodela como una insercioacuten o actualizacioacuten (nosotros la utilizaremos solamente comoactualizacioacuten) Es una propiedad idempotente Cuando se utiliza PUT el cliente conoce

Servicios Rest

11

la identidad del recurso que estaacute creando o actualizando Es idempotente porque enviar elmismo mensaje PUT maacutes de una vez no tiene ninguacuten efecto sobre el servicio subyacenteUna analogiacutea podriacutea ser un documento de texto que estemos editando No importa cuaacutentasveces pulsemos el botoacuten de grabar el fichero que contiene el documento loacutegicamenteseraacute el mismo documento

DELETEEsta operacioacuten se utiliza para eliminar recursos Tambieacuten es idempotente

POSTPost es la uacutenica operacioacuten HTTP que no es idempotente ni segura Cada peticioacuten POSTpuede modificar el servicio de forma exclusiva Se puede enviar o no informacioacutencon la peticioacuten Tambieacuten podemos recibir o no informacioacuten con la respuesta Paraimplementar servicios REST es deseable enviar informacioacuten con la peticioacuten y tambieacutenrecibir informacioacuten con la respuesta

Adicionalmente podemos utilizar otras dos operaciones HTTP

HEADEs una operacioacuten exactamente igual que GET excepto que en lugar de devolver un cuerpode mensaje solamente devuelve un coacutedigo de respuesta y alguna cabecera asociada conla peticioacuten

OPTIONSSe utiliza para solicitar informacioacuten sobre las opciones disponibles sobre un recurso en elque estamos interesados Esto permite al cliente determinar las capacidades del servidory del recurso sin tener que realizar ninguna peticioacuten que provoque una accioacuten sobre elrecurso o la recuperacioacuten del mismo

PATCHSe utiliza para para realiza reemplazos (actualizaciones) parciales de un documento yaque la operacioacuten PUT soacutelo permite una actualizacioacuten completa del recurso (y requiereindicar una representacioacuten completa del mismo) Es uacutetil cuando el recurso a modificares complejo y solamente queremos actualizar parte de su contenido En este caso solonecesitamos indicar la parte que queremos cambiar

13 Disentildeo de servicios Web RESTful

El disentildeo de servicios RESTful no es muy diferente del disentildeo de aplicaciones webtradicionales tenemos requerimientos de negocio tenemos usuarios que quieren realizaroperaciones sobre los datos y tenemos restricciones hardware que van a condicionar nuestraarquitectura software La principal diferencia reside en el hecho de que tenemos que buscara partir de los requerimientos cuaacuteles van a ser los recursos que van a ser accedidos a traveacutesde los servicios sin preocuparnos de queacute operaciones o acciones especiacuteficas van a poderserealizar sobre dichos recursos (el proceso de disentildeo depende de los nombres no de losverbos)

Podemos resumir los principios de disentildeo de servicios web RESTful en los siguientes cuatropasos

1 Elicitacioacuten de requerimientos y creacioacuten del modelo de objetos Este paso es similar aldisentildeo orientado a objetos El resultado del proceso puede ser un modelo de clases UML

2 Identificacioacuten de recursos Este paso consiste en identificar los objetos de nuestromodelo sin preocuparnos de las operaciones concretas a realizar sobre dichos objetos

Servicios Rest

12

3 Definicioacuten de las URIs Para satisfacer el principio de direccionabilidad de los recursostendremos que definir las URIs que representaraacuten los endpoints de nuestros servicios yque constituiraacuten los puntos de entrada de los mismos

4 Definicioacuten de la representacioacuten de los recursos Puesto que los sistemas REST estaacutenorientados a la representacioacuten tendremos que definir el formato de los datos queutilizaremos para intercambiar informacioacuten entre nuestros servicios y clientes

5 Definicioacuten de los meacutetodos de acceso a los recursos Finalmente tendremos que decidirqueacute meacutetodos HTTP nos permitiraacuten acceder a las URIs que queremos exponer asiacutecomo queacute haraacute cada meacutetodo Es muy importante que en este paso nos cintildeamos alas restricciones que definen los principios RESTful que hemos indicado en apartadosanteriores

14 Un primer servicio JAX-RS

Vamos a ilustrar los pasos anteriores con un ejemplo concretamente definiremos una interfazRESTful para un sistema sencillo de gestioacuten de pedidos de un hipoteacutetico comercio por internetLos potenciales clientes de nuestro sistema podraacuten realizar compras modificar pedidosexistentes en nuestro sistema asiacute como visualizar sus datos personales o la informacioacuten sobrelos productos que son ofertados por el comercio

Modelo de objetos

A partir de los requerimientos del sistema obtenemos el modelo de objetos El modelode objetos de nuestro sistema de ventas por internet es bastante sencillo Cada pedidoen el sistema representa una uacutenica transaccioacuten de compra y estaacute asociada con un clienteparticular Los pedidos estaraacuten formados por una o maacutes liacuteneas de pedido Las liacuteneas de pedidorepresentan el tipo y el nuacutemero de unidades del producto adquirido

Basaacutendonos en esta descripcioacuten de nuestro sistema podemos extraer que los objetos denuestro modelo son Pedido Cliente LineaPedido y Producto Cada objeto de nuestromodelo tiene un identificador uacutenico representado por la propiedad id dada por un valor detipo entero La siguiente figura muestra un diagrama UML de nuestro modelo

Estamos interesados en consultar todos los pedidos realizados asiacute como cada pedido deforma individual Tambieacuten queremos poder realizar nuevos pedidos asiacute como actualizar

Servicios Rest

13

pedidos existentes El objeto ServicioPedidos representa las operaciones que queremosrealizar sobre nuestos objetos Pedido Cliente LineaPedido y Producto

Modelado de URIs

Lo primero que haremos para crear nuestra interfaz distribuida es definir y poner nombre acada uno de los endpoints de nuestro sistema En un sistemam RESTful los endpoints seraacutenlos recursos del sistema que identificaremos mediante URIs

En nuestro modelo de objetos queremos poder interactuar con Pedidos Clientes y ProductosEacutestos seraacuten por lo tanto nuestros recursos de nivel maacutes alto Por otro lado estamosinteresados en obtener una lista de cada uno de estos elementos de alto nivel asiacute comointeractuar con los elementos indiduales de cada tipo El objeto LineaPedido es un objetoagregado del objeto Pedido por lo que no lo consideraremos com un recurso de nivel superiorMaacutes adelante veremos que podremos exponerlo como un subrecurso de un Pedido particularpero por ahora asumiremos que estaacute oculto por el formato de nuestros datos Seguacuten estouna posible lista de URIs que expondraacute nuestro sistema podriacutea ser

bull pedidos

bull pedidosid

bull productos

bull productosid

bull clientes

bull clientesid

Fiacutejate que hemos representado como URIs los nombres en nuestromodelo de objetos Recuerda que las URIS no deberiacutean utilizarse comomini-mecanismos de RPC ni deberiacutean identificar operaciones En vez deeso tenemos que utilizar una combinacioacuten de meacutetodos HTTP y de datos(recursos) para modelar las operaciones de nuestro sistema RESTful

Definicioacuten del formato de datos

Una de las cosas maacutes importantes que tenemos que hacer cuando definimos la interfazRESTful es determinar coacutemo se representaraacuten los recursos que seraacuten accedidos por losusuarios de nuestro API REST Quizaacute XML sea el formato maacutes popular de la web y puede serprocesado por la mayor parte de los lenguajes de programacioacuten Como veremos maacutes adelanteJSON es otro formato popular menos verboso que XML y que puede ser interpretadodirectamente por JavaScript (lo cual es perfecto para aplicaciones Ajax por ejemplo) Porahora utilizaremos el formato XML en nuestro ejemplo

Generalmente tendriacuteamos que definir un esquema XML para cada representacioacuten quequeramos transimitir a traves de la red Un esquema XML define la gramaacutetica del formato dedatos Por simplicidad vamos a omitir la creacioacuten de esquemas asumiendo que los ejemplosque proporcionamos se adhieren a sus correspondientes esquemas

A continuacioacuten distinguiremos entre dos formatos de datos uno para las operaciones delectura y actualizacioacuten y otro para la operacioacuten de creacioacuten de recursos

Formato de datos para operaciones de lectura y modificacioacuten de los recursos

Las representaciones de los recursos Pedido Cliente y Producto tendraacuten un elemento XMLen comuacuten al que denominaremos link

Servicios Rest

14

ltlink rel=self href=httporgexpertojavagt

El elemento (o etiqueta) link indica a los clientes que obtengan un documento XML comorepresentacioacuten del recurso doacutende pueden interaccionar en la red con dicho recurso enparticular El atributo self le indica al cliente queacute relacioacuten tiene dicho enlace con la URIdel recurso al que apunta (informacioacuten contenida en el atributo href ) El valor self indicaque estaacute apuntando a siacute mismo Maacutes adelante veremos la utilidad del elemento link cuandoagreguemos informacioacuten en documentos XML maacutes grandes

El formato de representacioacuten del recurso Cliente podriacutea ser

ltcliente id=8gt ltlink rel=self href=httporgexpertojavaclientes8gt ltnombregtPedroltnombregt ltapellidosgtGarcia Perezltapellidosgt ltdirecciongtCalle del Pino 5ltdirecciongt ltcodPostalgt08888ltcodPostalgt ltciudadgtMadridltciudadgtltclientegt

El formato de representacioacuten del recurso Producto podriacutea ser

ltproducto id=34gt ltlink rel=self href=httporgexpertojavaproductos34gt ltnombregtiPhone 6ltnombregt ltpreciogt800ltpreciogt ltcantidadgt1ltcantidadgtltproductogt

Finalmente el formato de la representacioacuten del recurso Pedido podriacutea ser

ltpedido id=233gt ltlink rel=self href=httporgexpertojavapedidos233gt lttotalgt800lttotalgt ltfechagtDecember 22 2014 0656ltfechagt ltcliente id=8gt ltlink rel=self href=httporgexpertojavaclientes8gt ltnombregtPedroltnombregt ltapellidosgtGarcia Perezltapellidosgt ltdirecciongtCalle del Pino 5ltdirecciongt ltcodPostalgt08888ltcodPostalgt ltciudadgtMadridltciudadgt ltclientegt ltlineasPedidogt ltlineaPedido id=1gt ltproducto id=34gt ltlink rel=self href=httporgexpertojavaproductos34gt ltnombregtiPhone 6ltnombregt

Servicios Rest

15

ltpreciogt800ltpreciogt ltcantidadgt1ltcantidadgt ltproductogt ltlineaPedidogt ltlineasPedidogtltpedidogt

El formato de datos de un Pedido tiene en un primer nivel la informacioacuten del total conel importe total del pedido asiacute como la fecha en la que se hizo dicho pedido Pedido es unbuen ejemplo de composicioacuten de datos ya que un pedido incluye informacioacuten sobre el Clientey el Productos Aquiacute es donde el elemento ltlinkgt puede resultar particularmente uacutetil Si elusuario estaacute interesado en interaccionar con el Cliente que ha realizado el pedido o en uno delos productos del mismo se proporciona la URI necesaria para interactuar con cada uno dedichos recursos De esta forma cuando el usuario del API consulte un pedido podraacute ademaacutesacceder a informacioacuten adicional relacionada con la consulta realizada

Formato de datos para operaciones de creacioacuten de los recursos

Cuando estamos creando nuevos Pedidos Clientes o Productos no tiene mucho sentidoincluir un atributo id y un elemento link en nuestro documento XML El servidor seraacute elencargado de crear los ids cuando inserte nuestro nuevo objeto en la base de datos Tampococonocemos la URI del nuevo objeto creado ya que seraacute el servidor el encargado de generarloPor lo tanto para crear un nuevo Producto el formato de la informacioacuten podriacutea ser el siguiente

ltproductogt ltlink rel=self href=httporgexpertojavaclientes8gt ltnombregtiPhoneltnombregt ltpreciogt800ltpreciogtltproductogt

Asignacioacuten de meacutetodos HTTP

Finalmente tendremos que decidir queacute meacutetodos HTTP expondremos en nuestro servicio paracada uno de los recursos asiacute como definir queacute haraacuten dichos meacutetodos Es muy importanteno asignar funcionaldad a un meacutetodo HTTP que sobrepase los liacutemites impuestos por laespecificacioacuten de dicho meacutetodo Por ejemplo una operacioacuten GET sobre un recurso concretodeberiacutea ser de soacutelo lectura No deberiacutea cambiar el estado del recurso cuando invoquemos laoperacioacuten GET sobre eacutel Si no seguimos de forma estricta la semaacutentica de los meacutetodos HTTPlos clientes asiacute como cualquier otra herramienta administrativa no pueden hacer asuncionessobre nuestros servicios de forma que nuestro sistema se vuelve maacutes complejo

Veamos para cada uno de los meacutetodos de nuestro modelo de objetos cuales seraacuten las URIsy meacutetodos HTTP que usaremos para representarlos

Visualizacioacuten de todos los Pedidos Clientes o Productos

Los tres objetos de nuestro modelo Pedidos Clientes y Productos son accedidos ymanipulados de forma similar Los usuarios pueden estar interesados en ver todos losPedidos Clientes o Productos en el sistema Las siguientes URIs representan dichos objetoscomo un grupo

bull pedidos

Servicios Rest

16

bull productos

bull clientes

Para obtener una lista de Pedidos Clientes o Productos el cliente remoto realizara unallamada al meacutetodo HTTP GET sobre la URI que representa el grupo de objetos Un ejemplode peticioacuten podriacutea ser la siguiente

GET productos HTTP11

Nuestro servicio responderaacute con los datos que representan todos los Pedidos de nuestrosistema Una respuesta podriacutea ser eacutesta

HTTP11 200 OKContent-Type applicationxml

ltproductosgt ltproducto id=111gt ltlink rel=self href=httporgexpertojavaproductos111gt ltnombregtiPhoneltnombregt ltpreciogt64899ltpreciogt ltproductogt ltproducto id=222gt ltlink rel=self href=httporgexpertojavaproductos222gt ltnombregtMacbookltnombregt ltpreciogt159999ltpreciogt ltproductogt ltproductosgt

Un problema que puede darse con esta peticioacuten es que tengamos miles de Pedidos Clienteso Productos en nuestro sistema por lo que podemos sobrecargar a nuestro cliente y afectarnegativamente a los tiempos de respuesta Para mitigar esta problema permitiremos que elusuario especifique unos paraacutemetros en la URI para limitar el tamantildeo del conjunto de datosque se va a devolver

GET pedidosstartIndex=0ampsize=5 HTTP11GET productosstartIndex=0ampsize=5 HTTP11GET clientesstartIndex=0ampsize=5 HTTP11

En las oacuterdenes anteriores hemos definido dos paraacutemetros de peticioacuten startIndex ysize El primero de ellos es un iacutendice numeacuterico que representa a partir de queacute posicioacuten enla lista de Pedidos Clientes o Productos comenzaremos a enviar la informacioacuten al clienteEl paraacutemetro size especifica cuaacutentos de estos objetos de la lista queremos que nos seandevueltos

Estos paraacutemetros seraacuten opcionales de forma que el cliente no tiene que especificarlos en suURI

Obtencioacuten de Pedidos Clientes o Productos individuales

Ya hemos comentado previamente que podriacuteamos utilizar las siguientes URIs para obtenerPedidos Clientes o Productos

Servicios Rest

17

bull pedidosid

bull productosid

bull clientesid

En este caso usaremos el meacutetodo HTTP GET para recuperar objetos individuales en elsistema Cada invocacioacuten GET devolveraacute la informacioacuten del correspondiente objeto Porejemplo

GET pedidos233 HTTP11

Para esta peticioacuten el cliente estaacute interesado en obtener una representacioacuten del Pedido conidentificador 233 Las peticiones GET para Productos y Clientes podriacutean funcionar de formasimilar El mensaje de respuesta podriacutea parecerse a algo como esto

HTTP11 200 OKContent-Type applicationxml

ltpedido id=233gtltpedidogt

El coacutedigo de respuesta es 200 OK indicando que la peticioacuten ha tenido eacutexito La cabeceraContent-Type especifica el formato del cuerpo de nuestro mensaje como XML y finalmenteobtenemos la representacioacuten del Pedido solicitado

Creacioacuten de un Pedido Cliente o Producto

Para crear un Pedido Cliente o Producto utilizaremos el meacutetodo POST En este caso el clienteenviacutea una representacioacuten del nuevo objeto que se prentende crear a la URI padre de surepresentacioacuten y por lo tanto podremos omitir el identificador del recurso Por ejemplo

Peticioacuten POST para crear un pedidio

POST pedidos HTTP11Content-Type applicationxml

ltpedidogt lttotalgt19902lttotalgt ltfechagtDecember 22 2008 0656ltfechagt ltpedidogt

El servicio recibe el mensaje POST procesa la XML y crea un nuevo pedido en la basede datos utilizando un identificador generado de forma uacutenica Si bien esta aproximacioacutenfunciona perfectamente se le pueden plantear varias cuestiones al usuario iquestQueacute ocurre siel usuario quiere visualizar modificar o eliminar el pedido que acaba de crear iquestCuaacutel es elidentificador del nuevo recurso iquestCuaacutel es la URI que podemos utilizar para interactuar con elnuevo recurso Para resolver estas cuestiones antildeadiremos alguna informacioacuten al mensajede respuesta HTTP El cliente podriacutea recibir un mensaje similar a eacuteste

Respuesta de una peticioacuten POST para crear un pedido

HTTP11 201 Created

Servicios Rest

18

Content-Type applicationxmlLocation httporgexpertojavapedidos233

ltpedido id=233gt ltlink rel=self href=httporgexpertojavapedidos233gt lttotalgt19902lttotalgt ltfechagtDecember 22 2008 0656ltfechagt ltpedidogt

HTTP requiere que si POST crea un nuevo recurso se debe responder con un coacutedigo 201Created Tambieacuten se requieer que la cabecera Location en el mensaje de respuestaproporcione una URI al usuario que ha hecho la peticioacuten para que eacuteste pueda interactuar conla Peticioacuten que acaba de crear (por ejemplo para modificar dicho Pedido) Es opcional porparte del servidor devolver en la respuesta la representacioacuten del nuevo recurso creado Ennuestro ejemplo optamos por devolver una representacioacuten XML de la Peticion creada con elidentificador del atributo asiacute como el elemento link

Actualizacioacuten de un Pedido Cliente o Producto

Para realizar modificaciones sobre los recursos que ya hemos creado utilizaremos el meacutetodoPUT En este caso un ejemplo de peticioacuten podriacutea ser eacutesta

Peticioacuten PUT para modificar un pedidio

PUT pedidos233 HTTP11Content-Type applicationxml

ltproducto id=111gt ltnombregtiPhoneltnombregt ltpreciogt64999ltpreciogtltproductogt

Tal y como he hemos indicado anteriormente la operacioacuten PUT es idempotente Lo quesignifica que no importa cuaacutentas veces solicitemos la peticioacuten PUT el producto subyacentesigue permaneciendo con el mismo estado final

Cuando un recurso se modifica mediante PUT la especificacioacuten HTTP requiere que el servidorenviacutee un coacutedigo de respuesta 200 OK y un cuerpo de mensaje de respuesta o bien el coacutedigo204 No Content sin ninguacuten cuerpo de mensaje en la respuesta

En nuestro caso devolveremos un coacutedigo de estado 204 y un mensaje sin cuerpo derespuesta

RECUERDA Es importante NO confundir POST con PUTMuchas veces se confunden los meacutetodos PUT y POST El significado deestos meacutetodos es el siguiente

bull POST Publica datos en un determinado recurso El recurso debe existirpreviamente y los datos enviados son antildeadidos a eacutel Por ejemplo paraantildeadir nuevos pedidos con POST hemos visto que debiacuteamos hacerlocon el recurso lista de pedidos (pedidos) ya que la URI del nuevopedido todaviacutea no existe La operacioacuten NO es idempotente es decir si

Servicios Rest

19

antildeadimos varias veces el mismo alumno apareceraacute repetido en nuestralista de pedidos con URIs distintas

bull PUT Hace que el recurso indicado tome como contenido los datosenviados El recurso podriacutea no existir previamente y en caso de queexistiese seriacutea sobrescrito con la nueva informacioacuten A diferencia dePOST PUT es idempotente Muacuteltiples llamadas ideacutenticas a la mismaaccioacuten PUT siempre dejaraacuten el recurso en el mismo estado Laaccioacuten se realiza sobre la URI concreta que queremos establecer (porejemplo pedidos215) de forma que varias llamadas consecutivas conlos mismos datos tendraacuten el mismo efecto que realizar soacutelo una deellas

Borrado de un Pedido Cliente o Producto

Modelaremos el borrado de los recursos utilizando el meacutetodo HTTP DELETE El usuariosimplemente invocaraacute el meacutetodo DELETE sobre la URI que representa el objeto que queremoseliminar Este meacutetodo haraacute que dicho recurso ya no exista en nuestro sistema

Cuando eliminamos un recurso con DELETE la especificacioacuten requiere que se enviacutee un coacutedigode respuesta 200 OK y un cuerpo de mensaje de respuesta o bien un coacutedigo de respuesta204 No Content sin un cuerpo de mensaje de respuesta

En nuestro caso devolveremos un coacutedigo de estado 204 y un mensaje sin cuerpo derespuesta

Cancelacioacuten de un Pedido

Hasta ahora las operaciones de nuestro modelo de objetos encajan bastante bien enla especificacioacuten de los correspondientes meacutetodos HTTP Hemos utilzado GET para leerdatos PUT para realizar modificaciones POST para crear nuevos recursos y DELETE paraeliminarlos En nuestro sistema los Pedidos pueden eliminarse o tambieacuten cancelarse Yahemos comentado que el borrado de un recurso lo elimina completamente de nuestra basede datos La operacioacuten de cancelacioacuten solamente cambia el estado del Pedido y lo siguemanteniendo en el sistema iquestCoacutemo podriacuteamos modelar esta operacioacuten

Cuando modelamos una interfaz RESTful para las operaciones de nuestro modelo de objetosdeberiacuteamos plantearnos la siguiente pregunta iquestla operacioacuten es un estado del recurso Sila respuesta es siacute entonces deberiacuteamos modelar esta operacioacuten dentro del formato de losdatos

La cancelacioacuten de un pedido es un ejemplo perfecto de esto que acabamos de decir La claveestaacute en que esta operacioacuten en realidad es un estado especiacutefico del Pedido eacuteste puede estarcancelado o no Cuando un usuario accede a un Pedido puede desear conocer si el Pedidoha sido o no cancelado Por lo tanto la informacioacuten sobre la cancelacioacuten deberiacutea formar partedel formato de datos de un Pedido Asiacute antildeadiremos un nuevo elemento a la informacioacuten delPedido

ltpedido id=233gt ltlink rel=self href=httporgexpertojavapedidos233gt lttotalgt19902lttotalgt ltfechagtDecember 22 2008 0656ltfechagt ltcanceladogtfalseltcanceladogt

Servicios Rest

20

ltpedidogt

Ya que el estado cancelado se modela en el propio formato de datos modelaremos la accioacutende cancelacioacuten con una operacioacuten HTTP PUT que ya conocemos

PUT pedidos233 HTTP11Content-Type applicationxml

ltpedido id=233gt ltlink rel=self href=httporgexpertojavapedidos233gt lttotalgt19902lttotalgt ltfechagtDecember 22 2008 0656ltfechagt ltcanceladogttrueltcanceladogt ltpedidogt

En este ejemplo modificamos la representacioacuten del Pedido con el elemento ltcanceladogtcon valor true

Este patroacuten de modelado no siempre sirve en todos los casos Por ejemplo imaginemosque queremos ampliar el sistema de forma que borremos del sistema todos los pedidoscancelados No podemos modelar esta operacioacuten de la misma manera que la de cancelacioacutenya que esta operacioacuten no cambia el estado de nuestra aplicacioacuten (no es en siacute misma un estadode la aplicacioacuten)

Para resolver este problema podemos modelar esta nueva operacioacuten como un subrecursode pedidos y realizar un borrado de los pedidos cancelados mediante el meacutetodo HTTP POSTde dicho subrecurso de la siguiente forma

POST pedidoseliminacion HTTP11

Un efecto interesante de lo que acabamos de hacer es que puesto que ahora eliminaciones una URI podemos hacer que la interfaz de nuestro servicios RESTful evolucionen conel tiempo Por ejemplo la orden GET pedidoseliminacion podriacutea devolver la uacuteltima fechaen la que se procedioacute a eliminar todos los pedidos cancelados asiacute como queacute pedidosfueron cancelados iquestY si queremos antildeadir alguacuten criterio a la hora de realizar el borradode pedidos cancelados Podriacuteamos introducir paraacutemetros para indicar que soacutelo queremoseliminar aquellos pedidos que esteacuten cancelados en una fecha anterior a una dada Comovemos podemos mantener una interfaz uniforme y centildeirnos a las operaciones HTTP tal ycomo estaacuten especificadas y a la vez dotar de una gran flexiblidad a la interfaz de nuestrosistema RESTful Hablaremos con maacutes detalle de los subrecursos en la siguiente sesioacuten

Implementacioacuten del servicio Creacioacuten del proyecto Maven

Vamos a utilizar Maven para crear la estructura del proyecto que contendraacute la implementacioacutende nuestro servicio Rest Inicialmente podemos utilizar el mismo arquetipo con el que habeacuteistrabajado en sesiones anteriores Y a continuacioacuten modificaremos la configuracioacuten del ficheropomxml para implementar nuestros servicios

Una opcioacuten es generar la estructura del proyecto directamente desde liacutenea de comandos Elcomando es el siguiente (recuerda que debes escribirlo en una misma liacutenea Los caracteres

Servicios Rest

21

que aparecen en el comando no forman parte del mismo simplemente indican que no sedebe pulsar el retorno de carro)

mvn --batch-mode archetypegenerate -DarchetypeGroupId=orgcodehausmojoarchetypes -DarchetypeArtifactId=webapp-javaee7 -DgroupId=orgexpertojava -DartifactId=ejemplo-rest

En donde

bull archetypeGroupId y archetypeArtifactId son los nombres del groupId yartifactId del arquetipo Maven que nos va a generar la plantilla para nuestro proyecto

bull groupId y artifactId son los nombres que asignamos como groupId y artifactId denuestro proyecto En este caso hemos elegido los valores orgexpertojava y ejemplo-restrespectivamente

Si utilizamos IntelliJ para crear el proyecto tenemos que

1 Crear un nuevo proyecto (New Project)

2 Elegir el tipo de proyecto Maven

3 Crear el proyecto Maven a partir de un arquetipo con las siguientes coordenadas

bull GroupId orgcodehausmojoarchetypes

bull ArtifactId webapp-javaee7

bull Version 11

4 Indicar las coordenadas de nuestro proyecto

bull GroupId orgexpertojava

bull ArtifactId ejemplo-rest

bull Version 10-SNAPSHOT

5 Confirmamos los datos introducidos

6 Para finalizar especificamos el nombre de nuestro proyecto en IntelliJ

bull Project Name ejemplo-rest (este valor tambieacuten identificaraacute el nombre del moacutedulo enIntelliJ)

7 Por comodidad marcaremos Enable autoimport para importar automaacuteticamente cualquiercambio en el proyecto

Una vez que hemos creado el proyecto con IntelliJ el paso siguiente es cambiar laconfiguracioacuten del pommxl que nos ha generado el arquetipo para incluir las propiedadesdependencias pluginshellip que necesitaremos para implementar nuestros recursos REST

Como ya sabemos el fichero pomxml contiene la configuracioacuten que utiliza Maven paraconstruir el proyecto A continuacioacuten indicamos las modificaciones en el fichero pomxmlgenerado inicialmente para adecuarlo a nuestras necesidades particulares

bull Cambiamos las propiedades del proyecto (etiqueta ltpropertiesgt ) por

Servicios Rest

22

Propiedades del proyecto

ltpropertiesgt ltprojectbuildsourceEncodinggtUTF-8ltprojectbuildsourceEncodinggtltpropertiesgt

bull Indicamos las dependencias del proyecto (etiqueta ltdependenciesgt en donde seincluyen las libreriacuteas necesarias para la construccioacuten del proyecto) En nuestro casonecesitamos incluir la libreriacutea javaxjavaee-web-api70 que contiene el apiestaacutendar de javaee 7 Marcamos el aacutembito de la libreriacutea (etiqueta ltscopegt ) comoprovided Con esto estamos indicando que soacutelo necesitaremos el jar correspondientepara compilar el proyecto y por lo tanto no incluiremos dicho jar en el fichero wargenerado para nuestra aplicacioacuten ya que dicha libreriacutea ya estaraacute disponible desde elservidor de aplicaciones en el que residiraacute nuestra aplicacioacuten

Libreriacuteas utilizadas para construir el proyecto (dependencias)

ltdependenciesgt ltdependencygt ltgroupIdgtjavaxltgroupIdgt ltartifactIdgtjavaee-web-apiltartifactIdgt ltversiongt70ltversiongt ltscopegtprovidedltscopegt ltdependencygtltdependenciesgt

bull A continuacioacuten configuramos la construccioacuten del proyecto (etiqueta ltbuildgt ) de lasiguiente forma (cambiamos la configuracioacuten original por la que mostramos a continuacioacuten)

Configuracioacuten de la construccioacuten del proyecto

ltbuildgt lt-- Especificamos el nombre del war que seraacute usado como context root cuando despleguemos la aplicacioacuten --gt ltfinalNamegt$projectartifactIdltfinalNamegt

ltpluginsgt lt-- Compilador de java Utilizaremos la versioacuten 17 --gt ltplugingt ltgroupIdgtorgapachemavenpluginsltgroupIdgt ltartifactIdgtmaven-compiler-pluginltartifactIdgt ltversiongt31ltversiongt ltconfigurationgt ltsourcegt17ltsourcegt lttargetgt17lttargetgt ltconfigurationgt ltplugingt

lt-- Servidor de aplicaciones wildfly --gt ltplugingt ltgroupIdgtorgwildflypluginsltgroupIdgt ltartifactIdgtwildfly-maven-pluginltartifactIdgt ltversiongt102Finalltversiongt ltconfigurationgt lthostnamegtlocalhostlthostnamegt

Servicios Rest

23

ltportgt9990ltportgt ltconfigurationgt ltplugingt

lt-- Cuando generamos el war no es necesario que el fichero webxml esteacute presente --gt ltplugingt ltgroupIdgtorgapachemavenpluginsltgroupIdgt ltartifactIdgtmaven-war-pluginltartifactIdgt ltversiongt23ltversiongt ltconfigurationgt ltfailOnMissingWebXmlgtfalseltfailOnMissingWebXmlgt ltconfigurationgt ltplugingt ltpluginsgtltbuildgt

Implementacioacuten del servicio Recursos JAX-RS

Una vez que tenemos la estructura del proyecto implementaremos los recursos de nuestraaplicacioacuten que seraacuten clases Java que utilizaraacuten anotaciones JAX-RS para enlazar y mapearpeticiones HTTP especiacuteficas a meacutetodos java los cuales serviraacuten dichas peticiones Eneste caso vamos a ilustrar con un ejemplo una posible implementacioacuten para el recursoCliente Tenemos que diferenciar entre las clases java que representaraacuten entidades denuestro dominio (objetos java que representan elementos de nuestro negocio y que seraacutenalmacenados tiacutepicamente en una base de datos) de nuestros recursos JAX-RS que tambieacutenseraacuten clases java anotadas y que utilizaraacuten objetos de nuestro dominio para llevar a cabo lasoperaciones expuestas en el API RESTful que hemos disentildeado

Asiacute por ejemplo implementaremos las clases

bull Clientejava representa una entidad del dominio Contiene atributos y suscorrespondientes getters y setters

bull ClienteResourcejava representa las operaciones RESTful sobre nuestro recurso Clienteque hemos definido en esta sesioacuten Es una clase java con anotaciones JAX-RS que nospermitiraacute insertar modificar borrar consultar un cliente asiacute como consultar la lista declientes de nuestro sistema

Clases de nuestro dominio (entidades) Clientejava

La clase que representa nuestra entidad del dominio Cliente es una clase java plana con suscorrespondientes atributos y getters y setters

Implementacioacuten del dominio clientejava

package orgexpertojavadomain

XmlRootElement(name=cliente)XmlAccessorType(XmlAccessTypeFIELD)public class Cliente private int id private String nombre private String apellidos private String direccion

Servicios Rest

24

private String codPostal private String ciudad

public int getId() return id public void setId(int id) thisid = id

public String getNombre() return nombre public void setNombre(String nom) thisnombre = nom

public String getApellidos() return apellidos public void setApellidos(String apellidos) thisapellidos = apellidos

public String getDireccion() return direccion public void setDireccion(String dir) thisdireccion = dir

public String getCodPostal() return codPostal public void setCodPostal(String cp) thiscodPostal = cp

public String getCiudad() return ciudad public void setCiudad(String ciudad) thisciudad = ciudad

Hemos anotado la clase Cliente con XmlRootElement y XmlAccessorType Hablaremos de estas anotaciones en sesiones posteriores las cuales se encargan delserializadodeserializado del cuerpo del mensaje (en formato xml o json) a nuestra clase javaCliente

Clases de nuestro servicio RESTful ClienteResourcejava

Una vez definido el objeto de nuestro dominio que representaraacute un objeto Cliente vamos a vercoacutemo implementar nuestros servicio JAX-RS para que diferentes usuarios de forma remotapuedan interactuar con nuestra base de datos de clientes

La implementacioacuten del servicio es lo que se denomina una resource class que no es maacutesque una clase java que utiliza anotaciones JAX-RS

Por defecto una nueva instancia de nuestra clase de recursos se crea para cada peticioacuten aese recurso Es lo que se conoce como un objeto per-request Esto implica que se crea unobjeto Java para procesar cada peticioacuten de entrada y se destruye automaacuteticamente cuandola peticioacuten se ha servido Per-request tambieacuten implica sin estado ya que no se guarda elestado del servicio entre peticiones

Comencemos con la implementacioacuten del servicio

package orgexpertojavaservices

import

Path(clientes)public class ClienteResource

private static MapltInteger Clientegt clienteDB = new ConcurrentHashMapltInteger Clientegt()

Servicios Rest

25

private static AtomicInteger idContador = new AtomicInteger()

Podemos observar que ClientResource es una clase java planay que no implementaninguna interfaz JAX-RS particular La anotacioacuten javaxwsrsPath indica que laclase ClienteResource es un servicio JAX-RS Todas las clases que queramos que seanreconocidas como servicios JAX-RS tienen que tener esta anotacioacuten Fiacutejate que estaanotacioacuten tiene el valor clientes Este valor representa la raiacutez relativa de la URI de nuestroservicio RESTful Si la URI absoluta de nuestro servidor es por ejemplo httpexpertojavaorglos meacutetodos expuestos por nuestra clase ClienteResource estariacutean disponibles bajo la URIhttpexpertojavaorgclientes

En nuestra clase definimos un Mapa para el campo ClienteDB quealmacenaraacute en memoria a los objetos Cliente de nuestro sistema Utilizamos unjavautilconcurrentConcurrentHashMap como tipo de clienteDB ya que nuestro recursoseraacute accedido concurrentemente por los usuarios de nuestro servicio rest El campoidContador lo utilizaremos para generar nuevos identificadores de nuestros objetos Clientecreados El tipo de este campo es javautilconcurrentatomicAtomicInteger para garantizarque siempre generaremos un identificador uacutenico aunque tengamos peticiones concurrentes

Justificacioacuten del caracter static de los atributosComo nuestros objetos seraacuten de tipo per-request el runtime de JAX-RScrearaacute una instancia de ClienteResource para cada pecioacuten que se realicesobre nuestro servicio La maacutequina virtual de java ejecutaraacute cada peticioacutena nuestro servicio en un hilo (thread) diferente permitiendo asiacute el accesoconcurrente a nuestro recurso Puesto que hemos decidido almacenaren memoria la informacioacuten de los clientes necesitamos que los atributosclienteDB y idContador sean static para que todas las instancias deClienteResource tengan acceso a la lista de clientes en memoria y nohaya problemas de concurrencia En realidad lo que estamos haciendocon eacutesto es permitir que el servicio guarde el estado entre peticiones Enun sistema real ClienteResource probablemente interactuacutee con una basede datos para almacenar y recuperar la informacioacuten de los clientes y porlo tanto no necesitaremos guardar el estado entre peticiones

Una mejor solucioacuten seriacutea no utilizar variables estaacuteticas y definir nuestroservicio como singleton Si hacemos eacutesto solamente se creariacutea unainstancia de clienteResource y estariacuteamos manteniendo el estado delas peticiones En la siguiente sesioacuten explicaremos coacutemo configurar unservicio como singleton Por simplicidad de momento optaremos por laopcioacuten de que los objetos RESTful sean per-request

Creacioacuten de clientes

Para implementar la creacioacuten de un nuevo cliente utilizamos el mismo modelo que hemosdisentildeado previamente Una peticioacuten HTTP POST enviacutea un documento XML que representaal cliente que queremos crear

El coacutedigo para crear nuevos clientes en nuestro sistema podriacutea ser eacuteste

POST

Consumes(applicationxml)

public Response crearCliente(Cliente cli)

Servicios Rest

26

el paraacutemetro cli se instancia con los datos del cliente del body del mensaje HTTP idContador++ clisetId(idContadorincrementAndGet())

clienteDBput(cligetId() cli)

Systemoutprintln(Cliente creado + cligetId()) return Responsecreated(URIcreate(clientes

+ cligetId()))build()

se recibe una peticioacuten POSTel cuerpo de la peticioacuten debe tener formato xmlcontiene la informacioacuten del documento xml del cuerpo de la peticioacuten de entradase antildeade el nuevo objeto Cliente a nuestro mapa de clientes (clienteDB)este meacutetodo se ejectua en el servidor por lo que el mensaje soacutelo seraacute visible por ejemplosi consultamos los mensajes generados por el servidor durante la ejecucioacuten el meacutetododevuelve un coacutedigo de respuesta 201 Created junto con una cabecera Locationapuntando a la URI absoluta del cliente que acabamos de crear

Vamos a explicar la implementacioacuten con maacutes detalle

Para enlazar peticiones HTTP POST con el meacutetodo crearCliente() lo anotamos conla anotacioacuten javaxwsrsPOST La anotacioacuten Path combinada con la anotacioacutenPOST enlaza todas las peticiones POST dirigidas a la URI relativa clientes al meacutetodo JavacrearCliente()

La anotacioacuten javaxwsrsConsumes aplicada a crearCliente() especifica queacute media typeespera el meacutetodo en el cuerpo del mensaje HTTP de entrada Si el cliente incluye en su peticioacutenPOST un media type diferente de XML se enviacutea un coacutedigo de error al cliente

El meacutetodo crearCliente() tiene un paraacutemetro de tipo Cliente En JAX-RS cualquier paraacutemetrono anotado con anotaciones JAX-RS se considera que es una representacioacuten del cuerpodel mensaje de la peticioacuten de entrada HTTP Las anotaciones que hemos introducido en laclase Cliente de nuestro dominio realizan el trabajo de convertir el documento xml contenidoen el cuerpo de la peticioacuten htpp de entrada en una instancia de nuestra clase Cliente

Solamente UNO de los paraacutemetros del meacutetodo Java puede representar elcuerpo del mensaje de la peticioacuten HTTP Esto significa que el resto deparaacutemetros deben anotarse con alguna anotacioacuten JAX-RS que veremosmaacutes adelante

El meacutetodo crearCliente() devuelve una respuesta de tipo javaxwsrscoreResponse El meacutetodo estaacutetico Responsecreated() crea un objeto Response que contiene un coacutedigode estado 201 Created Tambieacuten antildeade una cabecera Location con un valor similar ahttpexpertojavaorgclientes123 dependiendo del valor del valor de base de la raiacutez dela URI del servidor y el identificador generado para el objeto Cliente (en este caso se habriacuteagenerado el identificador 123) Maacutes adelante explicaremos con detalle el uso de la claseResponse

Consulta de clientes

A continuacioacuten mostramos un posible coacutedigo para consultar la informacioacuten de un cliente

GET

Servicios Rest

27

Path(id)Produces(applicationxml)public Cliente recuperarClienteId(PathParam(id) int id) final Cliente cli = clienteDBget(id) if (cli == null) throw new WebApplicationException(ResponseStatusNOT_FOUND) return new Cliente(cligetId() cligetNombre() cligetApellidos() cligetDireccion() cligetCodPostal() cligetCiudad())

En este caso anotamos el meacutetodo recuperarClienteId() con la anotacioacutenjavaxwsrsGET para enlazar las operaciones HTTP GET con este meacutetodo Java

Tambieacuten anotamos recuperarClienteId() con la anotacioacuten javaxwsrsPRODUCES Estaanotacioacuten indica a JAX-RS que valor tiene la cabecera HTTP Content-Type en la respuestaproporcionada por la operacioacuten GET En este caso estamos indicando que seraacute de tipoapplicationxml

En la implementacioacuten del meacutetodo utilizamos el paraacutemetro id para consultar si existe unobjeto Cliente en nuestro mapa clienteDB Si dicho cliente no existe lanzaremos la excepcioacutenjavaxwsrsWebApplictionException Esta excepcioacuten provocaraacute que el coacutedigo derespuesta HTTP sea 404 Not Found y significa que el recurso cliente requerido no existeDiscutiremos maacutes adelante el tema del manejo de excepciones

Modificacioacuten de clientes

Vamos a mostrar coacutemo seriacutea el coacutedigo para modificar un cliente

PUTPath(id)Consumes(applicationxml)public void modificarCliente(PathParam(id) int id Cliente nuevoCli)

Cliente actual = clienteDBget(id) if (actual == null) throw new WebApplicationException(ResponseStatusNOT_FOUND)

actualsetNombre(nuevoCligetNombre()) actualsetApellidos(nuevoCligetApellidos()) actualsetDireccion(nuevoCligetDireccion()) actualsetCodPostal(nuevoCligetCodPostal()) actualsetCiudad(nuevoCligetCiudad())

Anotamos el meacutetodo modificarCliente() con javaxwsrsPUT para enlazar laspeticiones HTTP PUT a este meacutetodo Al igual que hemos hecho con recuperarClienteId() elmeacutetodo modificarCliente() estaacute anotado adicionalmente con Path de forma que podamosatender peticiones a traveacutes de las URIs clientesid

El meacutetodo modificarCliente() tiene dos paraacutemetros El primero es un paraacutemetro id querepresenta el objeto Cliente que estamos modificando Al igual que ocurriacutea con el meacutetodorecuperarClienteId() utilizamos la anotacioacuten PathParam para extraer el identificador a

Servicios Rest

28

partir de la URI de la peticioacuten de entrada El segundo paraacutemetro es un objeto Cliente querepresenta el cuerpo del mensaje de entrada ya que no tiene ninguna anotacioacuten JAX-RS

El meacutetodo intenta encontrar un objeto Cliente en nuestro mapa clienteDB Si no existeprovocamos una WebApplicationException que enviaraacute una respuesta al usuario con elcoacutedigo 404 Not Found Si el objeto Cliente existe modificamos nuestro objeto Clienteexistente con los nuevos valores que obtenemos de la peticioacuten de entrada

Construccioacuten y despliegue del servicio

Una vez implementado nuestro servicio RESTful necesitamos poner en marcha el procesode construccioacuten El proceso de construccioacuten compilaraacutehellip empaquetaraacute hellip y finalmente nospermitiraacute desplegar nuestro servicio en el servidor de aplicaciones

Para poder empaquetar nuestro servicio RESTful como un war que se desplegaraacute en elservidor de aplicaciones vamos a incluir un proveedor de servicios JAX-RS en el descriptorde despliegue de nuestra aplicacioacuten (fichero webxml) En la siguiente sesioacuten justificaremos laexistencia de dicho proveedor (que seraacute un servlet) y explicaremos el modelo de desplieguede los servicios JAX-RS Los pasos a seguir desde IntelliJ para configurar el despliegue denuestro servicio son

bull Antildeadimos el directorio WEB-INF como subdirectorio de webapp

bull Nos vamos a FileProject StructurehellipFacetsWeb y antildeadimos el fichero webxml (enel panel Deployment descriptors pulsamos sobre + y seleccionamos webxml) Editamoseste fichero para antildeadir el servlet que serviraacute las peticiones de nuestros servicios RESTindicando cuaacutel seraacute la ruta en la que estaraacuten disponibles dichos servicios (en nuestroejemplo indicaremos la ruta rest) Dicha ruta es relativa a la ruta del contexto de nuestraaplicacioacuten y que por defecto es el nombre del artefacto war desplegado que hemosindicado en la etiqueta ltfinalNamegt dentro del ltbuildgt del fichero de configuracioacuten deMaven (pomxml)

Contenido del fichero webxml

ltweb-app version=30 xmlns=httpjavasuncomxmlnsjavaee xmlnsxsi=httpwwww3org2001XMLSchema-instance xsischemaLocation=httpjavasuncomxmlnsjavaee httpjavasuncomxmlnsjavaeeweb-app_3_0xsdgt lt-- One of the way of activating REST Services is adding these lines the server is responsible for adding the corresponding servlet automatically if the src folder has the Annotations to receive REST invocation--gt ltservlet-mappinggt ltservlet-namegtjavaxwsrscoreApplicationltservlet-namegt lturl-patterngtrestlturl-patterngt ltservlet-mappinggtltweb-appgt

A continuacioacuten ya estamos en disposicioacuten de iniciar la construccioacuten del proyecto con Mavenpara compilar empaquetar y desplegar nuestro servicio en Wildfly

Si utilizamos el terminal la secuencia de pasos para empaquetar y desplegar nuestroproyecto seriacutean

cd ejemplo-rest

Servicios Rest

29

mvn package

usrlocalwildfly-821Finalbinstandalonesh

mvn wildflydeploy

Nos situamos en el directorio que contiene el pomxml de nuestro proyectoEmpaquetamos el proyecto (obtendremos el war)Arrancamos el servidor wildflyDesplegamos el war generado en el servidor wildfly

Secuencia correcta de acciones

En ejecuciones posteriores despueacutes de realizar modificaciones en nuestro coacutedigo esrecomendable ejecutar mvn clean previamente al empaquetado del proyecto

Por lo tanto y suponiendo que el servidor de aplicaciones ya estaacute en marcha lasecuencia de acciones (comandos maven) que deberiacuteamos realizar para asegurarnosde que vamos a ejecutar exactamente la aplicacioacuten con los uacuteltimos cambios quehayamos introducido son

bull mvn wildflyundeploy

bull mvn clean

bull mvn package

bull mvn wildflydeploy

Tambieacuten podriacuteamos realizar todas estas acciones con un uacutenico comando maven

bull mvn wildflyundeploy clean package wildflydeploy

Si utilizamos IntelliJ antildeadiremos un nuevo elemento de configuracioacuten de ejecucioacuten desdeRunEdit Configurations Pulsamos el icono + y antildeadimos la configuracioacuten de tipo JBosssServerLocal Podemos ponerle por ejemplo como nombre Wilfdly start A continuacioacutenconfiguramos la ruta del servidor wildfly como usrlocalwildfly-821Final

Cuando lancemos este elemento de ejecucioacuten desde IntelliJ automaacuteticamente seconstruiraacute el proyecto (obtendremos el war) y arrancaremos wildfly Para desplegarel war utlizaremos la ventana Maven Projects y haremos doble click sobre ejemplo-restPluginswildflywildflydeploy

Probando nuestro servicio

Podemos probar nuestro servicio de varias formas Vamos a mostrar como hacerlodirectamente desde liacutenea de comandos utilizando IntelliJ o bien utilizando la herramientaPostman (que teneacuteis disponible desde el navegador Chrome)

Invocacioacuten del servicio desde liacutenea de comandosUtilizaremos la herramienta curl Por ejemplo para realizar una insercioacuten de un cliente elcomando seriacutea

Servicios Rest

30

curl -i -H Accept applicationxml -H Content-Type applicationxml -X POST -d clientexml httplocalhost8080ejemplo-restrestclientes

En donde

-iTambieacuten se puede utilizar la opcioacuten equivalente --include Indica que se debe incluirlas cabeceras HTTP en la respuesta recibida Recuerda que la peticioacuten POST devuelveen la cabecera Location el enlace del nuevo recurso creado (puede hacerlo en unacabedera Location o como un campo ltlinkgt del elemento creado en el cuerpo delmensaje lo veremos maacutes adelante) Esta informacioacuten seraacute necesaria para poder consultarla informacioacuten del nuevo cliente creado

-HIndica un par cabecera_valor_ En nuestro caso lo utilizamos para especificar los valoresde las cabeceras HTTP Accept y Content-Type

-XIndica el meacutetodo a invocar (GET POST PUThellip)

-dTambieacuten se puede utilizar --data Indica cuaacuteles son los datos enviados en el mensajede entrada en una peticioacuten POST Si los datos especificados van precedidos por estamos indicando que dichos datos estaacuten en un fichero Por ejemplo en la orden anteriorescribimos en el fichero clientexml los datos del cliente que queremos antildeadir en nuestrosistema

El contenido del fichero clientexml podriacutea ser eacuteste

ltxml version=10 encoding=UTF-8gtltclientesgt ltclientegt ltnombregtPepe ltnombregt ltapellidosgtGarcia Lopezltapellido1gt ltdirecciongtCalle del pino 3ltapellido2gt ltcodPostalgt0001ltcodPostalgt ltciudadgtAlicanteltciudadgt ltclientegtltclientesgt

Finalmente en la orden indicamos la URI a la que queremos acceder en este caso

httplocalhost8080ejemplo-restrestclientes

Una vez insertado el cliente podemos recuperar el cliente utilizando el enlace que se incluyeen la cabecera de respuesta Location

curl -i -H Accept applicationxml -H Content-Type applicationxml -X GET httplocalhost8080ejemplo-restrestclientes1

Servicios Rest

31

Invocacioacuten del servicio desde IntelliJIntelliJ nos proporciona una herramienta para probar servicios REST desde ToolsTestRESTful Web Service Desde esta nueva ventana podremos invocar al servicio RESTindicando el tipo de peticioacuten HTTP asiacute como las cabeceras y cuerpo de la peticioacuten

La siguiente figura muestra la elaboracioacuten de una peticioacuten POST a nuestro servicio REST

A continuacioacuten mostramos la ejecucioacuten de una peticioacuten GET

Cuando realizamos una peticioacuten POST debemos indicar el contenido del cuerpo del mensajeEn la siguiente figura observamos que tenemos varias opciones disponibles como por ejemploteclear directamente dicho contenido (opcioacuten Text) o bien subir dicha informacioacuten desdeun fichero en nuestro disco duro (opcioacuten File Contents) Podemos ver que hemos elegido estauacuteltima opcioacuten para probar nuestro servicio

Invocacioacuten del servicio desde PostmanOtra alternativa sencilla para probar nuestro servicio REST es la herramienta postmanque podemos lanzar desde el navegador en nuestro caso Chrome

Accederemos a la aplicacioacuten desde la barra de marcadores seleccionando Aplicaciones yy a continuacioacuten pulsaremos sobre el icono Postman

El aspecto de la herramienta es el que mostramos a continuacioacuten

Servicios Rest

32

Postman a diferencia de las alternativas anteriores nos permitiraacute guardar un historial depeticiones de forma que podamos repetir la ejecucioacuten de nuestros tests exactamente de lamisma forma aunque no de forma automaacutetica sino que tenemos que lanzar manualmentecada test que queramos volver a ejecutar

Tambieacuten podemos crear colecciones que no son maacutes que carpetas que contienen unconjunto de peticiones de nuestro historial Por ejemplo podemos crear la coleccioacuten s1-rest-ejercicio1 en donde guardaremos todas las peticiones que hayamos hecho sobre el ejercicio1 de la primera sesioacuten de rest

Podeacuteis crearos una cuenta gratuita para almacener y gestionar vuestras peticiones rest Unavez que tengaacuteis creadas varias colecciones Postman nos permite guardarlas en nuestrodisco duro en formato json

Servicios Rest

33

15 Ejercicios

Antes de empezar a crear los proyectos debes descargarte el repositorio git java_uaejercicios-rest-expertojava en el que vas a implementar los ejercicios relativos a laasignatura de Servicios REST El proceso es el mismo que el seguido en sesiones anteriores

1 Accedemos al repositorio y realizamos un Fork en nuestra cuenta personal (asiacute podremostener una copia con permisos de escritura)

2 Realizamos un Clone en nuestra maacutequina

$ git clone httpsbitbucketorgltalumnogtejercicios-rest-expertojava

De esta forma se crea en nuestro ordenador el directorio ejercicios-rest-expertojavay se descarga en eacutel un proyecto IntelliJ en el que iremos antildeadiendo MOacuteDULOS para cada unode los ejercicios Contiene tambieacuten el fichero gitignore asiacute como diferentes moacutedulos conlas plantillas que vayamos a necesitar para realizar los ejercicios

A partir de este momento se puede trabajar con dicho proyecto y realizar Commit y Pushcuando sea oportuno

$ cd ejercicios-rest-expertojava$ git add $ git commit -a -m Mensaje de commit$ git push origin master

Los MOacuteDULOS IntelliJ que iremos antildeadiendo tendraacuten todos el prefijo sx- siendo x elnuacutemero de la sesioacuten correspondiente (por ejemplo s1-ejercicio s2-otroEjerciciohellip)

Servicio REST ejemplo (0 puntos)

Para familiarizarnos con las peticiones http POST PUT GET DELETE se proporciona elMOacuteDULO s1-ejemplo-rest con la implementacioacuten de un servicio rest que podeacuteis probar biendesde liacutenea de comandos con la utilidad curl desde el navegador con la herramienta postman o bien desde IntelliJ con el cliente REST incluido en el IDE

En el directorio srcmainresources de dicho moacutedulo teneacuteis un fichero de texto(instruccionestxt) con las instrucciones para construir desplegar y probar la aplicacioacuten deejemplo

Servicio REST saludo (1 punto)

Vamos a implementar un primer servicio RESTful muy sencillo Para ello seguiremos lassiguientes indicaciones

bull Creamos un MOacuteDULO Maven con IntelliJ (desde el directorio ejercicios-rest-expertojava ) con el arquetipo webapp-javaee7 tal y como hemos visto en losapuntes de la sesioacuten Las coordenadas del artefacto Maven seraacuten

GroupId orgexpertojava

ArtifactId s1-saludo-rest

Servicios Rest

34

version 10-SNAPSHOT

bull Configuramos el pommxl del proyecto para poder compilar empaquetar y desplegarnuestro servicio Consulta los apuntes para ver cuaacutel debe ser el contenido de las etiquetasltpropertiesgt ltdependenciesgt y ltbuildgt

bull Creamos la carpeta WEB-INF y antildeadimos el fichero de configuracioacuten webxml tal y comohemos visto en los apuntes (esto seraacute necesario para configurar el despliegue) En estecaso queremos mapear los servicios REST contenidos en el paquete orgexpertojavaal directorio recursos dentro de nuestro contexto (recuerda que el contexto de nuestraaplicacioacuten web vendraacute dado por el valor de la etiqueta ltfinalNamegt anidada dentro deltbuildgt)

bull Creamos un recurso de nombre HolaMundoResource que se mapee a la direccioacuten holamundo Implementar un meacutetodo de forma que al acceder a eacutel por GET nos devuelvaen texto plano (textplain) el mensaje Hola mundo Una vez desplegada la aplicacioacutenen el servidor WildFly prueba el servicio mediante la utilidad Postman desde ChromeComprobar que la invocacioacuten

GET httplocalhost8080saludo-restholamundo

Devuelve como cuerpo del mensaje Hola mundo

bull Vamos a antildeadir un segmento variable a la ruta Implementa un meacutetodo GET nuevode forma que si accedemos a holamundonombre antildeade el nombre indicado al saludo(separado por un espacio en blanco y seguido por )

Una vez desplegada la aplicacioacuten en el servidor WildFly prueba el servicio mediante la utilidadTest RESTFul Web Service de IntelliJ o con Postman Comprobar que la invocacioacuten

GET httplocalhost8080saludo-restholamundopepe

Devuelve como cuerpo del mensaje Hola mundo pepe

bull Hacer que se pueda cambiar el saludo mediante un meacutetodo PUT El nuevo saludo llegaraacutetambieacuten como texto plano en el cuerpo de la peticioacuten y posteriores invocaciones a losmeacutetodos GET utilizaraacuten el nuevo saludo Almacenaremos el nuevo saludo en una variableestaacutetica de nuestro recurso iquestQueacute pasa si no lo es (lo hemos explicado en los apuntespuedes hacer la prueba para ver queacute ocurre si la variable no es estaacutetica)

Una vez desplegada la aplicacioacuten en el servidor WildFly prueba el servicio con Postmano bien mediante la utilidad Test RESTFul Web Service de IntelliJ Realizar las siguientesinvocaciones (en este orden)

PUT httplocalhost8080saludo-restholamundoy en el cuerpo del mensaje Buenos diacuteas

GET httplocalhost8080saludo-restholamundoGET httplocalhost8080saludo-restholamundopepe

Servicios Rest

35

La segunda invocacioacuten debe devolver como cuerpo del mensaje Buenos dias Al ejecutarla tercera invocacioacuten el cuerpo del mensaje de respuesta deberiacutea ser Buenos diasPepe

Servicio REST foro (1 punto)

Vamos a implementar un servicio RESTful que contemple las cuatro operaciones baacutesicas(GET PUT POST y DELETE) Se trata de un foro con en el que los usuarios pueden intervenirde forma anoacutenima en diferentes conversaciones

Primero debes crear un nuevo moacutedulo Maven configurar el pomxml asiacute como el ficherowebxml de la misma forma que hemos hecho en el ejercicio anterior pero para esteejercicio

bull Las coordenadas del moacutedulo Maven seraacuten

GroupId orgexpertojava

ArtifactId s1-foro-rest

version 10-SNAPSHOT

bull Nuestros servicios REST estaraacuten disponibles en la URI httplocalhost8080s1-foro-rest

El foro estaraacute formado por diferentes mensajes Por lo tanto el modelo del dominio de nuestraaplicacioacuten estaraacute formado por la clase Mensaje que contendraacute un identificador y una cadenade caracteres que representaraacute el contenido del mensaje (recuerda que debes implementarlos correspondientes getters y setters)

Por simplicidad vamos a almacenar los mensajes de nuestro foro en memoria Estos estaraacutendisponibles desde la clase DatosEnMemoria que contendraacute la variable estaacutetica

static MapltInteger Mensajegt datos = new HashMapltInteger Mensajegt()

Los servicios que proporcionaraacute el foro estaraacuten implementados en la claseMensajeResource Se accederaacute a ellos traveacutes de la ruta relativa a la raiacutez de nuestrosservicios mensajes Concretamente podremos realizar las siguientes operaciones

bull Antildeadir un nuevo mensaje al foro con la URI relativa a la raiacutez de nuestros servicios mensajes El texto del mensaje estaraacute en el cuerpo de la peticioacuten y el tipo MIME asociadoseraacute textplain (contenido de la cabecera Content-type de la peticioacuten HTTP)Nuestra respuesta debe incluir en la cabecera Location de la respuesta HTTP la URIdel nuevo recurso creado Utiliza para ello la clase Response tal y como hemos mostradoen el coacutedigo de ejemplo proporcionado para el ejercicio anterior Hablaremos con detallesobre esta clase en sesiones posteriores

Recuerda que para acceder al cuerpo de la peticioacuten basta con definir unparaacutemetro de tipo String JAX-RS automaacuteticamente lo instanciaraacute a partirdel cuerpo de la peticioacuten y lo convertiraacute en un objeto de tipo String

bull Modificar un mensaje determinado con un identificador con valor id a traveacutes de la URIrelativa a la raiacutez de nuestros servicios mensajesid ( id debe ser por tanto unsegmento de ruta variable) Si no existe ninguacuten mensaje con el identificador id se lanzaraacutela excepcioacuten WebApplicationException(ResponseStatusNOT_FOUND)

Servicios Rest

36

bull Borrar un mensaje determinado con un identificador con valor id atraveacutes de la URI relativa a la raiacutez de nuestros servicios mensajesid Igual que en el caso anterior si el identificador proporcionado no secorresponde con el de ninguacuten mensaje del foro se lanzaraacute la excepcioacutenWebApplicationException(ResponseStatusNOT_FOUND)

bull Consultar todos los mensajes del foro (la URI relativa seraacute mensajes ) El resultado semostraraacute en tantas liacuteneas como mensajes Cada mensaje iraacute precedido de su identificadorTambieacuten se informaraacute del nuacutemero total de mensajes en el foro (La respuesta seraacute unacadena de caracteres Al final del ejercicio mostramos un ejemplo de mensaje de respuestapara esta operacioacuten)

bull Consultar un mensaje determinado con un identificador con valor id a traveacutes dela URI relativa a la raiacutez de nuestros servicios mensajesid Si el identificadorproporcionado no se corresponde con el de ninguacuten mensaje del foro se lanzaraacute laexcepcioacuten WebApplicationException(ResponseStatusNOT_FOUND)

Prueba el servicio utilizando Postman o el cliente de IntelliJ para servicios REST con lassiguientes entradas

bull Crea los mensajes Mensaje numero 1 Mensaje numero 2 Mensaje numero 3 en esteorden

bull Consulta los mensajes del foro El resultado debe ser

1 Mensaje numero 1

2 Mensaje numero 2

3 Mensaje numero 3

Numero total de mensajes = 3

bull Cambia el mensaje con identificador 2 por Nuevo mensaje numero 2

bull Consulta los mensajes del foro El resultado debe ser

1 Mensaje numero 1

2 Nuevo Mensaje numero 2

3 Mensaje numero 3

Numero total de mensajes = 3

bull Borra el mensaje con identificador 3

bull Consulta el mensaje con el identificador 3 Se debe obtener una respuesta 404 NotFound

bull Consulta los mensajes del foro El resultado debe ser

1 Mensaje numero 1

2 Nuevo Mensaje numero 2

Numero total de mensajes = 2

bull Antildeade el mensaje Mensaje final Vuelve a consultar los mensajes el resultado debe ser

1 Mensaje numero 1

Servicios Rest

37

2 Nuevo Mensaje numero 2

4 Mensaje final

Numero total de mensajes = 3

Para evitar problemas con el id generado si hemos borrado mensajeslo maacutes sencillo es que el identificador vaya incrementaacutendose siemprecon cada nuevo mensaje Esto puede hacer que queden huecos en lanumeracioacuten como en el ejemplo anterior

Servicios Rest

38

2 Anotaciones baacutesicas JAX-RS El modelo de despliegue

Ya hemos visto como crear un servicio REST baacutesico Ahora se trata de analizar con maacutesdetalle aspectos fundamentales sobre la implementacioacuten de los servicios Comenzaremos pordetallar los usos de la anotacioacuten Path que es la que nos permite etiquetar una clase Javacomo un recurso REST sobre el que podremos realizar las operaciones que hemos identificadoen la sesioacuten anterior Tambieacuten hablaremos algo maacutes sobre las anotaciones Produces yConsumes que ya hemos utilizado para implementar nuestro primer servicio

En segundo lugar hablaremos sobre la extraccioacuten de informacioacuten de las peticiones HTTP ycoacutemo podemos inyectar esa informacioacuten en nuestro coacutedigo java Esto nos permitiraacute servir laspeticiones sin tener que escribir demasiado coacutedigo adicional

Finalmente explicaremos maacutes detenidamente coacutemo configurar el despliegue de nuestraaplicacioacuten REST de forma que sea portable

21 iquestCoacutemo funciona el enlazado de meacutetodos HTTP

JAX-RS define cinco anotaciones que se corresponden con operaciones HTTP especiacuteficas

bull javaxwsrsGET

bull javaxwsrsPUT

bull javaxwsrsPOST

bull javaxwsrsDELETE

bull javaxwsrsHEAD

En la sesioacuten anterior ya hemos utilizado estas anotaciones para hacer corresponder (enlazar)peticiones HTTP GET con un meacutetodo Java concreto

Por ejemplo

Path(clientes)public class ServicioCliente

GET Produces(applicationxml) public String getTodosLosClientes()

En este coacutedigo la anotacioacuten GET indica al runtime JAX-RS que el meacutetodo javagetTodosLosClientes() atiende peticiones HTTP GET dirigidas a la URI clientes

Soacutelamente se puede utilizar una de las anotaciones anteriores para unmismo meacutetodo Si se aplica maacutes de uno se produce un error durante eldespliegue de la aplicacioacuten

Es interesante conocer que cada una de estas anotaciones a su vez estaacute anotada con otrasanotaciones (podriacuteamos llamarlas meta anotaciones) Por ejemplo la implementacioacuten de laanotacioacuten GET tiene este aspecto

package javaxwsrsimport

Servicios Rest

39

Target(ElementTypeMETHOD)Retention(RetentionPolicyRUNTIME)HttpMethod(HttpMethodGET)public interface GET

GET en siacute mismo no tiene ninguacuten significado especial para el proveedor JAX-RS (runtimede JAX-RS) Lo que hace que la anotacioacuten GET sea significativo para el runtime de JAX-RSes el valor de la meta anotacioacuten javaxwsrsHttpMethod (en este caso HttpMethodGET )Este valor es el que realmente decide que un determinado meacutetodo Java se enlace con undeterminado meacutetodo HTTP

iquestCuaacuteles son las implicaciones de eacutesto Pues que podemos crear nuevas anotaciones quepodemos enlazar a otros meacutetodos HTTP que no sean GET POST PUT DELETE o HEAD Deesta forma podriacuteamos permitir que diferentes tipos de clientes que hacen uso de la operacioacutenHTTP LOCK puedan ser atendidos por nuestro servicio REST (como por ejemplo un clienteWebDAV )

22 La anotacioacuten Path

La anotacioacuten Path identifica la plantilla de path para la URI del recurso al que se accede yse puede especificar a nivel de clase o a nivel de meacutetodo de dicho recurso

El valor de una anotacioacuten Path es una expresioacuten que denota una URI relativa a la URIbase del servidor en el que se despliega el recurso a la raiz del contexto de la aplicacioacuten yal patroacuten URL al que responde el runtime de JAX-RS

Un segmento de la URI es cada una de las subcadenas delimitadas por que aparecenen dicha URI Por ejemplo la URI httpejemploclientescomclientesviprecientes contiene 4segmentos de ruta ejemploclientescom clientes vip y recientes

La anotacioacuten Path no es necesario que contenga una ruta que empieceo termine con el caraacutecter El runtime de JAX-RS analiza igualmente laexpresioacuten indicada como valor de Path

Para que una clase Java sea identificada como una clase que puede atender peticiones HTTPeacutesta tiene que estar anotada con al menos la expresioacuten Path() Este tipo de clasesse denominan recursos JAX-RS raiacutez

Para recibir una peticioacuten un meacutetodo Java debe tener al menos una anotacioacuten de meacutetodoHTTP como por ejemplo javaxwsrsGET Este meacutetodo no requiere tener ninguna anotacioacutenPath adicional Por ejemplo

Path(pedidos)public class PedidoResource GET public String getTodosLosPedidos()

Una peticioacuten HTTP GET pedidos se delegaraacute en el meacutetodo getTodosLosPedidos()

Servicios Rest

40

Podemos aplicar tambieacuten Path a un meacutetodo Java Si hacemos esto la expresioacuten de laanotacioacuten Path de la clase se concatenaraacute con la expresioacuten de la anotacioacuten Path delmeacutetodo Por ejemplo

Path(pedidos)public class PedidoResource

GET Path(noPagados) public String getPedidosNoPagados()

De esta forma una peticioacuten GET pedidosnoPagados se delegaraacute en el meacutetodogetPedidosNoPagados()

Podemos tener anotaciones Path para cada meacutetodo que seraacuten relativos a la ruta indicadaen la anotacioacuten Path de la definicioacuten de la clase Por ejemplo la siguiente clase de recursosirve peticiones a la URI pedidos

Path(pedidos)public class PedidoResource

GET public String getPedidos()

Si quisieacuteramos proporcionar el servicio en la URI pedidosincidencias por ejemplono necesitamos una nueva definicioacuten de clase y podriacuteamos anotar un nuevo meacutetodogetIncidenciasPedidos() de la siguiente forma

Path(pedidos)public class PedidoResource

GET public String getPedidos()

GET Path(incidencias) public String getIncidenciasPedidos()

Ahora tenemos una clase de recurso que gestiona peticiones para pedidos y para pedidosincidencias

Expresiones Path

El valor de una anotacioacuten Path puede ser una cadena de caracteres o tambieacutenpuede contener expresiones maacutes complejas si es necesario nos referiremos a ellas comoexpresiones Path

Servicios Rest

41

Una expresioacuten Path puede incluir variables que se indican entre llaves que seraacuten sustituidasen tiempo de ejecucioacuten dependiendo del valor que se indique en la llamada al recurso Asiacutepor ejemplo si tenemos la siguiente anotacioacuten

GETPath(clientesid)

y el usuario realiza la llamada

GET httporgexpertojavacontextorestclientesPedro

la peticioacuten se delegaraacute en el meacutetodo que esteacute anotado con las anotaciones anteriores y el valorde id seraacute instanciado en tiempo de ejecucioacuten a Pedro

Para obtener el valor del nombre del cliente utilizaremos la anotacioacuten PathParam en losparaacutemetros del meacutetodo de la siguiente forma

GETPath(clientesnombre)public String getClientePorNombre(PathParam(nombre) String nombre)

Una expresioacuten Path puede tener maacutes de una variable cada una figuraraacute entre llaves Porejemplo si utilizamos la siguiente expresioacuten Path

Path(nombre1nombre2)public class MiResource

podremos atender peticiones dirigidas a URIs que respondan a la plantilla

httporgexpertojavacontextorecursosnombre1nombre2

como por ejemplo

httporgexpertojavacontextorecursosPedroLopez

Las expresiones Path pueden incluir maacutes de una variable para referenciar un segmento deruta Por ejemplo

Path()public class ClienteResource GET Path(clientesapellido1-apellido2) public String getCliente(PathParam(apellido1) String ape1 PathParam(apellido2) String ape2)

Servicios Rest

42

Una peticioacuten del tipo

GET httporgexpertojavacontextoclientesPedro-Lopez

seraacute procesada por el meacutetodo getCliente()

Expresiones regulares

Las anotaciones Path pueden contener expresiones regulares (asociadas a las variables)Por ejemplo si nuestro meacutetodo getClienteId() tiene un paraacutemetro de tipo entero podemosrestringir las peticiones para tratar solamente aquellas URIs que contengan diacutegitos en elsegmento de ruta que nos interese

Path(clientes)public class ClienteResource GET Path(id d+) solo soporta diacutegitos public String getClienteId(PathParam(id) int id)

Si la URI de la peticioacuten de entrada no satisface ninguna expresioacuten regular de ninguno de losmetodos del recurso entonces se devolveraacute el coacutedigo de error 404 Not Found

El formato para especificar expresiones regulares para las variables del path es

nombre-variable [ expresion-regular ]

El uso de expresiones regulares es opcional Si no se proporciona una expresioacuten regularpor defecto se admite cualquier caraacutecter En teacuterminos de una expresioacuten regular la expresioacutenregular por defecto seriacutea

[^]+

Por ejemplo si queremos aceptar solamente nombres que comiencen por una letra y acontinuacioacuten puedan contener una letra o un diacutegito lo expresariacuteamos como

Path(clientes)public class ClienteResource GET Path(nombre [a-zA-Z][a-zA-Z_0-9]) public String getClienteNombre(PathParam(nombre) string nom)

Servicios Rest

43

De esta forma la URI clientesaaa no seriacutea vaacutelida la URI clientesa9 activariacutea elmeacutetodo getClienteNombre() y la URI clientes89 activariacutea el meacutetodo getClienteId()

Las expresiones regulares no se limitan a un soacutelo segmento de la URI Por ejemplo

Path(clientes)public class ClienteResource GET Path(id +) public String getCliente(PathParam(id) String id)

GET Path(id +direccion) public String getDireccion(PathParam(id) String id)

La expresioacuten regular + indica que estaacuten permitidos cualquier nuacutemero de caracteres Asiacutepor ejemplo la peticioacuten GET clientespedrolopez podriacutea delegarse en el meacutetodogetClientes()

El meacutetodo getDireccion() tiene asociada una expresioacuten maacutes especiacutefica la cual puedemapearse con cualquier cadena de caracteres que termine con direccion Seguacuten eacutestola peticioacuten GET clientespedrolopezdireccion podriacutea delegarse en el meacutetodogetDireccion()

Reglas de precedencia

En el ejemplo anterior acabamos de ver que las expresiones Path para getCliente() ygetDireccion() son ambiguas Una peticioacuten GET clientespedrolopezdireccionpodriacutea mapearse con cualquiera de los dos meacutetodos La especificacioacuten JAX-RS define lassiguientes reglas para priorizar el mapeado de expresiones regulares

bull El primer criterio para ordenar las acciones de mapeado es el nuacutemero de caracteres literalesque contiene la expresioacuten Path teniendo prioridad aquellas con un mayor nuacutemero decaracteres literales El patroacuten de la URI para el meacutetodo getCliente() tiene 10 caraacutecteresliterales clientes El patroacuten para el meacutetodo getDireccion() tiene 19 clientesdireccion Por lo tanto se elegiriacutea primero el meacutetodo getDireccion()

bull El segundo criterio es el nuacutemero de variables en expresiones Path (por ejemplo id oid +) Teniendo precedencia las patrones con un mayor nuacutemero de variables

bull El tercer criterio es el nuacutemero de variables que tienen asociadas expresiones regulares(tambieacuten en orden descendente)

A continuacioacuten mostramos una lista de expresiones Path ordenadas en orden descendentede prioridad

1 clientesidnombredireccion

2 clientesid +direccion

3 clientesiddireccion

4 clientesid +

Servicios Rest

44

Las expresiones 13 se analizariacutean primero ya que tienen maacutes caracteres literales que laexpresioacuten nuacutemero 4 Si bien las expresiones 13 tienen el mismo nuacutemero de caracteresliterales La expresioacuten 1 se analizariacutea antes que las otras dos debido a la segunda regla (tienemaacutes variables) Las expresiones 2 y 3 tienen el mismo nuacutemero de caracteres literales y elmismo nuacutemero de variables pero la expresioacuten 2 tiene una variable con una expresioacuten regularasociada

Estas reglas de ordenacioacuten no son perfectas Es posible que siga habiendo ambiguumledadespero cubren el 90 de los casos Si el disentildeo de nuestra aplicacioacuten presenta ambiguumledadesaplicando estas reglas es bastante probable que hayamos complicado dicho disentildeo y seriacuteaconveniente revisarlo y refactorizar nuestro esquema de URIs

Paraacutemetros matrix (Matrix parameters)

Los paraacutemetros matrix con pares nombre-valor incluidos como parte de la URI Aparecen alfinal de un segmento de la URI (segmento de ruta) y estaacuten delimitados por el caraacutecter Por ejemplo

httpejemplocochescomseatibizacolor=black2006

En la ruta anterior el paraacutemetro matrix aparece despueacutes del segmento de ruta ibiza Su nombrees color y el valor asociado es black

Un paraacutemetro matrix es diferente de lo que denominamos paraacutemetro de consulta (queryparameter) ya que los paraacutemetros matrix representan atributos de ciertos segmentos de laURI y se utilizan para propoacutesitos de identificacioacuten Pensemos en ellos como adjetivos Losparaacutemetros de consulta por otro lado siempre aparecen al final de la URI y siemprepertenecen al recurso completo que estemos referenciando

Los paraacutemetros matrix son ignorados cuando el runtime de JAX-RS realiza el matching de laspeticiones de entrada a meacutetodos de recursos REST De hecho es ilegal incluir paraacutemetrosmatrix en las expresiones Path Por ejemplo

Path(seat)public class SeatService

GET Path(ibizaanyo) Produces(imagejpeg) public Response getIbizaImagen(PathParam(anyo) String anyo)

Si la peticioacuten de entrada es GET seatibizacolor=black2009 el meacutetodo getIbizaImagen()seriacutea elegido por el proveedor de JAX-RS para servir la peticioacuten de entrada y seriacutea invocadoLos paraacutemetros matrix NO se consideran parte del proceso de matching debido a quenormalmente son atributos variables de la peticioacuten

Subrecursos (Subresource Locators)

Acabamos de ver la capacidad de JAX-RS para hacer corresponder de forma estaacuteticaa traveacutes de la anotacioacuten Path URIs especificadas en la entrada de la peticioacuten conmeacutetodos Java especiacuteficos JAX-RS tambieacuten nos permitiraacute de forma dinaacutemica servir nosotros

Servicios Rest

45

mismos las peticiones a traveacutes de los denominados subresource locators (localizadores desubrecursos)

Los subresource locators son meacutetodos Java anotados con Path pero sin anotacionesGET PUT hellip Este tipo de meacutetodos devuelven un objeto que es en siacute mismo un servicioJAX-RS que sabe coacutemo servir el resto de la peticioacuten Vamos a describir mejor este conceptocon un ejemplo

Supongamos que queremos extender nuestro servicio que proporciona informacioacuten sobre losclientes Disponemos de diferentes bases de datos de clientes seguacuten regiones geograacuteficasQueremos antildeadir esta informacioacuten en nuestro esquema de URIs pero desacoplando labuacutesqueda del servidor de base de datos de la consulta particular de un cliente en concretoAntildeadiremos la informacioacuten de la zona geograacutefica en la siguiente expresioacuten Path

clienteszona-dbclienteId

A continuacioacuten definimos la clase ZonasClienteResource que delegaraacute en la claseClienteResource que ya teniacuteamos definida

Path(clientes)public class ZonasClienteResource

Path(zona-db) public ClienteResource getBaseDeDatos(PathParam(zona) String db) devuelve una instancia dependiendo del paraacutemetro db ClienteResource resource = localizaClienteResource(db) return resource

protected ClienteResource localizaClienteResource(String db)

La clase ZonasClienteResource es nuestro recurso raiacutez Dicha clase no atiende ningunapeticioacuten HTTP directamente Nuestro recurso raiacutez procesa el segmento de URI que hacereferencia a la base de datos en donde buscar a nuestro cliente y devuelve una instancia dedicha base de datos (o maacutes propiamente dicho del objeto con en que accederemos a dichabase de datos) El proveedor de JAX-RS utiliza dicha instancia para servir el resto de lapeticioacuten

public class ClienteResource private MapltInteger Clientegt clienteDB = new ConcurrentHashMapltInteger Clientegt() private AtomicInteger idContador = new AtomicInteger()

public ClienteResource(MapltInteger Clientegt clienteDB) thisclienteDB = clienteDB

POST Consumes(applicationxml)

Servicios Rest

46

public Response crearCliente(InputStream is)

GET Path(id) Produces(applicationxml) public Cliente recuperarClienteId(PathParam(id) int id)

PUT Path(id) Consumes(applicationxml) public void modificarCliente(PathParam(id) int id Cliente cli)

Si un usuario enviacutea la peticioacuten GET clientesnorteamerica-db333 el proveedorJAX-RS primero realizaraacute un matching de la expresioacuten sobre el meacutetodoZonasClienteResourcegetBaseDeDatos() A continuacioacuten procesaraacute el resto de la peticioacuten(333) a traveacutes del meacutetodo ClienteResourcerecuperarClienteId()

Podemos observar que la nueva clase ClienteResource ademaacutes de tener un nuevoconstructor ya no estaacute anotada con Path Esto implica que ya no es un recurso de nuestrosistema es un subrecurso y no debe ser registrada en el runtime de JAX-RS a traveacutes de laclase Application (como veremos maacutes adelante)

Veamos otro ejemplo Supongamos que tenemos un conjunto de alumnos del que podemosobtener el listado completo de alumnos y antildeadir nuevos alumnos pero ademaacutes queremos quecada alumno individual pueda consultarse modificarse o borrarse Una forma sencilla de trataresto es dividir el coacutedigo en un recurso (lista de alumnos) y un subrecurso (alumno individual)de la siguiente forma

Path(alumnos)public class AlumnosResource

Context UriInfo uriInfo

GET Produces(MediaTypeAPPLICATION_XML MediaTypeAPPLICATION_JSON) public ListltAlumnoBeangt getAlumnos() return FactoriaDaosgetAlumnoDao()getAlumnos()

POST Consumes(MediaTypeAPPLICATION_JSON) public void addAlumno(AlumnoBean alumno) throws IOException String dni = FactoriaDaosgetAlumnoDao()addAlumno(alumno) URI uri = uriInfogetAbsolutePathBuilder()path(dni)build(dni) Responsecreated(uri)build()

Path(alumno) public AlumnoResource getAlumno( PathParam(alumno) String dni)

Servicios Rest

47

return new AlumnoResource(uriInfo dni)

Vemos que en este recurso inyectamos informacioacuten sobre la URI solicitada como variable deinstancia (utilizando la anotacioacuten Context de la que hablaremos maacutes adelante) Para elconjunto de alumnos ofrecemos dos operaciones obtener la lista de alumnos y antildeadir unnuevo alumno a la lista la cual devuelve como respuesta la URI que nos da acceso al recursoque acabamos de antildeadir

Sin embargo lo maacutes destacable es el uacuteltimo meacutetodo Eacuteste se ejecutaraacute cuando antildeadamos a laruta el identificador de un alumno (por ejemplo alumnos15 ) En este caso lo que hace esdevolver un subrecurso (AlumnoResource) para asiacute tratar un alumno individual (destacamosque el nombre estaacute en singular para distinguirlo del recurso anterior que representa elconjunto)

Cuando hacemos esto estamos delegando en el nuevo Recurso para tratar la peticioacuten

public class AlumnoResource

UriInfo uriInfo

String dni

public AlumnoResource(UriInfo uriInfo String dni) thisuriInfo = uriInfo thisdni = dni

GET Produces(MediaTypeAPPLICATION_XMLMediaTypeAPPLICATION_JSON) public AlumnoBean getAlumno() AlumnoBean alumno = FactoriaDaosgetAlumnoDao()getAlumno(dni) if(alumno==null) throw new WebApplicationException(StatusNOT_FOUND) return alumno

PUT Consumes(MediaTypeAPPLICATION_XML) public Response setAlumno(AlumnoBean alumno) El DNI del alumno debe coincidir con el de la URI alumnosetDni(dni)

if(FactoriaDaosgetAlumnoDao()getAlumno(dni) = null) FactoriaDaosgetAlumnoDao()updateAlumno(alumno) return ResponsenoContent()build() else FactoriaDaosgetAlumnoDao()addAlumno(alumno) return Responsecreated(uriInfogetAbsolutePath())build()

Servicios Rest

48

DELETE public void deleteAlumno() FactoriaDaosgetAlumnoDao()deleteAlumno(dni)

Este recurso ya no es un recurso raiacutez mapeado a una ruta determinada (podemos ver que laclase no lleva la anotacioacuten Path) sino que es creado desde otro recurso Es por lo tantoun subrecurso

Como ya hemos visto los subrecursos nos permiten simplificar la forma de trabajar conconjuntos de recursos definiendo en un uacutenico meacutetodo la ruta de acceso a un recurso individualen lugar de tenerlo que hacer de forma independiente para cada operacioacuten

Ademaacutes este disentildeo modular de los recursos nos va a permitir reutilizar determinadosrecursos dentro de otros Por ejemplo dentro del recurso de un alumno podriacuteamos ver la listade asignaturas en las que se ha matriculado y reutilizar el subrecurso encargado de acceder alas asignaturas para poder acceder a sus datos a partir del recurso del alumno No deberemosabusar de esta caracteriacutestica ya que si creamos relaciones ciacuteclicas perdemos la caracteriacutesticadeseable de los servicios REST de que cada recurso estaacute asignado a una uacutenica URI

En un subrecurso NO podemos inyectar objetos de contexto mediantela anotacioacuten Context ya que no estamos en un recurso raiacutez Porese motivo en el ejemplo hemos proporcionado el objeto UriInfo enel constructor del subrecurso De forma alternativa tambieacuten podriacuteamoshaber inyectado este objeto como paraacutemetro de sus meacutetodos en ese casosi que habriacutea sido posible la inyeccioacuten

Caraacutecter dinaacutemico del dispatching de peticiones

En los ejemplos anteriores hemos ilustrado el concepto de subresource locator aunque nohemos mostrado completamente su caraacutecter dinaacutemico Asiacute si volvemos al primero de ellosel meacutetodo ZonasClienteResourcegetBaseDeDatos() puede devolver cualquier instancia decualquier clase En tiempo de ejecucioacuten el proveedor JAX-RS buscaraacute el interior de estainstancia meacutetodos de recurso que puedan gestionar la peticioacuten

Supongamos que tenemos dos bases de datos de clientes con diferentes tipos deidentificadores Una de ellas utiliza una clave numeacuterica La otra utiliza una clave formada porel nombre y apellidos Necesitamos tener dos clases diferentes para extraer la informacioacutenadecuada de la URI de la peticioacuten Cambiaremos la implementacioacuten de la siguiente forma

Path(clientes)public class ZonasClienteResourceResource protected ClienteResource europa = new ClienteResource() protected OtraClaveClienteResource norteamerica = new OtraClaveClienteResource()

Path(zona-db) public Object getBaseDeDatos(PathParam(zona) String db) if (dbequals(europa)) return europa else if (dbequals(norteamerica)) return northamerica

Servicios Rest

49

else return null

En lugar de devolver una instancia de ClienteResource el meacutetodo getBaseDeDatos() devuelveuna instancia de javalangObject JAX-RS analizaraacute la instancia devuelta para ver coacutemoprocesar el resto de la peticioacuten

Ahora si un usuario enviacutea la peticioacuten GET clienteseuropa-db333 se utilizaraacute la claseClienteResource para servir el resto de la peticioacuten Si la peticioacuten es GET clientesnorteamerica-dbjohn-smith utilizaremos el nuevo subrecurso OtraClaveClienteResource

public class OtraClaveClienteResource private MapltString Clientegt clienteDB = new ConcurrentHashMapltString Clientegt() GET Path(nombre-apellidos) Produces(applicationxml) public Cliente getCliente(PathParam(nombre) String nombre PathParam(apellidos) String apelllidos)

PUT Path(nombre-apellidos) Consumes(applicationxml) public void actualizaCliente()PathParam(nombre) String nombre PathParam(apellidos) String apelllidos Cliente cli)

23 Usos de las anotaciones Produces y Consumes

La informacioacuten enviada a un recurso y posteriormente devuelta al cliente que realizoacute la peticioacutense especifica con la cabecera HTTP Media-Type tanto en la peticioacuten como en la respuestaComo ya hemos visto podemos especificar que representaciones de los recursos (valor deMedia_Type) son capaces de aceptar yo producir nuestros servicios mediante las siguientesanotaciones

bull javaxwsrsConsumes

bull javaxwsrsProduces

La ausencia de dichas anotaciones es equivalente a incluirlas con el valor de media type es decir su ausencia implica que se soporta (acepta) cualquier tipo de representacioacuten

Anotacioacuten Consumes

Esta anotacioacuten funciona conjuntamente con POST y PUT Le indica al framework (libreriacuteasJAX-RS) a queacute meacutetodo se debe delegar la peticioacuten de entrada Especiacuteficamente el clientefija la cabecera HTTP Content-Type y el framework delega la peticioacuten al correspondientemeacutetodo capaz de manejar dicho contenido Un ejemplo de anotacioacuten con PUT es lasiguiente

Servicios Rest

50

Path(pedidos)public class PedidoResource PUT Consumes(applicationxml) public void modificarPedido(Pedido representation)

Si Consumes se aplica a la clase por defecto los meacutetodos correspondientes aceptan lostipos especificados de tipo MIME Si se aplica a nivel de meacutetodo se ignora cualquier anotacioacutenConsumes a nivel de clase para dicho meacutetodo

En este ejemplo le estamos indicando al framework que el meacutetodo modificarPedido() aceptaun recurso cuya representacioacuten (tipo MIME) es applicationxml (y que se almacenaraacute en lavariable representation hablaremos de ello en la siguiente sesioacuten) Por lo tanto un clienteque se conecte al servicio web a traveacutes de la URI pedidos debe enviar una peticioacuten HTTPPUT conteniendo el valor de applicationxml como tipo MIME de la cabecera HTTPContent-Type y el cuerpo (body) del mensaje HTTP debe ser por tanto un documentoxml vaacutelido

Si no hay meacutetodos de recurso que puedan responder al tipo MIME solicitado (tipo MIMEespecificado en la anotacioacuten Consumes del servicio) se le devolveraacute al cliente un coacutedigoHTTP 415 (Unsupported Media Type) Si el meacutetodo que consume la representacioacutenindicada como tipo MIME no devuelve ninguna representacioacuten se enviaraacute un el coacutedigo HTTP204 (No content) A continuacioacuten mostramos un ejemplo en el que sucede eacutesto

POSTConsumes(applicationxml)public void creaPedido(Pedido pedido) Crea y almacena un nuevo _Pedido_

Podemos ver que el meacutetodo consume una representacioacuten en texto plano pero devuelvevoid es decir no devuelve ninguna representacioacuten En este caso se enviacutea el coacutedigo de estadoHTTP 204 No content en la respuesta

Un recurso puede aceptar diferentes tipos de entradas Asiacute podemos utilizar la anotacioacutenPUT con maacutes de un meacutetodo para gestionar las repuestas con tipos MIME diferentes Porejemplo podriacuteamos tener un meacutetodo para aceptar estructuras XML y otro para aceptarestructuras JSON

Path(pedidos)public class PedidoResource PUT Consumes(applicationxml) public void modificarPedidoXML(InputStream pedido) PUT Consumes(applicationjson) public void modificarPedidoJson(InputStream pedido)

Servicios Rest

51

Anotacioacuten Produces

Esta anotacioacuten funciona conjuntamente con GET POST y PUT Indica al frameworkqueacute tipo de representacioacuten se enviacutea de vuelta al cliente

De forma maacutes especiacutefica el cliente enviacutea una peticioacuten HTTP junto con una cabecera HTTPAccept que se mapea directamente con el Content-Type que el meacutetodo produce Por lo tantosi el valor de la cabecera Accept HTTP es applicationxml el meacutetodo que gestiona la peticioacutendevuelve un stream de tipo MIME applicationxml Esta anotacioacuten tambieacuten puede utilizarse enmaacutes de un meacutetodo en la misma clase de recurso Un ejemplo que devuelve representacionesXML y JSON seriacutea el siguiente

Path(pedidos)public class PedidoResource

GET Produces(applicationxml) public String getPedidoXml()

GET Produces(applicationjson) public String getPedidoJson()

Si un cliente solicita una peticioacuten a una URI con un tipo MIME no soportadopor el recurso el framework JAX-RS lanza la excepcioacuten adecuadaconcretamente el runtime de JAX-RS enviacutea de vuelta un error HTTP 406Not acceptable

Se puede declarar maacutes de un tipo en la misma declaracioacuten Produces como por ejemplo

Produces(applicationxml applicationjson)public String getPedidosXmlOJson()

El meacutetodo getPedidosXmlOJson() seraacute invocado si cualquiera de los dos tipos MIMEespecificados en la anotacioacuten Produces son aceptables (la cabecera Accept de la peticioacutenHTTP indica queacute representacioacuten es aceptable) Si ambas representaciones son igualmenteaceptables se elegiraacute la primera

En lugar de especificar los tipos MIME como cadenas detexto en Consumes y Produces podemos utilizar lasconstantes definidas en la clase javaxwsrscoreMediaTypecomo por ejemplo MediaTypeAPPLICATION_XML oMediaTypeAPPLICATION_JSON en lugar de applicationxml yapplicationjson

24 Inyeccioacuten de paraacutemetros JAX-RS

Buena parte del trabajo de JAX-RS es el extraer informacioacuten de una peticioacuten HTTP einyectarla en un meacutetodo Java Podemos estar interesados en un fragmento de la URI de

Servicios Rest

52

entrada en los paraacutemetros de peticioacutenhellip El cliente tambieacuten podriacutea enviar informacioacuten en lascabeceras de la peticioacuten A continuacioacuten indicamos una lista con algunas de las anotacionesque podemos utilizar para inyectar informacioacuten de las peticiones HTTP

bull javaxwsrsPathParam

bull javaxwsrsMatrixParam

bull javaxwsrsQueryParam

bull javaxwsrsFormParam

bull javaxwsrsHeaderParam

bull javaxwsrsContext

bull javaxwsrsBeanParam

Habitualmente estas anotaciones se utilizan en los paraacutemetros de un meacutetodo de recurso JAX-RX Cuando el proveedor de JAX-RS recibe una peticioacuten HTTP busca un meacutetodo Java quepueda servir dicha peticioacuten Si el meacutetodo Java tiene paraacutemetros anotados con alguna de estasanotaciones extraeraacute la informacioacuten de la peticioacuten HTTP y la pasaraacute como un paraacutemetrocuando se invoque el meacutetodo

javaxwsrsPathParam

Ya la hemos utilizado en la sesioacuten anterior PathParam nos permite inyectar el valor de losparaacutemetros de la URI definidos en expresiones Path Recordemos el ejemplo

Path(clientes)public class ClienteResource

GET Path(id) Produces(applicationxml) public Cliente recuperarClienteId(PathParam(id) int id)

Podemos referenciar maacutes de un paraacutemetro en el path de la URI en nuestros meacutetodo javaPor ejemplo supongamos que estamos utilizando el nombre y apellidos para identificar a uncliente en nuestra clase de recurso

Path(clientes)public class ClienteResource

GET Path(nombre-apellidos) Produces(applicationxml) public Cliente recuperarClienteId(PathParam(nombre) String nom PathParam(apellidos) String ape)

Servicios Rest

53

En ocasiones un parameacutetro de path de la URI puede repetirse en diferentes expresionesPath que conforman el patroacuten de matching completo para un meacutetodo de un recurso (porejemplo puede repetirse en la expresioacuten Path de la clase y de un meacutetodo) En estos casos laanotacioacuten PathParam siempre referencia el paraacutemetro path final Asiacute en el siguiente coacutedigo

Path(clientesid)public class ClienteResource

GET Path(direccionid) Produces(textplain) public String getDireccion(PathParam(id) String direccionId)

Si nuestra peticioacuten HTTP es GET clientes123direccion456 el paraacutemetro direccionIddel meacutetodo getDireccion() tendriacutea el valor inyectado de 456

Interfaz UriInfo

Podemos disponer ademaacutes de un API maacutes general para consultar y extraer informacioacuten sobrelas peticiones URI de entrada Se trata de la interfaz javaxwsrscoreUriInfo

public interface UriInfo public javanetURI getAbsolutePath() public UriBuilder getAbsolutePathBuilder()

public javanetURI getBaseUri() public UriBuilder getBaseUriBuilder()

public String getPath() public ListltPathSegmentgt getPathSegments() public MultivaluedMapltString Stringgt getPathParameters()

Los meacutetodos getAbsolutePathBuilder() y getAbsolutePath() devuelven la ruta absoluta dela peticioacuten HTTP en forma de UriBuilder y URI respectivamente

Los meacutetodos getBaseUri() y getBaseUriBuilder() devuelven la ruta base de la aplicacioacuten(ruta raiz de nuestros servicios rest) en forma de UriBuilder y URI respectivamente

El meacutetodo UriInfogetPath() permite obtener la ruta relativa de nuestros servicios RESTutilizada para realizar el matching con nuestra peticioacuten de entrada (es la ruta de la peticioacutenactual relativa a la ruta base de la peticioacuten rest)

El meacutetodo UriInfogetPathSegments() divide la ruta relativa de nuestro servicio REST enuna serie de objetos PathSegment (segmentos de ruta delimitados por )

El meacutetodo UriInfogetPathParameters() devuelve un objeto de tipo MultivaluedMap con todoslos paraacutemetros del path definidos en todas las expresiones Path de nuestra peticioacuten rest

Servicios Rest

54

Por ejemplo si la ruta de nuestra petcioacuten http es httplocalhost8080contextorestclientes2(siendo contexto la ruta raiacutez del war desplegado y rest la ruta de servicio de jax-rs)

bull la ruta absoluta (meacutetodo getAbsolutePath()) seriacutea httplocalhost8080contextorestclientes2

bull la ruta base (meacutetodo getBaseUri) seriacutea httplocalhost8080contextorest

bull la ruta relativa a la ruta base (meacutetodo getPath()) seriacutea clientes2

bull el nuacutemero de segmentos de la peticioacuten rest (meacutetodo getPathSegments()) seriacutean 2 clientesy 2

Podemos inyectar una instancia de la interfaz UriInfo utilizando la anotacioacutenjavaxwsrscoreContext A continuacioacuten mostramos un ejemplo

Path(cochesmarca)public class CarResource

GET Path(modeloanyo) Produces(imagejpeg) public Response getImagen(Context UriInfo info) String fabricado = infogetPathParameters()getFirst(marca) PathSegment modelo = infogetPathSegments()get(2) String color = modelogetMatrixParameteres()getFirst(color)

En este ejemplo inyectamos una instancia de UriInfo como paraacutemetro del meacutetodo getImagen()A continuacioacuten hacemos uso de dicha instancia para extraer informacioacuten de la URI

Recuerda que tambieacuten podriacuteamos inyectar una instancia de UriInfo en unavariable de instancia de la clase raiacutez de nuestro recurso

El meacutetodo CarResourcegetImagen() utiliza la interfazjavaxwsrscorePathSegment que como ya hemos indicado representa unsegmento de ruta

package javaxwsrscorepublic interface PathSegment String getPath() MultivaluedMapltString Stringgt getMatrixParameters()

El meacutetodo PathSegmentgetPath() devuelve el valor de la cadena de caracteres del segmentode ruta actual sin considerar niguacuten paraacutemetro matrix que pudiese contener

El meacutetodo PathSegmentgetMatrixParameters() devuelve un mapa con todos losparaacutemetros matrix aplicados a un segmento de ruta

Supongamos que realizamos la siguiente peticioacuten http para el coacutedigo anterior (claseCarResource)

Servicios Rest

55

GET cochesseatleoncolor=rojo2015

Esta peticioacuten es delegada en el meacutetodo ClarResourcegetImagen() La ruta contiene 4segmentos coches seat leon y 2015 La variable _modelo tomaraacute el valor leon y la variablecolor se instanciaraacute con el valor rojo

javaxwsrsMatrixParam

La especificacioacuten JAX-RS nos permite inyectar una matriz de valores de paraacutemetros a traveacutesde la anotacioacuten javaxwsrsMatrixParam

Path(cochesmarca)public class CarResource

GET Path(modeloanyo) Produces(imagejpeg) public Response getImagen(PathParam(marca) String marca PathParam(modelo) String modelo MatrixParam(color) String color)

El uso de la anotacioacuten MatrixParam simplifica nuestro coacutedigo y lo hace algo maacutes legibleSi por ejemplo la peticioacuten de entrada es

GET cochesseatibizacolor=black2009

entonces el paraacutemetro color del meacutetodo CarResourcegetImagen() tomariacutea el valor black

javaxwsrsQueryParam

La anotacioacuten javaxwsrsQueryParam nos permite inyectar paraacutemetros de consulta(query parameters) de la URI en los valores de los paraacutemetros de los meacutetodos java denuestros recursos Por ejemplo supongamos que queremos consultar informacioacuten de nuestrosclientes y queremos recuperar un subconjunto de clientes de nuestra base de datos NuestraURI de peticioacuten podriacutea ser algo asiacute

GET clientesinicio=0amptotal=10

El paraacutemetro de consulta inicio representa el iacutendice (o posicioacuten) del primer cliente quequeremos consultar y el paraacutemetro total representa cuaacutentos clientes en total queremosobtener como respuesta Una implementacioacuten del servicio RESTful podriacutea contener elsiguiente coacutedigo

Path(clientes)public class ClienteResource GET Produces(applicationxml) public String getClientes(QueryParam(inicio) int inicio

Servicios Rest

56

QueryParam(total) int total)

En este ejemplo el paraacutemetro inicio tomariacutea el valor 0 y el paraacutemetro total tomariacutea elvalor 10 (JAX-RS convierte automaacuteticamente las cadenas de caracteres de los paraacutemetrosde consulta en enteros)

javaxwsrsFormParam

La anotacioacuten javaxwsrsFormParam se utiliza para acceder al cuerpo del mensajede la peticioacuten HTTP de entrada cuyo valor de Content-Type es applicationx-www-form-urlencoded Es decir se utiliza para acceder a entradas individuales de un formulario HTMLPor ejemplo supongamos que para registrar a nuevos clientes en el sistema tenemos querellenar el siguiente formulario

ltFORM action=httpejemplocomclientes method=postgt ltPgt Nombre ltINPUT type=text name=nombregtltBRgt Apellido ltINPUT type=text name=apellidogtltBRgt ltINPUT type=submit value=Sendgt ltPgtltFORMgt

La ejecucioacuten de este coacutedigo inyectaraacute los valores del formulario como paraacutemetros de nuestromeacutetodo Java que representa el servicio de la siguiente forma

Path(clientes)public class ClienteResource POST public void crearCliente(FormParam(nombre) String nom FormParam(apellido) String ape)

Aquiacute estamos inyectando los valores de nombre y apellidos del formulario HTML en losparaacutemetors nom y ape del meacutetodo java crearCliente() Los datos del formulario viajan atraveacutes de la red codificados como URL-encoded Cuando se utiliza la anotacioacuten FormParamJAX-RS decodifica de forma automaacutetica las entradas del fomulario antes de inyectar susvalores

Asiacute por ejemplo si tecleamos los valores Maria Luisa y_Perlado_ como valores en loscampos de texto nombre y apellido del formulario el cuerpo de nuestro mensaje HTTP seraacutenombre=Maria20Luisaapellido=Perlado Este mensaje seraacute recibido por nuestro meacutetodoque extraeraacute los valores correspondientes y los instanciaraacute en los paraacutemetros nom y ape delmeacutetodo _ClienteResourcecrearCliente()

javaxwsrsHeaderParam

La anotacioacuten javaxwsrsHeaderParam se utiliza para inyectar valores de lascabeceras de las peticiones HTTP Por ejemplo si estamos interesados en la paacutegina web quenos ha referenciado o enlazado con nuestro servicio web podriacuteamos acceder a la cabeceraHTTP Referer utilizando la anotacioacuten HeaderParam de la siguiente forma

Servicios Rest

57

Path(miservicio)public class MiServicio GET Produces(texthtml) public String get(HeaderParam(Referer) String referer)

De forma alternativa podemos acceder de forma programativa a todas las cabeceras de lapeticioacuten de entrada utilizando la interfaz javaxwsrscoreHttpHeaders

public interface HttpHeaders public ListltStringgt getRequestHeader(String name) public MultivaluedMapltString Stringgt getRequestHeaders()

El meacutetodo getRequestHeader() permite acceder a una cabecera en concreto y el meacutetodogetRequestHeaders() nos proporciona un objeto de tipo Map que representa todas lascabeceras A continuacioacuten mostramos un ejemplo que accede a todas las cabeceras de lapeticioacuten HTTP de entrada

Path(miservicio)public class MiServicio GET Produces(texthtml) public String get(Context HttpHeaders cabeceras) String referer = headersgetRequestHeader(Referer)get(0) for (String header headersgetRequestHeaders()keySet()) Systemoutprintln(Se ha utilizado esta cabecera + header)

javaxwsrscoreContext

Dentro de nuestros recursos JAX-RS podemos inyectar determinados objetos coninformacioacuten sobre el contexto de JAX-RS sobre el contexto de servlets o sobreelementos de la peticioacuten recibida desde el cliente Para ello utilizaremos la anotacioacutenjavaxwsrscoreContext

En los ejemplos de esta sesioacuten ya hemos visto como utilizarla para inyectar objetos de tipoUriInfo y HttpHeaders

A continuacioacuten mostramos un ejemplo en el que podos obtener detalles sobre el contextodel despliegue de la aplicacion asi como del contexto de peticiones individuales utilizando laanotacion Context

Implementacioacuten de un servicio que muestra informacioacuten sobre el contexto de la peticioacuten

Path(orders)

Servicios Rest

58

public class PedidoResource

Context Application app

Context UriInfo uri

Context HttpHeaders headers

Context Request request

Context SecurityContext security

Context Providers providers

GET Produces(applicationxml) public ListltOrdergt getAll(QueryParam(start)int from QueryParam(end)int to) (appgetClasses()) (urigetPath()) (headersgetRequestHeader(HttpHeadersACCEPT)) (headersgetCookies()) (requestgetMethod()) (securityisSecure())

Application proporciona acceso a la informacioacuten de la configuracioacuten de la aplicacioacuten(clase Application)UriInfo proporciona acceso a la URI de la peticioacutenHttpHeaders proporciona acceso a las cabeceras de la peticioacuten HTTP La anotacioacutenHeaderParam puede tambieacuten utilizarse para enlazar una cabecera HTTP a unparaacutemetro de un meacutetodo de nuestro recurso a un campo del mismo o a una propiedadde un beanRequest se utiliza para procesar la respuestas tiacutepicamente se usa juntamente con laclase Response para construir la respuesta de forma dinaacutemicaSecurityContext proporciona acceso a la informacioacuten de la peticioacuten actual relacionadacon la seguridadProviders proporciona informacioacuten sobre la buacutesqueda del runtime de las instancias deproveedores utilizando un conjunto de criterios de buacutesqueda

Con respecto a contexto de servlets podremos inyectar informacioacuten de ServletContextServletConfig HttpServletRequest y HttpServletResponse Debemos recordar que losrecursos JAX-RS son invocados por un servlet dentro de una aplicacioacuten web por lo quepodemos necesitar tener acceso a la informacioacuten del contexto de servlets Por ejemplo sinecesitamos acceder a la ruta en disco donde tenemos los datos de nuestra aplicacioacuten webtendremos que inyectar el objeto ServletContext

GETProduces(imagejpeg)public InputStream getImagen(Context ServletContext sc) return scgetResourceAsStream(fotos + nif + jpg)

javaxwsrsBeanParam

La anotacioacuten javaxwsrsBeanParam nos permite inyectar una clase especiacutefica cuyosmeacutetodos o atributos esteacuten anotados con alguna de las anotaciones de inyeccioacuten de paraacutemetrosxxxParam que hemos visto en esta sesioacuten Por ejemplo supongamos esta clase

Servicios Rest

59

public class ClienteInput FormParam(nombre) String nombre

FormParam(apellido) String apellido

HeaderParam(Content-Type) String contentType

public String getFirstName()

La clase ClienteInput es un simple POJO (Plain Old Java Object) que contiene el nombrey apellidos de un cliente asiacute como el tipo de contenido del mismo Podemos dejar que JAX-RScree inicialice e inyecte esta clase usando la anotacioacuten BeanParam de la siguiente forma

Path(clientes)public class ClienteResource POST public void crearCliente(BeanParam ClienteInput newCust)

El runtime de JAX-RS analizaraacute los paraacutemetros anotados con BeanParam para inyectarlas anotaciones correspondientes y asignar el valor que corresponda En este ejemplo la claseClienteInput contendraacute dos valores de un formulario de entrada y uno de los valores de lacabecera de la peticioacuten De esta forma nos podemos evitar una larga lista de paraacutemetros enel meacutetodo crearCliente() (en este caso son soacutelo tres pero podriacutean ser muchos maacutes)

Conversioacuten automaacutetica de tipos

Todas las anotaciones que hemos visto referencian varias partes de la peticioacuten HTTP Todasellas se representan como una cadena de caracteres en dicha peticioacuten HTTP JAX-RS puedeconvertir esta cadena de caracteres en cualquier tipo Java siempre y cuando se cumpla almenos uno de estos casos

1 Se trata de un tipo primitivo Los tipos int short float double byte char y booleanpertenecen a esta categoriacutea

2 Se trata de una clase Java que tiene un constructor con un uacutenico paraacutemetro de tipo String

3 Se trata de una clase Java que tiene un meacutetodo estaacutetico denominado valueOf() que tomaun uacutenico String como argumento y devuelve una instancia de la clase

4 Es una clase de tipo javautilListltTgt javautilSetltTgt o javautilSortedSetltTgt en dondeT es un tipo que satisface los criterios 2 oacute 3 o es un String Por ejemplo ListltDoublegtSetltStringgt o SortedSetltIntegergt

Si el runtime JAX-RS falla al convertir una cadena de caracteres en el tipo Java especificadose considera un error del cliente Si se produce este fallo durante el procesamiento de unainyeccioacuten de tipo MatrixParam QueryParam o PathParam se devuelve al clienteun error 404 Not found Si el fallo tiene lugar con el procesamiento de las inyecciones

Servicios Rest

60

HeaderParam o CookieParam (esta uacuteltima no la hemos visto) entonces se enviacutea al clienteel eror 400 Bad Request

Valores por defecto (DefaultValue)

Suele ser habitual que algunos de los paraacutemetros proporcionados en las peticiones a serviciosRESTful sean opcionales Cuando un cliente no proporciona esta informacioacuten opcional en lapeticioacuten JAX-RS inyectaraacute por defecto un valor null si se trata de un objeto o un valor ceroen el caso de tipos primitivos

Estos valores por defecto no siempre son los que necesitamos para nuestro servicioPara solucionar este problema podemos definir nuestro propio valor por defecto para losparaacutemetros que sean opcionales utilizando la anotacioacuten javaxwsrsDefaultValue

Consideremos el ejemplo anterior relativo a la recuperacioacuten de la informacioacuten de unsubconjunto de clientes de nuestra base de datos Para ello utilizaacutebamos dos paraacutemetros deconsulta para indicar el iacutendice del primer elemento asiacute como el nuacutemero total de elementosque estamos interesados en recuperar En este caso no queremos que el cliente tengaque especificar siempre estos paraacutemetros al realizar la peticion Usaremos la anotacioacutenDefaultValue para indicar los valores por defecto que nos interese

Path(clientes)public class ClienteResource GET Produces(applicationxml) public String getClientes( DefaultValue(0) QueryParam(inicio) int inicio DefaultValue(10) QueryParam(total) int total)

Hemos usado DefaultValue para especificar un iacutendice de comienzo con valor cero y untamantildeo del subconjunto de los datos de la respuesta JAX-RS utilizaraacute las reglas de conversioacutende cadenas de caracteres que acabamos de indicar para convertir el valor del paraacutemetro enel tipo Java que especifiquemos

25 Configuracioacuten y despliegue de aplicaciones JAX-RS

Como ya hemos visto en la sesioacuten anterior implementamos nuestros servicios REST utilizandoel API de Java JAX-RS (especificacioacuten JSR-339) Una aplicacioacuten JAX-RS consiste en unoo maacutes recursos y cero o maacutes proveedores En este apartado vamos a describir ciertosaspectos aplicados a las aplicaciones JAX-RS como un todo concretamente a la configuracioacuteny tambieacuten a la publicacioacuten de las mismas cuando utilizamos un servidor de aplicacionesJavaEE 7 o bien un contenedor de servlets 30 que incluyan una implementacioacuten del APIJAX-RS Tambieacuten indicaremos coacutemo configurar el despliegue en el caso de no disponer comomiacutenimo de un contenedor de servlets 30

Configuracioacuten mediante la clase Application

Tanto los recursos (clases anotadas con Path) como los proveedores que conforman nuestraaplicacioacuten JAX-RS pueden configurarse utilizando una subclase de Application Cuandohablamos de configuracioacuten nos estamos refiriendo en este caso a definir los mecanismospara localizar las clases que representan los recursos asiacute como a los proveedores

Servicios Rest

61

Un proveedor es una clase que implementa una o algunade las siguientes interfaces JAX-RS MesssageBodyReaderMessageBodyWriter ContextResolverltTgt y ExceptionMapperltTgt Lasdos primeras permiten crear proveedores de entidades (entity providers)la tercera es un proveedor de contexto (context provider) y la uacuteltima unproveedor de mapeado de excepciones (exception mapping provider) Lasclases que actuacutean como proveedores estaacuten anotadas con Providerpara que puedan ser identificadas automaacuteticamente por el runtime JAX-RS

El uso de una subclase de Application para configurar nuestros servicios REST constituyela forma maacutes sencilla de desplegar los servicios JAX-RS en un servidor de aplicacionescertificado como Java EE (en este caso Wildfly cumple con este requisito) o un contenedorstandalone de Servlet 3 (como por ejemplo Tomcat)

Pasemos a conocer la clase javaxwsrscoreApplication El uso de la claseApplication es la uacutenica forma portable de decirle a JAX-RS queacute servicios web (clasesanotadas con Path) asiacute como queacute otros elementos como filtros interceptoreshellip queremospublicar (desplegar)

La clase Application se define como

package javaxwsrscore

import javautilCollectionsimport javautilSet

public abstract class Application private static final SetltObjectgt emptySet = CollectionsemptySet()

public abstract SetltClassltgtgt getClasses()

public SetltObjectgt getSingletons() return emptySet

La clase Application es muy simple Como ya hemos indicado su propoacutesito es proporcionaruna lista de clases y objetos que queremos desplegar

El meacutetodo getClasses() devuelve una lista de clases de servicios web y proveedores JAX-RS Cualquier servicio JAX-RS devuelto por este meacutetodo sigue el modelo per-request queya hemos introducido en la sesioacuten anterior Cuando la implementacioacuten de JAX-RS determinaque una peticioacuten HTTP necesita ser procesada por un meacutetodo de una de estas clases secrearaacute una instancia de dicha clase durante la peticioacuten y se destruiraacute al finalizar la mismaEn este caso estamos delegando en el runtime JAX-RS la creacioacuten de los objetos Lasclases proveedoras son instanciadas por el contenedor JAX-RS y registradas una uacutenica vezpor aplicacioacuten

El meacutetodo getSingletons() devuelve una lista de servicios y proveedores web JAX-RSya instanciados Nosotros como programadores de las aplicaciones somos responsablesde crear estos objetos El runtime JAX-RS iteraraacute a traveacutes de la lista de objetos y los registraraacuteinternamente

Servicios Rest

62

Un ejemplo de uso de una subclase de Application podriacutea ser eacuteste

package orgexpertojava

import javaxwsrscoreApplicationimport javaxwsrsApplicationPath

ApplicationPath(rest)public class ComercioApplication extends Application

public SetltClassltgtgt getClasses() HashSetltClassltgtgt set = new HashSetltClassltgtgt() setadd(ClienteResourceclass) setadd(PedidoResourceclass) return set

public SetltObjectgt getSingletons() JsonWriter json = new JsonWriter() TarjetaCreditoResource servicio = new TarjetaCreditoResource()

HashSetltObjectgt set = new HashSet() setadd(json) setadd(servicio) return set

La anotacioacuten ApplicationPath define la base URL de la ruta para todos nuestrosservicios JAX-RS desplegados Asiacute por ejemplo accederemos a todos nuestros serviciosJAX-RS seraacuten desde la ruta rest cuando los ejecutemos En el ejemplo anterior estamosindicando que ClienteResource y PedidoResource son servicios per-request El meacutetodogetSingletons() devuelve el servicio de tipo TarjetaCreditoResource asiacute como el proveedorJsonWriter (que implementa la interfaz MessageBodyWriter)

Si tenemos al menos una implementacioacuten de la clase Application anotada conApplicationPath esta seraacute detectada y desplegada automaacuteticamente por el servidor deaplicaciones

Podemos aprovechar completamente esta capacidad para escanear y detectarautomaacuteticamente nuestros servicios si tenemos implementada una subclase de Applicationpero dejamos que getSingletons() devuelva el conjunto vaciacuteo y no indicamos nada en elmeacutetodo getClasses() de esta forma

package orgexpertojava

import javaxwsrsApplicationPathimport javaxwsrscoreApplication

ApplicationPath(rest)public class ComercioApplication extends Application

Servicios Rest

63

En este caso el servidor de aplicaciones se encargaraacute de buscar en el directorio WEB-INFclasses y en cualquier fichero jar dentro del directorio WEB-INFlib A continuacioacuten antildeadiraacutecualquier clase anotada con Path o Provider a la lista de cosas que necesitan serdesplegadas y registradas en el runtime JAX-RS

Los servicios REST son atendidos por un servlet que es especiacutefico de la implementacioacutenJAX-RS utilizada por el servidor de aplicaciones El servidor wildfly utiliza la implementacioacutende JAX-RS 20 denomindada resteasy (otra implementacioacuten muy utilizada es jersey porejemplo con el servidor de aplicaciones Glassfish) El runtime de JAX-RS contiene un servletinicializado con un paraacutemetro de inicializacioacuten de tipo javaxwsrsApplication cuyo valor seraacuteinstanciado automaacuteticamente por el servidor de aplicaciones con el nombre de la subclasede Application que sea detectada en el war de nuestra aplicacioacuten

Configuracioacuten mediante un fichero webxml

En la sesioacuten anterior no hemos utilizado de forma expliacutecita la clase Application para configurarel despliegue En su lugar hemos indicado esta informacioacuten en el fichero webxml

ltweb-app version=30 xmlns=httpjavasuncomxmlnsjavaee xmlnsxsi=httpwwww3org2001XMLSchema-instance xsischemaLocation=httpjavasuncomxmlnsjavaee httpjavasuncomxmlnsjavaeeweb-app_3_0xsdgt lt-- Con estas liacuteneas el servidor es el responsable de antildeadir el servlet correspondiente de forma automaacutetica Si en nuestro war tenemos clases anotadas con anotaciones JAX-RS para recibir invocaciones REST eacutestas seraacuten detectadas y registradas--gt ltservlet-mappinggt ltservlet-namegtjavaxwsrscoreApplicationltservlet-namegt lturl-patterngtrestlturl-patterngt ltservlet-mappinggtltweb-appgt

Esta configuracioacuten es equivalente a incluir una subclase de Application sin sobreescribir losmeacutetodos correspondientes En este caso se antildeade de forma dinaacutemica el servlet que sirvelas peticiones REST con el nombre javaxwsrscoreApplication de forma que se detectenautomaacuteticamente todas las clases de recursos y clases proveedoras empaquetadas en el warde la aplicacioacuten

Configuracioacuten en un contenedor que no disponga de una implementacioacuten JAX-RS

Si queremos hacer el despliegue sobre servidores de aplicaciones o servidores web queden soporte a una especificacioacuten de servlets con una versioacuten inferior a la 30 tendremosque configurar MANUALMENTE el fichero webxml para que cargue el servlet de nuestraimplementacioacuten propietaria de JAX-RS (cuyos ficheros jar deberemos incluir en el directorioWEB-INFlib de nuestro war) Un ejemplo de configuracioacuten podriacutea ser eacuteste

Configuracioacuten del fichero webxml (directorio de fuentes webappWEB-INFwebxml)

ltxml version=10gtltweb-appgt ltservletgt

Servicios Rest

64

ltservlet-namegtJAXRSltservlet-namegt ltservlet-classgt orgjbossresteasypluginsserverservletHttpServletDispatcher ltservlet-classgt ltinit-paramgt ltparam-namegt javaxwsrsApplication ltparam-namegt ltparam-valuegt orgexpertoJavaComercioApplication ltparam-valuegt ltinit-paramgt ltservletgt

ltservlet-mappinggt ltservlet-namegtJAXRSltservlet-namegt lturl-patterngtrestlturl-patterngt ltservlet-mappinggtltweb-appgt

En la configuracioacuten anterior estamos indicando de forma expliacutecita el servlet JAX-RS que recibelas peticiones REST que a su vez utilizaraacute la clase Application para detectar queacute servicios yproveedores REST seraacuten desplegados en el servidor

Tambieacuten seraacute necesario incluir la libreriacutea con la implementacioacuten JAX-RS 20 de formaexpliacutecita en el war generado (recordemos que para ello tendremos que utilizar la etiquetaltscopegtcompileltscopegt para que se antildeadan los jar correspondientes)

Libreriacutea con la implementacioacuten de JAX-RS 20

ltdependencygt ltgroupIdgtjavaxltgroupIdgt ltartifactIdgtjavaee-web-apiltartifactIdgt ltversiongt70ltversiongt ltscopegtcompileltscopegtltdependencygt

Servicios Rest

65

26 Ejercicios

Para esta sesioacuten antildeadiremos un nuevo moacutedulo en el que implementaremos un servicio restincorporando los conceptos que hemos explicado durante la sesioacuten En concreto

bull Creamos un moacutedulo Maven con IntelliJ (desde el directorio ejercicios-rest-expertojava ) con el arquetipo webapp-javaee7 tal y como hemos visto en losapuntes de la sesioacuten Las coordenadas del artefacto Maven seraacuten

GroupId orgexpertojava

ArtifactId s2-foro-nuevo

version 10-SNAPSHOT

bull Configuramos el pommxl del proyecto para poder compilar empaquetar y desplegarnuestro servicio en el servidor de aplicaciones Wildfly Consulta los apuntes para ver cuaacuteldebe ser el contenido de las etiquetas ltpropertiesgt ltdependenciesgt y ltbuildgt

bull Vamos a estructurar los fuentes (directorio srcmainjava) de nuestro proyecto en lossiguientes paquetes

orgexpertojavadatos contendraacute clases relacionadas con los datos a los que accedenuestra aplicacioacuten rest Por simplicidad almacenaremos en memoria los datos denuestra aplicacioacuten

orgexpertojavamodelo contiene las clases de nuestro modelo de objetos que seraacutenclases java con atributos y sus correspondientes getters y setters

orgexpertojavarest contiene los recursos JAX-RS que implementan nuestrosservicios rest asiacute como las clases necesarias para automatizar el despliegue de dichosrecursos

Creacioacuten de un recurso creacioacuten y consulta de temas en el foro (05 puntos)

Vamos a crear un recurso JAX-RS al que denominaremos TemasResource (en el paqueteorgexpertojavarest ) En el siguiente ejercicio al configurar la aplicacioacuten haremos que esterecurso sea un singleton Nuestro recurso gestionaraacute sus propios datos en memoria Porejemplo podemos utilizar un atributo private de tipo HashMap en el que almacenaremos lostemas cada uno con un identificador numeacuterico como clave Tambieacuten necesitaremos un atributopara generar las claves para cada uno de los temas Por ejemplo

private MapltInteger Temagt temasDB = new HashMapltInteger Temagt()private int contadorTemas = 0

Fiacutejate que si utilizamos los tipos HashMap e int podemos tener problemasde concurrencia si muacuteltiples usuarios estaacuten realizando peticiones paracrear yo consultar los temas del foro En una situacioacuten real deberiacuteamosutilizar en su lugar los tipos ConcurrentHasMap y AtomicInteger paraevitar el que dos usuarios intentaran crear un nuevo tema con la mismaclave perdieacutendose asiacute uno de los dos temas creados Al tratarse de unejercicio en el que solamente tendremos un cliente no nos plantearaacuteninguacuten problema el trabajar con HashMap e int por lo que podeacuteis elegircualquiera de las dos opciones para realizar el ejercicio

Servicios Rest

66

bull Nuestro recurso estaraacute accesible en el servidor en la ruta temas (relativa a la raiacutez delcontexto de nuestra aplicacioacuten y a la ruta de nuestro servlet JAX-RS que determinaremoscon la anotacioacuten ApplicationPath de nuestra clase Application)

bull En el paquete orgexpertojavamodelo crearemos la clase Tema con los atributosprivados

int idString nombre

y sus correspondientes getters y setters

setId() getId()setNombre() getNombre()

bull Implementamos un primer meacutetodo en el recurso TemasResource denominadocreaTema() para poder crear un nuevo tema en el foro Dicho meacutetodo atenderaacutepeticiones POST a nuestro servicio Los datos de entrada (cadena de caracteres querespresenta el nombre del tema) se pasan a traveacutes de un formulario html en el que tenemosuna uacutenica entrada denominada nombre

Puedes incluir el siguiente contenido en el fichero indexhtml para introducir los datosdesde el navegador

ltDOCTYPE htmlgtlthtmlgtltheadgt lttitlegtStart Pagelttitlegt ltmeta http-equiv=Content-Type content=texthtml charset=UTF-8gtltheadgtltbodygtlth1gtAlta de temas en el foro lth1gtltform action=s2-foro-nuevoresttemas method=postgt Nombre del tema ltinput type=text name=nombre gtltbr gt

ltinput type=submit value=Enviar gtltformgtltbodygtlthtmlgt

Cada nuevo Tema creado se antildeadiraacute a nuestra base de datos en memoria temasDB juntocon un identificador numeacuterico (que se iraacute incrementando para cada nueva instancia creada)

bull Implementamos un segundo meacutetodo para consultar los temas creados en el foro Elmeacutetodo se denominaraacute verTemasTodos() y devuelve (en formato texto) todos los temasactualmente creados Dado que puede haber un gran nuacutemero de ellos vamos a permitirque el usuario decida cuaacutentos elementos como maacuteximo quiere consultar a partir de unaposicioacuten determinada Por defecto si no se indica esta informacioacuten se mostraraacuten comomaacuteximo los primeros 8 temas registrados en el foro Si el identificador a partir del cualqueremos iniciar la consulta es mayor que el nuacutemero de temas almacenados entoncesdevolveremos la cadena No es posible atender la consulta Ejemplos de URIs que aceptadicho meacutetodo son

Servicios Rest

67

temas

en este caso y suponiendo que hayamos creado solamente los tres temas del apartadoanterior el resultado seriacutea

Listado de temas del 1 al 81 animales2 plantas3 ab

temasinicio=2amptotal=2

el resultado seriacutea

Listado de temas del 2 al 32 plantas3 ab

temasinicio=7amptotal=1

el resultado seriacutea

No es posible atender la consulta

Como ya hemos comentado las URIs indicadas en este ejercicio sonrelativas a la raiacutez del contexto de nuestra aplicacioacuten y a la ruta especificadapara nuestros servicios rest Recuerda que si has configurado el pomxmlcomo en la sesioacuten anterior la raiacutez del contexto de la aplicacioacuten vendraacutedada por el valor de la etiqueta ltfinalNamegt anidada en ltbuildgt Ennuestro caso deberiacutea ser s2-foro-nuevo Maacutes adelante fijaremos la rutade nuestros servicios rest como rest Por ejemplo la URI completa parael uacuteltimo apartado seriacutea httplocalhost8080s2-foro-nuevoresttemasinicio=7amptotal=1

Despliegue y pruebas del recurso (05 puntos)

Vamos a construir y desplegar nuestro servicio en el servidor de aplicaciones Para ello vamosa utilizar una subclase de Application que antildeadiremos en el paquete orgexpertojavarestLa ruta en la que se van a servir nuestras peticiones rest seraacute rest Fiacutejate que el recursoque hemos creado es el encargado de gestionar (crear modificarhellip) sus propios datosPor lo tanto necesitamos que nuestro recurso REST sea un singleton Implementa la claseForoApplication y realiza la construccioacuten y despliegue del proyecto A continuacioacutenprueba el servicio utilizando postman Puedes probar la insercioacuten de temas utilizando tambieacutenel formulario a traveacutes de la URI httplocalhost8080s2-foro-nuevo Podemos utilizar lasentradas del apartado anterior de forma que comprobemos que se crean correctamentelos temas animales plantas y ab y que obtenemos los listados correctos tanto si noindicamos el inicio y total de elementos como si decidimos mostrar los temas desde el 2 hastael 3

Servicios Rest

68

Cuando utilices el cliente IntelliJ para probar meacutetodos POST debesproporcionar un Request Body no vaciacuteo En este caso como en lapropia URI incluimos el contenido del mensaje que es el nombre del temaque queremos antildeadir al foro tendraacutes que seleccionar Text aunque norellenemos el campo correspondiente De no hacerlo asiacute obtendremoscomo respuesta un cuerpo de mensaje vaciacuteo y la cabecera de respuestaHTTP11 415 Unsupported Media Type

Muacuteltiples consultas de los temas del foro (05 puntos)

Implementa tres nuevas consultas de los temas del foro de forma que

bull Se pueda realizar una consulta de un tema concreto a partir de su identificador numeacuterico(el meacutetodo solamente debe admitir identificadores formados por uno o maacutes diacutegitos) Si eltema consultado no existe se debe devolver una excepcioacuten con la cabecera de respuestaHTTP11 404 Not Found Por ejemplo

temas2

Debe devolver lo siguiente

Ver el tema 2plantas

temas4

Obtenemos como respuesta un cuerpo de mensaje vaciacuteo y la cabecera de respuestaHTTP11 404 Not Found

bull Se pueda realizar una consulta de los temas que comiencen por uno de los siguientescaracteres a b c oacute d Por ejemplo teniendo en cuenta que hemos introducido los temasanteriores

temasa

Debe devolver lo siguiente

Listado de temas que comienzan por aanimales

temasd

Debe devolver Listado de temas que comienzan por d

bull Se pueda realizar una consulta de los temas que contengan una subcadena de caracteresPor ejemplo teniendo en cuenta que hemos introducido los temas anteriores

temasma + Debe devolver lo siguiente

Listado de temas que contienen la subcadena maanimales

Servicios Rest

69

Creacioacuten de subrecursos (05 puntos)

Vamos a crear el subrecurso MensajesResource (en el paquete orgexpertojavarest)de forma que este recurso gestione la creacioacuten y consulta de mensajes para cada unode los temas del foro Este subrecurso debe atender peticiones desde rutas del tipotemasidentificadorTemamensajes siendo identificadorTema la clave numeacutericaasociada a uno de los temas almacenados

bull En este caso nuestro subrecurso no seraacute un singleton por lo que necesitaremos almacenarlos mensajes en otra clase diferente (ya que crearemos una nueva instancia del recursopara cada peticioacuten) La clase DatosEnMemoria (en el paquete orgexpertojavadatos)seraacute la encargada de almacenar en memoria la informacioacuten de los mensajes publicadospara cada tema Por ejemplo puedes utilizar los siguientes campos estaacuteticos paragestionar los mensajes

public static MapltMensaje Stringgt mensajesDB = new HashMapltMensaje Stringgt()

La clave seraacute el propio mensaje (objeto Mensaje que se asociaraacute al tema correspondiente)

public static int contadorMen = 0

Como ya hemos comentado puedes usar ConcurrentHashMap yAtomicInteger en lugar de los tipos anteriores para evitar problemas deconcurrencia

bull En el paquete orgexpertojavadatos crearemos la clase Mensaje con los atributosprivados

int idString textoString autor=anonimo

y sus correspondientes getters y setters

setId() getId()setTexto() getTexto()setAutor() getAutor()

bull Vamos a crear un meacutetodo para poder realizar la publicacioacuten de un mensaje de texto en elforo en uno de los temas ya creados Independientemente del tipo de peticioacuten realizadasobre los mensajes si el tema indicado en la URI no existe lanzaremos la excepcioacutenWebApplicationException(ResponseStatusNOT_FOUND) Veamos alguacuten ejemplo

Deberemos poder realizar una peticioacuten POST a temas1mensajes con el cuerpo demensaje = Mensaje numero 1 El mensaje creado por defecto tendraacute asociado el autoranonimo

Servicios Rest

70

Si realizamos una peticioacuten para antildeadir un mensaje a la URI temas9mensajesdeberiacuteamos obtener como cabecera de respuesta HTTP11 404 Not Foundindependientemente del cuerpo del mensaje

bull Vamos a crear un meacutetodo para realizar una consulta de todos los mensajes publicados enun tema concreto Por ejemplo

Una peticioacuten GET a temas1mensajes deberiacutea dar como resultado

Lista de mensajes para el tema animales1 Mensaje anonimo

Si realizamos una peticioacuten GET a la URI temas9mensajes deberiacuteamos obtenercomo cabecera de respuesta HTTP11 404 Not Found independientemente delcuerpo del mensaje

bull Finalmente vamos a antildeadir dos nuevos meacutetodos para (a) antildeadir un nuevo mensajeen un tema concreto indicando el autor del mensaje Como restriccioacuten el nombre delautor deberaacute estar formado solamente por caracteres alfabeacuteticos utilizando mayuacutesculas ominuacutesculas y como miacutenimo tiene que tener un caracter y (b) consultar todos los mensajesque un determinado autor ha publicado en el foro en un tema determinado

Una peticioacuten POST a la URI temas1mensajespepe con el cuerpo de mensaje convalor mensaje de pepe deberiacutea crear un nuevo mensaje para el tema con identificador2 y devolver como resultado el nuevo id (yo la URI del nuevo recurso en la cabecerade respuesta Location si seguimos la ortodoxia REST) En caso de que devolvamos laURI del nuevo recurso podemos utilizar la orden

return Responsecreated(uriInfogetAbsolutePathBuilder()

segment(StringvalueOf(id))

build())

build()

Obtenemos el path absoluto de la uri que nos ha invocadoAntildeadimos el identificador id del nuevo recurso creadoConstruimos la nueva URIConstruimos el objeto Response

Veremos coacutemo manipular objetos de tipo Response en sesiones posteriores

Recuerda que para acceder al cuerpo de la peticioacuten basta con definir unparaacutemetro de tipo String JAX-RS automaacuteticamente lo instanciaraacute con elcuerpo de la peticioacuten como una cadena

bull Una peticioacuten GET a la URI temas1mensajesanonimo dariacutea como resultado

Lista de mensajes tema= animales y autor= anonimo

1 Mensaje anonimo

bull Una peticioacuten GET a la URI temas1mensajes dariacutea como resultado

Lista de mensajes para el tema animales

Servicios Rest

71

1 Mensaje anonimo2 mensaje de pepe

bull Una peticioacuten GET a la URI temas1mensajesroberto dariacutea como resultado

Lista de mensajes tema= animales y autor= roberto

Servicios Rest

72

3 Manejadores de contenidos Respuestas del servidor ymanejo de excepciones

En la sesioacuten anterior hemos hablado de coacutemo inyectar informacioacuten contenida en las cabecerasde las peticiones HTTP ahora nos detendremos en el cuerpo del mensaje tanto de la peticioacutencomo de la respuesta En el caso de las peticiones explicaremos el proceso de transformarlos datos de entrada en objetos Java para poder ser procesados por nuestros serviciosCon respecto a las respuestas proporcionadas por nuestros servicios analizaremos tanto loscoacutedigos de respuesta por defecto como la elaboracioacuten de respuestas complejas y manejo deexcepciones

31 Proveedores de entidades

JAX-RS define lo que se denominan proveedores de entidades que son clases queproporcionan servicios de mapeado entre las representaciones del cuerpo del mensaje HTTPy los correspondientes tipos java que utilizaremos en nuestros recursos (paraacutemetros en losmeacutetodos o bien como tipo de la respuesta de los mismos) Las entidades tambieacuten se conocencon el nombre de message payload o simplemente como payload y representan elcontenido del cuerpo del mensaje HTTP

ProvidersEl runtime de JAX-RS puede extenderse (ampliarse) utilizandoclases proveedoras (providers) suministradas por nuestra aplicacioacutenConcretamente JAX-RS nos proporciona un conjunto de interfaces quepodemos implementar en nuestra aplicacioacuten creando asiacute dichas clasesproveedoras de entidades (entity providers) La especificacioacuten de JAX-RS define un proveedor como una clase que implementa una o maacutesinterfaces JAX-RS (de entre un conjunto determinado) y que puedenanotarse con provider para ser descubiertas de forma automaacuteticapor el runtime de JAX-RS

Nuestra aplicacioacuten puede proporcionar su propio mapeado entre representaciones(tipos MIME) del mensaje de entrada y tipos Java implementando las interfacesMessageBodyWriter y MessageBodyReader convirtieacutendose asiacute en clases proveedorasde entidades (entity providers) Por ejemplo podemos tener nuestro propio proveedor deentidades para el formato XML o JSON de forma que utilizando las libreriacuteas de java paraprocesamiento XML o JSON (Java API for XML Processing JAXP1 y Java API for JSONProcessing JSON-P2) implementemos el serializadodeserializado del cuerpo del mensajeHTTP de entrada cuando eacuteste presente los tipos MIME applicationxml o application_jsonLas clases que realizan dichos mapeados son clases entity provider

Interfaz javaxwsrsextMessageBodyReader

La interfaz MessageBodyReader define el contrato entre el runtime de JAX-RS y loscomponentes que proporcionan servicios de mapeado desde diferentes representaciones(indicadas como tipos mime) al tipo Java correspondiente Cualquier clase que quieraproporcionar dicho servicio debe implementar la interfaz MessageBodyReader y debeanotarse con Provider para poder ser detectada de forma automaacutetica por el runtime deJAX-RS

1 httpswwwjcporgenjsrdetailsummaryid=2062 httpsjcporgenjsrdetailid=353

Servicios Rest

73

La secuencia loacutegica de pasos seguidos por una implementacioacuten de JAX-RS cuando se mapeael cuerpo de un mensaje HTTP de entrada a un paraacutemetro de un meacutetodo Java es la siguiente

1 Se obtiene el media type de la peticioacuten (valor de la cabecera HTTP Content-Type ) Sila peticioacuten no contiene una cabecera Content-Type se usaraacute applicationoctet-stream

2 Se identifica el tipo java del paraacutemetro cuyo valor seraacute mapeado desde el cuerpo delmensaje

3 Se localiza la clase MessageBodyReader que soporta el media type de la peticioacuten y seusa su meacutetodo readFrom() para mapear el contenido del cuerpo del mensaje HTTP enel tipo Java que corresponda

4 Si no es posible encontrar el MessageBodyReader adecuado se genera la excepcioacutenNotSupportedException con el coacutedigo 405

Interfaz javaxwsrsextMessageBodyWriter

La interfaz MessageBodyWriter define el contrato entre el runtime de JAX-RS ylos componentes que proporcionan servicios de mapeado desde un tipo Java a unarepresentacioacuten determinada Cualquier clase que quiera proporcionar dicho servicio debeimplementar la interfaz MessageBodyWriter y debe anotarse con Provider para poderser detectada de forma automaacutetica por el runtime de JAX-RS

La secuencia loacutegica de pasos seguidos por una implementacioacuten de JAX-RS cuando se mapeaun valor de retorno de un meacutetodo del recurso a una entidad del cuerpo de un mensaje HTTPes la siguiente

1 Se obtiene el objeto que seraacute mapeado a la entidad del cuerpo del mensaje

2 Se determina el media type de la respuesta

3 Se localiza la clase MessageBodyWriter que soporta el objeto que seraacute mapeado a laentidad del cuerpo del mensaje HTTP y se utiliza su meacutetodo writeTo() para realizardicho mapeado

4 Si no es posible encontrar el MessageBodyWriter adecuado se generala excepcioacuten InternalServerErrorException (que es una subclase deWebApplicationException ) con el coacutedigo 500

32 Proveedores de entidad estaacutendar incluidos en JAX-RS

Cualquier implementacioacuten de JAX-RS debe incluir un conjunto de implementaciones deMessageBodyReader y MessageBodyWriter de forma predeterminada para ciertascombinaciones de tipos Java y media types

Table 3 Proveedores de entidades estaacutendar de una implementacioacuten JAX-RS

Tipo Java Media Type

byte[] (Cualquier media type)

javalangString (Cualquier media type)

javaioInputStream (Cualquier media type)

Servicios Rest

74

Tipo Java Media Type

javaioReader (Cualquier media type)

javaioFile (Cualquier media type)

javaxactivationDataSource (Cualquier media type)

javaxxmltransformSource textxml applicationxml application+xml(tipos basados en xml)

javaxxmlbindJAXBElement andapplication-supplied JAXB classes

textxml applicationxml application+xml(tipos basados en xml)

MultivaluedMapltStringStringgt applicationx-www-form-urlencoded(Contenido de formularios)

StreamingOutput (Cualquier media type) (SoacuteloMessageBodyWriter )

javalangBoolean javalangCharacterjavalangNumber

textplain

A continuacioacuten comentaremos algunos de estos proveedores de entidades estaacutendar oconversores por defecto que permiten convertir el cuerpo del mensaje HTTP a objetos Javade diferentes tipos y viceversa

javaxwsrscoreStreamingOutput

StreamingOutput es una interfaz callback que implementamos cuando queremos tratar comoun flujo continuo (streaming) el cuerpo de la respuesta Constituye una alternativa ligera aluso de MessageBodyWriter

public interface StreamingOutput void write(OutputStream output) throws IOException WebApplicationException

Implementamos una instancia de esta interfaz y la utilizamos como tipo de retorno denuestros meacutetodos de recursos Cuando el runtime de JAX-RS estaacute listo para escribir elcuerpo de respuesta del mensaje se invoca al meacutetodo write() de la instancia deStreamingOutput Veamos un ejemplo

Path(miservicio) public class MiServicio GET Produces(textplain) StreamingOutput get() return new StreamingOutput() public void write(OutputStream output) throws IOException WebApplicationException outputwrite(hello worldgetBytes())

Hemos utilizado una clase interna anoacutenima que implementa la interfaz StreamingOutputen lugar de crear una clase puacuteblica separada La razoacuten de utilizar una clase interna es porque

Servicios Rest

75

en este caso al contener tan pocas liacuteneas de coacutedigo resulta beneficioso mantener dicha loacutegicadentro del meacutetodo del recurso JAX-RS de forma que el coacutedigo sea maacutes faacutecil de seguirNormalmente no tendremos necesidad de reutilizar la loacutegica implementada en otros meacutetodospor lo que no tiene demasiado sentido crear otra clase especiacutefica

iquestY por queacute no inyectamos un OutputStream directamente iquestPor queacute necesitamos un objetocallback La razoacuten es que asiacute dejamos que el runtime de JAX-RS maneje la salida de lamanera que quiera Por ejemplo por razones de rendimiento puede ser conveniente que JAX-RS utilice un thread para responder diferente del thread de peticioacuten

javaioInputStream javaioReader

Para leer el cuerpo de un mensaje de entrada podemos utilizar las clases InputStream oReader Por ejemplo

Path()public class MiServicio PUT Path(dato) public void modificaDato(InputStream is) byte[] bytes = readFromStream(is) String input = new String(bytes) Systemoutprintln(input)

private byte[] readFromStream(InputStream stream) throws IOException ByteArrayOutputStream baos = new ByteArrayOutputStream() byte[] buffer = new byte[1000] int wasRead = 0 do wasRead = streamread(buffer) if (wasRead gt 0) baoswrite(buffer 0 wasRead) while (wasRead gt -1) return baostoByteArray()

En este caso estamos leyendo bytes a partir de un javaioInputStream para convertirloen una cadena de caracteres que mostramos por pantalla

En el siguiente ejemplo creamos un javaioLineNumberReader a partir de un objetoReader e imprimimos cada liacutenea del cuerpo del mensaje de entrada

PUTPath(maslineas)public void putMasLineas(Reader reader) LineNumberReader lineReader = new LineNumberReader(reader) do String line = lineReaderreadLine() if (line = null) Systemoutprintln(line)

Servicios Rest

76

while (line = null)

No estamos limitados solamente a utilizar instancias de InputStream yo Reader paraleer el cuerpo de los mensajes de entrada Tambieacuten podemos devolver dichos objetos comorespuesta Por ejemplo

Path(fichero)public class FicheroServicio private static final String basePath =

GET Path(rutafichero ) Produces(textplain) public InputStream getFichero(PathParam(rutafichero) String path) FileInputStream is = new FileInputStream(basePath + path) return is

Aquiacute estamos inyectando un valor PathParam para crear una referencia a un fichero realde nuestro disco duro Creamos una instancia de javaioFileInputStream a partir delvalor de la ruta inyectada como paraacutemetro y la devolvemos como cuerpo de nuestro mensajede respuesta La implementacioacuten de JAX-RS leeraacute la respuesta de este stream de entrada y laalmacenaraacute en un buffer para posteriormente escribirla de forma incremental en el stream desalida de la respuesta En este caso debemos especificar la anotacioacuten Produces para quela implementacioacuten de JAX-RS conozca el valor que debe asignar a la cabecera Content-Type en la respuesta

javaioFile

Se pueden utilizar instancias de la clase javaioFile para entrada y salida decualquier MIME-TYPE (especificado en Content-Type yo Accept y en las anotacionesProduces yo Consumes ) El siguiente coacutedigo por ejemplo devuelve una referencia aun fichero en nuestro disco

Path(fichero)public class FicheroServicio private static final String baseRuta =

GET Path(rutafichero ) Produces(textplain) public File getFichero(PathParam(rutafichero) String ruta) return new File(baseRuta + ruta)

En este caso inyectamos el valor de la ruta del fichero con la anotacioacuten PathParam A partirde dicha ruta creamos un objeto javaioFile y lo devolvemos como cuerpo del mensaje

Servicios Rest

77

de respuesta La implementacioacuten JAX-RS leeraacute la informacioacuten abriendo un InputStreambasado en esta referencia al fichero y la escribiraacute en un buffer Posteriormente y de formaincremental volveraacute a escribir el contenido del buffer en el stream de salida de la respuesta Aligual que en el ejemplo anterior debemos especificar la anotacioacuten Produces para que JAX-RS sepa coacutemo rellenar la cabecera Content-Type de la respuesta

Tambieacuten podemos inyectar instancias de javaioFile a partir del cuerpo del mensaje dela peticioacuten Por ejemplo

POSTPath(masdatos)public void post(File fichero) Reader reader = new Reader(new FileInputStream(fichero)) LineNumberReader lineReader = new LineNumberReader(reader) do String line = lineReaderreadLine() if (line = null) Systemoutprintln(line) while (line = null)

En este caso la implementacioacuten de JAX-RS crea un fichero temporal en el disco para laentrada Lee la informacioacuten desde el buffer de la red y guarda los bytes leiacutedos en este ficherotemporal En el ejemplo los datos leiacutedos desde la red estaacuten representados por el el objetoFile inyectado por el runtime de JAX-RS (recuerda que soacutelo puede haber un paraacutemetro sinanotaciones en los meacutetodos del recurso y que eacuteste representa el cuerpo del mensaje de lapeticioacuten HTTP) A continuacioacuten el meacutetodo post() crea un javaioFileInputStreama partir del objeto File inyectado Finalmente utilizamos eacuteste stream de entrada para crearun objeto LineNumberReader y mostrar los datos por la consola

byte[]

Podemos utilizar un array de bytes como entrada y salida para cualquier tipo especificadocomo media-type A continuacioacuten mostramos un ejemplo

Path()public class MiServicio GET Produces(textplain) public byte[] get() return hello worldgetBytes()

POST Consumes(textplain) public void post(byte[] bytes) Systemoutprintln(new String(bytes))

Para cualquier meacutetodo de recurso JAX-RS que devuelva un array de bytes debemosespecificar la anotacioacuten Produces para que JAX-RS sepa queacute valor asignar a la cabeceraContent-Type

Servicios Rest

78

String char[]

La mayor parte de formatos en internet estaacuten basados en texto JAX-RS puede convertircualquier formato basado en texto a un String o a cualquier array de caracteres Porejemplo

Path()public class MiServicio GET Produces(applicationxml) public String get() return ltcustomergtltnamegtSergio Garcialtnamegtltcustomergt

POST Consumes(textplain) public void post(String str) Systemoutprintln(str)

Para cualquier meacutetodo de recurso JAX-RS que devuelva un Sring o un array de caracteresdebemos especificar la anotacioacuten Produces para que JAX-RS sepa que valor asignar a lacabecera Content-Type

MultivaluedMapltString Stringgt y formularios de entrada

Los formularios HTML son usados habitualmente para enviar datos a servidores web Losdatos del formulario estaacuten codificados con el media type applicationx-www-form-urlencoded Ya hemos visto como utilizar la anotacioacuten FormParam para inyectarparaacutemetros individuales de un formulario de las peticiones de entrada Tambieacuten podremosinyectar una instancia de MultivaluedMapltString Stringgt que representa todos losdatos del formulario enviado en la peticioacuten Por ejemplo

Path() public class MiServicio POST Consumes(applicationx-www-form-urlencoded) Produces(applicationx-www-form-urlencoded) public MultivaluedMapltStringStringgt post( MultivaluedMapltString Stringgt form) el formulario tiene los campos fieldName1 y fieldName2 Systemoutprintln(formgetFirst(fieldName1)) Systemoutprintln(formgetFirst(fieldName2)) return form

En este coacutedigo nuestro meacutetodo post() acepta peticiones POST y recibe unMultivaluedMapltStringStringgt que contiene todos los datos de nuestro formularioEn este caso tambieacuten devolvemos una instancia de un formulario como respuesta

Los datos del formulario pueden representarse en el cuerpo de la petcioacuten como paresnombre=valor separados por amp Por ejemplo

Servicios Rest

79

fieldName1=valor20con20espaciosampfielName2=otroValor

Los espacios en blanco se codifican como 20 No es necesario poner comillas

33 Muacuteltiples representaciones de recursos

Por defecto un recurso RESTful se produce o consume con el tipo MIME Un recursoRESTful puede restringir los media types que soporta tanto en la peticioacuten como en larespuesta utilizando las anotaciones Consumes y Produces respectivamente Estasanotaciones pueden especificarse como ya hemos visto a nivel de clase o de meacutetodo derecurso Las anotaciones especificadas sobre el meacutetodo prevalecen sobre las de la clase Laausencia de estas anotaciones es equivalente a su inclusioacuten con el tipo MIME () es decirsu ausencia implica que se soporta cualquier tipo

A continuacioacuten mostramos un ejemplo en el que un Pedido puede producirse tanto en formatoxml como en formato json

GETPath(id)Produces(applicationxml applicationjson)public Pedido getPedido(PathParam(id)int id)

El meacutetodo getPedido() puede generar ambas representaciones para el pedido El tipo exactode la respuesta viene determinado por la cabecera HTTP Accept de la peticioacuten

Otro ejemplo en el que pueden consumirse varios tipos MIME puede ser el siguiente

POSTPath(id)Consumes(applicationxml applicationjson)public Pedido addPedido(PathParam(id)int id)

En este caso el formato consumido vendraacute dado por el valor de la cabecera HTTP Content-Type de la peticioacuten

JAX-RS 20 nos permite indicar la preferencia por un media type en el lado del servidorutilizando el paraacutemetro qs (quality on service) qs toma valores entre 0000 y 1000 eindica la calidad relativa de una representacioacuten comparado con el resto de representacionesdisponibles Una representacioacuten con un valor de qs de 0000 nunca seraacute elegido Unarepresentacioacuten sin valor para el paraacutemetro qs se asume que dicho valor es 1000

Ejemplo

POSTPath(id)Consumes(applicationxml qs=075 applicationjson qs=1)public Pedido addPedido(PathParam(id)int id)

Si un cliente realiza una peticioacuten y no manifiesta ninguna preferencia por ningunarepresentacioacuten en particular o con una cabecera Accept con valor application entonces el

Servicios Rest

80

servidor seleccionaraacute la representacioacuten con el valor de qs maacutes alto (en este caso applicationjson) Los valores de qs son relativos y como tales solamente son comparables con otrosvalores qs dentro de la misma instancia de la anotacioacuten Consumes (o Produces)

Los clientes pueden indicar tambieacuten sus preferencias utilizando otro factor relativo de calidaden forma de paraacutemetro denominado q El valor del paraacutemetro q se utiliza para ordenar elconjunto de tipos aceptados q toma valores entre 0000 y 1000 (maacutexima preferencia) Al igualque antes Los valores de q son relativos y como tales solamente son comparables con otrosvalores q dentro de la misma cabecera Accept o Content-type

Las preferencias del servidor (valores de los paraacutemetros qs) soacutelo se tienenen cuenta si el cliente acepta muacuteltiples media types con el mismo valorde q

Veamos un ejemplo

GETPath(id)Produces(applicationxml qs=1 applicationjson qs=075)public Pedido getPedido(PathParam(id)int id)

Supongamos que un cliente lanza una petcioacuten GET con una valor para la cabecera Acceptde application q=05 texthtml En este caso el servidor determina que los tipos MIMEapplicationxml y applicationjson tienen la misma preferencia por parte del cliente (con valorde 05) por lo tanto el servidor elegiraacute la representacioacuten applicationjson ya que tiene un valorde qs mayor

34 Introduccioacuten a JAXB

JAXB (Java Architecture for XML Binding) es una especificacioacuten Java antigua (JSR 2223) yno estaacute definida por JAX-RS JAXB es un framework de anotaciones que mapea clases Java aXML y esquemas XML Es extremadamente uacutetil debido a que en lugar de interactuar con unarepresentacioacuten abstracta de un documento XML podemos trabajar con objetos Java realesque estaacuten maacutes cercanos al dominio que estamos modelando JAX-RS proporciona soportepara JAXB pero antes de revisar los manejadores de contenidos JAXB incuidos con JAX-RSveamos una pequentildea introduccioacuten al framework JAXB

Como ya hemos dicho si queremos mapear una clase Java existente a XML podemos utilizarJAXB a traveacutes de un conjunto de anotaciones Veaacutemoslo mejor con un ejemplo

XmlRootElement(name=cliente)XmlAccessorType(XmlAccessTypeFIELD)public class Cliente XmlAttribute protected int id

XmlElement protected String nombre

public Customer()

3 httpsjcporgaboutJavacommunityprocessmreljsr222index2html

Servicios Rest

81

public int getId() return thisid public void setId(int id) thisid = id

public String getNombre() return thisnombre public void setNombre(String nombre thisnombre = nombre

La anotacioacuten javaxxmlbindannotationXmlRootElement se utiliza en clasesjava para denotar que representan elementos XML (etiqueta XML raiacutez) En este caso estamosdiciendo que la clase Java representa un documento XML que tiene como etiqueta raiacutezltclientegt Las clases java anotadas con XmlRootElement se denomina beansJAXB

La anotacioacuten javaxxmlbindannotationXmlAttribute la hemos asociado alcampo id de nuestra clase Cliente Esta anotacioacuten indica que el campo id de la clasedebe mapearse como el atributo id del elemento raiacutez ltclientegt del documento XML Laanotacioacuten XmlAttribute tiene un atributo name de forma que podemos especificar elnombre exacto del atributo XML dentro del documento Por defecto tiene el mismo nombreque el campo anotado

Hemos utilizado la anotacioacuten javaxxmlbindannotationXmlElement en el camponombre de la clase Cliente Esta anotacioacuten indica a JAXB que debe mapearse el camponombre como el elemento ltnombregt anidado en la etiqueta raiacutez ltclientegt Igual queantes podemos especificar el nombre concreto del elememto XML Por defecto toma el mismonombre que el correspondiente campo anotado

La anotacioacuten javaxxmlbindannotationXmlAccessorType permite controlar laserializacioacuten por defecto de los atributos de la clase Esta anotacioacuten soacutelo puede ser usadaconjuntamente con XmlRootElement (y alguna otra anotacioacuten que no mostramos aquiacute)Hemos usado como valor XmlAccessTypeFIELD lo que significa que por defecto se debenserializar todos los campos (fields) de la clase (esteacuten anotados o no) y las propiedades(properties) de la clase que tengan anotaciones JAXB (a menos que la anotacioacuten seaXMLTransient)

Si alguno de los campos de la clase no tiene anotaciones JAXB asociadas por defecto seserializaraacuten como elementos (etiquetas) en el documento XML correspondiente Seguacuten ladocumentacioacuten4 de JAXB un campo es una variable de instancia no estaacutetica (normalmenteprivada)

Las propiedades de la clase vienen dadas por las combinaciones gettersetter de los atributosde la clase El coacutedigo anterior tiene dos propiedades nombre (dado por el par getNombresetNombre_) e id (par getIdsetId) Normalmente se anotan los meacutetodos getter Dichaspropiedades no estaacuten anotadas por lo que JAXB no las serializaraacute

Al proceso de serializar (convertir) un objeto Java en un documento XMLse le denomina marshalling El proceso inverso la conversioacuten de XML aobjetos Java se denomina unmarshalling

Con las anotaciones anteriores un ejemplo de una instancia de nuestra clase Cliente conun id de 42 y el valor de nombre Pablo Martinez tendriacutea el siguiente aspecto

ltcliente id=42gt

4 httpsjaxbjavanetnonav226docsapi

Servicios Rest

82

ltnombreCompletogtPablo MartinezltnombreCompletogtltclientegt

Observamos que se han serializado los campos (variables de instancia de la clase)

Si no especificamos la anotacioacuten XmlAccessorType por defecto se utilizaraacute

XmlAccessorType(XmlAccessTypePUBLIC_MEMBER)

El valor XmlAccessTypePUBLIC_MEMBER indica a JAXB que se deben serializar todoslos campos puacuteblicos de la clase todos los campos anotados y todas las propiedades (paresgettersetter) a menos que esteacuten anotadas con XMLTransient

Tambieacuten podriacuteamos utilizar

XmlAccessorType(XmlAccessTypeNONE)

En este caso la anotacioacuten XmlAccessTypeNONE indica soacutelo se deben serializar aquellaspropiedades yo campos de la clase que esteacuten anotados

A continuacioacuten indicamos en forma de tabla el uso de los diferentes XmlAccessType

Table 4 Valores utilizados para XmlAccessorType() conjuntamentecon XmlRootElement()

Valor Significado

XmlAccessTypePUBLIC_MEMBER Serializacioacuten por defecto si no se especificaXmlAccessorType() Serializa laspropiedades (pares gettersetter) y campospuacuteblicos a menos que esteacuten anotados conXMLTransient Si alguacuten campo nopuacuteblico estaacute anotado tambieacuten se serializa

XmlAccessTypeFIELD Serializa todos los campos (puacuteblicos oprivados) a menos que esteacuten anotados conXMLTransient

XmlAccessTypeNONE Solamente serializa aquellos camposy propiedades que esteacuten anotadas conanotaciones JAXB

XmlAccessTypePROPERTY Serializa cada par gettersetter a menosque esteacuten anotados con XMLTransient Si alguacuten campo (no puacuteblico) estaacute anotadotambieacuten se serializa

Podemos utilizar la anotacioacuten XmlElement para anidar otras clases anotadas con JAXBPor ejemplo supongamos que queremos antildeadir una clase Direccion a nuestra claseCliente

XmlRootElement(name=direccion)

Servicios Rest

83

XmlAccessorType(XmlAccessTypeFIELD)public class Direccion XmlElement protected String calle

XmlElement protected String cludad

XmlElement protected String codPostal

getters y setters

Simplemente tendriacuteamos que antildeadir el campo de tipo Direccion a nuestra clase Clientede la siguiente forma

XmlRootElement(name=cliente)XmlAccessorType(XmlAccessTypeFIELD)public class Cliente XmlAttribute protected int id

XmlElement protected String nombreCompleto

XmlElement protected Direccion direccion

public Customer()

getters y setters

En este caso una instancia de un Cliente con valores id =56 nombre =RicardoLopez calle =calle del oso 35 ciudad =Alicante y coacutedigo_postal =01010 seriacuteaserializado como

ltcliente id=56gt ltnombregtRicardo Lopezltnombregt ltdirecciongt ltcallegtcalle del oso 35ltcallegt ltciudadgtAlicanteltciudadgt ltcodPostalgt01010ltcodPostalgt ltdirecciongt ltclientegt

Veamos otro ejemplo Supongamos que tenemos el recurso EstadoResource con meacutetodosque responden a peticiones http GET y PUT

Path(estado)

Servicios Rest

84

public class EstadoResource

private static EstadoBean estadoBean = new EstadoBean()

GET Produces(applicationxml) public EstadoBean getEstado() return estadoBean

PUT Consumes(applicationxml) public void setEstado(EstadoBean estado) thisestadoBean = estado

En este caso la clase EstadoBean debe utilizar anotaciones JAXB para poder ser convertidaautomaacuteticamente por el runtime de JAX-RS en un documento XML y viceversa

XmlRootElement(name = estadoImpresora)public class EstadoBean

public String estado = Idle public int tonerRestante = 25 public ListltTareaBeangt tareas = new ArrayListltTareaBeangt()

Por defecto la anotacioacuten XmlRootElement realiza la serializacioacuten de la claseEstadoBean en formato xml utilizando los campos puacuteblicos y propiedades definidas en laclase ( estado tonerRestante y tareas ) Vemos que el campo tareas a su vez esuna coleccioacuten de elementos de tipo TareaBean que tambieacuten necesitan ser serializados Acontinuacioacuten mostramos la implementacioacuten de la clase TareaBeanjava

XmlRootElement(name = tarea)public class TareaBean public String nombre public String estado public int paginas

public TareaBean()

public TareaBean(String nombre String estado int paginas) thisnombre = nombre thisestado = estado thispaginas = paginas

Para serializar la clase es necesario que la clase tenga un constructor sin paraacutemetros

Servicios Rest

85

Si accedemos al servicio anterior nos devolveraacute la informacioacuten sobre el estado de la siguienteforma (resultado devuelto por el meacutetodo getEstado() anotado con GET)

ltxml version=10 encoding=UTF-8 standalone=yesgtltestadoImpresoragt ltestadogtIdleltestadogt lttonerRestantegt25lttonerRestantegt lttareasgt ltnombregttextodocltnombregt ltestadogtimprimiendoltestadogt ltpaginasgt13ltpaginasgt lttareasgt lttareasgt ltnombregttexto2docltnombregt ltestadogten esperaltestadogt ltpaginasgt5ltpaginasgt lttareasgtltestadoImpresoragt

Podemos observar que se utiliza el elemento xml lttareasgt para representar cada una de lasinstancias de TareaBean las cuales forman parte de de la lista del campo tareas de nuestraclase EstadoBean

Vamos a usar las anotaciones XmlAttribute y XmlElement de la siguiente forma

XmlRootElement(name=estadoImpresora)public class EstadoBean XmlAttribute(name=valor) public String estado = Idle XmlAttribute(name=toner) public int tonerRestante = 25 XmlElement(name=tarea) public ListltTareaBeangt tareas = new ArrayListltTareaBeangt()

En este caso el XML resultante quedariacutea de la siguiente forma

ltestadoImpresora valor=Idle toner=25gt lttareagt ltnombregttextodocltnombregt ltestadogtimprimiendoltestadogt ltpaginasgt13ltpaginasgt lttareagt lttareagt ltnombregttexto2docltnombregt ltestadogten esperaltestadogt ltpaginasgt5ltpaginasgt lttareagtltestadoImpresoragt

Servicios Rest

86

Si no se indica lo contrario por defecto se convierten los campos a elementos del XML

En caso de que los campos no sean puacuteblicos etiquetaremos los getterscorrespondiente (propiedades de la clase)

Hemos visto que para las listas el nombre que especificamos en XmlElement se utilizapara nombrar cada elemento de la lista Si queremos que ademaacutes se incluya un elemento queenvuelva a toda la lista podemos utilizar la etiqueta XmlElementWrapper

XmlRootElement(name=estadoImpresora)public class EstadoBean

XmlAttribute(name=valor) public String estado = Idle

XmlAttribute(name=toner) public int tonerRestante = 25

XmlElementWrapper(name=tareas) XmlElement(name=tarea) public ListltTareaBeangt tareas = new ArrayListltTareaBeangt()

En este caso tendremos un XML como el que se muestra a continuacioacuten

ltestadoImpresora valor=Idle toner=25gt lttareasgt lttareagt ltnombregttextodocltnombregt ltestadogtimprimiendoltestadogt ltpaginasgt13ltpaginasgt lttareagt lttareagt ltnombregttexto2docltnombregt ltestadogten esperaltestadogt ltpaginasgt5ltpaginasgt lttareagt lttareasgtltestadoImpresoragt

Para etiquetar una lista tambieacuten podemos especificar distintos tipos de elemento seguacutenel tipo de objeto contenido en la lista Por ejemplo supongamos que en el ejemploanterior la clase TareaBean fuese una clase abstracta que tiene dos posible subclasesTareaSistemaBean y TareaUsuarioBean Podriacuteamos especificar una etiqueta distintapara cada elemento de la lista seguacuten el tipo de objeto del que se trate con la etiquetaXmlElements de la siguiente forma

XmlElementWrapper(name=tareas) XmlElements( XmlElement(name=usuariotype=TareaUsuarioBeanclass

Servicios Rest

87

XmlElement(name=sistematype=TareaSistemaBeanclass) public ListltTareaBeangt tareas = new ArrayListltTareaBeangt()

De esta forma podriacuteamos tener un XML como el siguiente

ltestadoImpresora valor=Idle toner=25gt lttareasgt ltusuariogt ltnombregttextodocltnombregt ltestadogtimprimiendoltestadogt ltpaginasgt13ltpaginasgt ltusuariogt ltsistemagt ltnombregttexto2docltnombregt ltestadogten esperaltestadogt ltpaginasgt5ltpaginasgt ltsistemagt lttareasgtltestadoImpresoragt

Hemos visto que por defecto se serializan todos los campos Si queremos excluir algunode ellos de la serializacioacuten podemos hacerlo anotaacutendolo con XmlTransient Comoalternativa podemos cambiar el comportamiento por defecto de la serializacioacuten de la claseetiquetaacutendola con XmlAccessorType Por ejemplo

XmlAccessorType(NONE)XmlRootElement(name=estadoImpresora)public class EstadoBean

En este uacuteltimo caso especificando como tipo NONE no se serializaraacute por defectoninguacuten campo soacutelo aquellos que hayamos anotado expliacutecitamente con XmlElement oXmlAttribute Los campos y propiedades (getters) anotados con estas etiquetas seserializaraacuten siempre Ademaacutes de ellos tambieacuten podriacuteamos especificar que se serialicenpor defecto todos los campos puacuteblicos y los getters ( PUBLIC_MEMBER ) todos los getters( PROPERTY ) o todos los campos ya sean puacuteblicos o privados ( FIELD ) Todos los que seserialicen por defecto sin especificar ninguna etiqueta lo haraacuten como elemento

Por uacuteltimo si nos interesa que toda la representacioacuten del objeto venga dada uacutenicamente porel valor de uno de sus campos podemos etiquetar dicho campo con XmlValue

Clase JAXBContext

Para serializar clases Java a y desde XML es necesario interactuar con la clasejavaxxmlbindJAXBContext Esta clase nos permite inspeccionar las clasesJava para comprender la estructura de nuestras clases anotadas Dichas clasesse utilizan como factoriacuteas para las interfaces javaxxmlbindMarshaller yjavaxxmlbindUnmarshaller Las instancias de Marshaller se utilizan para creardocumentos XML a partir de objetos Java Las instancias de Unmarshaller se utilizan para crearobjetos Java a partir de documentos XML A continuacioacuten mostramos un ejemplo de uso de

Servicios Rest

88

JAXB para convertir una instancia de la clase Cliente que hemos definido anteriormentea formato XML para posteriormente volver a crear el objeto de tipo Cliente

Cliente cliente = new Cliente()clientesetId(42)

clientesetNombre(Lucia Arg)

JAXBContext ctx = JAXBContextnewInstance(Clienteclass) StringWriter writer = new StringWriter()

ctxcreateMarshaller()marshal(cliente writer)

String modString = writertoString()

cliente = (Cliente)ctxcreateUnmarshaller()

unmarshal(new StringReader(modString))

Creamos e inicializamos una instancia de tipo ClienteInicializamos JAXBContext para que pueda analizar la clase `ClienteUtilizamos una instancia de Marshaller para escribir el objeto Cliente como unString de Java ( StringWriter es un stream de caracteres que utilizaremos paraconstruir un String )Utilizamos una instancia de Unmarshaller para recrear el objeto Cliente a partir delString que hemos obtenido en el paso anterior

La clase JAXBContext constituye un punto en entrada al API JAXB paralos clientes de nuestros servicios RESTful Proporciona una abstraccioacutenpara gestionar el enlazado (binding) de informacioacuten XMLJava necesariapara implementar las operaciones de marshalling y unmarshalling

bull Unmarshalling La clase Unmarshaller proporciona a la aplicacioacutencliente la capacidad para convertir datos XML en un aacuterbol de objetosJava

bull Marshalling La clase Marshaller proporciona a la aplicacioacutencliente la capacidad para convertir un aacuterbol con contenidos Java denuevo en datos XML

Una vez que hemos proporcionado una visioacuten general sobre coacutemo funciona JAXB vamos aver coacutemo se integra con JAX-RS

Manejadores JAX-RS para JAXB

La especificacioacuten de JAX-RS indica que cualquier implementacioacuten debe soportar deforma automaacutetica el proceso de marshalling y unmarshalling de clases anotadas conXmlRootElement A continuacioacuten mostramos un ejemplo de la implementacioacuten de unservicio que hace uso de dichos manejadores Para ello utilizamos la clase Cliente que hemosanotado previamente con XmlRootElement y que hemos mostrado en apartados anteriores

Path(clientes)public class ClienteResource GET Path(id)

Servicios Rest

89

Produces(applicationxml) public Cliente getCliente(PathParam(id) int id) Cliente cli = findCliente(id) return cust

POST Consumes(applicationxml) public void crearCliente(Cliente cli)

Como podemos ver una vez que aplicamos las anotaciones JAXB a nuestras clases Java ( eneste caso a la clase Cliente ) es muy sencillo intercambiar documentos XML entre el clientey nuestro servicio web Los manejadores JAXB incluidos en la implementacioacuten de JAX-RSgestionaraacuten el marshallingunmarshalling de cualquier clase con anotaciones JAXB para losvalores de Content-Type applicationxml textxml o application+xml Por defecto tambieacuten se encargan de la creacioacuten e inicializacioacuten de instancias JAXBContext Debido a que la creacioacuten de las instancias JAXBContext puede ser cara la implementacioacutende JAX-RS normalmente las guarda despueacutes de la primera inicializacioacuten

JAXB y JSON

JAXB es lo suficientemente flexible como para soportar otros formatos ademaacutes de XMLAunque la especificacioacuten de JAX-RS no lo requiere muchas implementaciones de JAX-RSincluyen adaptadores de JAXB para soportar el formato JSON ademaacutes de XML JSON es unformato basado en texto que puede ser interpretado directamente por Javascript De hechoes el formato de intercambio preferido por las aplicaciones Ajax

JSON es un formato mucho maacutes simple que XML Los objetos estaacuten entre llaves ycontienen pares de clavevalor separados por comas Los valores pueden ser cadenas decaracteres booleanos ( true o false ) valores numeacutericos o arrays de los tipos anteriores

Supongamos que tenemos la siguiente descripcioacuten de un producto en formato XML

ltxml version=10 encoding=UTF-8gtltproductogt ltidgt1ltidgt ltnombregtiPadltnombregt ltdescripciongtDispositivo moacutevilltdescripciongt ltpreciogt500ltpreciogtltproductogt

La representacioacuten JSON equivalente seriacutea

id1 nombreiPad descripcionDispositivo moacutevil precio500

Servicios Rest

90

El formato JSON asociado por ejemplo al siguiente objeto Cliente

ltcliente id=56gt ltnombregtRicardo Lopezltnombregt ltdirecciongt ltcallegtcalle del oso 35ltcallegt ltciudadgtAlicanteltciudadgt ltcodPostalgt01010ltcodPostalgt ltdirecciongt ltclientegt

quedariacutea como sigue

nombre Ricardo Lopez direccion calle calle del oso 35 ciudad Alicante codPostal 01010

Como vemos en el ejemplo el formato JSON consiste baacutesicamente en objetos situados entrellaves los cuales estaacuten formados por pares clavevalor separadas por comas Cada clavey valor estaacute separado por Los valores pueden cadenas de caracteres booleanos (true ofalse) valores numeacutericos o vectores de los tipos anteriores (los vectores estaacuten delimitadospor corchetes)

Podemos antildeadir el formato applicationjson o bienMediaTypeAPPLICATION_JSON a la anotacioacuten Produces en nuestros meacutetodos derecurso para generar respuestas en formato JSON

GETPath(get)Produces(applicationxmlapplicationjson)public Producto getProducto()

En este ejemplo se elegiraacute el formato JSON en la respuesta si el cliente realiza una peticioacutenGET que incluye en la cabecera

Accept applicationjson

El tipo de respuesta es de tipo Producto En este caso Producto debe ser un bean JAXBes decir una clase anotada con XmlRootElement

Los meacutetodos de recurso pueden aceptar tambieacuten datos JSON para clases con anotacionesJAXB

POSTPath(producto)Consumes(applicationxmlapplicationjson)

Servicios Rest

91

public Response crearProducto(Producto prod)

En este caso el cliente deberiacutea incluir la siguiente cabecera cuando realice la peticioacuten POSTque incluya los datos JSON anteriores en el cuerpo del mensaje

Content-Type applicationjson

Hablaremos con maacutes detalle del formato JSON en una sesioacuten posterior

Finalmente y como resumen de lo anterior

JAXB

Para dar soporte al serializado y deserializado XML se utilizan beans JAXBPorejemplo las clases anteriores EstadoBean TareaBean Cliente y Productoson ejemplos de beans JAXB La clase que se serializaraacute como un recurso XMLo JSON se tiene que anotar con XmlRootElement Si en alguacuten elemento deun recurso se retorna un elemento de esa clase y se etiqueta con los tiposProduces(MediaTypeAPPLICATION_XMLMediatypeAPPLICATION_JSON )eacuteste se serializa automaacuteticamente utilizando el tipo de representacioacuten aceptada por elcliente Se puede consultar el artiacuteculo de Lars Vogel5 para maacutes informacioacuten

35 Respuestas del servidor

Vamos a explicar cuaacutel es el comportamiento por defecto de los meacutetodos de recursos JAX-RS en particular veremos cuaacuteles son los coacutedigos de respuesta HTTP por defecto teniendo encuenta situaciones de eacutexito asiacute como de fallo

Dado que en ocasiones vamos a tener que enviar cabeceras de respuesta especiacuteficas antecondiciones de error complejas tambieacuten vamos a explicar coacutemo podemos elaborar respuestascomplejas utilizando el API JAX-RS

Coacutedigos de respuesta por defecto

Los coacutedigos de respuesta por defecto se corresponden con el comportamiento indicado en laespecificacioacuten6 de la definicioacuten de los meacutetodos HTTP 11 Vamos a examinar dichos coacutedigosde respuesta con el siguiente ejemplo de recurso JAX-RS

Path(clientes)public class ClienteResource

Path(id) GET Produces(applicationxml) public Cliente getCliente(PathParam(id) int id)

5 httpwwwvogelladearticlesJAXBarticlehtml6 httpwwww3orgProtocolsrfc2616rfc2616-sec9html

Servicios Rest

92

POST Produces(applicationxml) Consumes(applicationxml) public Cliente crearCliente(Cliente nuevoCli)

PUT Path(id) Consumes(applicationxml) public void updateCliente(PathParam(id) int id Cliente cli)

Path(id) DELETE public void borrarCliente(PathParam(id) int id)

Respuestas que indican eacutexito

Los nuacutemeros de coacutedigo de respuestas HTTP con eacutexito se situacutean en el rango de 200 a 399

bull Para los meacutetodos crearCliente() y getCliente() se devolveraacute una respuestaHTTP con el coacutedigo 200 OK si el objeto Cliente que devuelven dichos meacutetodos noes null

bull Para los meacutetodos crearCliente() y getCliente() se devolveraacute una respuestaHTTP con el coacutedigo 204 No Content si el objeto Cliente que devuelven dichosmeacutetodos es null El coacutedigo de respuesta 204 no indica una condicioacuten de error Solamenteavisa al cliente de que todo ha ido bien pero que el mensaje de respuesta no contienenada en el cuerpo de la misma Seguacuten eacutesto si un meacutetodo de un recurso devuelve void por defecto se devuelve el coacutedigo de respuesta 204 No content Este es el caso para losmeacutetodos updateCliente() y borrarCliente() de nuestro ejemplo

La especificacioacuten HTTP es bastante consistente para los meacutetodos PUT POST GET yDELETE Si una respuesta exitosa HTTP contiene informacioacuten en el cuerpo del mensaje derespuesta entonces el coacutedigo de respuesta es 200 OK Si el cuerpo del mensaje estaacute vaciacuteoentonces se debe devolver 204 No Content

Respuestas que indican una situacioacuten de fallo

Es habitual que las respuestas fallidas se programen de forma que se lance una excepcioacutenLo veremos en un apartado posterior Aquiacute comentaremos algunas condiciones de error pordefecto

Los nuacutemeros de coacutedigo de error de respuesta estaacutendar en HTTP se situacutean en el rango entre400 y 599 En nuestro ejemplo si un cliente se equivoca tecleando la URI y eacutesta queda porejemplo como httphellipcliente entonces el servidor no encontraraacute ninguacuten meacutetodo delrecurso que pueda servir dicha peticioacuten (la URI correcta seriacutea httphellipclientes ) Eneste caso se enviaraacute como respuesta el coacutedigo 404 Not Found

Para los meacutetodos getCliente() y crearCliente() de nuestro ejemplo si el clientesolicita una respuesta con el tipo MIME texthtml entonces la implementacioacuten de JAX-RS devolveraacute automaacuteticamente 406 Not Acceptable con un mensaje de respuesta con elcuerpo de dicho mensaje vaciacuteo Esta respuesta indica que JAX-RS puede encontrar una rutade URI relativa que coincide con la peticioacuten pero no encuentra ninguacuten meacutetodo del recursoque devuelva la respuesta con ese tipo MIME

Servicios Rest

93

Si el cliente invoca una peticioacuten HTTP sobre una URI vaacutelida para la que no se puedeencontrar un meacutetodo de recurso asociado entonces el runtime de JAX-RS devolveraacute el coacutedigo405 Method Not Allowed Asiacute en nuestro ejemplo si el cliente solicita una operacioacuten PUTGET o DELETE sobre la URI clientes obtendraacute como respuesta 405 Method NotAllowed puesto que POST es el uacutenico meacutetodo HTTP que puede dar soporte a dicha URILa implementacioacuten de JAX-RS tambieacuten devolveraacute una cabecera de respuesta Allow con lalista de meacutetodos HTTP que pueden dar soporte a dicha URI Por lo tanto si nuestra aplicacioacutencliente realiza la siguiente peticioacuten de entrada

GET clientes

el servidor devolveraacute la siguiente respuesta

HTTP11 405 Method Not AllowedAllow POST

Elaboracioacuten de respuestas con la clase Response

Como ya hemos visto por ejemplo para peticiones GET si todo va bien se estaraacute devolviendoun coacutedigo de respuesta 200 (Ok) junto con el contenido especificado en el tipo de datosutilizado en cada caso Si devolvemos void el coacutedigo de respuesta seraacute 204 (No Content)

Sin embargo en ocasiones el servicio web que estamos disentildeando no puede implementarseutilizando el comportamiento por defecto de peticioacutenrespuesta inherente a JAX-RS En estoscasos necesitaremos controlar de forma expliacutecita la respuesta que se le enviacutea al cliente (cuerpodel mensaje de la respuesta HTTP) Por ejemplo cuando creamos un nuevo recurso con POSTdeberiacuteamos devolver 201 (Created) Para tener control sobre este coacutedigo nuestros recursosJAX-RS podraacuten devolver instancias de javaxwsrscoreResponse

GETProduces(MediaTypeAPPLICATION_XML)public Response getClientes() ClientesBean clientes = obtenerClientes()

return Responseok(clientes)build()

POSTConsumes(MediaTypeAPPLICATION_XML)public Response addCliente(ClienteBean cliente Context UriInfo uriInfo)

String id = insertarCliente(cliente) URI uri = uriInfo getAbsolutePathBuilder() path(id)

build(id)

return Responsecreated(uri)build()

Al crear una respuesta con Response podemos especificar una entidad que podraacute serun objeto de cualquiera de los tipos vistos anteriormente y que representa los datos a

Servicios Rest

94

devolver como contenido Por ejemplo cuando indicamos ok(clientes) estamos creandouna respuesta con coacutedigo 200 (Ok) y con el contenido generado por nuestro beanJAXB clientes Esto seraacute equivalente a haber devuelto directamente ClientesBean comorespuesta pero con la ventaja de que en este caso podemos controlar el coacutedigo de estadode la respuestaInsertamos una instancia de ClienteBean en la base de datos y obtenemos la claveasociada a dicho clienteEn la sesioacuten anterior hemos hablado de la interfaz uriInfo Es una interfaz inyectableque proporciona acceso a informacioacuten sobre la URI de la aplicacioacuten o la URI de laspeticiones recibidas En este caso estamos construyendo una nueva URI formada porla ruta absoluta de la peticioacuten de entrada http POST antildeadieacutendole la plantilla id yfinalmente sustituyendo el paraacutemetro de la plantilla por el valor id Supongamos quela peticioacuten POST contiene la uri httplocalhost8080recursosclientes Y que el valor de id es 8 El valor de la variable uri seraacute por tanto httplocalhost8080recursosclientes8Devolvemos la respuesta incluyendo la URI anterior en la cabecera HTTP `Location

Veamos con maacutes detalle la clase Response

public abstract class Response

public abstract Object getEntity()

public abstract int getStatus()

public abstract MultivaluedMapltString Objectgt getMetadata()

public abstract URI getLocation()

public abstract MediaType getMediaType()

public abstract void close()

El meacutetodo getEntity() devuelve el objeto Java correspondiente al cuerpo delmensaje HTTPEl meacutetodo getStatus() devuelve el coacutedigo de respuesta HTTPEl meacutetodo getMetadata() devuelve una instancia de tipo MultivaluedMap con lascabeceras de la respuestaEl meacutetodo getLocation() devuelve la URI de la cabecera Location de la respuestaEl meacutetodo getMediaType() devuelve el mediaType del cuerpo de la respuestaEl meacutetodo close() cierra el input stream correspondiente a la entidad asociada delcuerpo del mensaje (en el caso de que esteacute disponible y abierto) Tambieacuten liberacualquier otro recurso asociado con la respuesta (como por ejemplo datos posiblementealmacenados en un buffer)

Estos meacutetodos tiacutepicamente seraacuten invocados dese el cliente tal y como veremos maacutes adelantecuando expliquemos el API cliente

Los objetos Response no pueden crearse directamente Tienen que crearse a partir deinstancias de javaxwsrscoreResponseResponseBuilder devueltas por uno delos siguientes meacutetodos estaacuteticos de Response

public abstract class Response public abstract void close() public static ResponseBuilder status(Status status) public static ResponseBuilder status(int status) public static ResponseBuilder ok()

Servicios Rest

95

public static ResponseBuilder ok(Object entity) public static ResponseBuilder ok(Object entity MediaType type) public static ResponseBuilder ok(Object entity String type) public static ResponseBuilder ok(Object entity Variant var) public static ResponseBuilder serverError() public static ResponseBuilder created(URI location) public static ResponseBuilder noContent() public static ResponseBuilder notModified() public static ResponseBuilder notModified(EntityTag tag) public static ResponseBuilder notModified(String tag) public static ResponseBuilder seeOther(URI location) public static ResponseBuilder temporaryRedirect(URI location) public static ResponseBuilder notAcceptable(ListltVariantgt variants) public static ResponseBuilder fromResponse(Response response)

Veamos por ejemplo el meacutetodo ok()

ResponseBuilder ok(Object entity MediaType type)

Este meacutetodo recibe como paraacutemetros un objeto Java que queremos convertir en una respuestaHTTP y el Content-Type de dicha respuesta Como valor de retorno se obtiene unainstancia de tipo ResponseBuilder pre-inicializada con un coacutedigo de estado de 200 OK

El meacutetodo created() devuelve un ResponseBuilder para un recurso creado y asigna a lacabecera Location de la respuesta el valor de la URI proporionado como paraacutemetro

La clase ResponseBuilder es una factoriacutea utilizada para crear instancias individuales detipo Response

public static abstract class ResponseBuilder public abstract Response build() public abstract ResponseBuilder clone()

public abstract ResponseBuilder status(int status) public ResponseBuilder status(Status status)

public abstract ResponseBuilder entity(Object entity) public abstract ResponseBuilder type(MediaType type) public abstract ResponseBuilder type(String type)

public abstract ResponseBuilder variant(Variant variant) public abstract ResponseBuilder variants(ListltVariantgt variants)

public abstract ResponseBuilder language(String language) public abstract ResponseBuilder language(Locale language)

public abstract ResponseBuilder location(URI location) public abstract ResponseResponseBuilder header(String name Object value) public abstract ResponseResponseBuilder link(URI uri String rel)

Servicios Rest

96

public abstract ResponseResponseBuilder link(String uri String rel)

Vamos a mostrar un ejemplo sobre coacutemo crear respuestas utilizando un objeto Response En este caso el meacutetodo getLibro() devuelve un String que representa el libro en el queestaacute interesado nuestro cliente

Path(libro)public class LibroServicio GET Path(restfuljava) Produces(textplain) public Response getLibro()

String libro =

ResponseBuilder builder = Responseok(libro)

builderlanguage(fr)header(Some-Header some value)

return builderbuild()

Recuperamos los datos del libro solicitado En este caso vamos a devolver una cadenade caracteres que representa el libro en el que estamos interesadosInicializamos el cuerpo de la respuesta utilizando el meacutetodo Responseok() El coacutedigode estado de ResponseBuilder se inicializa de forma automaacutetica con 200Usamos el meacutetodo ResponseBuilderlanguage() para asignar el valorde la cabecera Content-Languaje a franceacutes Tambieacuten usamos el meacutetodoResponseBuilderheader() para asignar un valor concreto a otra cabeceraFinalmente creamos y devolvemos el objeto Response usando el meacutetodoResponseBuilderbuild()

Un detalle que es interesante destacar en este coacutedigo es que no indicamos ninguacuten valor parael Content-Type de la respuesta Debido a que ya hemos especificado esta informacioacutenen la anotacioacuten Produces del meacutetodo el runtime de JAX-RS devolveraacute el valor adecuadodel media type de la respuesta por nosotros

Inclusioacuten de cookies en la respuesta

JAX-RS proporciona la clase javaxwsrscoreNewCookie que utilizaremos para crearnuevos valores de cookies y enviarlos en las respuestas

Para poder incluir las cookies en nuestro objeto Response primero crearemoslas instancias correspondientes de tipo NewCookie las pasaremos al meacutetodoResponseBuildercookie() Por ejemplo

Path(myservice)public class MyService GET public Response get()

NewCookie cookie = new NewCookie(nombre pepe)

ResponseBuilder builder = Responseok(hola textplain) return buildercookie(cookie)build()

Servicios Rest

97

Creamos una nueva cookie con el nombre de clave nombre y le asignamos el valorpepeEn este caso al no indicar el tipo mime de la respuesta con la anotacioacuten Producesnecesitamos indicarlo (en este caso como un paraacutemetro del meacutetodo ok() )

El tipo enumerado de coacutedigos de estado

JAX-RS proporciona el tipo enumerado javaxwsrscoreStatus para representarcoacutedigos de respuesta especiacuteficos

public enum Status OK(200 OK) CREATED(201 Created) ACCEPTED(202 Accepted) NO_CONTENT(204 No Content) MOVED_PERMANENTLY(301 Moved Permanently) SEE_OTHER(303 See Other) NOT_MODIFIED(304 Not Modified) TEMPORARY_REDIRECT(307 Temporary Redirect) BAD_REQUEST(400 Bad Request) UNAUTHORIZED(401 Unauthorized) FORBIDDEN(403 Forbidden) NOT_FOUND(404 Not Found) NOT_ACCEPTABLE(406 Not Acceptable) CONFLICT(409 Conflict) GONE(410 Gone) PRECONDITION_FAILED(412 Precondition Failed) UNSUPPORTED_MEDIA_TYPE(415 Unsupported Media Type) INTERNAL_SERVER_ERROR(500 Internal Server Error) NOT_IMPLEMENTED(501 Not Implemented) SERVICE_UNAVAILABLE(503 Service Unavailable) public enum Family INFORMATIONAL SUCCESSFUL REDIRECTION CLIENT_ERROR SERVER_ERROR OTHER

public Family getFamily() public int getStatusCode() public static Status fromStatusCode(final int statusCode)

Cada valor del tipo Status se asocia con una familia especiacutefica de coacutedigos de respuestaHTTP Estas familias se identifican por el enumerado StatusFamily

bull Los coacutedigos en el rango del 100 se consideran informacionales

bull Los coacutedigos en el rango del 200 se consideran exitosos

bull Los coacutedigos en el rango del 300 son coacutedigos con eacutexito pero dentro de la categoriacutearedireccioacuten

bull Los coacutedigos de error pertenecen a los ragos 400 y 500 En el rango de 400 se consideranerrores del cliente y en el rango de 500 son errores del servidor

Servicios Rest

98

Tanto el meacutetodo Responsestatus() como ResponseBuilderstatus() puedenaceptar un valor enumerado de tipo Status Por ejemplo

DELETEResponse delete() return Responsestatus(StatusGONE)build()

En este caso estamos indicando al cliente que lo que queremos borrar ya no existe (410)

La clase javaxwsrscoreGenericEntity

Cuando estamos creando objetos de tipo Response se nos plantea un problema cuandoqueremos devolver tipos geneacutericos ya que el manejador JAXB necesita extraer la informacioacutendel tipo parametrizado de la respuesta en tiempo de ejecucioacuten Para estos casos JAX-RS proporciona la clase javaxwsrscoreGenericEntity Veamos su uso con unejemplo

GETProduces(applicationxml)public Response getListaClientes() ListltClientegt list = new ArrayListltClientegt() listadd(new Cliente())

GenericEntity entity =

new GenericEntityltListltClientegtgt(list)

return Responseok(entity)build()

La clase GenericEntity es tambieacuten una clase geneacuterica Lo que hacemos escrear una clase anoacutenima que extiende GenericEntity inicializando la plantilla deGenericEntity con el tipo geneacuterico que estemos utilizando

36 Manejadores de excepciones

Vamos a explicar coacutemo podemos tratar las excepciones en nuestros servicios RESTful

Los errores pueden enviarse al cliente bien creando y devolviendo el objeto Responseadecuado o lanzando una excepcioacuten Podemos lanzar cualquier tipo de excepcioacutentanto las denominadas checked (clases que heredan de javalangException ) comolas excepciones unchecked (clases que extienden javalangRuntimeException )Las excepciones generadas son manejadas por el runtime de JAX-RS si tenemosregistrado un mapper de excepciones Dichos mappers (o mapeadores) de excepcionespueden convertir una excepcioacuten en una respuesta HTTP Si las excepciones no estaacutengestionadas por un mapper eacutestas se propagan y se gestionan por el contenedor (deservlets) en el que se estaacute ejecutando JAX-RS JAX-RS proporciona tambieacuten la clasejavaxwsrsWebApplicationException Esta excepcioacuten puede lanzarse por elcoacutedigo de nuestra aplicacioacuten y seraacute procesado automaacuteticamente por JAX-RS sin necesidadde disponer de forma expliacutecita de ninguacuten mapper Vamos a ver coacutemo utilizar esta clase

Servicios Rest

99

La clase javaxwsrsWebApplicationException

JAX-RS incluye una excepcioacuten unchecked que podemos lanzar desde nuestraaplicacioacuten RESTful (ver la documentacioacuten del httpdocsoraclecomjavaee7apijavaxwsrsWebApplicationExceptionhtml [API]) Esta excepcioacuten se puede pre-inicializar con un objetoResponse o con un coacutedigo de estado particular

Clase javaxwsrsWebApplicationException

public class WebApplicationException extends RuntimeException Constructores public WebApplicationException()

public WebApplicationException(Response response) public WebApplicationException(int status)

public WebApplicationException(ResponseStatus status)

public WebApplicationException(String message) public WebApplicationException(Throwable cause) public WebApplicationException(Throwable cause Response response) public WebApplicationException(Throwable cause int status) public WebApplicationException(Throwable cause ResponseStatus status)

public Response getResponse() ]

Podemos crear una instancia a partir de un objeto ResponseCreacioacuten de una instancia a partir de un coacutedigo de estadoCreacioacuten de una instancia a partir de un String Por defecto se incluye el coacutedigo de estado500 El String que se pasa como paraacutemetro se almacena para su posterior recuperacioacutena traveacutes del mensaje `getMessage()

Cuando JAX-RS detecta que se ha lanzado la excepcioacuten WebApplicationException lacaptura y realiza una llamada al meacutetodo WebApplicationExceptiongetResponse()para obtener un objeto Response que enviaraacute al cliente Si la aplicacioacuten ha inicializado laexcepcioacuten WebApplicationException con un coacutedigo de estado o un objeto Response dicho coacutedigo o Response se utilizaraacuten para crear la respuesta HTTP real En otro caso laexcepcioacuten WebApplicationException devolveraacute el cliente el coacutedigo de respuesta 500Internal Server Error

Por ejemplo supongamos que tenemos un servicio web que permite a los usuarios solicitarinformacioacuten de nuestros clientes utilizando una representacioacuten XML

Path(clientes)public class ClienteResource GET Path(id) Produces(applicationxml) public Cliente getCliente(PathParam(id) int id) Cliente cli = recuperarCliente(id) if (cli == null) throw new WebApplicationException(ResponseStatusNOT_FOUND) return cli

Servicios Rest

100

En este ejemplo si no encontramos una instancia de Cliente con el id proporcionadolanzaremos una WebApplicationException que provocaraacute que se le enviacutee al clientecomo coacutedigo de respuesta 404 Not Found

Mapeado de excepciones

Normalmente las aplicaciones tienen que tratar con multitud de excepciones lanzadas desdenuestro coacutedigo de aplicacioacuten o por frameworks de terceros Dejar que el servlet JAX-RSque reside en el servidor maneje la excepcioacuten no nos proporciona demasiada flexibilidadSi capturamos y redirigimos todas estas excepciones a WebApplicationExceptionpodriacutea resultar bastante tedioso De forma alternativa podemos implementar y registrarinstancias de javaxwsrsextExceptionMapper Estos objetos saben coacutemo mapearuna excepcioacuten lanzada por la aplicacioacuten a un objeto Response

public interface ExceptionMapperltE extends Throwablegt Response toResponse(E exception)

Las clases que implementan la interfaz ExceptionMapperltTgt son proveedores demappings de excepciones (Exception Mapping Providers) y mapean una excepcioacuten runtimeo checked a una instancia de Response

Cuando un recurso JAX-RS lanza una excepcioacuten para la que existe un proveedor de mappingde excepciones eacuteste uacuteltimo se utiliza para devolver una instancia de Response Estarespuesta resultante se procesa como si hubiese sido generada por el recurso

Por ejemplo una excepcioacuten bastante utilizada por aplicaciones de bases de datos que utilizanJPA (Java Persistence Api) es javaxpersistenceEntityNotFoundException Estaexcepcioacuten se lanza cuando JPA no puede encontrar un objeto particular en la base de datosEn lugar de escribir coacutedigo que maneje dicha excepcioacuten de forma expliacutecita podemos escribirun ExceptionMapper para que gestione dicha excepcioacuten por nosotros Veaacutemos coacutemo

Provider public class EntityNotFoundMapper

implements ExceptionMapperltEntityNotFoundExceptiongt

public Response toResponse(EntityNotFoundException e)

return Responsestatus(ResponseStatusNOT_FOUND)build()

Nuestra implementacioacuten de ExceptionMapper debe anotarse con Provider Estole indica al runtime de JAX-RS que esta clase es un componente RESTLa clase que implementa ExceptionMapper debe proporcionar el tipo que se quiereparametrizar JAX-RS utiliza esta informacioacuten para emparejar las excepciones de tipoEntityNotFoundException con nuestra clase EntityNotFoundMapperEl meacutetodo toResponse() recibe la excepcioacuten lanzada ycrea un objeto Response que se utilizaraacute para construir la respuesta HTTP

Servicios Rest

101

Otro ejemplo es la excepcioacuten EJBException lanzada por aplicaciones que utilizan EJBs

Jerarquiacutea de excepciones

JAX-RS 20 proporciona una jerarquiacutea de excepciones para varias condiciones deerror para las peticiones HTTP La idea es que en lugar de crear una instancia deWebApplicationException e inicializarla con un coacutedigo de estado especiacutefico podemosuilizar en su lugar una de las excepciones de la jeraquiacutea (clases que heredan de la claseWebApplicationException) Por ejemplo podemos cambiar el coacutedigo anterior que utilizabaWebApplicationException y en su lugar usar javaxwsrsNotFoundException

Path(clientes)public class ClienteResource GET Path(id) Produces(applicationxml) public Cliente getCliente(PathParam(id) int id) Cliente cli = recuperarCliente(id) if (cli == null) throw new NotFoundException() return cli

Al igual que el resto de excepciones de la jerarquiacutea la excepcioacuten NotFoundExceptionhereda de WebApplicationException La siguiente tabla muestra algunas excepcionesque podemos utilizar todas ellas del paquete javaxwsrs Las que incluyen un coacutedigo deestado en el rango de 400 son subclases de ClientErrorException y como ya hemosindicado representan errores en las peticiones Las que presentan un coacutedigo de estado enel rango de 500 son subclases de ServerErrorException y representan errores delservidor

Table 5 Jerarquiacutea de excepciones JAX-RS

Excepcioacuten Coacutedigo de estado Descripcioacuten

BadRequestException 400 Mensaje mal formado

NotAuthorizedException 401 Fallo de autenticacioacuten

ForbiddenException 403 Acceso no permitido

NotFoundException 404 No se ha podido encontrar elrecurso

NotAllowedException 405 Meacutetodo HTTP no soportado

NotAcceptableException 406 Media type solicitado porel cliente no soportado(cabecera Accept de lapeticion)

NotSupportedException 415 El cliente ha incluido unMedia type no soportado(cabecera Content-Type dela peticion)

Servicios Rest

102

Excepcioacuten Coacutedigo de estado Descripcioacuten

InternalServerErrorException 500 Error general del servidor

ServiceUnavailableException 503 El servidor estaacute ocupadoo temporalmente fuera deservicio

bull La excepcioacuten BadRequestException se utiliza cuando el cliente enviacutea algo al servidorque eacuteste no puede interpretar Ejemplos de escenarios concretos que provocan que elruntime JAX-RS son cuando una peticioacuten PUT o POST contiene un cuerpo del mensaje conun documento XML o JSON mal formado de forma que falle el parsing del documento ocuando no puede convertir un valor especificado en la cabecera o cookie al tipo deseadoPor ejemplo

HeaderParam(Cabecera-Particular) int cabeceraCookieParam(miCookie) int cookie

Si el valor de la cabecera HTTP de la peticioacuten o el valor miCookie no puede convertirseen un entero se lanzaraacute la excepcioacuten BadRequestException

bull La excepcioacuten NotAuthorizedException (coacutedigo 401) se usa cuando queremosescribir nuestros propios protocolos de autorizacioacuten y queremos indicar al cliente que eacutestenecesita autenticarse con el servidor

bull La excepcioacuten ForbiddenException (coacutedigo 403)se usa generalmente cuando elcliente realiza una invocacioacuten para la que no tiene permisos de acceso Esto ocurrenormalmente debido a que el cliente no tiene el rol requerido

bull La excepcioacuten NotFoundException (coacutedigo 404) se usa cuando queremos comunicar alcliente que el recurso que estaacute solicitando no existe Esta excepcioacuten tambieacuten se generaraacute deforma automaacutetica por el runtime de JAX-RS cuando a eacuteste no le sea posible inyectar un valoren un PathParam QueryParam o MatrixParam Al igual que hemos comentado paraBadRequestException esto puede ocurrir si intentamos convertir el valor del paraacutemetroa un tipo que no admite esta conversioacuten

bull La excepcioacuten NotAllowedException (coacutedigo 405) se usa cuando el meacutetodo HTTP queel cliente estaacute intentando invocar no estaacute soportado por el recurso al que el cliente estaacuteaccediendo El runtime de JAX-RS lanza automaacuteticamente esta excepcioacuten si no encuentraninguacuten meacutetodo que pueda emparejar con el meacutetodo HTTP invocado

bull La excepcioacuten NotAcceptableException (coacutedigo 406) se usa cuando un cliente estaacutesolicitando un formato especiacutefico a traveacutes de la cabecera Accept El runtime de JAX-RSlanza automaacuteticamente esta excepcioacuten si no hay un meacutetodo con una anotacioacuten Producesque sea compatible con la cabecera Accept del cliente

bull La excepcioacuten NotSupportedException (coacutedigo 415)se usa cuando un cliente estaacuteenviando una representacioacuten que el servidor no comprende El runtime de JAX-RS lanzaautomaacuteticamente esta excepcioacuten si no hay un meacutetodo con una anotacioacuten Consumes quecoincida con el valor de la cabecera Content-Type de la peticioacuten

bull La excepcioacuten InternalServerErrorException (coacutedigo 500)es una excepcioacuten depropoacutesito general lanzada por el servidor Si en nuestra aplicacioacuten queremos lanzar estaexcepcioacuten deberiacuteamos hacerlo si se ha producido una condicioacuten de error que realmenteno encaja en ninguna de las situaciones que hemos visto El runtime de JAX-RS lanzaautomaacuteticamente esta excepcioacuten si falla un MessageBodyWriter o si se lanza algunaexcepcioacuten desde alguacuten ExceptionMapper

Servicios Rest

103

bull La excepcioacuten ServiceUnavailableException (coacutedigo 503)se usa cuando el servidorestaacute ocupado o temporalmente fuera de servicio En la mayoriacutea de los casos es suficientecon que el cliente vuelva a intentar realizar la llamada un tiempo maacutes tarde

Servicios Rest

104

37 Ejercicios

Servicio REST ejemplo

Para familiarizarnos con las el uso de diferentes manejadores de contenidos y manejo deexcepciones proporcionamos el moacutedulo el MOacuteDULO s3-ejemplo-rest con la implementacioacutende un servicio rest sencillo que podeacuteis probar con la herramienta postman

En el directorio srcmainresources de dicho moacutedulo teneacuteis un fichero de texto con lasinstrucciones (instruccionestxt) para construir desplegar y probar la aplicacioacuten de ejemplo

Plantillas que se proporcionan

Para esta sesioacuten proporcionamos un proyecto como plantilla con el nombre s3-filmotecaque tendraacutes utilizar como punto de partida para aplicar lo que hemos aprendido en esta sesioacuten

Se trata de una implementacioacuten parcial para gestionar una filmoteca con informacioacuten depeliacuteculas y actores

La estructura loacutegica del proyecto proporcionado es la siguiente

bull Paquete orgexpertojavadomain es la capa que contiene los objetos del dominiode la aplicacioacuten Por simplicidad no usamos una base de datos real sino que trabajamoscon datos en memoria

bull Paquete orgexpertojavaservice contiene la implementacioacuten de los servicios denuestra aplicacioacuten que seraacuten accedidos desde la capa rest

bull Paquete orgexpertojavarest constituye la capa rest de nuestra aplicacioacuten Estacapa es cliente de la capa de servicios

En la carpeta srcmainresources teneacuteis un fichero de texto (instruccionestxt) coninformacioacuten detallada sobre el API rest implementado

Uso de JAXB (05 puntos)

Utiliza las anotaciones JAXB oportunas para realizar el serializado de las entidades java a xmly json de forma que la lista de peliacuteculas de la filmoteca en formato xml sea

Peticioacuten rest GET peliculas

ltxml version=10 encoding=UTF-8 standalone=yesgtltpeliculasgt ltpelicula duracion=120 estreno=2015-12-08T000000+0100 id=1gt ltdirectorgtStanley Kubrickltdirectorgt lttitulogtEl resplandorlttitulogt ltpeliculagt ltpelicula duracion=155 estreno=2015-18-07T000000+0100 id=2gt ltdirectorgtDirector 2ltdirectorgt lttitulogtPelicula 2lttitulogt ltpeliculagtltpeliculasgt

Servicios Rest

105

Por defecto JAXB serializa los tipos Date con el formato ISO 80617

para los contenidos en el cuerpo de la petioacutenrespuesta Dicho formatoes YYYY-MM-DD (seguido de HHMMSS) Por otro lado si antildeadimosactores a las peliacuteculas eacutestos deberaacuten mostrarse bajo la etiquetaltactoresgt tal y como mostramos en el siguiente ejemplo

Los datos mostrados para una peliacutecula en formato xml tienen que presentar el siguienteaspecto

sourcejava] Peticioacuten rest GET peliculas1

ltxml version=10 encoding=UTF-8 standalone=yesgtltpelicula duracion=120 estreno=2015-12-08T000000+0100 id=1gt ltactoresgt ltactor nombre=Jack Nicholson personaje=Jack Torrancegt ltactoresgt ltdirectorgtStanley Kubrickltdirectorgt lttitulogtEl resplandorlttitulogtltpeliculagt

Puedes comprobar si has hecho bien el ejercicio utilizando Postman

Uso de manejadores de contenidos y clase Response (075 puntos)

Implementa una nueva peticioacuten POST que reciba los datos de una nueva peliacutecula desdeun formulario Recuerda que los datos de un formulario pueden ponerse en el cuerpo de lapeticioacuten HTTP como pares nombre=valor separados por amp Los espacios en blanco secodifican como 20 No es necesario poner comillas Por ejemplo

nombre1=valor20con20espaciosampnombre2=valor

Se proporciona el fichero indexhtml con un formulario para utilizarlo como alternativa apostman Para acceder al formulario usaremos httplocalhost8080s3-filmoteca

Modifica las peticiones POST sobre peliacuteculas y actores de forma que devuelvan enla cabecera Location la URI del nuevo recurso creado Por ejemplo

httplocalhost8080s3-filmotecapeliculas3

en el caso de una nueva peliacutecula y

httplocalhost8080s3-filmotecapeliculas3actoresSigourney20Weaver

si por ejemplo hemos antildeadido un nuevo actor a la peliacutecula con id=3

Modifica los meacutetodos GET para que devuelvan el estado 204 No Content en los casos enlos que la peliacutecula yo actor consultado no exista

7 httpseswikipediaorgwikiISO_8601

Servicios Rest

106

Modifica los meacutetodos GET para que devuelvan el estado 404 Not Found en los casos enlos que las listas de peliacuteculas yo actores esteacuten vaciacuteas

Implementa el coacutedigo para antildeadir un nuevo actor Tendraacutes que obtener la informacioacuten de lapeliacutecula y nombre del actor de la URI de la peticioacuten

Puedes comprobar si has hecho bien el ejercicio utilizando Postman

Manejo de excepciones (075 puntos)

Modifica el meacutetodo addPelicula de la capa de servicio (paquete orgexpertojavaservice)para que lance una excepcioacuten de tipo ServiceException con el mensaje El tiacutetulo de la peliacuteculano puede ser nulo ni vaciacuteo cuando se intente antildeadir una peliacutecula con un tiacutetulo con valor nullo vaciacuteo

El meacutetodo addPelicula debe lanzar tambieacuten una excepcioacuten de tipo ServiceException conel mensaje La peliacutecula ya existe cuando se intente antildeadir una peliacutecula con un tiacutetulo que yaexiste

Modifica el meacutetodo addActor de la capa de servicio para que lance las excepciones de tipoServiceException con los mensajes El tiacutetulo de la peliacutecula no puede ser nulo ni vaciacuteo cuandose intente antildeadir un actor a una peliacutecula cuyo tiacutetulo no existe o bien el mensaje EL actor yaexiste si intentamos antildeadir un actor a una peliacutecula que ya habiacuteamos antildeadido previamente

Implementa un mapper para capturar las excepciones de la capa de servicio de forma quese devuelva el estado 500 Internal Server error y como entidad del cuerpo de la respuestael mensaje asociado a las excepciones generadas por el servicio (El tiacutetulo de la peliacutecula nopuede ser nulo ni vaciacuteo EL actor ya existe hellip) La nueva clase pertenceraacute a la capa restPuedes ponerle el nombre ServiceExceptionMapper

Puedes comprobar si has hecho bien el ejercicio utilizando Postman

Servicios Rest

107

4 HATEOAS y Seguridad

En esta sesioacuten trataremos uno de los principios REST de obligado cumplimiento para poderhablar de un servicio RESTful Nos referimos a HATEOAS Hasta ahora hemos visto coacutemo losclientes pueden cambiar el estado de los recursos (el nombre del recurso se especifica en laURI de la peticioacuten) a traveacutes de los contenidos del cuerpo del mensaje o utilizando paraacutemetroso cabeceras de peticioacuten A su vez los servicios comunican el estado resultante de la peticioacuten alos clientes a traveacutes del contenido del cuerpo del mensaje coacutedigos de respuesta y cabecerasde respuesta Pues bien teniendo en cuenta lo anterior HATEOAS hace referencia a quecuando sea necesario tambieacuten deben incluirse los enlaces a los recursos (URI) en el cuerpode la respuesta (o en las cabeceras) para asiacute poder recuperar el recurso en cuestioacuten o losrecursos relacionados

En esta sesioacuten tambieacuten explicaremos algunos conceptos baacutesicos para poder dotar deseguridad a nuestros servicios REST

41 iquestQueacute es HATEOAS

Comuacutenmente se hace referencia a Internet como la Web (web significa red telarantildea) debidoa que la informacioacuten estaacute interconectada mediante una serie de hiperenlaces embebidosdentro de los documentos HTML Estos enlaces crean una especie de hilos o hebras entrelos sitios web relacionados en Internet Una consecuencia de ello es que los humanos puedennavegar por la Web buscando elementos de informacioacuten relacionados de su intereacutes haciendoclick en los diferentes enlaces desde sus navegadores Los motores de buacutesqueda puedentrepar o desplazarse por estos enlaces y crear iacutendices enormes de datos susceptibles deser buscados Sin ellos Internet no podriacutea tener la propiedad de ser escalable No habriacuteaforma de indexar faacutecilmente la informacioacuten y el registro de sitios web seriacutea un proceso manualbastante tedioso

Ademaacutes de los enlaces (links) otra caracteriacutestica fundamental de Internet es HTML Enocasiones un sitio web nos solicita que rellenemos alguna informacioacuten para compraralgo o registrarnos en alguacuten servicio El servidor nos indica a nosotros como clientes queacuteinformacioacuten necesitamos proporcionar para completar una accioacuten descrita en la paacutegina webque estamos viendo El navegador nos muestra la paacutegina web en un formado que podemosentender faacutecilmente Nosotros leemos la paacutegina web y rellenamos y enviamos el formulario Unformulario HTML es un formato de datos interesante debido a que auto-describe la interaccioacutenentre el cliente y el servidor

El principio arquitectoacutenico que describe el proceso de enlazado (linking) y el enviacuteo deformularios se denomina HATEOAS Las siglas del teacutermino HATEOAS significan HypermediaAs The Engine Of Application State (es decir el uso de Hipermedia como mecanismo demaacutequina de estados de la aplicacioacuten) La idea de HATEOAS es que el formato de los datosproporciona informacioacuten extra sobre coacutemo cambiar el estado de nuestra aplicacioacuten En laWeb los enlaces HTML nos permiten cambiar el estado de nuestro navegador Por ejemplocuando estamos leyendo una paacutegina web un enlace nos indica queacute posibles documentos(estados) podemos ver a continuacioacuten Cuando hacemos click sobre un enlace el estado delnavegador cambia al visitar y mostrar una nueva paacutegina web Los formularios HTML por otraparte nos proporcionan una forma de cambiar el estado de un recurso especiacutefico de nuestroservidor Por uacuteltimo cuando compramos algo en Internet por ejemplo estamos creando dosnuevos recursos en el servicio una transaccioacuten con tarjeta de creacutedito y una orden de compra

Servicios Rest

108

42 HATEOAS y Servicios Web

Cuando aplicamos HATEOAS a los servicios web la idea es incluir enlaces en nuestrosdocumentos XML o JSON La mayoriacutea de las aplicaciones RESTful basadas en XML utilizanel formato Atom Syndication Format8 para implementar HATEOAS

Enlaces Atom

Los enlaces Atom constituyen un mecanismo estaacutendar para incluir enlaces (links) en nuestrosdocumentos XML Veamos un ejemplo

ltclientesgt ltlink rel=next href=httpejemplocomclientesinicio=2amptotal=2 type=applicationxmlgt ltcliente id=123gt ltnombregtJuan Garcialtnombregt ltclientegt ltcliente id=332gt ltnombregtPablo Bozoltnombregt ltclientegtltclientesgt

El documento anterior representa una lista de clientes y el elemento ltlinkgt que contieneun enlace indica la forma de obtener los siguientes clientes de la lista

Un enlace Atom es simplemente un elemento XML (elemento ltlinkgt ) con unos atributosespeciacuteficos

bull El atributo rel Se utiliza para indicar la relacioacuten del enlace con el elemento XML en elque anidamos dicho enlace Es el nombre loacutegico utilizado para referenciar el enlace Esteatributo tiene el mismo significado para la URL que estamos enlazando que la etiquetaHTML ltagt tiene para la URL sobre la que estamos haciendo click con el ratoacuten en elnavegador Si el enlace hace referencia al propio elemento XML en el que incluimos elenlace entonces asignaremos el valor del atributo self

bull El atributo href es la URL a la que podemos acceder para obtener nueva informacioacuten ocambiar el estado de nuestra aplicacioacuten

bull El atributo type indica el media type asociado con el recurso al que apunta la URL

Cuando un cliente recibe un documento con enlaces Atom eacuteste busca la relacioacuten en la queestaacute interesado (atributo rel ) e invoca la URI indicada en el atributo href

Ventajas de utilizar HATEOAS con Servicios Web

Resulta bastante obvio por queacute los enlaces y los formularios tienen mucho que ver en laprevalencia de la Web Con un navegador tenemos una ventana a todo un mundo deinformacioacuten y servicios Las maacutequinas de buacutesqueda rastrean Internet e indexan sitios webpara que todos los datos esteacuten al alcance de nuestros dedos Esto es posible debido a quela Web es auto-descriptiva Cuando accedemos a un documento conocemos coacutemo recuperarinformacioacuten adicional siguiendo los enlaces situados en dicho documento Por ejemplo

8 httpwwww3org2005Atom

Servicios Rest

109

conocemos coacutemo realizar una compra en Amazon debido a que los formularios HTML nosindican coacutemo hacerlo

Cuando los clientes son maacutequinas en lugar de personas (los servicios Web tambieacuten seconocen como Web para maacutequinas frente a la Web para humanos proporcionada por elacceso a un servidor web a traveacutes de un navegador) el tema es algo diferente puesto que lasmaacutequinas no pueden tomar decisiones sobre la marcha cosa que los humanos siacute puedenhacer Las maacutequinas requieren que los programadores les digan coacutemo interpretar los datosrecibidos desde un servicio y coacutemo realizar transiciones entre estados como resultado de lasinteracciones entre clientes y servidores

En este sentido HATEOAS proporciona algunas ventajas importantes para contribuir a quelos clientes sepan coacutemo utilizar los servicios a la vez que acceden a los mismos Vamos acomentar algunas de ellas

Transparencia en la localizacioacuten

En un sistema RESTful gracias a HATEOAS soacutelo es necesario hacer puacuteblicas unas pocasURIs Los servicios y la informacioacuten son representados con enlaces que estaacuten embebidosen los formatos de los datos devueltos por las URIs puacuteblicas Los clientes necesitan conocerlos nombres loacutegicos de los enlaces para buscar a traveacutes de ellos pero no necesitan conocerlas ubicaciones reales en la red de los servicios a los que acceden

Los enlaces proporcionan un nivel de indireccioacuten de forma que los servicios subyacentespueden cambiar sus localizaciones en la red sin alterar la loacutegica ni el coacutedigo del cliente

Desacoplamiento de los detalles de la interaccioacuten

Consideremos una peticioacuten que nos devuelve una lista de clientes en una base de datosGET clientes Si nuestra base de datos tiene miles de datos probablemente noquerremos devolver todos ellos de una soacutela vez Lo que podemos hacer es definir una vistaen nuestra base de datos utilizando paraacutemetros de consulta por ejemplo

customersinicio=indiceInicioamptotal=numeroElementosDevueltos

El paraacutemetro inicio identifica el iacutendice inicial de nuestra lista de clientes El paraacutemetrototal especifica cuaacutentos clientes queremos que nos sean devueltos como respuesta

Lo que estamos haciendo en realidad es incrementar la cantidad de conocimiento que elcliente debe tener predefinido para interactuar con el servicio (es decir no soacutelo necesita saberla URI sino ademaacutes conocer la existencia de estos paraacutemetros) Supongamos que en el futuroel servidor decide que necesita cambiar la forma en la que se accede al nuacutemero de datossolicitados por el cliente Si el servidor cambia la interfaz los clientes antiguos dejaraacuten defuncionar a menos que cambien su coacutedigo

En lugar de publicar la interfaz REST anterior para obtener datos de los clientes podemosincluir dicha informacioacuten en el documento de respuesta por ejemplo

ltclientesgt ltlink rel=next href=httpejemplocomclientesinicio=2amptotal=2 type=applicationxmlgt ltcliente id=123gt

Servicios Rest

110

ltnombregtJuan Garcialtnombregt ltclientegt ltcliente id=332gt ltnombregtPablo Bozoltnombregt ltclientegtltclientesgt

Cuando incluimos un enlace Atom en un documento estamos asignando un nombre loacutegicoa una transicioacuten de estados En el ejemplo anterior la transicioacuten de estados es el siguienteconjunto de clientes a los que podemos acceder En lugar de tener que recordar cuaacuteles sonlos paraacutemetros de la URI que tenemos que utilizar en la siguiente invocacioacuten para obtener maacutesclientes lo uacutenico que tenemos que hacer es seguir el enlace proporcionado El cliente notiene que contabilizar en ninguacuten sitio la interaccioacuten ni tiene que recordar queacute seccioacuten de labase de datos estamos consultando actualmente

Ademaacutes el XML devuelto es auto-contenido iquestQueacute pasa si tenemos que pasar estedocumento a un tercero Tendriacuteamos que decirle que se trata de una vista parcial de la basede datos y especificar el iacutedice de inicio Al incluir el enlace en el documento ya no es necesarioproporcionar dicha informacioacuten adicional ya que forma parte del propio documento

Reduccioacuten de errores de transicioacuten de estados

Los enlaces no se utilizan solamente como un mecanismo para agregar informacioacuten denavegacioacuten Tambieacuten se utilizan para cambiar el estado de los recursos Pensemos en unaaplicacioacuten de comercio web a la que podemos acceder con la URI pedidos333

ltpedido id=333gt ltcliente id=123gtltclientegt ltimportegt9999ltimportegt ltlineas-pedidogt ltlineas-pedidogtltpedidogt

Supongamos que un cliente quiere cancelar su pedido Podriacutea simplemente invocar la peticioacutenHTTP DELETE pedidos333 Esta no es siempre la mejor opcioacuten ya que normalmenteel sistema necesitaraacute retener el pedido para propoacutesitos de almacenaje Por ello podriacuteamosconsiderar una nueva representacioacuten del pedido con un elemento cancelado a true

PUT pedidos333 HTTP11Content-Type applicationxmlltpedido id=333gt ltcliente id=123gtltclientegt ltimportegt9999ltimportegt ltcanceladogttrueltcanceladogt ltlineas-pedidogt ltlineas-pedidogtltpedidogt

Pero iquestqueacute ocurre si el pedido no puede cancelarse Podemos tener un cierto estado ennuestro proceso de pedidos en donde esta accioacuten no estaacute permitida Por ejemplo si el pedido

Servicios Rest

111

ya ha sido enviado entonces no puede cancelarse En este caso realmente no hay niguacutencoacutedigo de estado HTTP de respuesta que represente esta situacioacuten Una mejor aproximacioacutenes incluir un enlace para poder realizar la cancelacioacuten

ltpedido id=333gt ltcliente id=123gtltclientegt ltimportegt9999ltimportegt ltcanceladogtfalseltcanceladogt ltlink rel=cancelar href=httpejemplocompedidos333canceladogt ltlineas-pedidogt ltlineas-pedidogtltpedidogt

El cliente podriacutea invocar la orden GET pedidos333 y obtener el documento XMLque representa el pedido Si el documento contiene el enlace cancelar entonces el clientepuede cambiar el estado del pedido a cancelado enviando una orden PUT vaciacutea a la URIreferenciada en el enlace Si el documento no contiene el enlace el cliente sabe que estaoperacioacuten no es posible Esto permite que el servicio web controle en tiempo real la forma enla que el cliente interactua con el sistema

Enlaces en cabeceras frente a enlaces Atom

Una alternativa al uso de enlaces Atom en el cuerpo de la respuesta es utilizar enlaces enlas cabeceras de la respuesta (httptoolsietforghtmlrfc5988) Vamos a explicar eacutesto con unejemplo

Consideremos el ejemplo de cancelacioacuten de un pedido que acabamos de ver En lugar deutilizar un enlace Atom para especificar si se permite o no la cancelacioacuten del pedido podemosutilizar la cabecera Link (es uno de los posibles campos que podemos incluir como cabeceraen una respuesta HTTP)) De esta forma si un usuario enviacutea la peticioacuten GET pedidos333 recibiraacute la siguiente respuesta HTTP

HTTP11 200 OKContent-Type applicationxmlLink lthttpejemplocompedidos333canceladogt rel=cancel

ltpedido id=333gt ltpedidogt

La cabecera Link tiene las mismas caracteriacutesticas que un enlace Atom La URI estaacute entrelos signos lt y gt y estaacute seguida por uno o maacutes atributos delimitados por El atributo reles obligatorio y tiene el mismo significado que el correspondiente atributo Atom com el mismonombre En el ejemplo no se muestra pero podriacuteamos especificar el media type utilizando elatributo type

43 HATEOAS y JAX-RS

JAX-RS no proporciona mucho soporte para implementar HATEOAS HATEOAS se definepor la aplicacioacuten por lo que no hay mucho que pueda aportar ninguacuten framework Lo que siacute

Servicios Rest

112

proporciona JAX-RS son algunas clases que podemos utilizar para construir las URIs de losenlaces HATEOAS

Construccioacuten de URIs con UriBuilder

Una clase que podemos utilizar es javaxwsrscoreUriBuilder Esta clase nospermite construir URIs elemento a elemento y tambieacuten permite incluir plantillas de paraacutemetros(segmentos de ruta variables)

Clase UriBuilder meacutetodos para instanciar objetos de la clase

public abstract class UriBuilder public static UriBuilder fromUri(URI uri) throws IllegalArgumentException public static UriBuilder fromUri(String uri) throws IllegalArgumentException public static UriBuilder fromPath(String path) throws IllegalArgumentException public static UriBuilder fromResource(Classltgt resource) throws IllegalArgumentException public static UriBuilder fromLink(Link link) throws IllegalArgumentException

Las instancias de UriBuilder se obtienen a partir de meacutetodos estaacuteticos con la formafromXXX() Podemos inicializarlas a partir de una URI una cadena de caracteres o laanotacioacuten Path de una clase de recurso

Para extraer modificar yo componer una URI se pueden utilizar meacutetodos como

Clase UriBuilder meacutetodos para manipular las URIs

public abstract UriBuilder clone() crea una copia

crea una copia con la informacioacuten de un objeto URIpublic abstract UriBuilder uri(URI uri) throws IllegalArgumentException

meacutetodos para asignarmodificar valores de los atributos de los objetos UriBuilderpublic abstract UriBuilder scheme(String scheme) throws IllegalArgumentExceptionpublic abstract UriBuilder userInfo(String ui)public abstract UriBuilder host(String host) throws IllegalArgumentExceptionpublic abstract UriBuilder port(int port) throws IllegalArgumentExceptionpublic abstract UriBuilder replacePath(String path)

meacutetodos que antildeaden elementos a nuestra URIpublic abstract UriBuilder path(String path)public abstract UriBuilder segment(String segments)public abstract UriBuilder matrixParam(String name Object values)public abstract UriBuilder queryParam(String name Object values)

Servicios Rest

113

meacutetodo que instancia el valor de una plantilla de la URIpublic abstract UriBuilder resolveTemplate(String name Object value)

Los meacutetodos build() construyen la URI Eacutesta puede contener plantillas de paraacutemetros( segmentos de ruta variables) que deberemos inicializar utilizando pares nombrevalor o bienuna lista de valores que reemplazaraacuten a los paraacutemetros de la plantilla en el orden en el queaparezcan

Clase UriBuilder meacutetodos buildXXX() para construir las URIs

public abstract URI buildFromMap(MapltString extends Objectgt values) throws IllegalArgumentException UriBuilderException

public abstract URI build(Object values) throws IllegalArgumentException UriBuilderException

Veamos alguacuten ejemplo que muestra coacutemo crear inicializar componer y construir una URIutilizando un UriBuilder

UriBuilder builder = UriBuilderfromPath(clientesid)builderscheme(http) host(hostname) queryParam(param=param)

Con este coacutedigo estamos definiendo una URI como

httphostnameclientesidparam=param

Puesto que tenemos plantillas de paraacutemetros necesitamos inicializarlos con valores quepasaremos como argumentos para crear la URI final Si queremos reutilizar la URI quecontiene las plantillas deberiacuteamos realizar una llamada a clone() antes de llamar al meacutetodobuild() ya que eacuteste reemplazaraacute los paraacutemetros de las plantillas en la estructura internadel objeto

UriBuilder clone = builderclone()URI uri = clonebuild(ejemplocom 333 valor)

El coacutedigo anterior dariacutea lugar a la siguiente URI

httpejemplocomclientes333param=valor

Tambieacuten podemos definir un objeto de tipo Map que contenga los valores de las plantillas

MapltString Objectgt map = new HashMapltString Objectgt()

Servicios Rest

114

mapput(hostname ejemplocom)mapput(id 333)mapput(param valor)UriBuilder clone = builderclone()URI uri = clonebuildFromMap(map)

Otro ejemplo interesante es el de crear una URI a partir de las expresiones Path definidasen las clases JAX-RS anotadas A continuacioacuten mostramos el coacutedigo

Path(clientes)public class ServicioClientes

Path(id) public Cliente getCliente(PathParam(id) int id)

Podemos referenciar esta clase y el meacutetodo getCliente() a traveacutes de la claseUriBuilder de la siguiente forma

UriBuilder builder = UriBuilderfromResource(ServicioClientesclass)builderhost(hostname)builderpath(ServicioClientesclass getCliente)

El coacutedigo anterior define la siguiente plantilla para la URI

httphostnameclientesid

A partir de esta plantilla podremos construir la URI utilizando alguno de los meacutetodos`buildXXX()

Tambieacuten podemos querer utilizar UriBuilder para crear URIS a partir de plantillas Para ellodisponemos de meacutetodos resolveTemplateXXX() que nos facilitan el trabajo

Clase UriBuilder meacutetodos resolveTemplateXXX() para crear URIs a partir de plantillas

public abstract UriBuilder resolveTemplate(String name Object value)public abstract UriBuilder resolveTemplate(String name Object value boolean encodeSlashInPath)public abstract UriBuilder resolveTemplateFromEncoded(String nameObject value)public abstract UriBuilder resolveTemplates(MapltString Objectgt templateValues)public abstract UriBuilder resolveTemplates( MapltStringObjectgt templateValues boolean encodeSlashInPath) throws IllegalArgumentExceptionpublic abstract UriBuilder resolveTemplatesFromEncoded( MapltString Objectgt templateValues) Devuelve la URI de la plantilla como una cadena de caracterespublic abstract String toTemplate()

Servicios Rest

115

Funcionan de forma similar a los meacutetodos build() y se utilizan para resolver parcialmentelas plantillas contenidas en la URI Cada uno de los meacutetodos devuelve una nueva instanciade UriBuilder de forma que podemos encadenar varias llamadas para resolver todas lasplantillas de la URI Finalmente usaremos el meacutetodo toTemplate() para obtener la nuevaplantilla en forma de String

String original = httphostidString nuevaPlantilla = UriBuilderfromUri(original) resolveTemplate(host localhost) toTemplate()

El valor de nuevaPlantilla para el coacutedigo anterior seriacutea httplocalhostid

URIs relativas mediante el uso de UriInfo

Cuando estamos escribiendo servicios que distribuyen enlaces hay cierta informacioacuten queprobablemente no conozcamos cuando estamos escribiendo el coacutedigo Por ejemplo podemosno conocer todaviacutea los hostnames de los enlaces o incluso los base paths de las URIs en elcaso de que estemos enlazando con otros servicios REST

JAX-RS proporciona una forma sencilla de solucionar estos problemas utilizando la interfazjavaxwsrscoreUriInfo Ya hemos introducido algunas caracteriacutesticas de estainterfaz en sesiones anteriores Ademaacutes de poder consultar informacioacuten baacutesica de la rutatambieacuten podemos obtener instancias de UriBuilder preinicializadas con la URI base utilizadapara definir los servicios JAX-RS o la URI utilizada para invocar la peticioacuten HTTP actual

public interface UriInfo public URI getRequestUri() public UriBuilder getRequestUriBuilder() public URI getAbsolutePath() public UriBuilder getAbsolutePathBuilder() public URI getBaseUri() public UriBuilder getBaseUriBuilder()

Por ejemplo supongamos que tenemos un servicio que permite acceder a Clientes desdeuna base de datos En lugar de tener una URI base que devuelva todos los clientes en unuacutenico documento podemos incluir los enlaces previo y sigiente de forma que podamosnavegar por los datos Vamos a mostrar coacutemo crear estos enlaces utilizando la URI parainvocar la peticioacuten

Path(clientes)public class ServicioClientes GET Produces(applicationxml)

public String getCustomers(Context UriInfo uriInfo)

UriBuilder nextLinkBuilder = uriInfogetAbsolutePathBuilder() nextLinkBuilderqueryParam(inicio 5) nextLinkBuilderqueryParam(total 10) URI next = nextLinkBuilderbuild() rellenar el resto del documento

Servicios Rest

116

Para acceder a la instancia UriInfo que representa al peticioacuten usamos la anotacioacutenjavaxwsrscoreContext para inyectarla como un paraacutemetro del meacutetodo delrecurso RESTObtenemos un UriBuilder preininicializado con la URI utilizada para acceder alservicio

Para el coacutedigo anterior y dependiendo de coacutemo se despliegue el servicio la URI creada podriacuteaser

httporgexpertojavajaxrsclientesinicio=5amptotal=10

Construccioacuten de enlaces (Links) en documentos XML y en cabeceras HTTP

JAX-RS proporciona cierto soporte para construir los enlaces y devolverlos en las cabecerasde respuesta o bien incluirlos en los documentos XML Para ello podemos utilizar las clasesjavawsrscoreLink y javawsrscoreLinkBuilder

Clase abstracta javaxwsrscoreLink

public abstract class Link public abstract URI getUri() public abstract UriBuilder getUriBuilder() public abstract String getRel() public abstract ListltStringgt getRels() public abstract String getTitle() public abstract String getType() public abstract MapltString Stringgt getParams() public abstract String toString()

Link es una clase abstracta que representa todos los metadatos contenidos en una cabecerao en un enlace Atom El meacutetodo getUri() representa el atributo href del enlace Atom Elmeacutetodo getRel() representa el atributo rel y asiacute sucesivamente Podemos referenciar atodos los atributos a traveacutes del meacutetodo getParams() Finalmente el meacutetodo toString()convertiraacute la instancia Link en una cadena de caracteres con el formato de una cabeceraLink

Para crear instancias de Link utilizaremos un LinkBuilder que crearemos con algunode estos meacutetodos

public abstract class Link public static Builder fromUri(URI uri) public static Builder fromUri(String uri) public static Builder fromUriBuilder(UriBuilder uriBuilder) public static Builder fromLink(Link link) public static Builder fromPath(String path) public static Builder fromResource(Classltgt resource) public static Builder fromMethod(Classltgt resource String method)

Servicios Rest

117

Los meacutetodos fromXXX() funcionan de forma similar a UriBuilderfromXXX() Todosinicializan el UriBuilder subyacente que utilizaremos para construir el atributo href delenlace

Los meacutetodos link() uri() y uriBuilder() nos permiten sobreescribir la URIsubyacente del enlace que estamos creando

public abstract class Link interface Builder public Builder link(Link link) public Builder link(String link) public Builder uri(URI uri) public Builder uri(String uri) public Builder uriBuilder(UriBuilder uriBuilder)

Los siguientes meacutetodos nos permiten asignar valores a varios atributos del enlace que estamosconstruyendo

public Builder rel(String rel) public Builder title(String title) public Builder type(String type) public Builder param(String name String value)

Finalmente eacutel meacutetodo build() nos permitiraacute construir el enlace

public Link build(Object values)

El objeto LinkBuilder tiene asociado una UriBuilder subyacente Los valorespasados como paraacutemetros del meacutetodo build() son utilizados por el UriBuilder paracrear una URI para el enlace Veamos un ejemplo

Link link = LinkfromUri(httphostraizclientesid) rel(update)type(textplain) build(localhost 1234)

Si realizamos una llamada a toString() sobre la instancia del enlace ( link )obtendremos lo siguiente

httplocalhostraizclientes1234gt rel=update type=textplain

A continuacioacuten mostramos dos ejemplos que muestran coacutemo crear instancias Link en lascabeceras y en el cuerpo de la respuesta como un enlace Atom

Escritura de enlaces en cabeceras HTTP

Servicios Rest

118

PathGETResponse get() Link link = LinkfromUri(abc)build() Response response = ResponsenoContent() links(link) build() return response

Inclusioacuten de un enlace Atom en el documento XMl de respuesta

import javaxwsrscoreLink

XmlRootElementpublic class Cliente private String nombre private ListltLinkgt enlaces = new ArrayListltLinkgt()

XmlElement public String getNombre() return nombre

public void setNombre(String nom) thisnombre = nom

XmlElement(name = enlace)

XmlJavaTypeAdapter(LinkJaxbAdapterclass) public ListltLinkgt getEnlaces() return enlaces

La clase Link contiene tambieacuten un JaxbAdapter con una implementacioacuten de la claseJAXB XmlAdapter que mapea los objetos JAX-RS de tipo Link a un valor quepuede ser serializado y deserializado por JAXB

El coacutedigo de este ejemplo permite construir cualquier enlace y antildeadirlo a la clase Clientede nuestro dominio Los enlaces seraacuten convertidos a elementos XML que se incluiraacuten en eldocumento XML de respuesta

44 Seguridad

Es importante que los servicios rest permitan un acceso seguro a los datos y funcionalidadesque proporcionan Especialmente para servicios que permiten la realizacioacuten de actualizacionesen los datos Tambieacuten es interesante asegurarnos de que terceros no lean nuestros mensajese incluso permitir que ciertos usuarios accedan a determinadas funcionalidades pero a otrasno

Ademaacutes de la especificacioacuten JAX-RS podemos aprovechar los servicios de seguridad quenos ofrece la web y Java EE y utilizarla en nuestros servicios REST Estos incluyen

AutentificacioacutenHace referencia a la validacioacuten de la identidad del cliente que accede a los serviciosNormalmente implica la comprobacioacuten de si el cliente ha proporcionado unos credenciales

Servicios Rest

119

vaacutelidos tales como el password En este sentido podemos utilizar los mecanismos quenos proporciona la web y las facilidades del contenedor de servlets de Java EE paraconfigurar los protocolos de autentificacioacuten

AutorizacioacutenUna vea que el cliente se ha autenticado (ha validado su identidad) querraacute interactuarcon nuestro servicio REST La autorizacioacuten hace referencia a decidir si un cierto usuariopuede acceder e invocar un determinado meacutetodo sobre una determinada URI Por ejemplopodemos habilitar el acceso a operaciones PUTPOSTDELETE para ciertos usuarios peropara otros no En este caso utilizaremos las facilidades que nos propociona el contenedorde servlets de Java EE para realizar autorizaciones

EncriptadoCuando un cliente estaacute interaccionando con un servicio REST es posible que alguienintercepte los mensajes y los lea si la conexioacuten HTTP no es segura Los datos sensiblesdeberiacutean protegerse con servicios criptograacuteficos tales como SSL

Autentificacioacuten en JAX-RS

Hay varios protocolos de autentificacioacuten En este caso vamos a ver coacutemo realizar unaautenticacioacuten baacutesica sobre HTTP (y que ya habeacuteis utilizado para servlets) Este tipo deautentificacioacuten requiere enviar un nombre de usuario y password codificados como Base-64en una cabecera de la peticioacuten al servidor El servidor comprueba si existe dicho usuario en elsistema y verifica el password enviado Veaacutemoslo con un ejemplo

Supongamos que un cliente no autorizado quiere acceder a nuestros servicios REST

GET clientes333 HTTP11

Ya que la peticioacuten no contiene informacioacuten de autentificacioacuten el servidor deberiacutea responderla siguiente respuesta

HTTP11 401 UnauthorizedWWW-Autenticate Basic realm=Cliente Realm

La respuesta 401 nos indica que el cliente no estaacute autorizado a acceder a dicha URI Lacabecera WWW-Autenticate especifica queacute protocolo de autentificacioacuten se deberiacutea usarEn este caso Basic significa que se deberiacutea utilizar una autentificacioacuten de tipo Basic Elatributo realm identifica una coleccioacuten de recursos seguros en un sitio web En este ejemploindica que solamente estaacuten autorizados a acceder al meacutetodo GET a traveacutes de la URI anteriortodos aquellos uarios que pertenezcan al realm Cliente Realm y seraacuten autentificados porel servidor mediante una autentificacioacuten baacutesica

Para poder realizar la autentificacioacuten el cliente debe enviar una peticioacuten que incluya lacabecera Authorization cuyo valor sea Basic seguido de la siguiente cadena decaracteres loginpassword codificada en Base64 (el valor de login y password representael login y password del usuario) Por ejemplo supongamos que el nombre del usuario esfelipe y el password es locking la cadena felipelocking codificada como Base64es ZmVsaXBlOmxvY2tpbmc= Por lo tanto nuestra peticioacuten deberiacutea ser la siguiente

GET clientes333 HTTP11

Servicios Rest

120

Authorization Basic ZmVsaXBlOmxvY2tpbmc=

El cliente deberiacutea enviar esta cabecera con todas y cada una de las peticiones que haga alservidor

El inconveniente de esta aproximacioacuten es que si la peticioacuten es interceptada por alguna entidadhostil en la red el hacker puede obtner faacutecilmente el usuario y el passwork y utilizarlos parahacer sus propias peticiones Utilizando una conexioacuten HTTP encriptada (HTTPS) se solucionaeste problema

Creacioacuten de usuarios y roles

Para poder utilizar la autentificacioacuten baacutesica necesitamos tener creados previamente los realmsen el servidor de aplicaciones Wildfly y registrar los usuarios que pertenecen a dichos realmsLa forma de hacerlo es ideacutentica a lo que ya habeacuteis visto en la asignatura de ComponentesWeb (a traveacutes del comando add-usersh )

Utilizaremos el realm por defecto ApplicationRealm de Wildfly que nos permitiraacute ademaacutescontrolar la autorizacioacuten mediante la asignacioacuten de roles a usuarios

Lo uacutenico que tendremos que hacer es antildeadir los usuarios a dicho realm a traveacutes de laherramienta $WILDFLY_HOMEbinadd-usersh

Al ejecutarla desde liacutenea de comandos deberemos elegir el ream ApplicationRealm eintroducir los datos para cada nuevo usuario que queramos antildeadir indicando su loginpassword y el grupo (rol) al que queremos que pertenezca dicho usuario

Los datos sobre los nuevos usuarios creados se almacenan en los ficheros application-usersproperties y application-rolesproperties tanto en el directorio$WILDFLY_HOMEstandaloneconfiguration como en $WILDFLY_HOMEdomainconfiguration

Una vez creados los usuarios tendremos que incluir en el fichero de configuracioacuten webxml la siguiente informacioacuten

ltweb-appgt

ltlogin-configgt ltauth-methodgtBASICltauth-methodgt

ltrealm-namegtApplicationRealmltrealm-namegt ltlogin-configgt

ltsecurity-constraintgt ltweb-resource-collectiongt ltweb-resource-namegtcustomer creationltweb-resource-namegt

lturl-patterngtrestresourceslturl-patterngt

lthttp-methodgtPOSTlthttp-methodgt ltweb-resource-collectiongt ltsecurity-constraintgt ltweb-appgt

Servicios Rest

121

El elemento ltlogin-configgt define coacutemo queremos autentificar nuestro despliegueEl subelemento ltauth-methodgt puede tomar los valores BASIC DIGEST orCLIENT_CERT correspondieacutendose con la autentificacioacuten Basic Digest y ClientCertificate respectivamenteEl valor de la etiqueta ltrealm-namegt es el que se mostraraacute como valor del atributorealm de la cabecera WWW-Autenticate si intentamos acceder al recurso sin incluirnuestras credenciales en la peticioacutenEl elemento ltlogin-configgt realmente NO activa la autentificacioacuten Por defectocualquier cliente puede acceder a cualquier URL proporcionada por nuestra aplicacioacutenweb sin restricciones Para forzar la autentificacioacuten debemos especificar el patroacuten URLque queremos asegurar (elemento lturl-patterngt )El elemento lthttp-methodgt nos indica que solamente queremos asegurar laspeticiones POST sobre esta URL Si no incluimos el elemento lthttp-methodgt todoslos meacutetodos HTTP seraacuten seguros En este ejemplo solamente queremos asegurar losmeacutetodos POST dirigidos a la URL restresources

Autorizacioacuten en JAX-RS

Mientras que la autentificacioacuten hacer referencia a establecer y verificar la identidad del usuariola autorizacioacuten tiene que ver con los permisos iquestEl usuario X estaacute autorizado para acceder aun determinado recurso REST

JAX-RS se basa en las especificaciones Java EE y de servlets para definir la forma de autorizara los usuarios En Java EE la autorizacioacuten se realiza asociando uno o maacutes roles con un usuariodado y a continuacioacuten asignando permisos basados en dicho rol Ejemplos de roles puedenser administrador empleado Cada rol tiene asignando unos permisos de acceso adeterminados recursos por lo que asignaremos los permisos utilizando cada uno de los roles

Para poder realizar la autorizacioacuten tendremos que incluir determinadas etiquetas en el ficherode configuracioacuten webxml (tal y como ya habeacuteis visto en la asignatura de ComponentesWeb) Veaacutemoslo con un ejemplo (en el que tambieacuten incluiremos autentificacioacuten)

Volvamos a nuestra aplicacioacuten de venta de productos por internet En esta aplicacioacutenes posible crear nuevos clientes enviando la informacioacuten en formato XML a un recursoJAX-RS localizado por la anotacioacuten Path(clientes) El servicio REST esdesplegado y escaneado por la clase Application anotada con ApplicationPath(servicios) de forma que la URI completa es serviciosclientes Queremosproporcionar seguridad a nuestro servicio de clientes de forma que solamente losadministradores puedan crear nuevos clientes Veamos cuaacutel seriacutea el contenido del ficherowebxml

ltxml version=10gtltweb-appgt ltsecurity-constraintgt ltweb-resource-collectiongt ltweb-resource-namegtcreacion de clientesltweb-resource-namegt lturl-patterngtserviciosclienteslturl-patterngt lthttp-methodgtPOSTlthttp-methodgt ltweb-resource-collectiongt

ltauth-constraintgt ltrole-namegtadminltrole-namegt ltauth-constraintgt ltsecurity-constraintgt

Servicios Rest

122

ltlogin-configgt ltauth-methodgtBASICltauth-methodgt ltrealm-namegtApplicationRealmltrealm-namegt ltlogin-configgt

ltsecurity-rolegt ltrole-namegtadminltrole-namegt ltsecurity-rolegtltweb-appgt

Especificamos queacute roles tienen permiso para acceder mediante POST a la URLservicescustomers Para ello utilizamos el elemento ltauth-constraintgtdentro de ltsecurity-constraintgt Este elemento tiene uno o maacutes subelementosltrole-namegt que definen queacute roles tienen permisos de acceso definidos porltsecurity-constraintgt En nuestro ejemplo estamos dando al rol adminpermisos para acceder a la URL servicescustomers con el meacutetodo POST Si ensu lugar indicamos un ltrole-namegt con el valor cualquier usuario podriacutea accedera dicha URL En otras palabras un ltrole-namegt con el valor significa que cualquierusuario que sea capaz de autentificarse puede acceder al recursoPara cada ltrole-namegt que usemos en nuestras declaraciones ltauth-constraintsgt debemos definir el correspondiente ltsecurity-rolegt en eldescriptor de despliegue

Una limitacioacuten cuando estamos declarando las ltsecurity-contraintsgt para los recursosJAX-RS es que el elemento lturl-patterngt solamente soporta el uso de en el patroacutenurl especificado Por ejemplo rest txt

Encriptacioacuten

Por defecto la especificacioacuten de servlets no requiere un acceso a traveacutes de HTTPSSi queremos forzar un acceso HTTPS podemos especificar un elemento ltuser-data-constraintgt como parte de nuestra definicioacuten de restricciones de seguridad ( ltsecurity-constraintgt ) Vamos a modificar nuestro ejemplo anterior para forzar un acceso a traveacutesde HTTPS

ltweb-appgt ltsecurity-constraintgt

ltweb-resource-collectiongt ltweb-resource-namegtcreacion de clientesltweb-resource-namegt lturl-patterngtserviciosclienteslturl-patterngt lthttp-methodgtPOSTlthttp-methodgt ltweb-resource-collectiongt

ltauth-constraintgt ltrole-namegtadminltrole-namegt ltauth-constraintgt

ltuser-data-constraintgt

lttransport-guaranteegtCONFIDENTIALlttransport-guaranteegt ltuser-data-constraintgt ltsecurity-constraintgt

Servicios Rest

123

ltweb-appgt

Todo lo que tenemos que hacer es declarar un elemento lttransport-guaranteegtdentro de ltuser-data-constraintgt con el valor CONFIDENTIAL Si un usuariointenta acceder a una URL con el patroacuten especificado a traveacutes de HTTP seraacute redirigidoa una URL basada en HTTPS

Anotaciones JAX-RS para autorizacioacuten

Java EE define un conjunto de anotaciones para definir metadatos de autorizacioacuten Laespecificacioacuten JAX-RS sugiere aunque no es obligatorio que las implementaciones pordiferentes vendedores den soporte a dichas anotaciones Eacutestas se encuentran en elpaquete javaxannotationsecurity y son RolesAllowed DenyAll PermitAll yRunAs

La anotacioacuten RolesAllowed define los roles permitidos para ejecutar una determinadaoperacioacuten Si anotamos una clase JAX-RS define el acceso para todas las operacionesHTTP definidas en la clase JAX-RS Si anotamos un meacutetodo JAX-RS la restriccioacuten se aplicasolamente al meacutetodo que se estaacute anotando

La anotacioacuten PermitAll especifica que cualquier usuario autentificado puede invocar anuestras operaciones Al igual que RolesAllowed esta anotacioacuten puede usarse en la clasepara definir el comportamiento por defecto de toda la clase o podemos usarla en cada unode los meacutetodos Veamos un ejemplo

Path(clientes)

RolesAllowed(ADMIN CLIENTE) public class ClienteResource

GET Path(id) Produces(applicationxml) public Cliente getClienter(PathParam(id) int id)

RolesAllowed(ADMIN) POST Consumes(applicationxml) public void crearCliente(Customer cust)

PermitAll GET Produces(applicationxml) public Customer[] getClientes()

Por defecto solamente los usuarios con rol ADMIN y CLIENTE pueden ejecutar losmeacutetodos HTTP definidos en la clase ClienteResourceSobreescribimos el comportamiento por defecto Para el meacutetodo crearCliente()solamente permitimos peticiones de usuarios con rol ADMINSobreescribimos el comportamiento por defecto Para el meacutetodo getClientes() deforma que cualquier usuario autentificado puede acceder a esta operacioacuten a traveacutes de laURI correspondiente con el meacutetodo GET

Servicios Rest

124

La ventaja de utilizar anotaciones es que nos permite una mayor flexibilidad que el usodel fichero de configuracioacuten webxml pudiendo definir diferentes autorizaciones a nivel demeacutetodo

Seguridad programada

Hemos visto como utilizar una seguridad declarativa es decir basaacutendonos en meta-datosdefinidos estaacuteticamente antes de que la aplicacioacuten se ejecute JAX-RS proporciona unaforma de obtener informacioacuten de seguridad que nos permite implementar seguridad de formaprogramada en nuestras aplicaciones

Podemos utilizar la interfaz javaxwsrscoreSecurityContext para determinar laidentidad del usuario que realiza la invocacioacuten al meacutetodo proporcionando sus credencialesTambieacuten podemos comprobar si el usuario pertenece o no a un determinado rol Esto nospermite implementar seguridad de forma programada en nuestras aplicaciones

public interface SecurityContext public Principal getUserPrincipal() public boolean isUserInRole(String role) public boolean isSecure() public String getAuthenticationScheme()

El meacutetodo getUserPrincipal() devuelve un objeto de tipojavaxsecurityPrincipal que representa al usuario que actualmente estaacute realizandola peticioacuten HTTP

El meacutetodo isUserInRole() nos permite determinar si el usuario que realiza la llamadaactual pertenece a un determinado rol

El meacutetodo isSecure() devuelve cierto si la peticioacuten actual es una conexioacuten segura

El meacutetodo getAuthenticationScheme() nos indica queacute mecanismo de autentificacioacuten seha utilizado para asegurar la peticioacuten (valores tiacutepicos devueltos por el meacutetodo son BASIC DIGEST CLIENT_CERT y FORM )

Podemos acceder a una instancia de SecurityContext inyectaacutendola en un campo meacutetodosetter o un paraacutemetro de un recurso utilizando la anotacioacuten Context Veamos un ejemploSupongamos que queremos obtener un fichero de log con todos los accesos a nuestra basede datos de clientes hechas por usuarios que no son administradores

Path(clientes)public class CustomerService GET Produces(applicationxml) public Cliente[] getClientes(Context SecurityContext sec) if (secisSecure() ampamp secisUserInRole(ADMIN)) loggerlog(secgetUserPrincipal() + ha accedido a la base de datos de clientes)

Servicios Rest

125

En este ejemplo inyectamos una instancia de SecurityContext como un paraacutemetro delmeacutetodo getClientes() Utilizamos el meacutetodo SecurityContextisSecure() paradeterminar si se trata de una peticioacuten realizada a traveacutes de un canal seguro (como HTTPS) Acontinuacioacuten utilizamos el meacutetodo SecurityContextisUserInRole() para determinarsi el usuario que realiza la llamada tiene el rol ADMIN o no Finalmente imprimimos el resultadoen nuestro fichero de logs

Con la introduccioacuten del API de filtros en JAX-RS 20 podemos implementar la interfazSecurityContext y sobreescribir la peticioacuten actual sobre SecurityContext utilizandoel meacutetodo ContainerRequestContextsetSecurityContext() Lo interesante deesto es que podemos implementar nuestros propios protocolos de seguridad Por ejemplo

import javaxwsrscontainerContainerRequestContextimport javaxwsrscontainerContainerRequestFilterimport javaxwsrscontainerPreMatchingimport javaxwsrscoreSecurityContextimport javaxwsrscoreHttpHeaders

PreMatchingpublic class CustomAuth implements ContainerRequestFilter protected MyCustomerProtocolHandler customProtocol =

public void filter(ContainerRequestContext requestContext) throws IOException String authHeader = requestgetHeaderString(HttpHeadersAUTHORIZATION) SecurityContext newSecurityContext = customProtocolvalidate(authHeader) requestContextsetSecurityContext(authHeader)

Este filtro no muestra todos los detalles pero siacute la idea Extrae la cabecera Authorizationde la peticioacuten y la pasa a nuestro propio servicio customerProtocol Eacuteste devuelve unaimplementacioacuten de SecurityContext Finalmente sobreescribimos el SecurityContext pordefecto utilizando la nueva implementacioacuten

No vamos a explicar el API de filtros de JAS-RS 20 Como ya habeacuteis visto en la asignaturade Componentes Web los filtros son objetos que se interponen entre el procesamiento delas peticiones tanto del servidor como del cliente

El filtro mostrado en el ejemplo es un filtro de peticioacuten en la parte del servidor Este tipo defiltros se ejecuta antes de que se invoque a un meacutetodo JAX-RS

Servicios Rest

126

45 Ejercicios

Para los ejercicios de esta sesioacuten proporcionamos el MOacuteDULO s4-foroAvanzado quetendreacuteis que usar como plantilla para realizar las tareas planteadas

El proyecto estaacute estructurado loacutegicamente en los siguientes paquetes

bull orgexpertojavanegocio

bull orgexpertojavarest

A su vez cada uno de ellos contiene los subpaquetes api y modelo con las clasesrelacionadas con los servicios proporcionados y los datos utilizados por los serviciosrespectivamente

El API rest implementado es el siguiente

bull Recurso UsuariosResourcejava

GET usuarios proporciona un listado con los usuarios del foro

bull Subrecurso UsuarioResourcejava

GET usuarioslogin proporciona informacioacuten sobre el usuario cuyo login es login

PUT usuarioslogin actualiza los datos de un usuario

DELETE usuarioslogin borra los datos de un usuario

GET usuariosloginmensajes obtiene un listado de los mensajes de un usuario

bull Recurso MensajesResourcejava

GET mensajes proporciona un listado con los mensajes del foro

POST mensajes antildeade un mensaje nuevo en el foro

GET mensajesid proporciona informacioacuten sobre el mensaje cuyo id es id

PUT mensajesid modifica un mensaje

DELETE mensajesid borra un mensaje

Una vez desplegada la aplicacioacuten podeacuteis antildeadir datos a la base de datos del foro utilizandolos datos del fichero srcmainresourcesforosql Para ello simplemente tendreacuteis que invocarla goal Maven correspondiente desde la ventana Maven Projects gt s4-foroAvanzado gt Pluginsgt sql gt sqlexecute

En el directorio srcmainresources teneacuteis un fichero de texto ( instruccionestxt )con informacioacuten adicional sobre la implementacioacuten proporcionada

A partir de las plantillas se pide

Uso de Hateoas (1 puntos)

Vamos a antildeadir a los servicios enlaces a las operaciones que podemos realizar con cadarecurso siguiendo el estilo Hateoas

a Para los usuarios

Servicios Rest

127

bull En el listado de usuarios antildeadir a cada usuario un enlace con relacioacuten self que apuntea la direccioacuten a la que estaacute mapeado el usuario individual

bull En la operacioacuten de obtencioacuten de un usuario individual incluir los enlaces para ver elpropio usuario (self) modificarlo (usuariomodificar) borrarlo (usuarioborrar) o ver losmensajes que envioacute el usuario (usuariomensajes)

b Para los mensajes

bull En el listado de mensajes antildeadir a cada mensaje un enlace con relacioacuten self queapunte a la direccioacuten a la que estaacute mapeado el mensaje individual

bull En la operacioacuten de obtencioacuten de un mensaje individual incluir los enlaces para ver elpropio mensaje (self) modificarlo (mensajemodificar) borrarlo (mensajeborrar) o verlos datos del usuario que envioacute el mensaje (mensajeusuario)

Utiliza postman para comprobar las modificaciones realizadas

Ejercicio seguridad (1 punto)

Vamos ahora a restringir el acceso al servicio para que soacutelo usuarios registrados puedanrealizar modificaciones Se pide

a Antildeadir al usuario pepe en el ApplicationRealm de wildfly con la contrasentildea pepe yperteneciente al grupo (rol) registrado

b Configurar mediante seguridad declarativa para que las operaciones de modificacioacuten(POST PUT y DELETE) soacutelo la puedan realizar los usuarios con rol registrado Utilizarautentificacioacuten de tipo BASIC

c Ahora vamos a hacer que la modificacioacuten o borrado de usuarios soacutelo puedarealizarlas el mismo usuario que va a modificarse o borrarse Para ello utilizaremosseguridad programada En el caso de que el usuario que va a realizar lamodificacioacuten o borrado quiera borrarmodificar otros usuarios lanzaremos la excepcioacutenWebApplicationException(StatusFORBIDDEN)

d Vamos a hacer lo mismo con los mensajes Soacutelo podraacute modificar y borrar mensajes elmismo usuario que los creoacute y al publicar un nuevo mensaje forzaremos que el login delmensaje sea el del usuario que hay autentificado en el sistema

Utiliza postman para comprobar las modificaciones realizadas

Servicios Rest

128

5 Api cliente Procesamiento JSON y Pruebas

Hasta ahora hemos hablado sobre la creacioacuten de servicios web RESTful y hemos probadonuestros servicios utilizando el cliente que nos proporciona IntelliJ curl o Postman pararealizar peticiones y observar las respuestas JAX-RS 20 proporciona un API cliente parafacilitar la programacioacuten de clientes REST que presentaremos en esta sesioacuten

En sesiones anteriores hemos trabajado con representaciones de texto y xmlfundamentalmente Aquiacute hablaremos con maacutes detalle de JSON que constituye otra forma derepresentar los datos de las peticiones y respuestas de servicios REST muy extendida

Finalmente veremos coacutemo implementar pruebas sobre nuestro servicio utilizando el APIcliente y el framework junit

51 API cliente Visioacuten general

La especificacioacuten JAX-RS 20 incorpora un API cliente HTTP que facilita enormemente laimplementacioacuten de nuestros clientes RESTful y constituye una clara alternativa al uso declases Java como javanetURL libreriacuteas externas (como la de Apache) u otras solucionespropietarias

El API cliente estaacute contenido en el paquete javaxwsrsclient y estaacute disentildeado paraque se pueda utilizar de forma fluida (fluent) Esto significa como ya hemos visto que loutilizaremos encadenando una sucesioacuten de llamadas a meacutetodos del API permitieacutendonos asiacuteescribir menos liacuteneas de coacutedigo Baacutesicamente estaacute formado por tres clases principales ClientWebTarget y Response (ya hemos hablado de esta uacuteltima en sesiones anteriores)

Para acceder a un recurso REST mediante el API cliente es necesario seguir los siguientespasos

1 Obtener una instancia de la interfaz Client

2 Configurar la instancia Client a traveacutes de un target (instancia de WebTarget )

3 Crear una peticioacuten basada en el target anterior

4 Invocar la peticioacuten

Vamos a mostrar un coacutedigo ejemplo para ilustrar los pasos anteriores En este caso vamos ainvocar peticiones POST y GET sobre una URL (target) para crear y posteriormente consultarun objeto Cliente que representaremos en formato XML

Client client = ClientBuildernewClient()

WebTarget target =

clienttarget(httpexpertojavaorgclientes)

Response response = target

request()

post(Entityxml(new Cliente(Alvaro Gomez)))

responseclose()

Servicios Rest

129

Cliente cliente = targetqueryParam(nombre Alvaro Gomez) request()

get(Clienteclass) clientclose()

Obtenemos una instancia javaxwsrsclientClientCreamos un WebTargetCreamos la peticioacutenRealizamos una invocacioacuten POSTCerramos (liberamos) el input stream para esta respuesta (en el caso de que esteacutedisponible y abierto) Es una operacioacuten idempotente es decir podemos invocarlamuacuteltiples veces con el mismo efectoA partir de un WebTarget establecemos los valores de los queryParams de la URIde la peticioacuten creamos la peticioacuten y realizamos una invocacioacuten GET

A continuacioacuten explicaremos con detalle los pasos a seguir para implementar un clienteutilizando el API de JAX-RS

Obtenemos una instancia Client

La interfaz javaxwsrsclientClient es el principal punto de entrada del API ClienteDicha interfaz define las acciones e infraestructura necesarias requeridas por un cliente RESTpara consumir un servicio web RESTful Los objetos Client se crean a partir de la claseClientBuilder

Clase ClientBuilder utilizada para crear objetos Client

public abstract class ClientBuilder implements ConfigurableltClientBuildergt public static Client newClient() public static Client newClient(final Configuration configuration)

public static ClientBuilder newBuilder()

public abstract ClientBuilder sslContext(final SSLContext sslContext) public abstract ClientBuilder keyStore(final KeyStore keyStore final char[] password) public ClientBuilder keyStore(final KeyStore keyStore final String password) public abstract ClientBuilder trustStore(final KeyStore trustStore) public abstract ClientBuilder hostnameVerifier(final HostnameVerifier verifier)

public abstract Client build()

La forma maacutes sencilla de crear un objeto Client es medianteClientBuildernewClient() Este meacutetodo proporciona una instancia pre-inicializada detipo Client lista para ser usada La clase ClientBuilder nos proporciona meacutetodos adicionalescon los que podremos configurar diferentes propiedades del objeto

Veamos un ejemplo de uso de ClientBuildernewBuilder() utilizando ademaacutes algunode los meacutetodos proporcionados para configurar nuestra instancia de tipo Client que vamos

Servicios Rest

130

a crear Los meacutetodos register() y property() son meacutetodos de la interfaz Configurable(y que son implementados por ClientBuilder)

Ejemplo de uso de ClientBuilder

Client cliente = ClientBuildernewBuilder()

property(connectiontimeout 100)

sslContext(sslContext)

register(JacksonJsonProviderclass)

build()

Creamos un ClientBuilder invocando al meacutetodo estaacuteticoClientBuildernewBuilder()Asignamos una propiedad especiacutefica de la implementacioacuten concreta de JAX-RS queestemos utilizando que controla el timeout de las conexiones de los socketsEspecificamos el sslContext que queremos utilizar para gestionar las conexiones HTTPRegistramos a traveacutes del meacutetodo register() (de la interfaz Configurable) una claseanotada con Provider Dicha clase conoce coacutemo serializar objetos Java a JSONy viceversaFinalmente realizamos una llamada a build() para crear la instancia Client

Las instancias de Client gestionan conexiones con el cliente utilizando sockets y sonobjetos bastante pesados Se deberiacutean reutilizar las instancias de esta interfaz en la medida delo posible ya que la inicializacioacuten y destruccioacuten de dichas instancias consume mucho tiempoPor lo tanto por razones de rendimiento debemos limitar el nuacutemero de instancias Clienten nuestra aplicacioacuten

Client client = ClientBuildernewClient()

clientclose()

Obtenemos una instancia de tipo Client invocando al meacutetodoClientBuildernewClient()Utilizamos el meacutetodo close() para cerrar la instancia Client despueacutes de realizartodas las invocaciones sobre el target del recurso De esta forma cerramos la conexioacutende forma que se liberan sus recursos y ya no podremos seguir usaacutendola

Recuerda siempre invocar el meacutetodo close() sobre nuestros objetosClient despueacutes de que hayamos realizado todas las invocacionessobre el target dellos recursos REST A menudo los objetos Clientreutilizan conexiones por razones de rendimiento Si no los cerramosdespueacutes de utilizarlos estaremos desaprovechando recursos del sistemamuy valiosos Cerrar la conexioacuten implica cerrar el socket

Igualmente si el resultado de una invocacioacuten a un servicio rest es unainstancia de Response debemos invocar el meacutetodo close() sobredichos objetos Response para liberar la conexioacuten Liberar una conexionsignifica permitir que eacutesta esteacute disponible para otro uso por una instanciaClient Liberar la conexioacuten no implica cerrar el socket

La interfaz Client es una sub-interfaz de Configurable Esto nos permitiraacute utililizarlos meacutetodos property() y register() para cambiar la configuracioacuten y registrarcomponentes en la parte del cliente en tiempo de ejecucioacuten

Servicios Rest

131

Interfaz Client (es una subinterfaz de Configurable)

public interface Client extends ConfigurableltClientgt

public void close()

public WebTarget target(String uri) public WebTarget target(URI uri) public WebTarget target(UriBuilder uriBuilder) public WebTarget target(Link link)

Sin embargo el principal propoacutesito de Client es crear instancias de WebTarget comoveremos a continuacioacuten

Configuramos el target del cliente (URI)

La interfaz javaxwsrsclientWebTarget representa la URI especiacutefica quequeremos invocar para acceder a un recurso REST particular

Interfaz WebTarget (es una subinterfaz de Configurable)

public interface WebTarget extends ConfigurableltWebTargetgt

public URI getUri() public UriBuilder getUriBuilder()

public WebTarget path(String path) public WebTarget resolveTemplate(String name Object value) public WebTarget resolveTemplates(MapltString Objectgt templateValues) public WebTarget matrixParam(String name Object values) public WebTarget queryParam(String name Object values)

La interfaz WebTarget tiene meacutetodos para extender la URI inicial que hayamosconstruido Podemos antildeadir por ejemplo segmentos de path o paraacutemetros deconsulta invocando a los meacutetodos WebTargetpath() o WebTargetqueryParam() respectivamente Si la instancia de WebTarget contiene plantillas de paraacutemetros losmeacutetodos WebTargetresolveTemplate() pueden asignar valores a las variablescorrespondientes Por ejemplo

Ejemplo para crear la URI httpejemplocomclientes123verboso=true

WebTarget target = client

target(httpejemplocomclientesid)

resolveTemplate(id 123)

queryParam(verboso true)

Servicios Rest

132

Inicializamos un WebTarget con una URI que contiene una plantilla con un paraacutemetroid El objeto client es una instancia de la clase `ClientEl meacutetodo resolveTemplate() rellena la expresioacuten id con el valor 123Finalmente antildeadimos a la URI un paraacutemetro de consulta verboso=true

Las instancias de WebTarget son inmutables con respecto a la URI que contienen Estosignifica que los meacutetodos para especificar segmentos de path adicionales y paraacutemetrosdevuelven una nueva instancia de WebTarget Sin embargo las instancias de WebTargetson mutables respecto a su configuracioacuten Por lo tanto la configuracioacuten de objetosWebTarget no crea nuevas instancias

Veamos otro ejemplo

WebTarget base = clientetarget(httpexpertojavaorg)

WebTarget clienteURI = basepath(cliente)

clienteURIregister(MyProviderclass)

base es una instancia de WebTarget con el valor de URI httpexertojavaorgclienteURI es una instancia de WebTarget con el valor de URI httpexertojavaorgclienteConfiguramos clienteURI registrando la clase MyProvider

En este ejemplo creamos dos instancias de WebTarget La instancia clienteURI heredala configuracioacuten de base y posteriormente modificamos la configuramos registrando unaclase Provider Los cambios sobre la configuracioacuten de clienteURI no afectan a laconfiguracioacuten de base ni tampoco se crea una nueva instancia de WebTarget

Los beneficios del uso de WebTarget se hacen evidentes cuando construimos URIscomplejas por ejemplo cuando extendemos nuestra URI base con segmentos de pathadicionales o plantillas El siguiente ejemplo ilustra estas situaciones

WebTarget base = clientetarget(httpexpertojavaorg)

WebTarget saludo = basepath(hola)path(quien) Response res = saludoresolveTemplate(quien mundo)request()get()

base representa la URI httpexpertojavaorgsaludo representa la URI httpexpertojavaholaquien

En el siguiente ejemplo utilizamos una URI base y a partir de ella construimos otras URIs querepresentan servicios diferentes proporcionados por nuestro recurso REST

Client cli = ClientBuildernewClient()WebTarget base = clienttarget(httpejemplowebapi)

WebTarget lectura = basepath(leer)

WebTarget escritura = basepath(escribir)

lectura representa la uri httpejemplowebapileerescritura representa la uri httpejemplowebapiescribir

El meacutetodo WebTargetpath() crea una nueva instancia de WebTarget antildeadiendo a laURI actual el segmento de ruta que se pasa como paraacutemetro

Servicios Rest

133

Construimos y Realizamos la peticioacuten

Una vez que hemos creado y configurado convenientemente el WebTarget que representala URI que queremos invocar tenemos que construir la peticioacuten y finalmente realizarla

Para construir la peticioacuten podemos Utilizar uno de los meacutetodos WebTargetrequest() que mostramos a continuacioacuten

Interfaz WebTarget meacutetodos para comenzar a construir la peticioacuten

public interface WebTarget extends ConfigurableltWebTargetgt public InvocationBuilder request() public InvocationBuilder request(String acceptedResponseTypes) public InvocationBuilder request(MediaType acceptedResponseTypes)

Normalmente invocaremos WebTargetrequest() pasando como paraacutemetro el mediatype aceptado como respuesta en forma de String o utilizando una de las constantes dejavaxwsrscoreMediaType Los meacutetodos WebTargetrequest() devuelven unainstancia de InvocationBuilder una interfaz que proporciona meacutetodos para prepararla peticioacuten del cliente y tambieacuten para invocarla

La interface InvocationBuilder Contiene un conjunto de meacutetodos que nos permitenconstruir diferentes tipos de cabeceras de peticiones Asiacute por ejemplo proporciona variosmeacutetodos acceptXXX() para indicar diferentes tipos MIME lenguajes o encodingaceptados Tambieacuten proporciona meacutetodos cookie() para especificar cookies para enviaral servidor Finalmente proporciona meacutetodos header() para especificar diferentes valoresde cabeceras

Ejemplos de uso de esta interfaz para construir la peticioacuten

Client cli = ClientBuildernewClient()cliinvocation(LinkvalueOf(httpejemplorest))accept(applicationjson)get()si no utilizamos el meacutetodo invocation podemos hacerlo asiacuteclitarget(httpejemplorest)request(applicationjson)get()

Client cliente = ClientBuildernewClient()WebTarget miRecurso = clienttarget(httpejemplowebapimensaje) request(MediaTypeTEXT_PLAIN)

El uso de una constante MediaType es equivalente a utilizar el String que define el tipoMIME

InvocationBuilder builder = miRecursorequest(textplain)

Hemos visto que WebTarget implementa meacutetodos request() cuyosparaacutemetros especifican el tipo MIME de la cabecera Accept de la peticioacutenEl coacutedigo puede resultar maacutes legible si usamos en su lugar el meacutetodo

Servicios Rest

134

InvocationBuilderaccept() En cualquier caso es una cuestioacuten de gustospersonales

Despueacutes de determinar el media type de la respuesta invocamos la peticioacuten realizando unallamada a uno de los meacutetodos de la instancia de InvocationBuilder que se correspondecon el tipo de peticioacuten HTTP esperado por el recurso REST al que va dirigido dicha peticioacutenEstos meacutetodos son

bull get()

bull post()

bull delete()

bull put()

bull head()

bull options()

La interfaz InvocationBuilder es una subinterfaz de la interfaz SyncInvoker y es la queespecifica los meacutetodos anteriores (get post hellip) para realizar peticiones siacutencronas es decirque hasta que no nos conteste el servidor no podremos continuar procesando el coacutedigo enla parte del cliente

Las peticiones GET tienen los siguientes prototipos

Interface SyncInvoker peticiones GET siacutencronas

public interface SyncInvoker ltTgt T get(ClassltTgt responseType) ltTgt T get(GenericTypeltTgt responseType) Response get()

Los primeros dos meacutetodos geneacutericos convertiraacuten una respuesta HTTP con eacutexito a tipos Javaespeciacuteficos indicados como paraacutemetros del meacutetodo El tercero devuelve una instancia de tipoResponse Por ejemplo

Ejemplos de peticiones GET utilizando el API cliente jasx-rx 20

Client cli = ClientBuildernewClient()

peticioacuten get que devuelve una instancia de ClienteCliente cliRespuesta = clitarget(httpejemploclientes123) request(applicationjson)

get(Clienteclass)

peticioacuten get que devuelve una lista de objetos ClienteListltClientegt cliRespuesta2 = clitarget(httpejemploclientes) request(applicationxml)

get(new GenericTypeltListltClientegtgt() )

peticioacuten get que devuelve un objeto de tipo ResponseResponse respuesta = clitarget(httpejemploclientes245)

Servicios Rest

135

request(applicationjson)

get() try if (respuestagetStatus() == 200) Cliente cliRespuesta =

respuestareadEntity(Clienteclass) finally respuestaclose()

En la primera peticioacuten queremos que el servidor nos devuelva la respuesta en formatoJSON y posteriormente la convertiremos en el tipo Cliente utilizando un de loscomponentes MessageBodyReader registradosEn la segunda peticioacuten utilizamos la clase javaxwsrscoreGenericType parainformar al correspondiente MessageBodyReader del tipo de objetos de nuestra ListaPara ello creamos una clase anoacutenima a la que le pasamos como paraacutemetro el tipogeneacuterico que queremos obtenerEn la tercera peticioacuten obtenemos una instancia de Response a partir de la cual podemosobtener el cuerpo del mensaje de respuesta del servidorEl meacutetodo readEntity() asocia el tipo Java solicitado (en este caso el tipo java Cliente) yel contenido de la respuesta recibida con el correspondiente proveedor de entidades (detipo MessageBodyReader) para obtener dicho tipo Java a partir de la respuesta HTTPrecibida En sesiones anteriores hemos utilizado la clase Response desde el servicioREST para construir la respuesta que se enviacutea al cliente

Recordemos algunos de los meacutetodos que podemos utilizar desde el cliente para analizar larespuesta que hemos obtenido

Meacutetodos de la clase Response que utilizaremos desde el cliente

public abstract class Response

public abstract Object getEntity()

public abstract int getStatus()

public abstract ResponseStatusType getStatusInfo()

public abstract MultivaluedMapltString Objectgt getMetadata()

public abstract URI getLocation()

public abstract MediaType getMediaType()

public MultivaluedMapltStringObjectgt getHeaders()

public abstract ltTgt T readEntity(ClassltTgt entityType)

public abstract ltTgt T readEntity(GenericTypeltTgt entityType)

public abstract void close()

El meacutetodo getEntity() devuelve el objeto Java correspondiente al cuerpo delmensaje HTTPEl meacutetodo getStatus() devuelve el coacutedigo de respuesta HTTPEl meacutetodo getStatusInfo() devuelve la informacioacuten de estado asociada con larespuestaEl meacutetodo getMetadata() devuelve una instancia de tipo MultivaluedMap con lascabeceras de la respuesta

Servicios Rest

136

El meacutetodo getLocation() devuelve la URI de la cabecera Location de la respuestaEl meacutetodo getMediaType() devuelve el mediaType del cuerpo de la respuestaEl meacutetodo getHeaders() devuelve las cabeceras de respuesta con sus valorescorrespondientesEl meacutetodo readEntity() devuelve la entidad del cuerpo del mensaje utilizando unMessageBodyReader que soporte el mapeado del inputStream de la entidad a la claseJava especificada como paraacutemetroEl meacutetodo readEntity() tambieacuten puede devolver una clase geneacuterica si se disponedel MessageBodyReader correspondienteEl meacutetodo close() cierra el input stream correspondiente a la entidad asociada delcuerpo del mensaje (en el caso de que esteacute disponible y abierto) Tambieacuten liberacualquier otro recurso asociado con la respuesta (como por ejemplo datos posiblementealmacenados en un buffer)

Veamos otro ejemmplo Si el recurso REST espera una peticioacuten HTTP GET invocaremosel meacutetodo InvocationBuilderget() El tipo de retorno del meacutetodo deberiacuteacorresponderse con la entidad devuelta por el recurso REST que atenderaacute la peticioacuten

Client cliente = ClientBuildernewClient()WebTarget miRecurso = clientetarget(httpejemplowebapilectura)String respuesta = miRecursorequest(MediaTypeTEXT_PLAIN) get(Stringclass)

O tambieacuten podriacuteamos codificarlo como

Client cliente = ClientBuildernewClient()String respuesta = cliente target(httpejemplowebapilectura) request(MediaTypeTEXT_PLAIN) get(Stringclass)

Si el tipo de retorno de la peticioacuten GET es una coleccioacuten usaremosjavaxwsrscoreGenericTypeltTgt como paraacutemetro del meacutetodo en donde T es eltipo de la coleccioacuten

ListltPedidoAlmacengt pedidos = client target(httpejemplowebapilectura) path(pedidos) request(MediaTypeAPPLICATION_XML) get(new GenericTypeltListltPedidoAlmacengtgt() )

Si el recurso REST destinatario de la peticioacuten espera una peticioacuten de tipo HTTP POSTinvocaremos el meacutetodo InvocationBuilderpost()

Las peticiones POST tienen los siguientes prototipos

Interface SyncInvoker peticiones POST siacutencronas

public interface SyncInvoker ltTgt T post(Entityltgt entity ClassltTgt responseType)

Servicios Rest

137

ltTgt T post(Entityltgt entity GenericTypeltTgt responseType) Response post(Entityltgt entity)

Los primeros dos meacutetodos geneacutericos enviacutean una entidad (clase java + tipo MIME asociado)indicada como primer paraacutemetro del meacutetodo y como segundo paraacutemetro se indica el tipo javaal que se convertiraacute la respuesta recibida El tercero enviacutea una entidad y devuelve una instanciade tipo Response Por ejemplo

Veamos un ejemplo de invocacioacuten de peticiones POST

Ejemplo de peticioacuten POST utilizando el API cliente jasx-rx 20

Client cli = ClientBuildernewClient()Pedido pe = new PedidoAlmacen()Pedido peRespuesta = cli target() request() post(Entityentity(new Pedido() applicationjson) Pedidoclass)

En este caso estamos realizando una peticioacuten POST Como payload del mensaje enviamosun objeto Pedido representado en formato json La entidad esperada como respuesta debeser de tipo Pedido

Esto implica que en el lado del servidor el meacutetodo que atiende la peticioacuten Post tendraacute unparaacutemetro de tipo Pedido y se deberaacuten serializar los objetos de tipo Pedido a json ya que esel tipo MIME asociado a esta entidad ( especificado en la cabera Content-Type de la peticioacutenHTTP)

La clase Entity encapsula los objetos Java que queremos enviar con las peticiones GET oPOST No tiene un constructor puacuteblico En su lugar tenemos que invocar uno de sus meacutetodosestaacuteticos

Clase javaxwsrsclientEntity

public final class EntityltTgt

public static ltTgt EntityltTgt entity(T entity String mediaType)

public static ltTgt EntityltTgt entity(T entity MediaType mediaType)

public static ltTgt EntityltTgt xml(final T entity)

public static ltTgt EntityltTgt json(final T entity)

public static ltTgt EntityltTgt text(T entity)

public static EntityltFormgt form(final Form form)

El meacutetodo estaacutetico entity() crea una entidad (clase Java) con un tipo MIME asociado dadopor la cadena de caracteres mediaTypeEl meacutetodo estaacutetico entity() crea una entidad (clase Java) con un tipo MIME indicado enmediaTypeEl meacutetodo xml crea una entidad (clase Java) con el tipo MIME applicationxml

Servicios Rest

138

El meacutetodo json crea una entidad (clase Java) con el tipo MIME applicationjsomEl meacutetodo text crea una entidad (clase Java) con el tipo MIME textplainEl meacutetodo form crea una entidad (clase Java) con el tipo MIME applicationx-www-form-urlencoded

Veamos otro ejemplo de invocacioacuten POST que utiliza la clase Entity

Ejemplo de peticioacuten POST y uso de clase Entity

NumSeguimiento numSeg = client target(httpejemplowebapiescritura)

request(MediaTypeAPPLICATION_XML)

post(Entityxml(pedido) NumeroSeguimientoclass)

Especificamos como paraacutemetro de la peticioacuten request() el tipo MIME que aceptamos enla respuesta (cabecera HTTP Accept)Realizamos una peticioacuten POST El cuerpo del mensaje se crea con la llamadaEntityxml(pedido) El tipo Entity encapsula la entidad del mensaje (tipo JavaPedido) y el tipo MIME asociado (tipo MIME applicationxml)

Veamos un ejemplo en el que enviamos paraacutemetros de un formulario en una peticioacuten POST

Ejemplo de enviacuteo de datos de un formulario en una peticioacuten POST

Form form = new Form()param(nombre Pedro) param(apellido Garcia)Response response = clienttarget(httpejemploclientes) request() post(Entityform(form))responseclose()

La peticioacuten POST del coacutedigo anterior enviacutea los datos del formulario y espera recibir comorespuesta una entidad de tipo Response

El coacutedigo en el lado del servidor seraacute similar a eacuteste

Servicio rest que sirve una peticioacuten POST a partir de datos de un formulario

POSTPath(clientes)Produces(texthtml)public Response crearCliente(FormParam(nombre)String nom FormParam(apellido)String ape) creamos el nuevo cliente return Responseok(RESPONSE_OK)build()

Manejo de excepciones

Veamos queacute ocurre si se produce una excepcioacuten cuando utilizamos una forma de invocacioacutenque automaacuteticamente convierte la respuesta en el tipo especificado Supongamos el siguienteejemplo

Servicios Rest

139

Cliente cli = clienttarget(httptiendacomclientes123) request(applicationjson) get(Clienteclass)

En este escenario el framework del cliente convierte cualquier coacutedigo de error HTTP en unade las excepciones que antildeade JAX-RS 20 (BadRequesException ForbiddenExceptionhellip) yque ya hemos visto Podemos capturar dichas excepciones en nuestro coacutedigo para tratarlasadecuadamente

try Cliente cli = clienttarget(httptiendacomclientes123) request(applicationjson) get(Clienteclass) catch (NotAcceptableException notAcceptable) catch (NotFoundException notFound)

Si el servidor responde con un error HTTP no cubierto por alguna excepcioacutenespeciacutefica JAX-RS entonces se lanza una excepcioacuten de propoacutesito general La claseClientErrorException cubre cualquier coacutedigo de error en la franja del 400 La claseServerErrorException cubre cualquier coacutedigo de error en la franja del 500

Si el servidor enviacutea alguna de los coacutedigos de respuesta HTTP 3xx (clasificados como coacutedigosde la categoriacutea redireccioacuten) el API cliente lanza una RedirectionException a partir de lacual podemos obtener la URL para poder tratar la redireccioacuten nosotros mismos Por ejemplo

WebTarget target = clienttarget(httptiendacomclientes123)boolean redirected = false

Cliente cli = nulldo try cli = targetrequest(applicationjson) get(Clienteclass) catch (RedirectionException redirect) if (redirected) throw redirect redirected = true target = clienttarget(redirectgetLocation()) while (cli == null)

En este ejemplo volvemos a iterar si recibimos un coacutedigo de respuesta 3xx El coacutedigo seasegura de que soacutelo permitimos un coacutedigo de este tipo cambiando el valor de la variableredirect en el bloque en el que capturamos la exceptioacuten A continuacioacuten cambiamos elWebTarget (en el bloque catch ) al valor de la cabecera Location de la respuesta delservidor

Los coacutedigos de estado HTTP 3xx indican que es neceario realizar algunaaccioacuten adicional para que el servidor pueda completar la peticioacuten Laaccioacuten requerida puede llevarse a cabo sin necesidad de interactuar con el

Servicios Rest

140

cliente soacutelo si el meacutetodo utilizado en la segunda peticioacuten es GET o HEAD(ver httpwwww3orgProtocolsrfc2616rfc2616-sec10html)

52 Procesamiento JSON

JSON (JavaScript Object Notation) es un formato para el intercambio de datos basado en textoderivado de Javascript (Javascript disponde de una funcioacuten nativa eval() para convertirstreams JSON en objetos con propiedades que son accesibles sin necesidad de manipularninguna cadena de caracteres)

La especificacioacuten JSR 3539 proporciona un API para el procesamiento de datos JSON(parsing transformacioacuten y consultas)

La gramaacutetica de los objetos JSON es bastante simple Soacutelo se requieren dos estructurasobjetos y arrays Un objeto es un conjunto de pares nombre-valor y un array es una lista devalores JSON define siete tipos de valores string number object array true false y null

El siguiente ejemplo muestra datos JSON para un objeto que contiene pares nombre-valor

Ejemplo formato JSON

nombre John apellidos Smith edad 25 direccion calle 21 2nd Street ciudad New York codPostal 10021 telefonos [ tipo fijo numero 212 555-1234 tipo movil numero 646 555-4567 ]

El objeto anterior tiene cinco pares nombre-valor

bull Los dos primeros son nombre y apellidos con el valor de tipo String

bull El tercero es edad con el valor de tipo number

bull El cuarto es direccion con el valor de tipo object

bull El quinto es telefonos cuyo valor es de tipo array con dos objetos

JSON tiene la siguiente sintaxis

bull Los objetos estaacuten rodeados por llaves sus pares de elementos nombre-valor estaacutenseparados por una coma y el nombre y el valor de cada par estaacuten separados por dospuntos Los nombres en un objeto son de tipo String mientras que sus valores

9 httpsjcporgaboutJavacommunityprocessfinaljsr353indexhtml

Servicios Rest

141

pueden ser cualquiera de los siete tipos que ya hemos indicado incluyendo a otro objetou otro array

bull Los arrays estaacuten rodeados por corchetes [] y sus valores estaacuten separados por una coma Cada valor en un array puede ser de un tipo diferente incluyendo a otro objeto o array

bull Cuando los objetos y arrays contienen otros objetos yo arrays los datos adquieren unaestructura de aacuterbol

Los servicios web RESTful utilizan JSON habitualmente tanto en las peticiones como en lasrespuestas La cabecera HTTP utilizada para indicar que el contenido de una peticioacuten o unarespuesta es JSON es la siguiente

Content-Type applicationjson

La representacioacuten JSON es normalmente maacutes compacta que las representaciones XML debidoa que JSON no tiene etiquetas de cierre A diferencia de XML JSON no tiene un esquemade definicioacuten y validacioacuten de datos ampliamente aceptado

Actualmente las aplicaciones Java utilizan diferentes libreriacuteas para producirconsumir JSONque tienen que incluirse junto con el coacutedigo de la aplicacioacuten incrementando asiacute el tamantildeo delarchivo desplegado El API de Java para procesamiento JSON proporciona un API estaacutendarpara analizar y generar JSON de forma que las aplicaciones que utilicen dicho API sean maacutesligeras y portables

Para generar y parsear datos JSON hay dos modelos de programacioacuten que son similares alos usados para documentos XML

bull El modelo de objetos crea un aacuterbol en memoria que representa los datos JSON

bull El modelo basado en streaming utiliza un parser que lee los datos JSON elemento aelemento (uno cada vez)

Java EE incluye soporte para JSR 353 de forma que el API de java para procesamiento JSONse encuentra en los siguientes paquetes

bull El paquete javaxjson contiene interfaces para leer escribir y construir datos JSONseguacuten el modelo de objetos asiacute como otras utilidades

bull El paquete javaxjsonstream contiene una interfaz para parsear y generar datosJSON para el modelo streaming

Vamos a ver coacutemo producir y consumir datos JSON utilizando cada uno de los modelos

53 Modelo de procesamiento basado en el modelo de objetos

En este caso se crea un aacuterbol en memoria que representa los datos JSON (todos losdatos) Una vez construido el aacuterbol se puede navegar por eacutel analizarlo o modificarlo Estaaproximacioacuten es muy flexible y permite un procesamiento que requiera acceder al contenidocompleto del aacuterbol En contrapartida normalmente es maacutes lento que el modelo de streamingy requiere utilizar maacutes memoria El modelo de objetos genera una salida JSON navegandopor el aacuterbol entero de una vez

El siguiente coacutedigo muestra coacutemo crear un modelo de objetos a partir de datos JSON desdeun fichero de texto

Creacioacuten de un modelos de objetos a partir de datos JSON

Servicios Rest

142

import javaioFileReaderimport javaxjsonJsonimport javaxjsonJsonReaderimport javaxjsonJsonStructureJsonReader reader = JsoncreateReader(new FileReader(datosjsontxt))JsonStructure jsonst = readerread()

El objeto jsonst puede ser de tipo JsonObject o de tipo JsonArray dependiendo delos contenidos del fichero JsonObject y JsonArray son subtipos de JsonStructure Este objeto representa la raiacutez del aacuterbol y puede utilizarse para navegar por el aacuterbol o escribirloen un stream como datos JSON

Vamos a mostrar alguacuten ejemplo en el que utilicemos un StringReader

Objeto JSON con dos pares nombre-valor

jsonReader = JsoncreateReader(new StringReader( + manzanaroja + plaacutetanoamarillo + ))JsonObject json = jsonReaderreadObject()

jsongetString(manzana) jsongetString(plaacutetano)

El meacutetodo getString() devuelve el valor del string para la clave especificadacomo paraacutemetro Pueden utilizarse otros meacutetodos getXXX() para acceder al valorcorrespondiente de la clave en funcioacuten del tipo de dicho objeto

Un array con dos objetos cada uno de ellos con un par nombre-valor puede leerse como

Array con dos objetos

jsonReader = JsoncreateReader(new StringReader([ + manzanarojo + plaacutetanoamarillo + ]))

JsonArray jsonArray = jsonReaderreadArray()

La interfaz JsonArray tambieacuten tiene meacutetodos get para valores de tipo boolean integer y String en el iacutendice especificado (esta interfaz hereda de javautilList)

Creacioacuten de un modelos de objetos desde el coacutedigo de la aplicacioacuten

A continuacioacuten mostramos un ejemplo de coacutedigo para crear un modelo de objetos medianteprogramacioacuten

Ejemplo de creacioacuten de un modelo de objetos JSON desde programacioacuten

import javaxjsonJsonimport javaxjsonJsonObjectJsonObject modelo =

JsoncreateObjectBuilder() add(nombre Duke)

Servicios Rest

143

add(apellidos Java) add(edad 18) add(calle 100 Internet Dr) add(ciudad JavaTown) add(codPostal 12345) add(telefonos

JsoncreateArrayBuilder() add(JsoncreateObjectBuilder() add(tipo casa) add(numero 111-111-1111)) add(JsoncreateObjectBuilder() add(tipo movil) add(numero 222-222-2222))) build()

El tipo JsonObject representa un objeto JSON El meacutetodoJsoncreateObjectBuilder() crea un modelo de objetos en memoria antildeadiendoelementos desde el coacutedigo de nuestra aplicacioacutenEl meacutetodo JsoncreateArrayBuilder() crea un modelo de arrays en memoriaantildeadiendo elementos desde el coacutedigo de nuestra aplicacioacuten

El objeto modelo de tipo JsonObject representa la raiacutez del aacuterbol que es creado anidandollamadas a meacutetodos acuteadd()acute y construyendo el aacuterbol a traveacutes del meacutetodo build()

La estructura JSON generada es la siguiente

Ejemplo formato JSON generado mediante programacioacuten

nombre Duke apellidos Java edad 18 calle 100 Internet Dr ciudad JavaTown codPostal 12345 telefonos [ tipo casa numero 111-111-1111 tipo movil numero 222-222-2222 ]

Navegando por el modelo de objetos

A continuacioacuten mostramos un coacutedigo de ejemplo para navegar por el modelo de objetos

import javaxjsonJsonValueimport javaxjsonJsonObjectimport javaxjsonJsonArrayimport javaxjsonJsonNumberimport javaxjsonJsonStringpublic static void navegarPorElArbol(JsonValue arbol String clave)

Servicios Rest

144

if (clave = null) Systemoutprint(Clave + clave + ) switch(arbolgetValueType()) case OBJECT Systemoutprintln(OBJETO) JsonObject objeto = (JsonObject) arbol for (String nombre objectkeySet()) navegarPorElArbol(objectget(nombre) name) break case ARRAY Systemoutprintln(ARRAY) JsonArray array = (JsonArray) arbol for (JsonValue val array) navegarPorElArbol(val null) break case STRING JsonString st = (JsonString) arbol Systemoutprintln(STRING + stgetString()) break case NUMBER JsonNumber num = (JsonNumber) arbol Systemoutprintln(NUMBER + numtoString()) break case TRUE case FALSE case NULL Systemoutprintln(arbolgetValueType()toString()) break

El meacutetodo navegarPorElArbol() podemos usarlo con el ejemplo anterior de la siguienteforma

navegarPorElArbol(modelo OBJECT)

El meacutetodo navegarPorElArbol() tiene dos argumentos un elemento JSON y una claveLa clave se utiliza para imprimir los pares clave-valor dentro de los objetos Los elementos enel aacuterbol se representan por el tipo JsonValue Si el elemento es un objeto o un array serealiza una nueva llamada a este meacutetodo es invocada para cada elemento contenido en elobjeto o el array Si el elemento es un valor eacuteste se imprime en la salida estaacutendar

El meacutetodo JsonValuegetValueType() identifica el elemento como un objeto un arrayo un valor Para los objetos el meacutetodo JsonObjectkeySet() devuelve un conjunto deStrings que contienene las claves de los objetos y el meacutetodo JsonObjectget(Stringnombre) devuelve el valor del elemento cuya clave es nombre Para los arraysJsonArray implementa la interfaz ListltJsonValuegt Podemos utilizar bucles formejorados con el valor de SetltStringgt devuelto por JsonObjectkeySet() y coninstancias de JsonArray tal y como hemos mostrado en el ejemplo

Escritura de un modelo de objetos en un stream

Los modelos de objetos creados en los ejemplos anteriores pueden escribirse en un streamutilizando la clase JsonWriter de la siguiente forma

Servicios Rest

145

import javaioStringWriterimport javaxjsonJsonWriterStringWriter stWriter = new StringWriter()

JsonWriter jsonWriter = JsoncreateWriter(stWriter)

jsonWriterwriteObject(modelo)

jsonWriterclose()

String datosJson = stWritertoString()Systemoutprintln(datosJson)

El meacutetodo JsoncreateWriter() toma como paraacutemetro un OutputStreamEl meacutetodo JsonWriterwriteObject() escribe el objeto JsonObject en elstreamEl meacutetodo JsonWriterclose() cierra el stream de salida

Modelo de procesamiento basado en streaming

El modelo de streaming utiliza un parser basado en eventos que va leyendo los datos JSONde uno en uno El parser genera eventos y detiene el procesamiento cuando un objeto o arraycomienza o termina cuando encuentra una clave o encuentra un valor Cada elemento puedeser procesado o rechazado por el coacutedigo de la aplicacioacuten y a continuacioacuten el parser continuacuteacon el siguiente evento Esta aproximacioacuten es adecuada para un procesamiento local en elcual el procesamiento de un elemento no requiere informacioacuten del resto de los datos El modelode streaming genera una salida JSON para un determinado stream realizando una llamada auna funcioacuten con un elemento cada vez

A continuacioacuten veamos con ejemplos coacutemo utilizar el API para el modelo de streaming

bull Para leer datos JSON utilizando un parser (JsonParser)

bull Para escribir datos JSON utilizando un generador (JsonGenerator)

Lectura de datos JSON

El API para el modelo streaming es la aproximacioacuten maacutes eficiente para parsear datos JSONutilizando eventos

import javaxjsonJsonimport javaxjsonstreamJsonParserJsonParser parser = JsoncreateParser(new StringReader(datosJson))while (parserhasNext()) JsonParserEvent evento = parsernext() switch(evento) case START_ARRAY case END_ARRAY case START_OBJECT case END_OBJECT case VALUE_FALSE case VALUE_NULL case VALUE_TRUE Systemoutprintln(eventotoString())

Servicios Rest

146

break case KEY_NAME Systemoutprint(eventotoString() + + parsergetString() + - ) break case VALUE_STRING case VALUE_NUMBER Systemoutprintln(eventotoString() + + parsergetString()) break

El ejemplo consta de tres pasos

1 Obtener una instancia de un parser invocando el meacutetodo estaacuteticoJsoncreateParser()

2 Iterar sobre los eventos del parser utilizando los meacutetodos JsonParserhasNext() yJsonParsernext()

3 Realizar un procesamiento local para cada elemento

El ejemplo muestra los diez posibles tipos de eventos del parser El meacutetodoJsonParsernext() avanza al siguiente evento Para los tipos de eventos KEY_NAME VALUE_STRING y VALUE_NUMBER podemos obtener el contenido del elemento invocandoal meacutetodo JsonParsergetString() Para los eventos VALUE_NUMBER podemostambieacuten usar los siguientes meacutetodos

bull JsonParserisIntegralNumber

bull JsonParsergetInt

bull JsonParsergetLong

bull JsonParsergetBigDecimal

El parser genera los eventos START_OBJECT y END_OBJECT para un objeto JSON vaciacuteo

Para un objeto con dos pares nombre-valor

manzajaroja plaacutetanoamarillo

Mostramos los eventos generados

START_OBJECT manzajaKEY_NAMErojaVALUE_STRING plaacutetanoKEY_NAMEamarilloVALUE_STRINGEND_OBJECT

Los eventos generados para un array con dos objetos JSON seriacutean los siguientes

[START_ARRAY START_OBJECT manzajaKEY_NAMErojaVALUE_STRING END_OBJECT

Servicios Rest

147

START_OBJECT plaacutetanoKEY_NAMEamarilloVALUE_STRING END_OBJECT]END_ARRAY

Escritura de datos JSON

El siguiente coacutedigo muestra coacutemo escribir datos JSON en un fichero utilizando el API para elmodelo de streaming

Ejemplo de escritura de datos JSON con el modelo de streaming

FileWriter writer = new FileWriter(testtxt)JsonGenerator gen = JsoncreateGenerator(writer)genwriteStartObject() write(nombre Duke) write(apellidos Java) write(edad 18) write(calle 100 Internet Dr) write(ciudad JavaTown) write(codPostal 12345) writeStartArray(telefonos) writeStartObject() write(tipo casa) write(numero 111-111-1111) writeEnd() writeStartObject() write(tipo movil) write(numero 222-222-2222) writeEnd() writeEnd() writeEnd()genclose()

Este ejemplo obtiene un generador JSON invocando al meacutetodo estaacuteticoJsoncreateGenerator() que toma como paraacutemetro un output stream o un writerstream El ejemplo escribe los datos JSON en el fichero texttxt anidando llamadas a losmeacutetodos write() writeStartArray() writeStartObject() and writeEnd() El meacutetodo JsonGeneratorclose() cierra el output stream o writer stream subyacente

54 Pruebas de servicios REST

Hasta ahora hemos visto varias formas de probar nuestros servicios REST desde liacutenea decomandos con Curl desde IntelliJ con la herramienta Test RESTFul Web Service y desde elnavegador Chrome con Postman (siendo esta uacuteltima la que maacutes hemos utilizado)

Vamos a ver coacutemo implementar tests para nuestros servicios REST utilizando Maven y JUnitPara ello repasaremos algunas cuestiones baacutesicas sobre los ciclos de vida de Maven10

Ciclo de vida de Maven y tests JUnit

Un ciclo de vida en Maven es una secuencia de acciones determinada que defineel proceso de construccioacuten de un proyecto en concreto Como resultado del proceso deconstruccioacuten de un proyecto obtendremos un artefacto (fichero) de un cierto tipo (porejemplo jar war earhellip) Por lo tanto podriacuteamos decir que un ciclo de vida estaacute formado por

10 httpsmavenapacheorgref333maven-corelifecycleshtml

Servicios Rest

148

las acciones necesarias para convertir nuestros archivos fuente que constituyen el proyectoen por ejemplo un jar un warhellip

Maven propone 3 ciclos de vida es decir tres posibles secuencias de acciones que podemosutilizar (y modificar a nuestra conveniencia) para construir nuestro proyecto Dichos ciclos devida son clean site y el denominado default-lifecycle

Cada ciclo de vida estaacute formado por fases Una fase es un concepto abstracto y define el tipode acciones que se deberiacutean llevar a cabo Por ejemplo una fase del ciclo de vida por defectoes compile para referirse a las acciones que nos permiten convertir los ficheros java en losficheros class correspondientes

Cada fase estaacute formada por un conjunto de goals que son las acciones que se llevaraacuten a caboen cada una de las fases Las goals no viven de forma independiente sino que cualquiergoal siempre forma parte de un plugin Maven Podriacuteamos decir que un plugin por lo tantoes una agrupacioacuten loacutegica de una serie de goals relacionadas Por ejemplo el plugin wildflycontiene una serie de goals para desplegar re-desplegar deshacer-el-despliegue arrancarel servidor etc es decir agrupa las acciones que podemos realizar sobre el servidor wildflyUna goal se especifica siempre anteponiendo el nombre del plugin al que pertenece seguidode dos puntos por ejemplo wildflydeploy indica que se trata de la goal deploy que perteneceal plugin wildfly de maven

Pues bien por defecto Maven asocia ciertas goals a las fases de los tres ciclos de vidaCuando se ejecuta una fase de un ciclo de vida por ejemplo mvn package se ejecutan todaslas goals asociadas a todas las fases anteriores a la fase package en orden y finalmentelas goals asociadas a la fase package Por supuesto podemos alterar en cualquier momentoeste comportamiento por defecto incluyendo los plugins y goals correspondientes dentro dela etiqueta ltbuildgt en nuestro fichero de configuracioacuten pomxml

Vamos a implementar tests JUnit Los tests como ya habeacuteis visto en sesiones anterioresen el directorio srctest Algunas normas importantes son que los tests pertenezcan almismo paquete loacutegico al que pertencen las clases Java que estamos probando Por ejemplosi estamos haciendo pruebas sobre las clases del paquete orgexpertojavarest los testsdeberiacutean pertenecer al mismo paquete aunque fiacutesicamente el coacutedigo fuente y sus pruebasestaraacuten separados (el coacutedigo fuente estaraacute en srcmain y los tests en srctest)

Para realizar pruebas sobre nuestros servicios REST necesitamos que el servidor Wilfly esteacuteen marcha Tambieacuten necesitamos empaquetar el coacutedigo en un fichero war y desplegarlo enel servidor todo esto ANTES de ejecutar los tests

Las acciones para arrancar el servidor Wilfly y desplegar nuestra aplicacioacuten en eacutel NO formanparte de las acciones (o goals) incluidas por defecto en el ciclo de vida por defecto de Mavencuando nuestro proyecto tiene que empaquetarse como un war (etiqueta ltpackaginggt denuestro pomxml) Podeacuteis consultar aquiacute11 la lista de goals asociadas a las fases del ciclo devida por defecto de Maven

Por otro lado en el ciclo de vida por defecto se incluye una goal para ejecutar los testsasociada a la fase test Dicha goal es surefiretest El problema es que por defecto la fasetest se ejecuta ANTES de la fase package y por lo tanto antes de empaquetar y desplegarnuestra aplicacioacuten en Wildfly

Por lo tanto tendremos que alterar convenientemente este comportamiento por defectopara que se ejecuten las acciones de nuestro proceso de construccioacuten que necesitemos

11 httpsmavenapacheorgref333maven-coredefault-bindingshtmlPlugin_bindings_for_war_packaging

Servicios Rest

149

y en el orden en el que lo necesitemos Como ya hemos indicado antes esto lo haremosincluyendo dichas acciones en la etiqueta ltbuildgt de nuestro pomxml y configurandolasconvenientemente para asegurarnos que el orden en el que se ejecutan es el que queremos

La siguiente figura muestra parte de la secuencia de fases llevadas a cabo por Maven ensu ciclo de vida por defecto Para conseguir nuestros propoacutesitos simplemente antildeadiremosla goals wildflydeploy y la asociaremos a la fase pre-integration-test y cambiaremos lafase a la que estaacute asociada la goal surefiretest para que los tests se ejecuten DESPUEacuteS dehaber desplegado el war en Wildfly

A continuacioacuten mostramos los cambios que tenemos que realizar en el fichero de configuracioacutenpomxml

Adicioacuten de las goals wildflydeploy y surefiretest a las fases pre-integration-test ysurefiretest respectivamente

lt-- forzamos el despliegue del war generado durante la fase pre-integration-test justo despueacutes de obtener dicho war--gtltplugingt ltgroupIdgtorgwildflypluginsltgroupIdgt ltartifactIdgtwildfly-maven-pluginltartifactIdgt ltversiongt102Finalltversiongt ltconfigurationgt lthostnamegtlocalhostlthostnamegt ltportgt9990ltportgt ltconfigurationgt

Servicios Rest

150

ltexecutionsgt ltexecutiongt ltidgtwildfly-deployltidgt ltphasegtpre-integration-testltphasegt ltgoalsgt ltgoalgtdeployltgoalgt ltgoalsgt ltexecutiongt ltexecutionsgtltplugingt

lt--ejecutaremos los test JUnit en la fase integration-test inmediatamente despueacutes de la fase pre-integration-test y antes de la fase verify--gtltplugingt ltgroupIdgtorgapachemavenpluginsltgroupIdgt ltartifactIdgtmaven-surefire-pluginltartifactIdgt ltversiongt218ltversiongt ltconfigurationgt ltskipgttrueltskipgt ltconfigurationgt ltexecutionsgt ltexecutiongt ltidgtsurefire-itltidgt ltphasegtintegration-testltphasegt ltgoalsgt ltgoalgttestltgoalgt ltgoalsgt ltconfigurationgt ltskipgtfalseltskipgt ltconfigurationgt ltexecutiongt ltexecutionsgtltplugingt

Tambieacuten necesitamos incluir en el pomxml las libreriacuteas de las que depende el coacutedigo depruebas de nuestro proyecto (clases XXXXTest situadas en srctest) libreriacutea JUnit JAXB yel API cliente de JAX-RS Por lo que antildeadimos en el las dependencias correspondientes

Dependencias del coacutedigo srctest con JUnit y API cliente de JAX-RS

ltdependencygt ltgroupIdgtjunitltgroupIdgt ltartifactIdgtjunitltartifactIdgt ltversiongt412ltversiongt ltscopegttestltscopegtltdependencygtltdependencygt ltgroupIdgtorgjbossresteasyltgroupIdgt ltartifactIdgtresteasy-clientltartifactIdgt ltversiongt3013Finalltversiongt ltscopegttestltscopegtltdependencygtltdependencygt ltgroupIdgtorgjbossresteasyltgroupIdgt

Servicios Rest

151

ltartifactIdgtresteasy-jaxb-providerltartifactIdgt ltversiongt3013Finalltversiongtltdependencygt

Dado que vamos a trabajar con el API Json y dado que ejecutaremos los tests desde lamaacutequina virtual de Java y no dentro del servidor WildFly necesitamos antildeadir tambieacuten lassiguientes libreriacuteas

Dependencias del coacutedigo srctest con el API Json de jaxrs

lt--Libreriacuteas para serializardeserializar json --gtltdependencygt ltgroupIdgtorgjbossresteasyltgroupIdgt ltartifactIdgtresteasy-jackson-providerltartifactIdgt ltversiongt3013Finalltversiongtltdependencygt

lt--Jaxrs API json --gtltdependencygt ltgroupIdgtorgjbossresteasyltgroupIdgt ltartifactIdgtresteasy-json-p-providerltartifactIdgt ltversiongt3013Finalltversiongtltdependencygt

No hemos incluido en el pomxml la orden para arrancar Wildfly Vamosa hacer esto desde IntelliJ en un perfil de ejecucioacuten como ya habeacuteishecho en sesiones anteriores De esta forma podremos ver desde IntelliJla consola de logs del servidor En este caso podemos crear un perfilsolamente para arrancar el servidor Wildfly (no es necesario que se incluyael despliegue del war generado puesto que lo haremos desde la ventanaMaven Projects) Antes de iniciar el proceso de construccioacuten por lo tantotendremos que asegurarnos de que hemos arrancado Wildlfly

Con estos cambios en el pomxml y ejecutando el comando mvn verify se llevaraacuten a cabolas siguientes acciones en este orden

bull Despueacutes de compilar el proyecto obtenemos el war (fase package )

bull El war generado se despliega en el servidor de aplicaciones Wilfly (fase pre-integration-test )

bull Se ejecutan los test JUnit sobre la aplicacioacuten desplegada en el servidor (faseintegration-test )

Anotaciones JUnit y aserciones AssertThat

JUnit 4 proporciona anotaciones para forzar a que los meacutetodos anotados con Test seejecuten en el orden que nos interese (por defecto no estaacute garantizado que se ejecuten enel orden en el que se escriben)

En principio debemos programar los tests para que sean totalmente independientes unos deotros y por lo tanto el orden de ejecucioacuten no influya para nada en el resultado de su ejecucioacutentanto si se ejecuta el primero como a mitad o el uacuteltimo El no hacer los tests independientes

Servicios Rest

152

hace que el proceso de testing se alargue y complique innecesariamente ya que puede serque unos tests enmascaren en resultado de otros o que no podamos saber si ciertas partesdel coacutedigo estaacuten bien o mal implementadas hasta que los tests de los que dependemos sehayan superado con eacutexito

Auacuten asiacute y dado que muchas veces se obtienen errores por hacer asunciones en el ordende la ejecucioacuten de los tests JUnit nos permite fijar dicho orden Para ello utilizaremosla anotacioacuten FixMethodOrder indicando el tipo de ordenacioacuten como por ejemploMethodSortersNAME_ASCENDING de forma que se ejecutaraacuten los tests por ordenlexicograacutefico

Por ejemplo

Ejemplo para forzar el orden de ejecucioacuten de los test (orden lexicograacutefico)

FixMethodOrder(MethodSortersNAME_ASCENDING)public class TestMethodOrder

Test public void testB() Systemoutprintln(second)

Test public void testA() Systemoutprintln(first)

Test public void testC() Systemoutprintln(third)

En ese caso el orden de ejecucioacuten seraacute testA() a continuacioacuten testB() y finalmentetestC()

Otra aportacioacuten de JUnit 4 es la incorporacioacuten de aserciones de tipoassertThat En sesiones anteriores habeacuteis utilizado aserciones con meacutetodosAssertassertEquals(resultado_esperado resultado_real) Los nuevosmeacutetodos AssertassertThat() permiten una mayor flexibilidad a la hora de expresar lasaserciones realizadas en nuestros tests asiacute como una mayor legibilidad de los mismos Elprototipo general de las aserciones de este tipo es

assertThat([value] [matcher statement])

en donde [value] es el resultado real (valor sobre el que se quiere afirmar algo)y [matcher statement] es un Matcher u objeto que realiza operaciones deemparejamiento sobre una secuencia de caracteres seguacuten un determinado patroacuten

Por ejemplo

Ejemplos de sentencias assertThat

assertThat(x is(not(4)))

Servicios Rest

153

assertThat(responseStringJson

either(containsString(nombre))and(containsString(apellido)))

assertThat(myList hasItem(3))

Aquiacute utilizamos un matcher con el patroacuten 4 esta sentencia devuelve false si x = 4Podemos combinar varios matchers de forma que se tengan que satisfacer maacutes de unoEn este caso aplicamos el matcher sobre un conjunto de elementos

Hay varias libreriacuteas que implementan Matchers JUnit incluye parte de los matchers deHamcrest (Hamcrest es un framework para escribir objetos matcher permitiendo definir reglasde matching de forma declarativa) Otra libreriacutea interesante para realizar testing de serviciosrest que utilizan representaciones Json es la libreriacutea hamcrest-json que podemos utilizarpara realizar aserciones sobre dos objetos Json

Por ejemplo supongamos que nuestro objeto Json contiene una lista de enlaces Hateoas detipo Link Los objetos Link seraacuten serializadosdeserializados (Wildfly utiliza Jackson pararealizar estas tareas) convenientemente Cuando serializamos un objeto Link (obtenemossu representacioacuten Json) veremos ademaacutes de los objetos uri valor type valor y relvalor que son los que baacutesicamente utilizamos al crear los enlaces Hateoas otros comouriBuilder hellip paramshellip que puede que no nos interese consultar o incluso que no leshayamos asignado ninguacuten valor

Si en nuestro test queremos comprobar que el objeto Json que nos devuelve el servicio(resultado real) se corresponde con el valor esperado tendremos que comparar ambasrepresentaciones Ahora bien puede que solamente nos interese comparar ciertos valorescontenidos en el objeto Json no el objeto completo

Hacer esta comprobacioacuten elemento a elemento es bastante tediosoLa libreriacutea hamcrest-json nos proporciona lo que estamos buscandocon los meacutetodos sameJSONAs() allowingExtraUnexpectedFields() yallowingAnyArrayOrdering() de la siguiente forma

Meacutetodo para comparar dos representaciones Json ClaseukcodatumedgehamcrestjsonSameJSONAs

AssertassertThat(age43 friend_ids[16 52 23] sameJSONAs(friend_ids[52 23 16]) allowingExtraUnexpectedFields() allowingAnyArrayOrdering())

En este coacutedigo tenemos una representacioacuten formada por dos objetos uno de los cuales tienecomo valor un array de enteros Si el servicio rest devuelve un objeto Json con maacutes elementoso en otro orden en este caso el resultado de la sentencia assertThat es true Volviendo alejemplo anterior de un objeto Json que contiene enlaces Hatehoas podriacuteamos realizar lasiguiente comparacioacuten

Comparamos dos objetos Json que contienen hiperenlaces Hateoas (objetos Link)

JsonObject json_object = clienttarget(httplocalhost8080forousuarios) request(MediaTypeAPPLICATION_JSON)

get(JsonObjectclass)

String json_string = json_objecttoString()

Servicios Rest

154

JsonObject usuarios = JsoncreateObjectBuilder() add(usuarios JsoncreateArrayBuilder() add(JsoncreateObjectBuilder() add(nombre Pepe Lopez) add(links JsoncreateArrayBuilder() add(JsoncreateObjectBuilder() add(uri httplocalhost8080forousuariospepe) add(type applicationxmlapplicationjson) add(rel self)))) add(JsoncreateObjectBuilder() add(nombre Ana Garcia) add(links JsoncreateArrayBuilder() add(JsoncreateObjectBuilder() add(uri httplocalhost8080forousuariosana) add(type applicationxmlapplicationjson) add(rel self)))))

build()

AssertassertThat(json_string sameJSONAs(usuariostoString()) allowingExtraUnexpectedFields()

allowingAnyArrayOrdering())

Realizamos la llamada al servicio REST y recibimos como respuesta un objeto Json Eneste caso nuestro objeto Json estaacute formado por una lista de objetosObtenemos la representacioacuten de nuestro objeto Json (resultado real) en forma de cadenade caracteresCreamos un nuevo objeto Json con el resultado esperadoComparamos ambos objetos Si el resultado real incluye maacutes elementos que loscontenidos en json_string o en otro orden consideraremos que hemos obtenido larespuesta correcta

Para utilizar esta libreriacutea en nuestro proyecto simplemente tendremos que antildeadirla comodependencia en la configuracioacuten de nuestro pomxml

Libreriacutea para comparar objetos Json en los tests

lt--Hamcrest Json --gtltdependencygt ltgroupIdgtukcodatumedgeltgroupIdgt ltartifactIdgthamcrest-jsonltartifactIdgt ltversiongt02ltversiongtltdependencygt

Observaciones sobre los tests y algunos ejemplos de tests

Recuerda que para utilizar el API cliente necesitas utilizar instanciasjavaxwsrsclientClient que debemos cerrar siempre despueacutes de su uso paracerrar el socket asociado a la conexioacuten

Servicios Rest

155

Para ello podemos optar por Crear una uacutenica instancia Client antes de ejecutar cualquiertest (meacutetodo BeforeClass) y cerrar el socket despueacutes de ejecutar todos los tests (meacutetodoAfterClass) Crear una uacutenica instancia Client antes de ejecutar CADA test (meacutetodoBefore) y cerrar el socket despueacutes de ejecutar CADA tests (meacutetodo After)

Si el resultado de una invocacioacuten sobre la instancia Client es de tipojavaxwsrscoreResponse debemos liberar de forma expliacutecita la conexioacuten para quepueda ser usada de nuevo por dicha instancia Client

Por ejemplo supongamos que queremos realizar un test en el que realizamos una operacioacutenPOST y a continuacioacuten una operacioacuten GET para verificar que el nuevo recurso se ha antildeadididocorrectamente

Ejemplo de Test que utiliza una instancia Client para todos los tests

public class TestRESTServices private static final String BASE_URL = httplocalhost8080rest private static URI uri = UriBuilderfromUri(BASE_URL)build() private static Client client

BeforeClass public static void initClient()

client = ClientBuildernewClient()

AfterClass public static void closeClient()

clientclose()

Test public void createAndRetrieveACustomer()

Customer customer = Creamos un nuevo cliente Response response = clienttarget(uri) request() post(Entityentity(customer MediaTypeAPPLICATION_JSON)) assertEquals(ResponseStatusCREATED responsegetStatusInfo()) URI referenceURI = responsegetLocation()

responseclose()

Obtenemos el recurso que hemos antildeadido response = clienttarget(referenceURI)request()get()

Customer retrieved_customer = responsereadEntity(Customerclass) assertEquals(ResponseStatusOK responsegetStatusInfo()) assertEquals(retreivedRefgetName() rgetName())

responseclose()

Creamos una instancia Client ANTES de ejecutar cualquier testCerramos el socket asociado a la conexioacuten DESPUEacuteS de ejecutar TODOS los testsLiberamos la conexioacuten para poder reutilizarla

Servicios Rest

156

Liberamos la conexioacuten para poder reutilizarlaAhora veamos otro ejemplo en el que utilizamos una instancia Client para cada test

Ejemplo de Test que utiliza una instancia Client para CADA test

public class TestRESTServices

private Client client

Before public void setUp()

thisclient = ClientBuildernewClient()

After public void tearDown()

thisclientclose()

Test public void getAllCustomersAsJson() String uriString = httplocalhost8080restcustomers JsonArray json_array = client target(uriString) request(MediaTypeAPPLICATION_JSON) accept(MediaTypeAPPLICATION_JSON) get(JsonArrayclass)

AssertassertEquals(2 json_arraysize())

Test public void getAllCustomers() String uriString = httplocalhost8080restcustomers Consultamos los datos de todos los customers ListltCustomergt lista_usuarios = clienttarget(uriString) request(applicationjson) get(new GenericTypeltListltCustomergtgt() ) AssertassertEquals(2 lista_usuariossize())

Creamos una instancia Client ANTES de ejecutar CADA testCerramos el socket asociado a la conexioacuten DESPUEacuteS de ejecutar CADA los tests

Podemos ver en este uacuteltimo ejemplo que no es necesario liberar la conexioacuten entre usossucesivos de la instancia Client si no utilizamos la clase Response En este caso el procesose realiza de forma automaacutetica por el sistema

Finalmente comentaremos que debido a un bug en la especificacioacuten JAX-RS eldeserializado del de los objetos Link no se realiza por lo que obendremos una listade Links vaciacutea (ver httpkingsfleetblogspotcomes201405reading-and-writing-jax-rs-link-objectshtml) Podemos comprobar que si obtenemos la representacioacuten en formato texto dela entidad del mensaje dicha lista de objetos tendraacute el valor correcto

Si no utilizamos la solucioacuten propuesta en el enlace anterior deberemos usar la anotacioacutenJsonIgnoreProperties(ignoreUnknown = true) De esta forma ignoraremos el

Servicios Rest

157

deserializado de los objetos Link pero tendremos que utilizar la representacioacuten en formato decadena de caracteres del recurso json en lugar del objeto java Link asociado

Asiacute por ejemplo si nuestro recurso Customer tiene asociado una lista de objetos Link parapoder utilizar el API Cliente y acceder a la lista de enlaces usaremos la anotacioacuten anterior enla implementacioacuten de la clase Customer

JsonIgnoreProperties(ignoreUnknown = true)XmlRootElement(name=customer)public class Customer int id String name ListltLinkgt links

Servicios Rest

158

55 Ejercicios

Tests utilizando el API cliente y un mapeador de excepciones (1 punto)

Se proporciona como plantilla el MOacuteDULO IntelliJ s5-tienda con una implementacioacuten parcialde una tienda de clientes on-line Este proyecto ya contiene varios tests implementados amodo de ejemplo

Los recursos rest implementados lanzan excepciones de tipo RestException si porejemplo se intenta realizar una consulta sobre un producto yo usuario que no existe

Se ha implementado un mapeador de excepciones RestExceptionMapper quecaptura excepciones de tipo RuntimeException y devuelve una respuesta de tipoErrorMensajeBean que seraacute serializada a formato json yo formato xml (dependiendo delvalor de la cabecera Accept de la peticioacuten) con informacioacuten sobre el error producido

Implementa los siguientes dos tests test7recuperarTodosLosUsuarios() en el querealizamos una invocacioacuten GET sobre httplocalhost8080s5-tiendarestclientes Esta URIpodriacutea corresponderse con un meacutetodo anotado con GET y que devolviese una lista de todoslos clientes de la tienda Sin embargo no existe tal meacutetodo en nuestro recursos rest Verificaque dicha invocacioacuten devuelve el coacutedigo de estado 500 (Internal Server Error) y que en elcuerpo del mensaje se recibe Servicio no disponible

bull test8recuperarClienteQueNoExiste() en el que intentamos recuperar lainformacioacuten de un cliente que no exista en nuestra base de datos En este caso debemosverificar que el mensaje obtenido en formato json es el siguiente

status Not Found code 404 message El producto no se encuentra en la base de datos developerMessage error

Tests utilizando el API Json y JUnit (1 punto)

Vamos a seguir usando el proyecto s4-foroAvanzado con el que hemos trabajado en la sesioacutenanterior

Vamos a implementar algunos tests con JUnit en los que utilizaremos ademaacutes del API clienteel API Json que nos proporciona jaxrs

Para ejecutar los tests necesitamos modificar el pomxml antildeadiendo las dependenciascorrespondientes que hemos visto a lo largo de la sesioacuten y antildeadiendo las goals para que seejecuten los tests despueacutes de desplegar la aplicacioacuten en Wildfly

Proporcionamos el contenido del pomxml con las libreriacuteas y plugins que necesitaraacutes(aunque como ejercicio deberiacuteas intentar modificar la configuracioacuten tuacute mismo y luego puedescomprobar el resultado con el pomxml que se proporciona) El contenido del nuevo pomxmllo tienes en srctestresourcesnuevo-pommxl

Inicializacioacuten de los datos para los tests

Vamos a utilizar DBUnit para inicializar la BD para realizar los tests Para ello tendraacutes queantildeadir en el pomxml las dependencias necesarias (ya estaacuten antildeadidas en el fichero de

Servicios Rest

159

configuracioacuten proporcionado) En el fichero srctestresourcesforo-inicialxml encontrareacuteis elconjunto de datos con el que inicializaremos la base de datos para ejecutar nuestros tests

No es necesario (aunque es una muy buena praacutectica) que inicialicemos la BD para cada test

Implementacioacuten de los tests

Vamos a implementar los siguientes tests (que se ejecutaraacuten en en este mismo orden)

bull test1ConsultaTodosUsuarios() recuperamos los datos de todos los usuarios delforo Recuerda que previamente tienes que haber inicializado la BD con los datos del ficheroforo-inicialxml Recupera los datos en forma de JsonObject y comprueba que el nuacutemerode usuarios es el correcto Tambieacuten debes comprobar que tanto el login como los enlaceshatehoas para cada usuario estaacuten bien creados En concreto para cada usuario debesverificar que la uri (uri) el tipo mime (type) y el tipo de enlace (rel) son los correctos

bull test2CreamosMensajeDePepe() crearemos un nuevo mensaje del usuario con loginpepe Recuerda que este usuario tiene el rol registrado El mensaje tendraacute el asuntocena y el texto seraacute Mejor me voy al cine En este caso deberaacutes comprobar el valorde estado (debe ser 201) y debes recuperar (consultar con una peticioacuten REST) el mensajepara comprobar que la operacioacuten de insertar el mensaje ha tenido eacutexito

bull test3CreamosMensajeDeUsuarioNoAutorizado() creamos un nuevo mensaje deun usuario que no estaacute autorizado (por ejemplo de un usuario con login juan) En estecaso el mensaje tendraacute el asunto cena y el mensaje puede ser Pues yo tampoco voyEl resultado debe ser el coacutedigo de estado 401 ( Unauthorized)

bull test4ConsultaUsuario() Consultamos los datos del usuario pepe Recuperaremoslos datos como un JsonObject y comprobaremos que el valor de la uri para el tipo derelacioacuten self del enlace Link asociado es el correcto

  • Servicios Rest
  • Table of Contents
  • 1 Introduccioacuten a REST Disentildeo y creacioacuten de servicios RESTful
    • 11 iquestQueacute es un servicio Web
      • Servicios Web RESTful
        • 12 Fundamentos de REST
          • Recursos
          • Representacioacuten de los recursos
          • Direccionabilidad de los recursos URI
          • Uniformidad y restricciones de las interfaces
            • 13 Disentildeo de servicios Web RESTful
            • 14 Un primer servicio JAX-RS
              • Modelo de objetos
              • Modelado de URIs
              • Definicioacuten del formato de datos
                • Formato de datos para operaciones de lectura y modificacioacuten de los recursos
                • Formato de datos para operaciones de creacioacuten de los recursos
                  • Asignacioacuten de meacutetodos HTTP
                    • Visualizacioacuten de todos los Pedidos Clientes o Productos
                    • Obtencioacuten de Pedidos Clientes o Productos individuales
                    • Creacioacuten de un Pedido Cliente o Producto
                    • Actualizacioacuten de un Pedido Cliente o Producto
                    • Borrado de un Pedido Cliente o Producto
                    • Cancelacioacuten de un Pedido
                      • Implementacioacuten del servicio Creacioacuten del proyecto Maven
                      • Implementacioacuten del servicio Recursos JAX-RS
                        • Clases de nuestro dominio (entidades) Clientejava
                        • Clases de nuestro servicio RESTful ClienteResourcejava
                        • Creacioacuten de clientes
                        • Consulta de clientes
                        • Modificacioacuten de clientes
                          • Construccioacuten y despliegue del servicio
                          • Probando nuestro servicio
                            • 15 Ejercicios
                              • Servicio REST ejemplo (0 puntos)
                              • Servicio REST saludo (1 punto)
                              • Servicio REST foro (1 punto)
                                  • 2 Anotaciones baacutesicas JAX-RS El modelo de despliegue
                                    • 21 iquestCoacutemo funciona el enlazado de meacutetodos HTTP
                                    • 22 La anotacioacuten Path
                                      • Expresiones Path
                                        • Expresiones regulares
                                        • Reglas de precedencia
                                          • Paraacutemetros matrix (Matrix parameters)
                                          • Subrecursos (Subresource Locators)
                                            • Caraacutecter dinaacutemico del dispatching de peticiones
                                                • 23 Usos de las anotaciones Produces y Consumes
                                                  • Anotacioacuten Consumes
                                                  • Anotacioacuten Produces
                                                    • 24 Inyeccioacuten de paraacutemetros JAX-RS
                                                      • javaxwsrsPathParam
                                                      • Interfaz UriInfo
                                                      • javaxwsrsMatrixParam
                                                      • javaxwsrsQueryParam
                                                      • javaxwsrsFormParam
                                                      • javaxwsrsHeaderParam
                                                      • javaxwsrscoreContext
                                                      • javaxwsrsBeanParam
                                                      • Conversioacuten automaacutetica de tipos
                                                      • Valores por defecto (DefaultValue)
                                                        • 25 Configuracioacuten y despliegue de aplicaciones JAX-RS
                                                          • Configuracioacuten mediante la clase Application
                                                          • Configuracioacuten mediante un fichero webxml
                                                          • Configuracioacuten en un contenedor que no disponga de una implementacioacuten JAX-RS
                                                            • 26 Ejercicios
                                                              • Creacioacuten de un recurso creacioacuten y consulta de temas en el foro (05 puntos)
                                                              • Despliegue y pruebas del recurso (05 puntos)
                                                              • Muacuteltiples consultas de los temas del foro (05 puntos)
                                                              • Creacioacuten de subrecursos (05 puntos)
                                                                  • 3 Manejadores de contenidos Respuestas del servidor y manejo de excepciones
                                                                    • 31 Proveedores de entidades
                                                                      • Interfaz javaxwsrsextMessageBodyReader
                                                                      • Interfaz javaxwsrsextMessageBodyWriter
                                                                        • 32 Proveedores de entidad estaacutendar incluidos en JAX-RS
                                                                          • javaxwsrscoreStreamingOutput
                                                                          • javaioInputStream javaioReader
                                                                          • javaioFile
                                                                          • byte[]
                                                                          • String char[]
                                                                          • MultivaluedMapltString Stringgt y formularios de entrada
                                                                            • 33 Muacuteltiples representaciones de recursos
                                                                            • 34 Introduccioacuten a JAXB
                                                                              • Clase JAXBContext
                                                                              • Manejadores JAX-RS para JAXB
                                                                              • JAXB y JSON
                                                                                • 35 Respuestas del servidor
                                                                                  • Coacutedigos de respuesta por defecto
                                                                                    • Respuestas que indican eacutexito
                                                                                    • Respuestas que indican una situacioacuten de fallo
                                                                                      • Elaboracioacuten de respuestas con la clase Response
                                                                                        • Inclusioacuten de cookies en la respuesta
                                                                                        • El tipo enumerado de coacutedigos de estado
                                                                                        • La clase javaxwsrscoreGenericEntity
                                                                                            • 36 Manejadores de excepciones
                                                                                              • La clase javaxwsrsWebApplicationException
                                                                                              • Mapeado de excepciones
                                                                                              • Jerarquiacutea de excepciones
                                                                                                • 37 Ejercicios
                                                                                                  • Servicio REST ejemplo
                                                                                                  • Plantillas que se proporcionan
                                                                                                  • Uso de JAXB (05 puntos)
                                                                                                  • Uso de manejadores de contenidos y clase Response (075 puntos)
                                                                                                  • Manejo de excepciones (075 puntos)
                                                                                                      • 4 HATEOAS y Seguridad
                                                                                                        • 41 iquestQueacute es HATEOAS
                                                                                                        • 42 HATEOAS y Servicios Web
                                                                                                          • Enlaces Atom
                                                                                                          • Ventajas de utilizar HATEOAS con Servicios Web
                                                                                                            • Transparencia en la localizacioacuten
                                                                                                            • Desacoplamiento de los detalles de la interaccioacuten
                                                                                                            • Reduccioacuten de errores de transicioacuten de estados
                                                                                                              • Enlaces en cabeceras frente a enlaces Atom
                                                                                                                • 43 HATEOAS y JAX-RS
                                                                                                                  • Construccioacuten de URIs con UriBuilder
                                                                                                                  • URIs relativas mediante el uso de UriInfo
                                                                                                                  • Construccioacuten de enlaces (Links) en documentos XML y en cabeceras HTTP
                                                                                                                    • 44 Seguridad
                                                                                                                      • Autentificacioacuten en JAX-RS
                                                                                                                        • Creacioacuten de usuarios y roles
                                                                                                                          • Autorizacioacuten en JAX-RS
                                                                                                                          • Encriptacioacuten
                                                                                                                            • Anotaciones JAX-RS para autorizacioacuten
                                                                                                                              • Seguridad programada
                                                                                                                                • 45 Ejercicios
                                                                                                                                  • Uso de Hateoas (1 puntos)
                                                                                                                                  • Ejercicio seguridad (1 punto)
                                                                                                                                      • 5 Api cliente Procesamiento JSON y Pruebas
                                                                                                                                        • 51 API cliente Visioacuten general
                                                                                                                                          • Obtenemos una instancia Client
                                                                                                                                          • Configuramos el target del cliente (URI)
                                                                                                                                          • Construimos y Realizamos la peticioacuten
                                                                                                                                          • Manejo de excepciones
                                                                                                                                            • 52 Procesamiento JSON
                                                                                                                                            • 53 Modelo de procesamiento basado en el modelo de objetos
                                                                                                                                              • Creacioacuten de un modelos de objetos desde el coacutedigo de la aplicacioacuten
                                                                                                                                              • Navegando por el modelo de objetos
                                                                                                                                              • Escritura de un modelo de objetos en un stream
                                                                                                                                              • Modelo de procesamiento basado en streaming
                                                                                                                                                • Lectura de datos JSON
                                                                                                                                                • Escritura de datos JSON
                                                                                                                                                    • 54 Pruebas de servicios REST
                                                                                                                                                      • Ciclo de vida de Maven y tests JUnit
                                                                                                                                                      • Anotaciones JUnit y aserciones AssertThat
                                                                                                                                                      • Observaciones sobre los tests y algunos ejemplos de tests
                                                                                                                                                        • 55 Ejercicios
                                                                                                                                                          • Tests utilizando el API cliente y un mapeador de excepciones (1 punto)
                                                                                                                                                          • Tests utilizando el API Json y JUnit (1 punto)
                                                                                                                                                            • Inicializacioacuten de los datos para los tests
                                                                                                                                                              • Implementacioacuten de los tests
Page 3: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones

Servicios Rest

3

Ejercicio seguridad (1 punto) 1275 Api cliente Procesamiento JSON y Pruebas 128

51 API cliente Visioacuten general 128Obtenemos una instancia Client 129Configuramos el target del cliente (URI) 131Construimos y Realizamos la peticioacuten 133Manejo de excepciones 138

52 Procesamiento JSON 14053 Modelo de procesamiento basado en el modelo de objetos 141

Creacioacuten de un modelos de objetos desde el coacutedigo de la aplicacioacuten 142Navegando por el modelo de objetos 143Escritura de un modelo de objetos en un stream 144Modelo de procesamiento basado en streaming 145

54 Pruebas de servicios REST 147Ciclo de vida de Maven y tests JUnit 147Anotaciones JUnit y aserciones AssertThat 151Observaciones sobre los tests y algunos ejemplos de tests 154

55 Ejercicios 158Tests utilizando el API cliente y un mapeador de excepciones (1 punto) 158Tests utilizando el API Json y JUnit (1 punto) 158Implementacioacuten de los tests 159

Servicios Rest

4

1 Introduccioacuten a REST Disentildeo y creacioacuten de serviciosRESTful

En esta sesioacuten vamos a introducir los conceptos de servicio Web y servicio Web RESTfulque es el tipo de servicios con los que vamos a trabajar Explicaremos el proceso de disentildeodel API de un servicio Web RESTful y definiremos las URIs que constituiraacuten los puntos deentrada de nuestra aplicacioacuten REST Finalmente ilustraremos los pasos para implementardesplegar y probar un servicio REST utilizando Maven IntelliJ y el servidor de aplicacionesWildfly Tambieacuten nos familiarizaremos con Postman una herramienta para poder probar deforma sencilla los servicios web directamente desde el navegador

11 iquestQueacute es un servicio Web

El disentildeo del software tiende a ser cada vez maacutes modular Las aplicaciones estaacuten formadaspor una serie de componentes reutilizables (servicios) que pueden encontrarse distribuidosa lo largo de una serie de maacutequinas conectadas en red

El WC3 (World Wide Web Consortium) define un servicio Web como un sistema softwaredisentildeado para soportar interacciones maacutequina a maacutequina a traveacutes de la red Dicho de otromodo los servicios Web proporcionan una forma estaacutendar de interoperar entre aplicacionessoftware que se ejecutan en diferentes plataformas Por lo tanto su principal caracteriacutesticasu gran interoperabilidad y extensibilidad asiacute como por proporcionar informacioacuten faacutecilmenteprocesable por las maacutequinas gracias al uso de XML Los servicios Web pueden combinarsecon muy bajo acoplamiento para conseguir la realizacioacuten de operaciones complejas De estaforma las aplicaciones que proporcionan servicios simples pueden interactuar con otras paraentregar servicios sofisticados antildeadidos

Historia de los servicios Web

Los servicios Web fueron inventados para solucionar el problema de lainteroperabilidad entre las aplicaciones Al principio de los 90 con el desarrollo deInternetLANWAN aparecioacute el gran problema de integrar aplicaciones diferentes Unaaplicacioacuten podiacutea haber sido desarrollada en C++ o Java y ejecutarse bajo Unix unPC o un computador mainframe No habiacutea una forma faacutecil de intercomunicar dichasaplicaciones Fueacute el desarrollo de XML el que hizo posible compartir datos entreaplicaciones con diferentes plataformas hardware a traveacutes de la red o incluso a traveacutesde Internet La razoacuten de que se llamasen servicios Web es que fueron disentildeadospara residir en un servidor Web y ser llamados a traveacutes de Internet tiacutepicamente viaprotocolos HTTP o HTTPS De esta forma se asegura que un servicio puede serllamado por cualquier aplicacioacuten usando cualquier lenguaje de programacioacuten y bajocualquier sistema operativo siempre y cuaacutendo por supuesto la conexioacuten a Internetesteacute activa y tenga un puerto abierto HTTPHTTPS lo cual es cierto para casi cualquiercomputador que disponga de acceso a Internet

A nivel conceptual un servicio es un componente software proporcionado a traveacutes de unendpoint accesible a traveacutes de la red Los servicios productores y consumidores utilizanmensajes para intercambiar informacioacuten de invocaciones de peticioacuten y respuesta en formade documentos auto-contenidos que hacen muy pocas asunciones sobre las capacidadestecnoloacutegicas de cada uno de los receptores

Servicios Rest

5

iquestQueacute es un endpointLos servicios pueden interconectarse a traveacutes de la red En unaarquitectura orientada a servicios cualquier interaccioacuten punto a puntoimplica dos endpoints uno que proporciona un servicio y otro de loconsume Es decir que un endpoint es cada uno de los elementosen nuestro caso nos referimos a servicios que se situacutean en ambosextremos de la red que sirve de canal de comunicacioacuten entre ellosCuando hablamos de servicios Web un endpoint se especifica medianteuna URI

A nivel teacutecnico los servicios pueden implementarse de varias formas En estesentido podemos distinguir dos tipos de servicios Web los denominados servicios Webgrandes (big Web Services) los llamaremos servicios Web SOAP y servicios ligeros oservicios Web RESTful

Los servicios Web SOAP se caracterizan por utilizar mensajes XML que siguen el estaacutendarSOAP (Simple Object Access Protocol) Ademaacutes contienen una descripcioacuten de las operacionesproporcionadas por el servicio escritas en WSDL (Web Services Description Language) unlenguaje basado en XML

Los servicios Web RESTful por el contrario pueden intercambiar mensajes escritos endiferentes formatos y no requieren el publicar una descripcioacuten de las operaciones queproporcionan por lo que necesitan una menor infraestructura para su implementacioacutenNosotros vamos a centrarnos en el uso de estos servicios

Servicios Web RESTful

Son un tipo de Servicios Web que se adhieren a una serie de restricciones arquitectoacutenicasenglobadas bajo las siglas de REST y que utilizan estaacutendares Web tales como URIs HTTPXML y JSON

El API Java para servicios Web RESTful (JAX-RS) permite desarrollar servicios Web RESTfulde forma sencilla La versioacuten maacutes reciente del API es la 20 cuya especificacioacuten estaacute publicadaen el documento JSR-339 y que podemos descargar desde httpsjcporgenjsrdetailid=339 A lo largo de estas sesiones veremos coacutemo utilizar JAX-RS para desarrollar serviciosWeb RESTful Dicho API utiliza anotaciones Java para reducir los esfuerzos de programacioacutende los servicios

12 Fundamentos de REST

El teacutermino REST proviene de la tesis doctoral de Roy Fielding publicada en el antildeo 2000y significa REpresentational State Transfer (podemos acceder a la tesis original en httpwwwicsuciedu~fieldingpubsdissertationtophtm) REST es un conjunto de restriccionesque cuando son aplicadas al disentildeo de un sistema crean un estilo arquitectoacutenico de softwareDicho estilo arquitectoacutenico se caracteriza por seguir los siguientes principios

bull Debe ser un sistema cliente-servidor

bull Tiene que ser sin estado es decir no hay necesidad de que los servicios guarden lassesiones de los usuarios (cada peticioacuten al servicio tiene que ser independiente de lasdemaacutes)

bull Debe soportar un sistema de cacheacutes la infraestructura de la red deberiacutea soportar cacheacuteen diferentes niveles

Servicios Rest

6

bull Debe ser un sistema uniformemente accesible (con una interfaz uniforme) Estarestriccioacuten define coacutemo debe ser la interfaz entre clientes y servidores La idea es simplificary desacoplar la arquitectura permitiendo que cada una de sus partes puede evolucionarde forma independiente Una interfaz uniforme se debe caracterizar por

Estar basada en recursos La abstraccioacuten utilizada para representar la informacioacuten ylos datos en REST es el recurso y cada recurso debe poder ser accedido medianteuna URI (Uniform Resource Identifier)

Orientada a representaciones La interaccioacuten con los servicios tiene lugar atraveacutes de las representaciones de los recursos que conforman dicho servicio Unrecurso referenciado por una URI puede tener diferentes formatos (representaciones)Diferentes plataformas requieren formatos diferentes Por ejemplo los navegadoresnecesitan HTML JavaScript requiere JSON (JavaScript Object Notation) y unaaplicacioacuten Java puede necesitar XML

Interfaz restringida Se utiliza un pequentildeo conjunto de meacutetodos bien definidos paramanipular los recursos

Uso de mensajes auto-descriptivos cada mensaje debe incluir la suficienteinformacioacuten como para describir coacutemo procesar el mensaje Por ejemplo se puedeindicar coacutemo parsear el mensaje indicando el tipo de contenido del mismo (xml htmltextohellip)

Uso de Hipermedia como maacutequina de estados de la aplicacion (HATEOAS) Lospropios formatos de los datos son los que dirigen las transiciones entre estados dela aplicacioacuten Como veremos maacutes adelante con maacutes detalle el uso de HATEOAS(Hypermedia As The Engine Of Application State) va a permitir transferir de formaexpliacutecita el estado de la aplicacion en los mensajes intercambiados y por lo tantorealizar interacciones con estado

bull Tiene que ser un sistema por capas un cliente no puede discernir si estaacute accediendodirectamente al servidor o a alguacuten intermediario Las capas intermedias van a permitirsoportar la escalabilidad asiacute como reforzar las poliacuteticas de seguridad

A continuacioacuten analizaremos algunas de las abstracciones que constituyen un sistemaRESTful recursos representaciones URIs y los tipos de peticiones HTTP que constituyen lainterfaz uniforme utilizada en las transferencias clienteservidor

Recursos

Un recurso REST es cualquier cosa que sea direccionable (y por lo tanto accesible) a traveacutesde la Web Por direccionable nos referimos a recursos que puedan ser accedidos y transferidosentre clientes y servidores Por lo tanto un recurso es una correspondencia loacutegica ytemporal con un concepto en el dominio del problema para el cual estamos implementandouna solucioacuten

Algunos ejemplos de recursos REST son

bull Una noticia de un perioacutedico

bull La temperatura de Alicante a las 400pm

bull Un valor de IVA almacenado en una base de datos

bull Una lista con el historial de las revisiones de coacutedigo en un sistema CVS

bull Un estudiante en alguna aula de alguna universidad

Servicios Rest

7

bull El resultado de una buacutesqueda de un iacutetem particular en Google

Aun cuando el mapeado de un recurso es uacutenico diferentes peticiones a un recursopueden devolver la misma representacioacuten binaria almacenada en el servidor Por ejemploconsideremos un recurso en el contexto de un sistema de publicaciones En este casouna peticioacuten de la uacuteltima revisioacuten publicada y la peticioacuten de la revisioacuten nuacutemero 12 enalguacuten momento de tiempo pueden devolver la misma representacioacuten del recurso cuandola uacuteltima revisioacuten sea efectivamente la 12 Por lo tanto cuando la uacuteltima revisioacuten publicadase incremente a la versioacuten 13 una peticioacuten a la uacuteltima revisioacuten devolveraacute la versioacuten 13 yuna peticioacuten de la revisioacuten 12 continuaraacute devolviendo la versioacuten 12 En definitiva cada unode los recursos puede ser accedido directamente y de forma independiente pero diferentespeticiones podriacutean apuntar al mismo dato

Debido a que estamos utilizando HTTP para comunicarnos podemos transferir cualquier tipode informacioacuten que pueda transportarse entre clientes y servidores Por ejemplo si realizamosuna peticioacuten de un fichero de texto de la CNN nuestro navegador mostraraacute un fichero de textoSi solicitamos una peliacutecula flash a YouTube nuestro navegador recibiraacute una peliacutecula flash Enambos casos los datos son transferidos sobre TCPIP y el navegador conoce coacutemo interpretarlos streams binarios debido a la cabecera de respuesta del protocolo HTTP Content-Type Porlo tanto en un sistema RESTful la representacioacuten de un recurso depende del tipo deseado porel cliente (tipo MIME) el cual estaacute especificado en la peticioacuten del protocolo de comunicaciones

Representacioacuten de los recursos

La representacioacuten de los recursos es lo que se enviacutea entre los servidores y clientes Unarepresentacioacuten muestra el estado temporal del dato real almacenado en alguacuten dispositivo dealmacenamiento en el momento de la peticioacuten En teacuterminos generales es un stream binariojuntamente con los metadatos que describen coacutemo dicho stream debe ser consumido por elcliente yo servidor (los metadatos tambieacuten puden contener informacioacuten extra sobre el recursocomo por ejemplo informacioacuten de validacioacuten y encriptacioacuten o coacutedigo extra para ser ejecutadodinaacutemicamente)

A traveacutes del ciclo de vida de un servicio web pueden haber varios clientes solicitando recursosClientes diferentes son capaces de consumir diferentes representaciones del mismo recursoPor lo tanto una representacioacuten puede tener varias formas como por ejemplo una imagen untexto un fichero XML o un fichero JSON pero tienen que estar disponibles en la misma URL

Para respuestas generadas para humanos a traveacutes de un navegador una representacioacutentiacutepica tiene la forma de paacutegina HTML Para respuestas automaacuteticas de otros servicios web lalegibilidad no es importante y puede utilizarse una representacioacuten mucho maacutes eficiente comopor ejemplo XML

El lenguaje para el intercambio de informacioacuten con el servicio queda a eleccioacuten deldesarrollador A continuacioacuten mostramos algunos formatos comunes que podemos utilizarpara intercambiar esta informacioacuten

Table 1 Ejemplos de formatos utilizados por los servicios REST

Formato Tipo MIME

Texto plano textplain

HTML texthtml

XML applicationxml

JSON applicationjson

Servicios Rest

8

De especial intereacutes es el formato JSON Se trata de un lenguaje ligero de intercambio deinformacioacuten que puede utilizarse en lugar de XML (que resulta considerablemente maacutespesado) para aplicaciones AJAX De hecho en Javascript puede leerse este tipo de formatosimplemente utilizando el meacutetodo eval()

Direccionabilidad de los recursos URI

Una URI o Uniform Resource Identifier en un servicio web RESTful es un hiper-enlace aun recurso y es la uacutenica forma de intercambiar representaciones entre clientes y servidoresUn servicio web RESTful expone un conjunto de recursos que identifican los objetivos de lainteraccioacuten con sus clientes

El conjunto de restricciones REST no impone que las URIs deban ser hiper-enlacesSimplemente hablamos de hiper-enlaces porque estamos utilizando la Web para crearservicios web Si estuvieacutesemos utilizando un conjunto diferente de tecnologiacuteas soportadasuna URI RESTful podriacutea ser algo completamente diferente Sin embargo la idea dedireccionabilidad debe permanecer

En un sistema REST la URI no cambia a lo largo del tiempo ya que la implementacioacutende la arquitectura es la que gestiona los servicios localiza los recursos negocia lasrepresentaciones y enviacutea respuestas con los recursos solicitados Y lo que es maacutes importantesi hubiese un cambio en la estructura del dispositivo de almacenamiento en el lado del servidor(por ejemplo un cambio de servidores de bases de datos) nuestras URIs seguiraacuten siendo lasmismas y seraacuten vaacutelidas mientras el servicio web siga estando en marcha o el contexto delrecurso no cambie

Sin las restricciones REST los recursos se acceden por su localizacioacutenlas direcciones web tiacutepicas son URIs fijas Si por ejemplo renombramosun fichero en el servidor la URI seraacute diferente si movemos el fichero a undirectorio diferente la URI tambieacuten seraacute diferente

El formato de una URI se estandariza como sigue

schemehostportpathqueryStringfragment

En donde

bull scheme es el protocolo que estamos utilizando para comunicarnos con el servidor Paraservicios REST normalmente el protocolo seraacute http o https

bull El teacutermino host es un nombre DNS o una direccioacuten IP

bull A continuacioacuten se puede indicar de forma opcional un puerto (mediante port ) que es unvalor numeacuterico El host y el port representan la localizacioacuten de nuestro recurso en la red

bull Seguidamente aparece una expresioacuten path que es un conjunto de segmentos de textodelimitados por el caraacutecter (pensemos en la expresioacuten path como en una lista dedirectorios de un fichero en nuestra maacutequina)

bull Esta expresioacuten puede ir seguida opcionalmente por una queryString El caraacutecter )separa el path de la queryString Esta uacuteltima es una lista de paraacutemetros representadoscomo pares nombrevalor Cada par estaacute delimitado por el caraacutecter amp

La uacuteltima parte de la URI es el fragment delimitado por el caraacutecter Normalmente seutiliza para apuntar a cierto lugar del documento al que estamos accediendo

Servicios Rest

9

En una URI no todos los caracteres estaacuten permitidos de forma que algunos caracteres secodificaraacuten de acuerdo a las siguientes reglas

bull Los caracteres a-z A-Z 0-9 - y _ permanecen igual

bull El caracter espacio se convierte en el caraacutecter +

bull El resto de caracteres se codifican como una secuencia de bits siguiendo un esquema decodificacioacuten hexadecimal de forma que cada dos diacutegitos hexadecimales van precedidospor el caraacutecter

Un ejemplo de URI podriacutea ser eacuteste

httpexpertojavauaesrecursosclientesapellido=MartinezampcodPostal=02115

En el ejemplo anterior el host viene dado por expertojavauaes el path o ruta de acceso alrecurso es recursosclientes y hemos especificado los paraacutemetros apellido y codPostal conlos valores Martinez y 02115 respectivamente

Si por ejemplo en nuestra aplicacioacuten tenemos informacioacuten de clientes podriacuteamos acceder ala lista correspondiente mediante una URL como la siguiente

httpexpertojavauaesrecursosclientes

Esto nos devolveraacute la lista de clientes en el formato que el desarrollador del servicio hayadecidido Hay que destacar por lo tanto que en este caso debe haber un entendimiento entreel consumidor y el productor del servicio de forma que el primero comprenda el lenguajeutilizado por el segundo

La URL anterior nos podriacutea devolver un documento como el siguiente

ltxml version=10gtltclientesgt ltclientegthttpexpertojavauaesrecursosclientes1ltclientegt ltclientegthttpexpertojavauaesrecursosclientes2ltclientegt ltclientegthttpexpertojavauaesrecursosclientes4ltclientegt ltclientegthttpexpertojavauaesrecursosclientes6ltclientegtltclientesgt

En este documento se muestra la lista de clientes registrados en la aplicacioacuten cada uno deellos representado tambieacuten por una URL Accediendo a estas URLs a su vez podremosobtener informacioacuten sobre cada curso concreto o bien modificarlo

Uniformidad y restricciones de las interfaces

Ya hemos introducido los conceptos de recursos y sus representaciones Hemos dichoque los recursos son correspondencias (mappings) de los estados reales de las entidadesque son intercambiados entre los clientes y servidores Tambieacuten hemos dicho que lasrepresentaciones son negociadas entre los clientes y servidores a traveacutes del protocolo decomunicacioacuten en tiempo de ejecucioacuten (a traveacutes de HTTP) A continuacioacuten veremos con detalle

Servicios Rest

10

lo que significa el intercambio de estas representaciones y lo que implica para los clientes yservidores el realizar acciones sobre dichos recursos

El desarrollo de servicios web REST es similar al desarrollo de aplicaciones web Sin embargola diferencia fundamental entre el desarrollo de aplicaciones web tradicionales y las maacutesmodernas es coacutemo pensamos sobre las acciones a realizar sobre nuestras abstraccionesde datos De forma maacutes concreta el desarrollo moderno estaacute centrado en el concepto denombres (intercambio de recursos) el desarrollo tradicional estaacute centrado en el conceptode verbos (acciones remotas a realizar sobre los datos) Con la primera forma estamosimplementando un servicio web RESTful con la segunda un servicio similar a una llamada aprocedimiento remoto- RPC) Y lo que es maacutes un servicio RESTful modifica el estado delos datos a traveacutes de la representacioacuten de los recursos (por el contrario una llamada aun servicio RPC oculta la representacioacuten de los datos y en su lugar enviacutea comandos paramodificar el estado de los datos en el lado del servidor) Finalmente en el desarrollo modernode aplicaciones web limitamos la ambiguumledad en el disentildeo y la implementacioacuten debido aque tenemos cuatro acciones especiacuteficas que podemos realizar sobre los recursos CreateRetrieve Update y Delete (CRUD) Por otro lado en el desarrollo tradicional de aplicacionesweb podemos tener otras acciones con nombres o implementaciones no estaacutendar

A continuacioacuten indicamos la correspondencia entre las acciones CRUD sobre los datos y losmeacutetodos HTTP asociados

Table 2 Operaciones REST sobre los recursos

Accioacuten sobre los datos Protocolo HTTP equivalente

CREATE POST

RETRIEVE GET

UPDATE PUT

DELETE DELETE

El principio de uniformidad de la interfaz de acceso a recursos es fundamental y quizaacute el maacutesdifiacutecil de seguir por los programadores acostumbrados al modelo RPC (Remote ProcedureCall) La idea subyacente es utilizar uacutenicamente un conjunto finito y claramente establecidode operaciones para la interaccioacuten con los servicios Esto significa que no tendremos unparaacutemetro accioacuten en nuestra URI y que soacutelo utilizaremos los meacutetodos HTTP para accedera nuestros servicios Cada uno de los meacutetodos tiene un propoacutesito y significado especiacuteficosque mostramos a continuacioacuten

GETGET es una operacioacuten soacutelo de lectura Se utiliza para recuperar informacioacuten especiacuteficadel servidor Tambieacuten se trata de una operacioacuten idempotente y segura Idempotentesignifica que no importa cuaacutentas veces invoquemos esta operacioacuten el resultado (queobservaremos como usuarios) debe ser siempre el mismo Segura significa que unaoperacioacuten GET no cambia el estado del servidor en modo alguno es decir no debe exhibirninguacuten efecto lateral en el servidor Por ejemplo el hecho de leer un documento HTMLno deberiacutea cambiar el estado de dicho documento

PUTLa operacioacuten PUT solicita al servidor el almacenar el cuerpo del mensaje enviado condicha operacioacuten en la direccioacuten proporcionada en el mensaje HTTP Normalmente semodela como una insercioacuten o actualizacioacuten (nosotros la utilizaremos solamente comoactualizacioacuten) Es una propiedad idempotente Cuando se utiliza PUT el cliente conoce

Servicios Rest

11

la identidad del recurso que estaacute creando o actualizando Es idempotente porque enviar elmismo mensaje PUT maacutes de una vez no tiene ninguacuten efecto sobre el servicio subyacenteUna analogiacutea podriacutea ser un documento de texto que estemos editando No importa cuaacutentasveces pulsemos el botoacuten de grabar el fichero que contiene el documento loacutegicamenteseraacute el mismo documento

DELETEEsta operacioacuten se utiliza para eliminar recursos Tambieacuten es idempotente

POSTPost es la uacutenica operacioacuten HTTP que no es idempotente ni segura Cada peticioacuten POSTpuede modificar el servicio de forma exclusiva Se puede enviar o no informacioacutencon la peticioacuten Tambieacuten podemos recibir o no informacioacuten con la respuesta Paraimplementar servicios REST es deseable enviar informacioacuten con la peticioacuten y tambieacutenrecibir informacioacuten con la respuesta

Adicionalmente podemos utilizar otras dos operaciones HTTP

HEADEs una operacioacuten exactamente igual que GET excepto que en lugar de devolver un cuerpode mensaje solamente devuelve un coacutedigo de respuesta y alguna cabecera asociada conla peticioacuten

OPTIONSSe utiliza para solicitar informacioacuten sobre las opciones disponibles sobre un recurso en elque estamos interesados Esto permite al cliente determinar las capacidades del servidory del recurso sin tener que realizar ninguna peticioacuten que provoque una accioacuten sobre elrecurso o la recuperacioacuten del mismo

PATCHSe utiliza para para realiza reemplazos (actualizaciones) parciales de un documento yaque la operacioacuten PUT soacutelo permite una actualizacioacuten completa del recurso (y requiereindicar una representacioacuten completa del mismo) Es uacutetil cuando el recurso a modificares complejo y solamente queremos actualizar parte de su contenido En este caso solonecesitamos indicar la parte que queremos cambiar

13 Disentildeo de servicios Web RESTful

El disentildeo de servicios RESTful no es muy diferente del disentildeo de aplicaciones webtradicionales tenemos requerimientos de negocio tenemos usuarios que quieren realizaroperaciones sobre los datos y tenemos restricciones hardware que van a condicionar nuestraarquitectura software La principal diferencia reside en el hecho de que tenemos que buscara partir de los requerimientos cuaacuteles van a ser los recursos que van a ser accedidos a traveacutesde los servicios sin preocuparnos de queacute operaciones o acciones especiacuteficas van a poderserealizar sobre dichos recursos (el proceso de disentildeo depende de los nombres no de losverbos)

Podemos resumir los principios de disentildeo de servicios web RESTful en los siguientes cuatropasos

1 Elicitacioacuten de requerimientos y creacioacuten del modelo de objetos Este paso es similar aldisentildeo orientado a objetos El resultado del proceso puede ser un modelo de clases UML

2 Identificacioacuten de recursos Este paso consiste en identificar los objetos de nuestromodelo sin preocuparnos de las operaciones concretas a realizar sobre dichos objetos

Servicios Rest

12

3 Definicioacuten de las URIs Para satisfacer el principio de direccionabilidad de los recursostendremos que definir las URIs que representaraacuten los endpoints de nuestros servicios yque constituiraacuten los puntos de entrada de los mismos

4 Definicioacuten de la representacioacuten de los recursos Puesto que los sistemas REST estaacutenorientados a la representacioacuten tendremos que definir el formato de los datos queutilizaremos para intercambiar informacioacuten entre nuestros servicios y clientes

5 Definicioacuten de los meacutetodos de acceso a los recursos Finalmente tendremos que decidirqueacute meacutetodos HTTP nos permitiraacuten acceder a las URIs que queremos exponer asiacutecomo queacute haraacute cada meacutetodo Es muy importante que en este paso nos cintildeamos alas restricciones que definen los principios RESTful que hemos indicado en apartadosanteriores

14 Un primer servicio JAX-RS

Vamos a ilustrar los pasos anteriores con un ejemplo concretamente definiremos una interfazRESTful para un sistema sencillo de gestioacuten de pedidos de un hipoteacutetico comercio por internetLos potenciales clientes de nuestro sistema podraacuten realizar compras modificar pedidosexistentes en nuestro sistema asiacute como visualizar sus datos personales o la informacioacuten sobrelos productos que son ofertados por el comercio

Modelo de objetos

A partir de los requerimientos del sistema obtenemos el modelo de objetos El modelode objetos de nuestro sistema de ventas por internet es bastante sencillo Cada pedidoen el sistema representa una uacutenica transaccioacuten de compra y estaacute asociada con un clienteparticular Los pedidos estaraacuten formados por una o maacutes liacuteneas de pedido Las liacuteneas de pedidorepresentan el tipo y el nuacutemero de unidades del producto adquirido

Basaacutendonos en esta descripcioacuten de nuestro sistema podemos extraer que los objetos denuestro modelo son Pedido Cliente LineaPedido y Producto Cada objeto de nuestromodelo tiene un identificador uacutenico representado por la propiedad id dada por un valor detipo entero La siguiente figura muestra un diagrama UML de nuestro modelo

Estamos interesados en consultar todos los pedidos realizados asiacute como cada pedido deforma individual Tambieacuten queremos poder realizar nuevos pedidos asiacute como actualizar

Servicios Rest

13

pedidos existentes El objeto ServicioPedidos representa las operaciones que queremosrealizar sobre nuestos objetos Pedido Cliente LineaPedido y Producto

Modelado de URIs

Lo primero que haremos para crear nuestra interfaz distribuida es definir y poner nombre acada uno de los endpoints de nuestro sistema En un sistemam RESTful los endpoints seraacutenlos recursos del sistema que identificaremos mediante URIs

En nuestro modelo de objetos queremos poder interactuar con Pedidos Clientes y ProductosEacutestos seraacuten por lo tanto nuestros recursos de nivel maacutes alto Por otro lado estamosinteresados en obtener una lista de cada uno de estos elementos de alto nivel asiacute comointeractuar con los elementos indiduales de cada tipo El objeto LineaPedido es un objetoagregado del objeto Pedido por lo que no lo consideraremos com un recurso de nivel superiorMaacutes adelante veremos que podremos exponerlo como un subrecurso de un Pedido particularpero por ahora asumiremos que estaacute oculto por el formato de nuestros datos Seguacuten estouna posible lista de URIs que expondraacute nuestro sistema podriacutea ser

bull pedidos

bull pedidosid

bull productos

bull productosid

bull clientes

bull clientesid

Fiacutejate que hemos representado como URIs los nombres en nuestromodelo de objetos Recuerda que las URIS no deberiacutean utilizarse comomini-mecanismos de RPC ni deberiacutean identificar operaciones En vez deeso tenemos que utilizar una combinacioacuten de meacutetodos HTTP y de datos(recursos) para modelar las operaciones de nuestro sistema RESTful

Definicioacuten del formato de datos

Una de las cosas maacutes importantes que tenemos que hacer cuando definimos la interfazRESTful es determinar coacutemo se representaraacuten los recursos que seraacuten accedidos por losusuarios de nuestro API REST Quizaacute XML sea el formato maacutes popular de la web y puede serprocesado por la mayor parte de los lenguajes de programacioacuten Como veremos maacutes adelanteJSON es otro formato popular menos verboso que XML y que puede ser interpretadodirectamente por JavaScript (lo cual es perfecto para aplicaciones Ajax por ejemplo) Porahora utilizaremos el formato XML en nuestro ejemplo

Generalmente tendriacuteamos que definir un esquema XML para cada representacioacuten quequeramos transimitir a traves de la red Un esquema XML define la gramaacutetica del formato dedatos Por simplicidad vamos a omitir la creacioacuten de esquemas asumiendo que los ejemplosque proporcionamos se adhieren a sus correspondientes esquemas

A continuacioacuten distinguiremos entre dos formatos de datos uno para las operaciones delectura y actualizacioacuten y otro para la operacioacuten de creacioacuten de recursos

Formato de datos para operaciones de lectura y modificacioacuten de los recursos

Las representaciones de los recursos Pedido Cliente y Producto tendraacuten un elemento XMLen comuacuten al que denominaremos link

Servicios Rest

14

ltlink rel=self href=httporgexpertojavagt

El elemento (o etiqueta) link indica a los clientes que obtengan un documento XML comorepresentacioacuten del recurso doacutende pueden interaccionar en la red con dicho recurso enparticular El atributo self le indica al cliente queacute relacioacuten tiene dicho enlace con la URIdel recurso al que apunta (informacioacuten contenida en el atributo href ) El valor self indicaque estaacute apuntando a siacute mismo Maacutes adelante veremos la utilidad del elemento link cuandoagreguemos informacioacuten en documentos XML maacutes grandes

El formato de representacioacuten del recurso Cliente podriacutea ser

ltcliente id=8gt ltlink rel=self href=httporgexpertojavaclientes8gt ltnombregtPedroltnombregt ltapellidosgtGarcia Perezltapellidosgt ltdirecciongtCalle del Pino 5ltdirecciongt ltcodPostalgt08888ltcodPostalgt ltciudadgtMadridltciudadgtltclientegt

El formato de representacioacuten del recurso Producto podriacutea ser

ltproducto id=34gt ltlink rel=self href=httporgexpertojavaproductos34gt ltnombregtiPhone 6ltnombregt ltpreciogt800ltpreciogt ltcantidadgt1ltcantidadgtltproductogt

Finalmente el formato de la representacioacuten del recurso Pedido podriacutea ser

ltpedido id=233gt ltlink rel=self href=httporgexpertojavapedidos233gt lttotalgt800lttotalgt ltfechagtDecember 22 2014 0656ltfechagt ltcliente id=8gt ltlink rel=self href=httporgexpertojavaclientes8gt ltnombregtPedroltnombregt ltapellidosgtGarcia Perezltapellidosgt ltdirecciongtCalle del Pino 5ltdirecciongt ltcodPostalgt08888ltcodPostalgt ltciudadgtMadridltciudadgt ltclientegt ltlineasPedidogt ltlineaPedido id=1gt ltproducto id=34gt ltlink rel=self href=httporgexpertojavaproductos34gt ltnombregtiPhone 6ltnombregt

Servicios Rest

15

ltpreciogt800ltpreciogt ltcantidadgt1ltcantidadgt ltproductogt ltlineaPedidogt ltlineasPedidogtltpedidogt

El formato de datos de un Pedido tiene en un primer nivel la informacioacuten del total conel importe total del pedido asiacute como la fecha en la que se hizo dicho pedido Pedido es unbuen ejemplo de composicioacuten de datos ya que un pedido incluye informacioacuten sobre el Clientey el Productos Aquiacute es donde el elemento ltlinkgt puede resultar particularmente uacutetil Si elusuario estaacute interesado en interaccionar con el Cliente que ha realizado el pedido o en uno delos productos del mismo se proporciona la URI necesaria para interactuar con cada uno dedichos recursos De esta forma cuando el usuario del API consulte un pedido podraacute ademaacutesacceder a informacioacuten adicional relacionada con la consulta realizada

Formato de datos para operaciones de creacioacuten de los recursos

Cuando estamos creando nuevos Pedidos Clientes o Productos no tiene mucho sentidoincluir un atributo id y un elemento link en nuestro documento XML El servidor seraacute elencargado de crear los ids cuando inserte nuestro nuevo objeto en la base de datos Tampococonocemos la URI del nuevo objeto creado ya que seraacute el servidor el encargado de generarloPor lo tanto para crear un nuevo Producto el formato de la informacioacuten podriacutea ser el siguiente

ltproductogt ltlink rel=self href=httporgexpertojavaclientes8gt ltnombregtiPhoneltnombregt ltpreciogt800ltpreciogtltproductogt

Asignacioacuten de meacutetodos HTTP

Finalmente tendremos que decidir queacute meacutetodos HTTP expondremos en nuestro servicio paracada uno de los recursos asiacute como definir queacute haraacuten dichos meacutetodos Es muy importanteno asignar funcionaldad a un meacutetodo HTTP que sobrepase los liacutemites impuestos por laespecificacioacuten de dicho meacutetodo Por ejemplo una operacioacuten GET sobre un recurso concretodeberiacutea ser de soacutelo lectura No deberiacutea cambiar el estado del recurso cuando invoquemos laoperacioacuten GET sobre eacutel Si no seguimos de forma estricta la semaacutentica de los meacutetodos HTTPlos clientes asiacute como cualquier otra herramienta administrativa no pueden hacer asuncionessobre nuestros servicios de forma que nuestro sistema se vuelve maacutes complejo

Veamos para cada uno de los meacutetodos de nuestro modelo de objetos cuales seraacuten las URIsy meacutetodos HTTP que usaremos para representarlos

Visualizacioacuten de todos los Pedidos Clientes o Productos

Los tres objetos de nuestro modelo Pedidos Clientes y Productos son accedidos ymanipulados de forma similar Los usuarios pueden estar interesados en ver todos losPedidos Clientes o Productos en el sistema Las siguientes URIs representan dichos objetoscomo un grupo

bull pedidos

Servicios Rest

16

bull productos

bull clientes

Para obtener una lista de Pedidos Clientes o Productos el cliente remoto realizara unallamada al meacutetodo HTTP GET sobre la URI que representa el grupo de objetos Un ejemplode peticioacuten podriacutea ser la siguiente

GET productos HTTP11

Nuestro servicio responderaacute con los datos que representan todos los Pedidos de nuestrosistema Una respuesta podriacutea ser eacutesta

HTTP11 200 OKContent-Type applicationxml

ltproductosgt ltproducto id=111gt ltlink rel=self href=httporgexpertojavaproductos111gt ltnombregtiPhoneltnombregt ltpreciogt64899ltpreciogt ltproductogt ltproducto id=222gt ltlink rel=self href=httporgexpertojavaproductos222gt ltnombregtMacbookltnombregt ltpreciogt159999ltpreciogt ltproductogt ltproductosgt

Un problema que puede darse con esta peticioacuten es que tengamos miles de Pedidos Clienteso Productos en nuestro sistema por lo que podemos sobrecargar a nuestro cliente y afectarnegativamente a los tiempos de respuesta Para mitigar esta problema permitiremos que elusuario especifique unos paraacutemetros en la URI para limitar el tamantildeo del conjunto de datosque se va a devolver

GET pedidosstartIndex=0ampsize=5 HTTP11GET productosstartIndex=0ampsize=5 HTTP11GET clientesstartIndex=0ampsize=5 HTTP11

En las oacuterdenes anteriores hemos definido dos paraacutemetros de peticioacuten startIndex ysize El primero de ellos es un iacutendice numeacuterico que representa a partir de queacute posicioacuten enla lista de Pedidos Clientes o Productos comenzaremos a enviar la informacioacuten al clienteEl paraacutemetro size especifica cuaacutentos de estos objetos de la lista queremos que nos seandevueltos

Estos paraacutemetros seraacuten opcionales de forma que el cliente no tiene que especificarlos en suURI

Obtencioacuten de Pedidos Clientes o Productos individuales

Ya hemos comentado previamente que podriacuteamos utilizar las siguientes URIs para obtenerPedidos Clientes o Productos

Servicios Rest

17

bull pedidosid

bull productosid

bull clientesid

En este caso usaremos el meacutetodo HTTP GET para recuperar objetos individuales en elsistema Cada invocacioacuten GET devolveraacute la informacioacuten del correspondiente objeto Porejemplo

GET pedidos233 HTTP11

Para esta peticioacuten el cliente estaacute interesado en obtener una representacioacuten del Pedido conidentificador 233 Las peticiones GET para Productos y Clientes podriacutean funcionar de formasimilar El mensaje de respuesta podriacutea parecerse a algo como esto

HTTP11 200 OKContent-Type applicationxml

ltpedido id=233gtltpedidogt

El coacutedigo de respuesta es 200 OK indicando que la peticioacuten ha tenido eacutexito La cabeceraContent-Type especifica el formato del cuerpo de nuestro mensaje como XML y finalmenteobtenemos la representacioacuten del Pedido solicitado

Creacioacuten de un Pedido Cliente o Producto

Para crear un Pedido Cliente o Producto utilizaremos el meacutetodo POST En este caso el clienteenviacutea una representacioacuten del nuevo objeto que se prentende crear a la URI padre de surepresentacioacuten y por lo tanto podremos omitir el identificador del recurso Por ejemplo

Peticioacuten POST para crear un pedidio

POST pedidos HTTP11Content-Type applicationxml

ltpedidogt lttotalgt19902lttotalgt ltfechagtDecember 22 2008 0656ltfechagt ltpedidogt

El servicio recibe el mensaje POST procesa la XML y crea un nuevo pedido en la basede datos utilizando un identificador generado de forma uacutenica Si bien esta aproximacioacutenfunciona perfectamente se le pueden plantear varias cuestiones al usuario iquestQueacute ocurre siel usuario quiere visualizar modificar o eliminar el pedido que acaba de crear iquestCuaacutel es elidentificador del nuevo recurso iquestCuaacutel es la URI que podemos utilizar para interactuar con elnuevo recurso Para resolver estas cuestiones antildeadiremos alguna informacioacuten al mensajede respuesta HTTP El cliente podriacutea recibir un mensaje similar a eacuteste

Respuesta de una peticioacuten POST para crear un pedido

HTTP11 201 Created

Servicios Rest

18

Content-Type applicationxmlLocation httporgexpertojavapedidos233

ltpedido id=233gt ltlink rel=self href=httporgexpertojavapedidos233gt lttotalgt19902lttotalgt ltfechagtDecember 22 2008 0656ltfechagt ltpedidogt

HTTP requiere que si POST crea un nuevo recurso se debe responder con un coacutedigo 201Created Tambieacuten se requieer que la cabecera Location en el mensaje de respuestaproporcione una URI al usuario que ha hecho la peticioacuten para que eacuteste pueda interactuar conla Peticioacuten que acaba de crear (por ejemplo para modificar dicho Pedido) Es opcional porparte del servidor devolver en la respuesta la representacioacuten del nuevo recurso creado Ennuestro ejemplo optamos por devolver una representacioacuten XML de la Peticion creada con elidentificador del atributo asiacute como el elemento link

Actualizacioacuten de un Pedido Cliente o Producto

Para realizar modificaciones sobre los recursos que ya hemos creado utilizaremos el meacutetodoPUT En este caso un ejemplo de peticioacuten podriacutea ser eacutesta

Peticioacuten PUT para modificar un pedidio

PUT pedidos233 HTTP11Content-Type applicationxml

ltproducto id=111gt ltnombregtiPhoneltnombregt ltpreciogt64999ltpreciogtltproductogt

Tal y como he hemos indicado anteriormente la operacioacuten PUT es idempotente Lo quesignifica que no importa cuaacutentas veces solicitemos la peticioacuten PUT el producto subyacentesigue permaneciendo con el mismo estado final

Cuando un recurso se modifica mediante PUT la especificacioacuten HTTP requiere que el servidorenviacutee un coacutedigo de respuesta 200 OK y un cuerpo de mensaje de respuesta o bien el coacutedigo204 No Content sin ninguacuten cuerpo de mensaje en la respuesta

En nuestro caso devolveremos un coacutedigo de estado 204 y un mensaje sin cuerpo derespuesta

RECUERDA Es importante NO confundir POST con PUTMuchas veces se confunden los meacutetodos PUT y POST El significado deestos meacutetodos es el siguiente

bull POST Publica datos en un determinado recurso El recurso debe existirpreviamente y los datos enviados son antildeadidos a eacutel Por ejemplo paraantildeadir nuevos pedidos con POST hemos visto que debiacuteamos hacerlocon el recurso lista de pedidos (pedidos) ya que la URI del nuevopedido todaviacutea no existe La operacioacuten NO es idempotente es decir si

Servicios Rest

19

antildeadimos varias veces el mismo alumno apareceraacute repetido en nuestralista de pedidos con URIs distintas

bull PUT Hace que el recurso indicado tome como contenido los datosenviados El recurso podriacutea no existir previamente y en caso de queexistiese seriacutea sobrescrito con la nueva informacioacuten A diferencia dePOST PUT es idempotente Muacuteltiples llamadas ideacutenticas a la mismaaccioacuten PUT siempre dejaraacuten el recurso en el mismo estado Laaccioacuten se realiza sobre la URI concreta que queremos establecer (porejemplo pedidos215) de forma que varias llamadas consecutivas conlos mismos datos tendraacuten el mismo efecto que realizar soacutelo una deellas

Borrado de un Pedido Cliente o Producto

Modelaremos el borrado de los recursos utilizando el meacutetodo HTTP DELETE El usuariosimplemente invocaraacute el meacutetodo DELETE sobre la URI que representa el objeto que queremoseliminar Este meacutetodo haraacute que dicho recurso ya no exista en nuestro sistema

Cuando eliminamos un recurso con DELETE la especificacioacuten requiere que se enviacutee un coacutedigode respuesta 200 OK y un cuerpo de mensaje de respuesta o bien un coacutedigo de respuesta204 No Content sin un cuerpo de mensaje de respuesta

En nuestro caso devolveremos un coacutedigo de estado 204 y un mensaje sin cuerpo derespuesta

Cancelacioacuten de un Pedido

Hasta ahora las operaciones de nuestro modelo de objetos encajan bastante bien enla especificacioacuten de los correspondientes meacutetodos HTTP Hemos utilzado GET para leerdatos PUT para realizar modificaciones POST para crear nuevos recursos y DELETE paraeliminarlos En nuestro sistema los Pedidos pueden eliminarse o tambieacuten cancelarse Yahemos comentado que el borrado de un recurso lo elimina completamente de nuestra basede datos La operacioacuten de cancelacioacuten solamente cambia el estado del Pedido y lo siguemanteniendo en el sistema iquestCoacutemo podriacuteamos modelar esta operacioacuten

Cuando modelamos una interfaz RESTful para las operaciones de nuestro modelo de objetosdeberiacuteamos plantearnos la siguiente pregunta iquestla operacioacuten es un estado del recurso Sila respuesta es siacute entonces deberiacuteamos modelar esta operacioacuten dentro del formato de losdatos

La cancelacioacuten de un pedido es un ejemplo perfecto de esto que acabamos de decir La claveestaacute en que esta operacioacuten en realidad es un estado especiacutefico del Pedido eacuteste puede estarcancelado o no Cuando un usuario accede a un Pedido puede desear conocer si el Pedidoha sido o no cancelado Por lo tanto la informacioacuten sobre la cancelacioacuten deberiacutea formar partedel formato de datos de un Pedido Asiacute antildeadiremos un nuevo elemento a la informacioacuten delPedido

ltpedido id=233gt ltlink rel=self href=httporgexpertojavapedidos233gt lttotalgt19902lttotalgt ltfechagtDecember 22 2008 0656ltfechagt ltcanceladogtfalseltcanceladogt

Servicios Rest

20

ltpedidogt

Ya que el estado cancelado se modela en el propio formato de datos modelaremos la accioacutende cancelacioacuten con una operacioacuten HTTP PUT que ya conocemos

PUT pedidos233 HTTP11Content-Type applicationxml

ltpedido id=233gt ltlink rel=self href=httporgexpertojavapedidos233gt lttotalgt19902lttotalgt ltfechagtDecember 22 2008 0656ltfechagt ltcanceladogttrueltcanceladogt ltpedidogt

En este ejemplo modificamos la representacioacuten del Pedido con el elemento ltcanceladogtcon valor true

Este patroacuten de modelado no siempre sirve en todos los casos Por ejemplo imaginemosque queremos ampliar el sistema de forma que borremos del sistema todos los pedidoscancelados No podemos modelar esta operacioacuten de la misma manera que la de cancelacioacutenya que esta operacioacuten no cambia el estado de nuestra aplicacioacuten (no es en siacute misma un estadode la aplicacioacuten)

Para resolver este problema podemos modelar esta nueva operacioacuten como un subrecursode pedidos y realizar un borrado de los pedidos cancelados mediante el meacutetodo HTTP POSTde dicho subrecurso de la siguiente forma

POST pedidoseliminacion HTTP11

Un efecto interesante de lo que acabamos de hacer es que puesto que ahora eliminaciones una URI podemos hacer que la interfaz de nuestro servicios RESTful evolucionen conel tiempo Por ejemplo la orden GET pedidoseliminacion podriacutea devolver la uacuteltima fechaen la que se procedioacute a eliminar todos los pedidos cancelados asiacute como queacute pedidosfueron cancelados iquestY si queremos antildeadir alguacuten criterio a la hora de realizar el borradode pedidos cancelados Podriacuteamos introducir paraacutemetros para indicar que soacutelo queremoseliminar aquellos pedidos que esteacuten cancelados en una fecha anterior a una dada Comovemos podemos mantener una interfaz uniforme y centildeirnos a las operaciones HTTP tal ycomo estaacuten especificadas y a la vez dotar de una gran flexiblidad a la interfaz de nuestrosistema RESTful Hablaremos con maacutes detalle de los subrecursos en la siguiente sesioacuten

Implementacioacuten del servicio Creacioacuten del proyecto Maven

Vamos a utilizar Maven para crear la estructura del proyecto que contendraacute la implementacioacutende nuestro servicio Rest Inicialmente podemos utilizar el mismo arquetipo con el que habeacuteistrabajado en sesiones anteriores Y a continuacioacuten modificaremos la configuracioacuten del ficheropomxml para implementar nuestros servicios

Una opcioacuten es generar la estructura del proyecto directamente desde liacutenea de comandos Elcomando es el siguiente (recuerda que debes escribirlo en una misma liacutenea Los caracteres

Servicios Rest

21

que aparecen en el comando no forman parte del mismo simplemente indican que no sedebe pulsar el retorno de carro)

mvn --batch-mode archetypegenerate -DarchetypeGroupId=orgcodehausmojoarchetypes -DarchetypeArtifactId=webapp-javaee7 -DgroupId=orgexpertojava -DartifactId=ejemplo-rest

En donde

bull archetypeGroupId y archetypeArtifactId son los nombres del groupId yartifactId del arquetipo Maven que nos va a generar la plantilla para nuestro proyecto

bull groupId y artifactId son los nombres que asignamos como groupId y artifactId denuestro proyecto En este caso hemos elegido los valores orgexpertojava y ejemplo-restrespectivamente

Si utilizamos IntelliJ para crear el proyecto tenemos que

1 Crear un nuevo proyecto (New Project)

2 Elegir el tipo de proyecto Maven

3 Crear el proyecto Maven a partir de un arquetipo con las siguientes coordenadas

bull GroupId orgcodehausmojoarchetypes

bull ArtifactId webapp-javaee7

bull Version 11

4 Indicar las coordenadas de nuestro proyecto

bull GroupId orgexpertojava

bull ArtifactId ejemplo-rest

bull Version 10-SNAPSHOT

5 Confirmamos los datos introducidos

6 Para finalizar especificamos el nombre de nuestro proyecto en IntelliJ

bull Project Name ejemplo-rest (este valor tambieacuten identificaraacute el nombre del moacutedulo enIntelliJ)

7 Por comodidad marcaremos Enable autoimport para importar automaacuteticamente cualquiercambio en el proyecto

Una vez que hemos creado el proyecto con IntelliJ el paso siguiente es cambiar laconfiguracioacuten del pommxl que nos ha generado el arquetipo para incluir las propiedadesdependencias pluginshellip que necesitaremos para implementar nuestros recursos REST

Como ya sabemos el fichero pomxml contiene la configuracioacuten que utiliza Maven paraconstruir el proyecto A continuacioacuten indicamos las modificaciones en el fichero pomxmlgenerado inicialmente para adecuarlo a nuestras necesidades particulares

bull Cambiamos las propiedades del proyecto (etiqueta ltpropertiesgt ) por

Servicios Rest

22

Propiedades del proyecto

ltpropertiesgt ltprojectbuildsourceEncodinggtUTF-8ltprojectbuildsourceEncodinggtltpropertiesgt

bull Indicamos las dependencias del proyecto (etiqueta ltdependenciesgt en donde seincluyen las libreriacuteas necesarias para la construccioacuten del proyecto) En nuestro casonecesitamos incluir la libreriacutea javaxjavaee-web-api70 que contiene el apiestaacutendar de javaee 7 Marcamos el aacutembito de la libreriacutea (etiqueta ltscopegt ) comoprovided Con esto estamos indicando que soacutelo necesitaremos el jar correspondientepara compilar el proyecto y por lo tanto no incluiremos dicho jar en el fichero wargenerado para nuestra aplicacioacuten ya que dicha libreriacutea ya estaraacute disponible desde elservidor de aplicaciones en el que residiraacute nuestra aplicacioacuten

Libreriacuteas utilizadas para construir el proyecto (dependencias)

ltdependenciesgt ltdependencygt ltgroupIdgtjavaxltgroupIdgt ltartifactIdgtjavaee-web-apiltartifactIdgt ltversiongt70ltversiongt ltscopegtprovidedltscopegt ltdependencygtltdependenciesgt

bull A continuacioacuten configuramos la construccioacuten del proyecto (etiqueta ltbuildgt ) de lasiguiente forma (cambiamos la configuracioacuten original por la que mostramos a continuacioacuten)

Configuracioacuten de la construccioacuten del proyecto

ltbuildgt lt-- Especificamos el nombre del war que seraacute usado como context root cuando despleguemos la aplicacioacuten --gt ltfinalNamegt$projectartifactIdltfinalNamegt

ltpluginsgt lt-- Compilador de java Utilizaremos la versioacuten 17 --gt ltplugingt ltgroupIdgtorgapachemavenpluginsltgroupIdgt ltartifactIdgtmaven-compiler-pluginltartifactIdgt ltversiongt31ltversiongt ltconfigurationgt ltsourcegt17ltsourcegt lttargetgt17lttargetgt ltconfigurationgt ltplugingt

lt-- Servidor de aplicaciones wildfly --gt ltplugingt ltgroupIdgtorgwildflypluginsltgroupIdgt ltartifactIdgtwildfly-maven-pluginltartifactIdgt ltversiongt102Finalltversiongt ltconfigurationgt lthostnamegtlocalhostlthostnamegt

Servicios Rest

23

ltportgt9990ltportgt ltconfigurationgt ltplugingt

lt-- Cuando generamos el war no es necesario que el fichero webxml esteacute presente --gt ltplugingt ltgroupIdgtorgapachemavenpluginsltgroupIdgt ltartifactIdgtmaven-war-pluginltartifactIdgt ltversiongt23ltversiongt ltconfigurationgt ltfailOnMissingWebXmlgtfalseltfailOnMissingWebXmlgt ltconfigurationgt ltplugingt ltpluginsgtltbuildgt

Implementacioacuten del servicio Recursos JAX-RS

Una vez que tenemos la estructura del proyecto implementaremos los recursos de nuestraaplicacioacuten que seraacuten clases Java que utilizaraacuten anotaciones JAX-RS para enlazar y mapearpeticiones HTTP especiacuteficas a meacutetodos java los cuales serviraacuten dichas peticiones Eneste caso vamos a ilustrar con un ejemplo una posible implementacioacuten para el recursoCliente Tenemos que diferenciar entre las clases java que representaraacuten entidades denuestro dominio (objetos java que representan elementos de nuestro negocio y que seraacutenalmacenados tiacutepicamente en una base de datos) de nuestros recursos JAX-RS que tambieacutenseraacuten clases java anotadas y que utilizaraacuten objetos de nuestro dominio para llevar a cabo lasoperaciones expuestas en el API RESTful que hemos disentildeado

Asiacute por ejemplo implementaremos las clases

bull Clientejava representa una entidad del dominio Contiene atributos y suscorrespondientes getters y setters

bull ClienteResourcejava representa las operaciones RESTful sobre nuestro recurso Clienteque hemos definido en esta sesioacuten Es una clase java con anotaciones JAX-RS que nospermitiraacute insertar modificar borrar consultar un cliente asiacute como consultar la lista declientes de nuestro sistema

Clases de nuestro dominio (entidades) Clientejava

La clase que representa nuestra entidad del dominio Cliente es una clase java plana con suscorrespondientes atributos y getters y setters

Implementacioacuten del dominio clientejava

package orgexpertojavadomain

XmlRootElement(name=cliente)XmlAccessorType(XmlAccessTypeFIELD)public class Cliente private int id private String nombre private String apellidos private String direccion

Servicios Rest

24

private String codPostal private String ciudad

public int getId() return id public void setId(int id) thisid = id

public String getNombre() return nombre public void setNombre(String nom) thisnombre = nom

public String getApellidos() return apellidos public void setApellidos(String apellidos) thisapellidos = apellidos

public String getDireccion() return direccion public void setDireccion(String dir) thisdireccion = dir

public String getCodPostal() return codPostal public void setCodPostal(String cp) thiscodPostal = cp

public String getCiudad() return ciudad public void setCiudad(String ciudad) thisciudad = ciudad

Hemos anotado la clase Cliente con XmlRootElement y XmlAccessorType Hablaremos de estas anotaciones en sesiones posteriores las cuales se encargan delserializadodeserializado del cuerpo del mensaje (en formato xml o json) a nuestra clase javaCliente

Clases de nuestro servicio RESTful ClienteResourcejava

Una vez definido el objeto de nuestro dominio que representaraacute un objeto Cliente vamos a vercoacutemo implementar nuestros servicio JAX-RS para que diferentes usuarios de forma remotapuedan interactuar con nuestra base de datos de clientes

La implementacioacuten del servicio es lo que se denomina una resource class que no es maacutesque una clase java que utiliza anotaciones JAX-RS

Por defecto una nueva instancia de nuestra clase de recursos se crea para cada peticioacuten aese recurso Es lo que se conoce como un objeto per-request Esto implica que se crea unobjeto Java para procesar cada peticioacuten de entrada y se destruye automaacuteticamente cuandola peticioacuten se ha servido Per-request tambieacuten implica sin estado ya que no se guarda elestado del servicio entre peticiones

Comencemos con la implementacioacuten del servicio

package orgexpertojavaservices

import

Path(clientes)public class ClienteResource

private static MapltInteger Clientegt clienteDB = new ConcurrentHashMapltInteger Clientegt()

Servicios Rest

25

private static AtomicInteger idContador = new AtomicInteger()

Podemos observar que ClientResource es una clase java planay que no implementaninguna interfaz JAX-RS particular La anotacioacuten javaxwsrsPath indica que laclase ClienteResource es un servicio JAX-RS Todas las clases que queramos que seanreconocidas como servicios JAX-RS tienen que tener esta anotacioacuten Fiacutejate que estaanotacioacuten tiene el valor clientes Este valor representa la raiacutez relativa de la URI de nuestroservicio RESTful Si la URI absoluta de nuestro servidor es por ejemplo httpexpertojavaorglos meacutetodos expuestos por nuestra clase ClienteResource estariacutean disponibles bajo la URIhttpexpertojavaorgclientes

En nuestra clase definimos un Mapa para el campo ClienteDB quealmacenaraacute en memoria a los objetos Cliente de nuestro sistema Utilizamos unjavautilconcurrentConcurrentHashMap como tipo de clienteDB ya que nuestro recursoseraacute accedido concurrentemente por los usuarios de nuestro servicio rest El campoidContador lo utilizaremos para generar nuevos identificadores de nuestros objetos Clientecreados El tipo de este campo es javautilconcurrentatomicAtomicInteger para garantizarque siempre generaremos un identificador uacutenico aunque tengamos peticiones concurrentes

Justificacioacuten del caracter static de los atributosComo nuestros objetos seraacuten de tipo per-request el runtime de JAX-RScrearaacute una instancia de ClienteResource para cada pecioacuten que se realicesobre nuestro servicio La maacutequina virtual de java ejecutaraacute cada peticioacutena nuestro servicio en un hilo (thread) diferente permitiendo asiacute el accesoconcurrente a nuestro recurso Puesto que hemos decidido almacenaren memoria la informacioacuten de los clientes necesitamos que los atributosclienteDB y idContador sean static para que todas las instancias deClienteResource tengan acceso a la lista de clientes en memoria y nohaya problemas de concurrencia En realidad lo que estamos haciendocon eacutesto es permitir que el servicio guarde el estado entre peticiones Enun sistema real ClienteResource probablemente interactuacutee con una basede datos para almacenar y recuperar la informacioacuten de los clientes y porlo tanto no necesitaremos guardar el estado entre peticiones

Una mejor solucioacuten seriacutea no utilizar variables estaacuteticas y definir nuestroservicio como singleton Si hacemos eacutesto solamente se creariacutea unainstancia de clienteResource y estariacuteamos manteniendo el estado delas peticiones En la siguiente sesioacuten explicaremos coacutemo configurar unservicio como singleton Por simplicidad de momento optaremos por laopcioacuten de que los objetos RESTful sean per-request

Creacioacuten de clientes

Para implementar la creacioacuten de un nuevo cliente utilizamos el mismo modelo que hemosdisentildeado previamente Una peticioacuten HTTP POST enviacutea un documento XML que representaal cliente que queremos crear

El coacutedigo para crear nuevos clientes en nuestro sistema podriacutea ser eacuteste

POST

Consumes(applicationxml)

public Response crearCliente(Cliente cli)

Servicios Rest

26

el paraacutemetro cli se instancia con los datos del cliente del body del mensaje HTTP idContador++ clisetId(idContadorincrementAndGet())

clienteDBput(cligetId() cli)

Systemoutprintln(Cliente creado + cligetId()) return Responsecreated(URIcreate(clientes

+ cligetId()))build()

se recibe una peticioacuten POSTel cuerpo de la peticioacuten debe tener formato xmlcontiene la informacioacuten del documento xml del cuerpo de la peticioacuten de entradase antildeade el nuevo objeto Cliente a nuestro mapa de clientes (clienteDB)este meacutetodo se ejectua en el servidor por lo que el mensaje soacutelo seraacute visible por ejemplosi consultamos los mensajes generados por el servidor durante la ejecucioacuten el meacutetododevuelve un coacutedigo de respuesta 201 Created junto con una cabecera Locationapuntando a la URI absoluta del cliente que acabamos de crear

Vamos a explicar la implementacioacuten con maacutes detalle

Para enlazar peticiones HTTP POST con el meacutetodo crearCliente() lo anotamos conla anotacioacuten javaxwsrsPOST La anotacioacuten Path combinada con la anotacioacutenPOST enlaza todas las peticiones POST dirigidas a la URI relativa clientes al meacutetodo JavacrearCliente()

La anotacioacuten javaxwsrsConsumes aplicada a crearCliente() especifica queacute media typeespera el meacutetodo en el cuerpo del mensaje HTTP de entrada Si el cliente incluye en su peticioacutenPOST un media type diferente de XML se enviacutea un coacutedigo de error al cliente

El meacutetodo crearCliente() tiene un paraacutemetro de tipo Cliente En JAX-RS cualquier paraacutemetrono anotado con anotaciones JAX-RS se considera que es una representacioacuten del cuerpodel mensaje de la peticioacuten de entrada HTTP Las anotaciones que hemos introducido en laclase Cliente de nuestro dominio realizan el trabajo de convertir el documento xml contenidoen el cuerpo de la peticioacuten htpp de entrada en una instancia de nuestra clase Cliente

Solamente UNO de los paraacutemetros del meacutetodo Java puede representar elcuerpo del mensaje de la peticioacuten HTTP Esto significa que el resto deparaacutemetros deben anotarse con alguna anotacioacuten JAX-RS que veremosmaacutes adelante

El meacutetodo crearCliente() devuelve una respuesta de tipo javaxwsrscoreResponse El meacutetodo estaacutetico Responsecreated() crea un objeto Response que contiene un coacutedigode estado 201 Created Tambieacuten antildeade una cabecera Location con un valor similar ahttpexpertojavaorgclientes123 dependiendo del valor del valor de base de la raiacutez dela URI del servidor y el identificador generado para el objeto Cliente (en este caso se habriacuteagenerado el identificador 123) Maacutes adelante explicaremos con detalle el uso de la claseResponse

Consulta de clientes

A continuacioacuten mostramos un posible coacutedigo para consultar la informacioacuten de un cliente

GET

Servicios Rest

27

Path(id)Produces(applicationxml)public Cliente recuperarClienteId(PathParam(id) int id) final Cliente cli = clienteDBget(id) if (cli == null) throw new WebApplicationException(ResponseStatusNOT_FOUND) return new Cliente(cligetId() cligetNombre() cligetApellidos() cligetDireccion() cligetCodPostal() cligetCiudad())

En este caso anotamos el meacutetodo recuperarClienteId() con la anotacioacutenjavaxwsrsGET para enlazar las operaciones HTTP GET con este meacutetodo Java

Tambieacuten anotamos recuperarClienteId() con la anotacioacuten javaxwsrsPRODUCES Estaanotacioacuten indica a JAX-RS que valor tiene la cabecera HTTP Content-Type en la respuestaproporcionada por la operacioacuten GET En este caso estamos indicando que seraacute de tipoapplicationxml

En la implementacioacuten del meacutetodo utilizamos el paraacutemetro id para consultar si existe unobjeto Cliente en nuestro mapa clienteDB Si dicho cliente no existe lanzaremos la excepcioacutenjavaxwsrsWebApplictionException Esta excepcioacuten provocaraacute que el coacutedigo derespuesta HTTP sea 404 Not Found y significa que el recurso cliente requerido no existeDiscutiremos maacutes adelante el tema del manejo de excepciones

Modificacioacuten de clientes

Vamos a mostrar coacutemo seriacutea el coacutedigo para modificar un cliente

PUTPath(id)Consumes(applicationxml)public void modificarCliente(PathParam(id) int id Cliente nuevoCli)

Cliente actual = clienteDBget(id) if (actual == null) throw new WebApplicationException(ResponseStatusNOT_FOUND)

actualsetNombre(nuevoCligetNombre()) actualsetApellidos(nuevoCligetApellidos()) actualsetDireccion(nuevoCligetDireccion()) actualsetCodPostal(nuevoCligetCodPostal()) actualsetCiudad(nuevoCligetCiudad())

Anotamos el meacutetodo modificarCliente() con javaxwsrsPUT para enlazar laspeticiones HTTP PUT a este meacutetodo Al igual que hemos hecho con recuperarClienteId() elmeacutetodo modificarCliente() estaacute anotado adicionalmente con Path de forma que podamosatender peticiones a traveacutes de las URIs clientesid

El meacutetodo modificarCliente() tiene dos paraacutemetros El primero es un paraacutemetro id querepresenta el objeto Cliente que estamos modificando Al igual que ocurriacutea con el meacutetodorecuperarClienteId() utilizamos la anotacioacuten PathParam para extraer el identificador a

Servicios Rest

28

partir de la URI de la peticioacuten de entrada El segundo paraacutemetro es un objeto Cliente querepresenta el cuerpo del mensaje de entrada ya que no tiene ninguna anotacioacuten JAX-RS

El meacutetodo intenta encontrar un objeto Cliente en nuestro mapa clienteDB Si no existeprovocamos una WebApplicationException que enviaraacute una respuesta al usuario con elcoacutedigo 404 Not Found Si el objeto Cliente existe modificamos nuestro objeto Clienteexistente con los nuevos valores que obtenemos de la peticioacuten de entrada

Construccioacuten y despliegue del servicio

Una vez implementado nuestro servicio RESTful necesitamos poner en marcha el procesode construccioacuten El proceso de construccioacuten compilaraacutehellip empaquetaraacute hellip y finalmente nospermitiraacute desplegar nuestro servicio en el servidor de aplicaciones

Para poder empaquetar nuestro servicio RESTful como un war que se desplegaraacute en elservidor de aplicaciones vamos a incluir un proveedor de servicios JAX-RS en el descriptorde despliegue de nuestra aplicacioacuten (fichero webxml) En la siguiente sesioacuten justificaremos laexistencia de dicho proveedor (que seraacute un servlet) y explicaremos el modelo de desplieguede los servicios JAX-RS Los pasos a seguir desde IntelliJ para configurar el despliegue denuestro servicio son

bull Antildeadimos el directorio WEB-INF como subdirectorio de webapp

bull Nos vamos a FileProject StructurehellipFacetsWeb y antildeadimos el fichero webxml (enel panel Deployment descriptors pulsamos sobre + y seleccionamos webxml) Editamoseste fichero para antildeadir el servlet que serviraacute las peticiones de nuestros servicios RESTindicando cuaacutel seraacute la ruta en la que estaraacuten disponibles dichos servicios (en nuestroejemplo indicaremos la ruta rest) Dicha ruta es relativa a la ruta del contexto de nuestraaplicacioacuten y que por defecto es el nombre del artefacto war desplegado que hemosindicado en la etiqueta ltfinalNamegt dentro del ltbuildgt del fichero de configuracioacuten deMaven (pomxml)

Contenido del fichero webxml

ltweb-app version=30 xmlns=httpjavasuncomxmlnsjavaee xmlnsxsi=httpwwww3org2001XMLSchema-instance xsischemaLocation=httpjavasuncomxmlnsjavaee httpjavasuncomxmlnsjavaeeweb-app_3_0xsdgt lt-- One of the way of activating REST Services is adding these lines the server is responsible for adding the corresponding servlet automatically if the src folder has the Annotations to receive REST invocation--gt ltservlet-mappinggt ltservlet-namegtjavaxwsrscoreApplicationltservlet-namegt lturl-patterngtrestlturl-patterngt ltservlet-mappinggtltweb-appgt

A continuacioacuten ya estamos en disposicioacuten de iniciar la construccioacuten del proyecto con Mavenpara compilar empaquetar y desplegar nuestro servicio en Wildfly

Si utilizamos el terminal la secuencia de pasos para empaquetar y desplegar nuestroproyecto seriacutean

cd ejemplo-rest

Servicios Rest

29

mvn package

usrlocalwildfly-821Finalbinstandalonesh

mvn wildflydeploy

Nos situamos en el directorio que contiene el pomxml de nuestro proyectoEmpaquetamos el proyecto (obtendremos el war)Arrancamos el servidor wildflyDesplegamos el war generado en el servidor wildfly

Secuencia correcta de acciones

En ejecuciones posteriores despueacutes de realizar modificaciones en nuestro coacutedigo esrecomendable ejecutar mvn clean previamente al empaquetado del proyecto

Por lo tanto y suponiendo que el servidor de aplicaciones ya estaacute en marcha lasecuencia de acciones (comandos maven) que deberiacuteamos realizar para asegurarnosde que vamos a ejecutar exactamente la aplicacioacuten con los uacuteltimos cambios quehayamos introducido son

bull mvn wildflyundeploy

bull mvn clean

bull mvn package

bull mvn wildflydeploy

Tambieacuten podriacuteamos realizar todas estas acciones con un uacutenico comando maven

bull mvn wildflyundeploy clean package wildflydeploy

Si utilizamos IntelliJ antildeadiremos un nuevo elemento de configuracioacuten de ejecucioacuten desdeRunEdit Configurations Pulsamos el icono + y antildeadimos la configuracioacuten de tipo JBosssServerLocal Podemos ponerle por ejemplo como nombre Wilfdly start A continuacioacutenconfiguramos la ruta del servidor wildfly como usrlocalwildfly-821Final

Cuando lancemos este elemento de ejecucioacuten desde IntelliJ automaacuteticamente seconstruiraacute el proyecto (obtendremos el war) y arrancaremos wildfly Para desplegarel war utlizaremos la ventana Maven Projects y haremos doble click sobre ejemplo-restPluginswildflywildflydeploy

Probando nuestro servicio

Podemos probar nuestro servicio de varias formas Vamos a mostrar como hacerlodirectamente desde liacutenea de comandos utilizando IntelliJ o bien utilizando la herramientaPostman (que teneacuteis disponible desde el navegador Chrome)

Invocacioacuten del servicio desde liacutenea de comandosUtilizaremos la herramienta curl Por ejemplo para realizar una insercioacuten de un cliente elcomando seriacutea

Servicios Rest

30

curl -i -H Accept applicationxml -H Content-Type applicationxml -X POST -d clientexml httplocalhost8080ejemplo-restrestclientes

En donde

-iTambieacuten se puede utilizar la opcioacuten equivalente --include Indica que se debe incluirlas cabeceras HTTP en la respuesta recibida Recuerda que la peticioacuten POST devuelveen la cabecera Location el enlace del nuevo recurso creado (puede hacerlo en unacabedera Location o como un campo ltlinkgt del elemento creado en el cuerpo delmensaje lo veremos maacutes adelante) Esta informacioacuten seraacute necesaria para poder consultarla informacioacuten del nuevo cliente creado

-HIndica un par cabecera_valor_ En nuestro caso lo utilizamos para especificar los valoresde las cabeceras HTTP Accept y Content-Type

-XIndica el meacutetodo a invocar (GET POST PUThellip)

-dTambieacuten se puede utilizar --data Indica cuaacuteles son los datos enviados en el mensajede entrada en una peticioacuten POST Si los datos especificados van precedidos por estamos indicando que dichos datos estaacuten en un fichero Por ejemplo en la orden anteriorescribimos en el fichero clientexml los datos del cliente que queremos antildeadir en nuestrosistema

El contenido del fichero clientexml podriacutea ser eacuteste

ltxml version=10 encoding=UTF-8gtltclientesgt ltclientegt ltnombregtPepe ltnombregt ltapellidosgtGarcia Lopezltapellido1gt ltdirecciongtCalle del pino 3ltapellido2gt ltcodPostalgt0001ltcodPostalgt ltciudadgtAlicanteltciudadgt ltclientegtltclientesgt

Finalmente en la orden indicamos la URI a la que queremos acceder en este caso

httplocalhost8080ejemplo-restrestclientes

Una vez insertado el cliente podemos recuperar el cliente utilizando el enlace que se incluyeen la cabecera de respuesta Location

curl -i -H Accept applicationxml -H Content-Type applicationxml -X GET httplocalhost8080ejemplo-restrestclientes1

Servicios Rest

31

Invocacioacuten del servicio desde IntelliJIntelliJ nos proporciona una herramienta para probar servicios REST desde ToolsTestRESTful Web Service Desde esta nueva ventana podremos invocar al servicio RESTindicando el tipo de peticioacuten HTTP asiacute como las cabeceras y cuerpo de la peticioacuten

La siguiente figura muestra la elaboracioacuten de una peticioacuten POST a nuestro servicio REST

A continuacioacuten mostramos la ejecucioacuten de una peticioacuten GET

Cuando realizamos una peticioacuten POST debemos indicar el contenido del cuerpo del mensajeEn la siguiente figura observamos que tenemos varias opciones disponibles como por ejemploteclear directamente dicho contenido (opcioacuten Text) o bien subir dicha informacioacuten desdeun fichero en nuestro disco duro (opcioacuten File Contents) Podemos ver que hemos elegido estauacuteltima opcioacuten para probar nuestro servicio

Invocacioacuten del servicio desde PostmanOtra alternativa sencilla para probar nuestro servicio REST es la herramienta postmanque podemos lanzar desde el navegador en nuestro caso Chrome

Accederemos a la aplicacioacuten desde la barra de marcadores seleccionando Aplicaciones yy a continuacioacuten pulsaremos sobre el icono Postman

El aspecto de la herramienta es el que mostramos a continuacioacuten

Servicios Rest

32

Postman a diferencia de las alternativas anteriores nos permitiraacute guardar un historial depeticiones de forma que podamos repetir la ejecucioacuten de nuestros tests exactamente de lamisma forma aunque no de forma automaacutetica sino que tenemos que lanzar manualmentecada test que queramos volver a ejecutar

Tambieacuten podemos crear colecciones que no son maacutes que carpetas que contienen unconjunto de peticiones de nuestro historial Por ejemplo podemos crear la coleccioacuten s1-rest-ejercicio1 en donde guardaremos todas las peticiones que hayamos hecho sobre el ejercicio1 de la primera sesioacuten de rest

Podeacuteis crearos una cuenta gratuita para almacener y gestionar vuestras peticiones rest Unavez que tengaacuteis creadas varias colecciones Postman nos permite guardarlas en nuestrodisco duro en formato json

Servicios Rest

33

15 Ejercicios

Antes de empezar a crear los proyectos debes descargarte el repositorio git java_uaejercicios-rest-expertojava en el que vas a implementar los ejercicios relativos a laasignatura de Servicios REST El proceso es el mismo que el seguido en sesiones anteriores

1 Accedemos al repositorio y realizamos un Fork en nuestra cuenta personal (asiacute podremostener una copia con permisos de escritura)

2 Realizamos un Clone en nuestra maacutequina

$ git clone httpsbitbucketorgltalumnogtejercicios-rest-expertojava

De esta forma se crea en nuestro ordenador el directorio ejercicios-rest-expertojavay se descarga en eacutel un proyecto IntelliJ en el que iremos antildeadiendo MOacuteDULOS para cada unode los ejercicios Contiene tambieacuten el fichero gitignore asiacute como diferentes moacutedulos conlas plantillas que vayamos a necesitar para realizar los ejercicios

A partir de este momento se puede trabajar con dicho proyecto y realizar Commit y Pushcuando sea oportuno

$ cd ejercicios-rest-expertojava$ git add $ git commit -a -m Mensaje de commit$ git push origin master

Los MOacuteDULOS IntelliJ que iremos antildeadiendo tendraacuten todos el prefijo sx- siendo x elnuacutemero de la sesioacuten correspondiente (por ejemplo s1-ejercicio s2-otroEjerciciohellip)

Servicio REST ejemplo (0 puntos)

Para familiarizarnos con las peticiones http POST PUT GET DELETE se proporciona elMOacuteDULO s1-ejemplo-rest con la implementacioacuten de un servicio rest que podeacuteis probar biendesde liacutenea de comandos con la utilidad curl desde el navegador con la herramienta postman o bien desde IntelliJ con el cliente REST incluido en el IDE

En el directorio srcmainresources de dicho moacutedulo teneacuteis un fichero de texto(instruccionestxt) con las instrucciones para construir desplegar y probar la aplicacioacuten deejemplo

Servicio REST saludo (1 punto)

Vamos a implementar un primer servicio RESTful muy sencillo Para ello seguiremos lassiguientes indicaciones

bull Creamos un MOacuteDULO Maven con IntelliJ (desde el directorio ejercicios-rest-expertojava ) con el arquetipo webapp-javaee7 tal y como hemos visto en losapuntes de la sesioacuten Las coordenadas del artefacto Maven seraacuten

GroupId orgexpertojava

ArtifactId s1-saludo-rest

Servicios Rest

34

version 10-SNAPSHOT

bull Configuramos el pommxl del proyecto para poder compilar empaquetar y desplegarnuestro servicio Consulta los apuntes para ver cuaacutel debe ser el contenido de las etiquetasltpropertiesgt ltdependenciesgt y ltbuildgt

bull Creamos la carpeta WEB-INF y antildeadimos el fichero de configuracioacuten webxml tal y comohemos visto en los apuntes (esto seraacute necesario para configurar el despliegue) En estecaso queremos mapear los servicios REST contenidos en el paquete orgexpertojavaal directorio recursos dentro de nuestro contexto (recuerda que el contexto de nuestraaplicacioacuten web vendraacute dado por el valor de la etiqueta ltfinalNamegt anidada dentro deltbuildgt)

bull Creamos un recurso de nombre HolaMundoResource que se mapee a la direccioacuten holamundo Implementar un meacutetodo de forma que al acceder a eacutel por GET nos devuelvaen texto plano (textplain) el mensaje Hola mundo Una vez desplegada la aplicacioacutenen el servidor WildFly prueba el servicio mediante la utilidad Postman desde ChromeComprobar que la invocacioacuten

GET httplocalhost8080saludo-restholamundo

Devuelve como cuerpo del mensaje Hola mundo

bull Vamos a antildeadir un segmento variable a la ruta Implementa un meacutetodo GET nuevode forma que si accedemos a holamundonombre antildeade el nombre indicado al saludo(separado por un espacio en blanco y seguido por )

Una vez desplegada la aplicacioacuten en el servidor WildFly prueba el servicio mediante la utilidadTest RESTFul Web Service de IntelliJ o con Postman Comprobar que la invocacioacuten

GET httplocalhost8080saludo-restholamundopepe

Devuelve como cuerpo del mensaje Hola mundo pepe

bull Hacer que se pueda cambiar el saludo mediante un meacutetodo PUT El nuevo saludo llegaraacutetambieacuten como texto plano en el cuerpo de la peticioacuten y posteriores invocaciones a losmeacutetodos GET utilizaraacuten el nuevo saludo Almacenaremos el nuevo saludo en una variableestaacutetica de nuestro recurso iquestQueacute pasa si no lo es (lo hemos explicado en los apuntespuedes hacer la prueba para ver queacute ocurre si la variable no es estaacutetica)

Una vez desplegada la aplicacioacuten en el servidor WildFly prueba el servicio con Postmano bien mediante la utilidad Test RESTFul Web Service de IntelliJ Realizar las siguientesinvocaciones (en este orden)

PUT httplocalhost8080saludo-restholamundoy en el cuerpo del mensaje Buenos diacuteas

GET httplocalhost8080saludo-restholamundoGET httplocalhost8080saludo-restholamundopepe

Servicios Rest

35

La segunda invocacioacuten debe devolver como cuerpo del mensaje Buenos dias Al ejecutarla tercera invocacioacuten el cuerpo del mensaje de respuesta deberiacutea ser Buenos diasPepe

Servicio REST foro (1 punto)

Vamos a implementar un servicio RESTful que contemple las cuatro operaciones baacutesicas(GET PUT POST y DELETE) Se trata de un foro con en el que los usuarios pueden intervenirde forma anoacutenima en diferentes conversaciones

Primero debes crear un nuevo moacutedulo Maven configurar el pomxml asiacute como el ficherowebxml de la misma forma que hemos hecho en el ejercicio anterior pero para esteejercicio

bull Las coordenadas del moacutedulo Maven seraacuten

GroupId orgexpertojava

ArtifactId s1-foro-rest

version 10-SNAPSHOT

bull Nuestros servicios REST estaraacuten disponibles en la URI httplocalhost8080s1-foro-rest

El foro estaraacute formado por diferentes mensajes Por lo tanto el modelo del dominio de nuestraaplicacioacuten estaraacute formado por la clase Mensaje que contendraacute un identificador y una cadenade caracteres que representaraacute el contenido del mensaje (recuerda que debes implementarlos correspondientes getters y setters)

Por simplicidad vamos a almacenar los mensajes de nuestro foro en memoria Estos estaraacutendisponibles desde la clase DatosEnMemoria que contendraacute la variable estaacutetica

static MapltInteger Mensajegt datos = new HashMapltInteger Mensajegt()

Los servicios que proporcionaraacute el foro estaraacuten implementados en la claseMensajeResource Se accederaacute a ellos traveacutes de la ruta relativa a la raiacutez de nuestrosservicios mensajes Concretamente podremos realizar las siguientes operaciones

bull Antildeadir un nuevo mensaje al foro con la URI relativa a la raiacutez de nuestros servicios mensajes El texto del mensaje estaraacute en el cuerpo de la peticioacuten y el tipo MIME asociadoseraacute textplain (contenido de la cabecera Content-type de la peticioacuten HTTP)Nuestra respuesta debe incluir en la cabecera Location de la respuesta HTTP la URIdel nuevo recurso creado Utiliza para ello la clase Response tal y como hemos mostradoen el coacutedigo de ejemplo proporcionado para el ejercicio anterior Hablaremos con detallesobre esta clase en sesiones posteriores

Recuerda que para acceder al cuerpo de la peticioacuten basta con definir unparaacutemetro de tipo String JAX-RS automaacuteticamente lo instanciaraacute a partirdel cuerpo de la peticioacuten y lo convertiraacute en un objeto de tipo String

bull Modificar un mensaje determinado con un identificador con valor id a traveacutes de la URIrelativa a la raiacutez de nuestros servicios mensajesid ( id debe ser por tanto unsegmento de ruta variable) Si no existe ninguacuten mensaje con el identificador id se lanzaraacutela excepcioacuten WebApplicationException(ResponseStatusNOT_FOUND)

Servicios Rest

36

bull Borrar un mensaje determinado con un identificador con valor id atraveacutes de la URI relativa a la raiacutez de nuestros servicios mensajesid Igual que en el caso anterior si el identificador proporcionado no secorresponde con el de ninguacuten mensaje del foro se lanzaraacute la excepcioacutenWebApplicationException(ResponseStatusNOT_FOUND)

bull Consultar todos los mensajes del foro (la URI relativa seraacute mensajes ) El resultado semostraraacute en tantas liacuteneas como mensajes Cada mensaje iraacute precedido de su identificadorTambieacuten se informaraacute del nuacutemero total de mensajes en el foro (La respuesta seraacute unacadena de caracteres Al final del ejercicio mostramos un ejemplo de mensaje de respuestapara esta operacioacuten)

bull Consultar un mensaje determinado con un identificador con valor id a traveacutes dela URI relativa a la raiacutez de nuestros servicios mensajesid Si el identificadorproporcionado no se corresponde con el de ninguacuten mensaje del foro se lanzaraacute laexcepcioacuten WebApplicationException(ResponseStatusNOT_FOUND)

Prueba el servicio utilizando Postman o el cliente de IntelliJ para servicios REST con lassiguientes entradas

bull Crea los mensajes Mensaje numero 1 Mensaje numero 2 Mensaje numero 3 en esteorden

bull Consulta los mensajes del foro El resultado debe ser

1 Mensaje numero 1

2 Mensaje numero 2

3 Mensaje numero 3

Numero total de mensajes = 3

bull Cambia el mensaje con identificador 2 por Nuevo mensaje numero 2

bull Consulta los mensajes del foro El resultado debe ser

1 Mensaje numero 1

2 Nuevo Mensaje numero 2

3 Mensaje numero 3

Numero total de mensajes = 3

bull Borra el mensaje con identificador 3

bull Consulta el mensaje con el identificador 3 Se debe obtener una respuesta 404 NotFound

bull Consulta los mensajes del foro El resultado debe ser

1 Mensaje numero 1

2 Nuevo Mensaje numero 2

Numero total de mensajes = 2

bull Antildeade el mensaje Mensaje final Vuelve a consultar los mensajes el resultado debe ser

1 Mensaje numero 1

Servicios Rest

37

2 Nuevo Mensaje numero 2

4 Mensaje final

Numero total de mensajes = 3

Para evitar problemas con el id generado si hemos borrado mensajeslo maacutes sencillo es que el identificador vaya incrementaacutendose siemprecon cada nuevo mensaje Esto puede hacer que queden huecos en lanumeracioacuten como en el ejemplo anterior

Servicios Rest

38

2 Anotaciones baacutesicas JAX-RS El modelo de despliegue

Ya hemos visto como crear un servicio REST baacutesico Ahora se trata de analizar con maacutesdetalle aspectos fundamentales sobre la implementacioacuten de los servicios Comenzaremos pordetallar los usos de la anotacioacuten Path que es la que nos permite etiquetar una clase Javacomo un recurso REST sobre el que podremos realizar las operaciones que hemos identificadoen la sesioacuten anterior Tambieacuten hablaremos algo maacutes sobre las anotaciones Produces yConsumes que ya hemos utilizado para implementar nuestro primer servicio

En segundo lugar hablaremos sobre la extraccioacuten de informacioacuten de las peticiones HTTP ycoacutemo podemos inyectar esa informacioacuten en nuestro coacutedigo java Esto nos permitiraacute servir laspeticiones sin tener que escribir demasiado coacutedigo adicional

Finalmente explicaremos maacutes detenidamente coacutemo configurar el despliegue de nuestraaplicacioacuten REST de forma que sea portable

21 iquestCoacutemo funciona el enlazado de meacutetodos HTTP

JAX-RS define cinco anotaciones que se corresponden con operaciones HTTP especiacuteficas

bull javaxwsrsGET

bull javaxwsrsPUT

bull javaxwsrsPOST

bull javaxwsrsDELETE

bull javaxwsrsHEAD

En la sesioacuten anterior ya hemos utilizado estas anotaciones para hacer corresponder (enlazar)peticiones HTTP GET con un meacutetodo Java concreto

Por ejemplo

Path(clientes)public class ServicioCliente

GET Produces(applicationxml) public String getTodosLosClientes()

En este coacutedigo la anotacioacuten GET indica al runtime JAX-RS que el meacutetodo javagetTodosLosClientes() atiende peticiones HTTP GET dirigidas a la URI clientes

Soacutelamente se puede utilizar una de las anotaciones anteriores para unmismo meacutetodo Si se aplica maacutes de uno se produce un error durante eldespliegue de la aplicacioacuten

Es interesante conocer que cada una de estas anotaciones a su vez estaacute anotada con otrasanotaciones (podriacuteamos llamarlas meta anotaciones) Por ejemplo la implementacioacuten de laanotacioacuten GET tiene este aspecto

package javaxwsrsimport

Servicios Rest

39

Target(ElementTypeMETHOD)Retention(RetentionPolicyRUNTIME)HttpMethod(HttpMethodGET)public interface GET

GET en siacute mismo no tiene ninguacuten significado especial para el proveedor JAX-RS (runtimede JAX-RS) Lo que hace que la anotacioacuten GET sea significativo para el runtime de JAX-RSes el valor de la meta anotacioacuten javaxwsrsHttpMethod (en este caso HttpMethodGET )Este valor es el que realmente decide que un determinado meacutetodo Java se enlace con undeterminado meacutetodo HTTP

iquestCuaacuteles son las implicaciones de eacutesto Pues que podemos crear nuevas anotaciones quepodemos enlazar a otros meacutetodos HTTP que no sean GET POST PUT DELETE o HEAD Deesta forma podriacuteamos permitir que diferentes tipos de clientes que hacen uso de la operacioacutenHTTP LOCK puedan ser atendidos por nuestro servicio REST (como por ejemplo un clienteWebDAV )

22 La anotacioacuten Path

La anotacioacuten Path identifica la plantilla de path para la URI del recurso al que se accede yse puede especificar a nivel de clase o a nivel de meacutetodo de dicho recurso

El valor de una anotacioacuten Path es una expresioacuten que denota una URI relativa a la URIbase del servidor en el que se despliega el recurso a la raiz del contexto de la aplicacioacuten yal patroacuten URL al que responde el runtime de JAX-RS

Un segmento de la URI es cada una de las subcadenas delimitadas por que aparecenen dicha URI Por ejemplo la URI httpejemploclientescomclientesviprecientes contiene 4segmentos de ruta ejemploclientescom clientes vip y recientes

La anotacioacuten Path no es necesario que contenga una ruta que empieceo termine con el caraacutecter El runtime de JAX-RS analiza igualmente laexpresioacuten indicada como valor de Path

Para que una clase Java sea identificada como una clase que puede atender peticiones HTTPeacutesta tiene que estar anotada con al menos la expresioacuten Path() Este tipo de clasesse denominan recursos JAX-RS raiacutez

Para recibir una peticioacuten un meacutetodo Java debe tener al menos una anotacioacuten de meacutetodoHTTP como por ejemplo javaxwsrsGET Este meacutetodo no requiere tener ninguna anotacioacutenPath adicional Por ejemplo

Path(pedidos)public class PedidoResource GET public String getTodosLosPedidos()

Una peticioacuten HTTP GET pedidos se delegaraacute en el meacutetodo getTodosLosPedidos()

Servicios Rest

40

Podemos aplicar tambieacuten Path a un meacutetodo Java Si hacemos esto la expresioacuten de laanotacioacuten Path de la clase se concatenaraacute con la expresioacuten de la anotacioacuten Path delmeacutetodo Por ejemplo

Path(pedidos)public class PedidoResource

GET Path(noPagados) public String getPedidosNoPagados()

De esta forma una peticioacuten GET pedidosnoPagados se delegaraacute en el meacutetodogetPedidosNoPagados()

Podemos tener anotaciones Path para cada meacutetodo que seraacuten relativos a la ruta indicadaen la anotacioacuten Path de la definicioacuten de la clase Por ejemplo la siguiente clase de recursosirve peticiones a la URI pedidos

Path(pedidos)public class PedidoResource

GET public String getPedidos()

Si quisieacuteramos proporcionar el servicio en la URI pedidosincidencias por ejemplono necesitamos una nueva definicioacuten de clase y podriacuteamos anotar un nuevo meacutetodogetIncidenciasPedidos() de la siguiente forma

Path(pedidos)public class PedidoResource

GET public String getPedidos()

GET Path(incidencias) public String getIncidenciasPedidos()

Ahora tenemos una clase de recurso que gestiona peticiones para pedidos y para pedidosincidencias

Expresiones Path

El valor de una anotacioacuten Path puede ser una cadena de caracteres o tambieacutenpuede contener expresiones maacutes complejas si es necesario nos referiremos a ellas comoexpresiones Path

Servicios Rest

41

Una expresioacuten Path puede incluir variables que se indican entre llaves que seraacuten sustituidasen tiempo de ejecucioacuten dependiendo del valor que se indique en la llamada al recurso Asiacutepor ejemplo si tenemos la siguiente anotacioacuten

GETPath(clientesid)

y el usuario realiza la llamada

GET httporgexpertojavacontextorestclientesPedro

la peticioacuten se delegaraacute en el meacutetodo que esteacute anotado con las anotaciones anteriores y el valorde id seraacute instanciado en tiempo de ejecucioacuten a Pedro

Para obtener el valor del nombre del cliente utilizaremos la anotacioacuten PathParam en losparaacutemetros del meacutetodo de la siguiente forma

GETPath(clientesnombre)public String getClientePorNombre(PathParam(nombre) String nombre)

Una expresioacuten Path puede tener maacutes de una variable cada una figuraraacute entre llaves Porejemplo si utilizamos la siguiente expresioacuten Path

Path(nombre1nombre2)public class MiResource

podremos atender peticiones dirigidas a URIs que respondan a la plantilla

httporgexpertojavacontextorecursosnombre1nombre2

como por ejemplo

httporgexpertojavacontextorecursosPedroLopez

Las expresiones Path pueden incluir maacutes de una variable para referenciar un segmento deruta Por ejemplo

Path()public class ClienteResource GET Path(clientesapellido1-apellido2) public String getCliente(PathParam(apellido1) String ape1 PathParam(apellido2) String ape2)

Servicios Rest

42

Una peticioacuten del tipo

GET httporgexpertojavacontextoclientesPedro-Lopez

seraacute procesada por el meacutetodo getCliente()

Expresiones regulares

Las anotaciones Path pueden contener expresiones regulares (asociadas a las variables)Por ejemplo si nuestro meacutetodo getClienteId() tiene un paraacutemetro de tipo entero podemosrestringir las peticiones para tratar solamente aquellas URIs que contengan diacutegitos en elsegmento de ruta que nos interese

Path(clientes)public class ClienteResource GET Path(id d+) solo soporta diacutegitos public String getClienteId(PathParam(id) int id)

Si la URI de la peticioacuten de entrada no satisface ninguna expresioacuten regular de ninguno de losmetodos del recurso entonces se devolveraacute el coacutedigo de error 404 Not Found

El formato para especificar expresiones regulares para las variables del path es

nombre-variable [ expresion-regular ]

El uso de expresiones regulares es opcional Si no se proporciona una expresioacuten regularpor defecto se admite cualquier caraacutecter En teacuterminos de una expresioacuten regular la expresioacutenregular por defecto seriacutea

[^]+

Por ejemplo si queremos aceptar solamente nombres que comiencen por una letra y acontinuacioacuten puedan contener una letra o un diacutegito lo expresariacuteamos como

Path(clientes)public class ClienteResource GET Path(nombre [a-zA-Z][a-zA-Z_0-9]) public String getClienteNombre(PathParam(nombre) string nom)

Servicios Rest

43

De esta forma la URI clientesaaa no seriacutea vaacutelida la URI clientesa9 activariacutea elmeacutetodo getClienteNombre() y la URI clientes89 activariacutea el meacutetodo getClienteId()

Las expresiones regulares no se limitan a un soacutelo segmento de la URI Por ejemplo

Path(clientes)public class ClienteResource GET Path(id +) public String getCliente(PathParam(id) String id)

GET Path(id +direccion) public String getDireccion(PathParam(id) String id)

La expresioacuten regular + indica que estaacuten permitidos cualquier nuacutemero de caracteres Asiacutepor ejemplo la peticioacuten GET clientespedrolopez podriacutea delegarse en el meacutetodogetClientes()

El meacutetodo getDireccion() tiene asociada una expresioacuten maacutes especiacutefica la cual puedemapearse con cualquier cadena de caracteres que termine con direccion Seguacuten eacutestola peticioacuten GET clientespedrolopezdireccion podriacutea delegarse en el meacutetodogetDireccion()

Reglas de precedencia

En el ejemplo anterior acabamos de ver que las expresiones Path para getCliente() ygetDireccion() son ambiguas Una peticioacuten GET clientespedrolopezdireccionpodriacutea mapearse con cualquiera de los dos meacutetodos La especificacioacuten JAX-RS define lassiguientes reglas para priorizar el mapeado de expresiones regulares

bull El primer criterio para ordenar las acciones de mapeado es el nuacutemero de caracteres literalesque contiene la expresioacuten Path teniendo prioridad aquellas con un mayor nuacutemero decaracteres literales El patroacuten de la URI para el meacutetodo getCliente() tiene 10 caraacutecteresliterales clientes El patroacuten para el meacutetodo getDireccion() tiene 19 clientesdireccion Por lo tanto se elegiriacutea primero el meacutetodo getDireccion()

bull El segundo criterio es el nuacutemero de variables en expresiones Path (por ejemplo id oid +) Teniendo precedencia las patrones con un mayor nuacutemero de variables

bull El tercer criterio es el nuacutemero de variables que tienen asociadas expresiones regulares(tambieacuten en orden descendente)

A continuacioacuten mostramos una lista de expresiones Path ordenadas en orden descendentede prioridad

1 clientesidnombredireccion

2 clientesid +direccion

3 clientesiddireccion

4 clientesid +

Servicios Rest

44

Las expresiones 13 se analizariacutean primero ya que tienen maacutes caracteres literales que laexpresioacuten nuacutemero 4 Si bien las expresiones 13 tienen el mismo nuacutemero de caracteresliterales La expresioacuten 1 se analizariacutea antes que las otras dos debido a la segunda regla (tienemaacutes variables) Las expresiones 2 y 3 tienen el mismo nuacutemero de caracteres literales y elmismo nuacutemero de variables pero la expresioacuten 2 tiene una variable con una expresioacuten regularasociada

Estas reglas de ordenacioacuten no son perfectas Es posible que siga habiendo ambiguumledadespero cubren el 90 de los casos Si el disentildeo de nuestra aplicacioacuten presenta ambiguumledadesaplicando estas reglas es bastante probable que hayamos complicado dicho disentildeo y seriacuteaconveniente revisarlo y refactorizar nuestro esquema de URIs

Paraacutemetros matrix (Matrix parameters)

Los paraacutemetros matrix con pares nombre-valor incluidos como parte de la URI Aparecen alfinal de un segmento de la URI (segmento de ruta) y estaacuten delimitados por el caraacutecter Por ejemplo

httpejemplocochescomseatibizacolor=black2006

En la ruta anterior el paraacutemetro matrix aparece despueacutes del segmento de ruta ibiza Su nombrees color y el valor asociado es black

Un paraacutemetro matrix es diferente de lo que denominamos paraacutemetro de consulta (queryparameter) ya que los paraacutemetros matrix representan atributos de ciertos segmentos de laURI y se utilizan para propoacutesitos de identificacioacuten Pensemos en ellos como adjetivos Losparaacutemetros de consulta por otro lado siempre aparecen al final de la URI y siemprepertenecen al recurso completo que estemos referenciando

Los paraacutemetros matrix son ignorados cuando el runtime de JAX-RS realiza el matching de laspeticiones de entrada a meacutetodos de recursos REST De hecho es ilegal incluir paraacutemetrosmatrix en las expresiones Path Por ejemplo

Path(seat)public class SeatService

GET Path(ibizaanyo) Produces(imagejpeg) public Response getIbizaImagen(PathParam(anyo) String anyo)

Si la peticioacuten de entrada es GET seatibizacolor=black2009 el meacutetodo getIbizaImagen()seriacutea elegido por el proveedor de JAX-RS para servir la peticioacuten de entrada y seriacutea invocadoLos paraacutemetros matrix NO se consideran parte del proceso de matching debido a quenormalmente son atributos variables de la peticioacuten

Subrecursos (Subresource Locators)

Acabamos de ver la capacidad de JAX-RS para hacer corresponder de forma estaacuteticaa traveacutes de la anotacioacuten Path URIs especificadas en la entrada de la peticioacuten conmeacutetodos Java especiacuteficos JAX-RS tambieacuten nos permitiraacute de forma dinaacutemica servir nosotros

Servicios Rest

45

mismos las peticiones a traveacutes de los denominados subresource locators (localizadores desubrecursos)

Los subresource locators son meacutetodos Java anotados con Path pero sin anotacionesGET PUT hellip Este tipo de meacutetodos devuelven un objeto que es en siacute mismo un servicioJAX-RS que sabe coacutemo servir el resto de la peticioacuten Vamos a describir mejor este conceptocon un ejemplo

Supongamos que queremos extender nuestro servicio que proporciona informacioacuten sobre losclientes Disponemos de diferentes bases de datos de clientes seguacuten regiones geograacuteficasQueremos antildeadir esta informacioacuten en nuestro esquema de URIs pero desacoplando labuacutesqueda del servidor de base de datos de la consulta particular de un cliente en concretoAntildeadiremos la informacioacuten de la zona geograacutefica en la siguiente expresioacuten Path

clienteszona-dbclienteId

A continuacioacuten definimos la clase ZonasClienteResource que delegaraacute en la claseClienteResource que ya teniacuteamos definida

Path(clientes)public class ZonasClienteResource

Path(zona-db) public ClienteResource getBaseDeDatos(PathParam(zona) String db) devuelve una instancia dependiendo del paraacutemetro db ClienteResource resource = localizaClienteResource(db) return resource

protected ClienteResource localizaClienteResource(String db)

La clase ZonasClienteResource es nuestro recurso raiacutez Dicha clase no atiende ningunapeticioacuten HTTP directamente Nuestro recurso raiacutez procesa el segmento de URI que hacereferencia a la base de datos en donde buscar a nuestro cliente y devuelve una instancia dedicha base de datos (o maacutes propiamente dicho del objeto con en que accederemos a dichabase de datos) El proveedor de JAX-RS utiliza dicha instancia para servir el resto de lapeticioacuten

public class ClienteResource private MapltInteger Clientegt clienteDB = new ConcurrentHashMapltInteger Clientegt() private AtomicInteger idContador = new AtomicInteger()

public ClienteResource(MapltInteger Clientegt clienteDB) thisclienteDB = clienteDB

POST Consumes(applicationxml)

Servicios Rest

46

public Response crearCliente(InputStream is)

GET Path(id) Produces(applicationxml) public Cliente recuperarClienteId(PathParam(id) int id)

PUT Path(id) Consumes(applicationxml) public void modificarCliente(PathParam(id) int id Cliente cli)

Si un usuario enviacutea la peticioacuten GET clientesnorteamerica-db333 el proveedorJAX-RS primero realizaraacute un matching de la expresioacuten sobre el meacutetodoZonasClienteResourcegetBaseDeDatos() A continuacioacuten procesaraacute el resto de la peticioacuten(333) a traveacutes del meacutetodo ClienteResourcerecuperarClienteId()

Podemos observar que la nueva clase ClienteResource ademaacutes de tener un nuevoconstructor ya no estaacute anotada con Path Esto implica que ya no es un recurso de nuestrosistema es un subrecurso y no debe ser registrada en el runtime de JAX-RS a traveacutes de laclase Application (como veremos maacutes adelante)

Veamos otro ejemplo Supongamos que tenemos un conjunto de alumnos del que podemosobtener el listado completo de alumnos y antildeadir nuevos alumnos pero ademaacutes queremos quecada alumno individual pueda consultarse modificarse o borrarse Una forma sencilla de trataresto es dividir el coacutedigo en un recurso (lista de alumnos) y un subrecurso (alumno individual)de la siguiente forma

Path(alumnos)public class AlumnosResource

Context UriInfo uriInfo

GET Produces(MediaTypeAPPLICATION_XML MediaTypeAPPLICATION_JSON) public ListltAlumnoBeangt getAlumnos() return FactoriaDaosgetAlumnoDao()getAlumnos()

POST Consumes(MediaTypeAPPLICATION_JSON) public void addAlumno(AlumnoBean alumno) throws IOException String dni = FactoriaDaosgetAlumnoDao()addAlumno(alumno) URI uri = uriInfogetAbsolutePathBuilder()path(dni)build(dni) Responsecreated(uri)build()

Path(alumno) public AlumnoResource getAlumno( PathParam(alumno) String dni)

Servicios Rest

47

return new AlumnoResource(uriInfo dni)

Vemos que en este recurso inyectamos informacioacuten sobre la URI solicitada como variable deinstancia (utilizando la anotacioacuten Context de la que hablaremos maacutes adelante) Para elconjunto de alumnos ofrecemos dos operaciones obtener la lista de alumnos y antildeadir unnuevo alumno a la lista la cual devuelve como respuesta la URI que nos da acceso al recursoque acabamos de antildeadir

Sin embargo lo maacutes destacable es el uacuteltimo meacutetodo Eacuteste se ejecutaraacute cuando antildeadamos a laruta el identificador de un alumno (por ejemplo alumnos15 ) En este caso lo que hace esdevolver un subrecurso (AlumnoResource) para asiacute tratar un alumno individual (destacamosque el nombre estaacute en singular para distinguirlo del recurso anterior que representa elconjunto)

Cuando hacemos esto estamos delegando en el nuevo Recurso para tratar la peticioacuten

public class AlumnoResource

UriInfo uriInfo

String dni

public AlumnoResource(UriInfo uriInfo String dni) thisuriInfo = uriInfo thisdni = dni

GET Produces(MediaTypeAPPLICATION_XMLMediaTypeAPPLICATION_JSON) public AlumnoBean getAlumno() AlumnoBean alumno = FactoriaDaosgetAlumnoDao()getAlumno(dni) if(alumno==null) throw new WebApplicationException(StatusNOT_FOUND) return alumno

PUT Consumes(MediaTypeAPPLICATION_XML) public Response setAlumno(AlumnoBean alumno) El DNI del alumno debe coincidir con el de la URI alumnosetDni(dni)

if(FactoriaDaosgetAlumnoDao()getAlumno(dni) = null) FactoriaDaosgetAlumnoDao()updateAlumno(alumno) return ResponsenoContent()build() else FactoriaDaosgetAlumnoDao()addAlumno(alumno) return Responsecreated(uriInfogetAbsolutePath())build()

Servicios Rest

48

DELETE public void deleteAlumno() FactoriaDaosgetAlumnoDao()deleteAlumno(dni)

Este recurso ya no es un recurso raiacutez mapeado a una ruta determinada (podemos ver que laclase no lleva la anotacioacuten Path) sino que es creado desde otro recurso Es por lo tantoun subrecurso

Como ya hemos visto los subrecursos nos permiten simplificar la forma de trabajar conconjuntos de recursos definiendo en un uacutenico meacutetodo la ruta de acceso a un recurso individualen lugar de tenerlo que hacer de forma independiente para cada operacioacuten

Ademaacutes este disentildeo modular de los recursos nos va a permitir reutilizar determinadosrecursos dentro de otros Por ejemplo dentro del recurso de un alumno podriacuteamos ver la listade asignaturas en las que se ha matriculado y reutilizar el subrecurso encargado de acceder alas asignaturas para poder acceder a sus datos a partir del recurso del alumno No deberemosabusar de esta caracteriacutestica ya que si creamos relaciones ciacuteclicas perdemos la caracteriacutesticadeseable de los servicios REST de que cada recurso estaacute asignado a una uacutenica URI

En un subrecurso NO podemos inyectar objetos de contexto mediantela anotacioacuten Context ya que no estamos en un recurso raiacutez Porese motivo en el ejemplo hemos proporcionado el objeto UriInfo enel constructor del subrecurso De forma alternativa tambieacuten podriacuteamoshaber inyectado este objeto como paraacutemetro de sus meacutetodos en ese casosi que habriacutea sido posible la inyeccioacuten

Caraacutecter dinaacutemico del dispatching de peticiones

En los ejemplos anteriores hemos ilustrado el concepto de subresource locator aunque nohemos mostrado completamente su caraacutecter dinaacutemico Asiacute si volvemos al primero de ellosel meacutetodo ZonasClienteResourcegetBaseDeDatos() puede devolver cualquier instancia decualquier clase En tiempo de ejecucioacuten el proveedor JAX-RS buscaraacute el interior de estainstancia meacutetodos de recurso que puedan gestionar la peticioacuten

Supongamos que tenemos dos bases de datos de clientes con diferentes tipos deidentificadores Una de ellas utiliza una clave numeacuterica La otra utiliza una clave formada porel nombre y apellidos Necesitamos tener dos clases diferentes para extraer la informacioacutenadecuada de la URI de la peticioacuten Cambiaremos la implementacioacuten de la siguiente forma

Path(clientes)public class ZonasClienteResourceResource protected ClienteResource europa = new ClienteResource() protected OtraClaveClienteResource norteamerica = new OtraClaveClienteResource()

Path(zona-db) public Object getBaseDeDatos(PathParam(zona) String db) if (dbequals(europa)) return europa else if (dbequals(norteamerica)) return northamerica

Servicios Rest

49

else return null

En lugar de devolver una instancia de ClienteResource el meacutetodo getBaseDeDatos() devuelveuna instancia de javalangObject JAX-RS analizaraacute la instancia devuelta para ver coacutemoprocesar el resto de la peticioacuten

Ahora si un usuario enviacutea la peticioacuten GET clienteseuropa-db333 se utilizaraacute la claseClienteResource para servir el resto de la peticioacuten Si la peticioacuten es GET clientesnorteamerica-dbjohn-smith utilizaremos el nuevo subrecurso OtraClaveClienteResource

public class OtraClaveClienteResource private MapltString Clientegt clienteDB = new ConcurrentHashMapltString Clientegt() GET Path(nombre-apellidos) Produces(applicationxml) public Cliente getCliente(PathParam(nombre) String nombre PathParam(apellidos) String apelllidos)

PUT Path(nombre-apellidos) Consumes(applicationxml) public void actualizaCliente()PathParam(nombre) String nombre PathParam(apellidos) String apelllidos Cliente cli)

23 Usos de las anotaciones Produces y Consumes

La informacioacuten enviada a un recurso y posteriormente devuelta al cliente que realizoacute la peticioacutense especifica con la cabecera HTTP Media-Type tanto en la peticioacuten como en la respuestaComo ya hemos visto podemos especificar que representaciones de los recursos (valor deMedia_Type) son capaces de aceptar yo producir nuestros servicios mediante las siguientesanotaciones

bull javaxwsrsConsumes

bull javaxwsrsProduces

La ausencia de dichas anotaciones es equivalente a incluirlas con el valor de media type es decir su ausencia implica que se soporta (acepta) cualquier tipo de representacioacuten

Anotacioacuten Consumes

Esta anotacioacuten funciona conjuntamente con POST y PUT Le indica al framework (libreriacuteasJAX-RS) a queacute meacutetodo se debe delegar la peticioacuten de entrada Especiacuteficamente el clientefija la cabecera HTTP Content-Type y el framework delega la peticioacuten al correspondientemeacutetodo capaz de manejar dicho contenido Un ejemplo de anotacioacuten con PUT es lasiguiente

Servicios Rest

50

Path(pedidos)public class PedidoResource PUT Consumes(applicationxml) public void modificarPedido(Pedido representation)

Si Consumes se aplica a la clase por defecto los meacutetodos correspondientes aceptan lostipos especificados de tipo MIME Si se aplica a nivel de meacutetodo se ignora cualquier anotacioacutenConsumes a nivel de clase para dicho meacutetodo

En este ejemplo le estamos indicando al framework que el meacutetodo modificarPedido() aceptaun recurso cuya representacioacuten (tipo MIME) es applicationxml (y que se almacenaraacute en lavariable representation hablaremos de ello en la siguiente sesioacuten) Por lo tanto un clienteque se conecte al servicio web a traveacutes de la URI pedidos debe enviar una peticioacuten HTTPPUT conteniendo el valor de applicationxml como tipo MIME de la cabecera HTTPContent-Type y el cuerpo (body) del mensaje HTTP debe ser por tanto un documentoxml vaacutelido

Si no hay meacutetodos de recurso que puedan responder al tipo MIME solicitado (tipo MIMEespecificado en la anotacioacuten Consumes del servicio) se le devolveraacute al cliente un coacutedigoHTTP 415 (Unsupported Media Type) Si el meacutetodo que consume la representacioacutenindicada como tipo MIME no devuelve ninguna representacioacuten se enviaraacute un el coacutedigo HTTP204 (No content) A continuacioacuten mostramos un ejemplo en el que sucede eacutesto

POSTConsumes(applicationxml)public void creaPedido(Pedido pedido) Crea y almacena un nuevo _Pedido_

Podemos ver que el meacutetodo consume una representacioacuten en texto plano pero devuelvevoid es decir no devuelve ninguna representacioacuten En este caso se enviacutea el coacutedigo de estadoHTTP 204 No content en la respuesta

Un recurso puede aceptar diferentes tipos de entradas Asiacute podemos utilizar la anotacioacutenPUT con maacutes de un meacutetodo para gestionar las repuestas con tipos MIME diferentes Porejemplo podriacuteamos tener un meacutetodo para aceptar estructuras XML y otro para aceptarestructuras JSON

Path(pedidos)public class PedidoResource PUT Consumes(applicationxml) public void modificarPedidoXML(InputStream pedido) PUT Consumes(applicationjson) public void modificarPedidoJson(InputStream pedido)

Servicios Rest

51

Anotacioacuten Produces

Esta anotacioacuten funciona conjuntamente con GET POST y PUT Indica al frameworkqueacute tipo de representacioacuten se enviacutea de vuelta al cliente

De forma maacutes especiacutefica el cliente enviacutea una peticioacuten HTTP junto con una cabecera HTTPAccept que se mapea directamente con el Content-Type que el meacutetodo produce Por lo tantosi el valor de la cabecera Accept HTTP es applicationxml el meacutetodo que gestiona la peticioacutendevuelve un stream de tipo MIME applicationxml Esta anotacioacuten tambieacuten puede utilizarse enmaacutes de un meacutetodo en la misma clase de recurso Un ejemplo que devuelve representacionesXML y JSON seriacutea el siguiente

Path(pedidos)public class PedidoResource

GET Produces(applicationxml) public String getPedidoXml()

GET Produces(applicationjson) public String getPedidoJson()

Si un cliente solicita una peticioacuten a una URI con un tipo MIME no soportadopor el recurso el framework JAX-RS lanza la excepcioacuten adecuadaconcretamente el runtime de JAX-RS enviacutea de vuelta un error HTTP 406Not acceptable

Se puede declarar maacutes de un tipo en la misma declaracioacuten Produces como por ejemplo

Produces(applicationxml applicationjson)public String getPedidosXmlOJson()

El meacutetodo getPedidosXmlOJson() seraacute invocado si cualquiera de los dos tipos MIMEespecificados en la anotacioacuten Produces son aceptables (la cabecera Accept de la peticioacutenHTTP indica queacute representacioacuten es aceptable) Si ambas representaciones son igualmenteaceptables se elegiraacute la primera

En lugar de especificar los tipos MIME como cadenas detexto en Consumes y Produces podemos utilizar lasconstantes definidas en la clase javaxwsrscoreMediaTypecomo por ejemplo MediaTypeAPPLICATION_XML oMediaTypeAPPLICATION_JSON en lugar de applicationxml yapplicationjson

24 Inyeccioacuten de paraacutemetros JAX-RS

Buena parte del trabajo de JAX-RS es el extraer informacioacuten de una peticioacuten HTTP einyectarla en un meacutetodo Java Podemos estar interesados en un fragmento de la URI de

Servicios Rest

52

entrada en los paraacutemetros de peticioacutenhellip El cliente tambieacuten podriacutea enviar informacioacuten en lascabeceras de la peticioacuten A continuacioacuten indicamos una lista con algunas de las anotacionesque podemos utilizar para inyectar informacioacuten de las peticiones HTTP

bull javaxwsrsPathParam

bull javaxwsrsMatrixParam

bull javaxwsrsQueryParam

bull javaxwsrsFormParam

bull javaxwsrsHeaderParam

bull javaxwsrsContext

bull javaxwsrsBeanParam

Habitualmente estas anotaciones se utilizan en los paraacutemetros de un meacutetodo de recurso JAX-RX Cuando el proveedor de JAX-RS recibe una peticioacuten HTTP busca un meacutetodo Java quepueda servir dicha peticioacuten Si el meacutetodo Java tiene paraacutemetros anotados con alguna de estasanotaciones extraeraacute la informacioacuten de la peticioacuten HTTP y la pasaraacute como un paraacutemetrocuando se invoque el meacutetodo

javaxwsrsPathParam

Ya la hemos utilizado en la sesioacuten anterior PathParam nos permite inyectar el valor de losparaacutemetros de la URI definidos en expresiones Path Recordemos el ejemplo

Path(clientes)public class ClienteResource

GET Path(id) Produces(applicationxml) public Cliente recuperarClienteId(PathParam(id) int id)

Podemos referenciar maacutes de un paraacutemetro en el path de la URI en nuestros meacutetodo javaPor ejemplo supongamos que estamos utilizando el nombre y apellidos para identificar a uncliente en nuestra clase de recurso

Path(clientes)public class ClienteResource

GET Path(nombre-apellidos) Produces(applicationxml) public Cliente recuperarClienteId(PathParam(nombre) String nom PathParam(apellidos) String ape)

Servicios Rest

53

En ocasiones un parameacutetro de path de la URI puede repetirse en diferentes expresionesPath que conforman el patroacuten de matching completo para un meacutetodo de un recurso (porejemplo puede repetirse en la expresioacuten Path de la clase y de un meacutetodo) En estos casos laanotacioacuten PathParam siempre referencia el paraacutemetro path final Asiacute en el siguiente coacutedigo

Path(clientesid)public class ClienteResource

GET Path(direccionid) Produces(textplain) public String getDireccion(PathParam(id) String direccionId)

Si nuestra peticioacuten HTTP es GET clientes123direccion456 el paraacutemetro direccionIddel meacutetodo getDireccion() tendriacutea el valor inyectado de 456

Interfaz UriInfo

Podemos disponer ademaacutes de un API maacutes general para consultar y extraer informacioacuten sobrelas peticiones URI de entrada Se trata de la interfaz javaxwsrscoreUriInfo

public interface UriInfo public javanetURI getAbsolutePath() public UriBuilder getAbsolutePathBuilder()

public javanetURI getBaseUri() public UriBuilder getBaseUriBuilder()

public String getPath() public ListltPathSegmentgt getPathSegments() public MultivaluedMapltString Stringgt getPathParameters()

Los meacutetodos getAbsolutePathBuilder() y getAbsolutePath() devuelven la ruta absoluta dela peticioacuten HTTP en forma de UriBuilder y URI respectivamente

Los meacutetodos getBaseUri() y getBaseUriBuilder() devuelven la ruta base de la aplicacioacuten(ruta raiz de nuestros servicios rest) en forma de UriBuilder y URI respectivamente

El meacutetodo UriInfogetPath() permite obtener la ruta relativa de nuestros servicios RESTutilizada para realizar el matching con nuestra peticioacuten de entrada (es la ruta de la peticioacutenactual relativa a la ruta base de la peticioacuten rest)

El meacutetodo UriInfogetPathSegments() divide la ruta relativa de nuestro servicio REST enuna serie de objetos PathSegment (segmentos de ruta delimitados por )

El meacutetodo UriInfogetPathParameters() devuelve un objeto de tipo MultivaluedMap con todoslos paraacutemetros del path definidos en todas las expresiones Path de nuestra peticioacuten rest

Servicios Rest

54

Por ejemplo si la ruta de nuestra petcioacuten http es httplocalhost8080contextorestclientes2(siendo contexto la ruta raiacutez del war desplegado y rest la ruta de servicio de jax-rs)

bull la ruta absoluta (meacutetodo getAbsolutePath()) seriacutea httplocalhost8080contextorestclientes2

bull la ruta base (meacutetodo getBaseUri) seriacutea httplocalhost8080contextorest

bull la ruta relativa a la ruta base (meacutetodo getPath()) seriacutea clientes2

bull el nuacutemero de segmentos de la peticioacuten rest (meacutetodo getPathSegments()) seriacutean 2 clientesy 2

Podemos inyectar una instancia de la interfaz UriInfo utilizando la anotacioacutenjavaxwsrscoreContext A continuacioacuten mostramos un ejemplo

Path(cochesmarca)public class CarResource

GET Path(modeloanyo) Produces(imagejpeg) public Response getImagen(Context UriInfo info) String fabricado = infogetPathParameters()getFirst(marca) PathSegment modelo = infogetPathSegments()get(2) String color = modelogetMatrixParameteres()getFirst(color)

En este ejemplo inyectamos una instancia de UriInfo como paraacutemetro del meacutetodo getImagen()A continuacioacuten hacemos uso de dicha instancia para extraer informacioacuten de la URI

Recuerda que tambieacuten podriacuteamos inyectar una instancia de UriInfo en unavariable de instancia de la clase raiacutez de nuestro recurso

El meacutetodo CarResourcegetImagen() utiliza la interfazjavaxwsrscorePathSegment que como ya hemos indicado representa unsegmento de ruta

package javaxwsrscorepublic interface PathSegment String getPath() MultivaluedMapltString Stringgt getMatrixParameters()

El meacutetodo PathSegmentgetPath() devuelve el valor de la cadena de caracteres del segmentode ruta actual sin considerar niguacuten paraacutemetro matrix que pudiese contener

El meacutetodo PathSegmentgetMatrixParameters() devuelve un mapa con todos losparaacutemetros matrix aplicados a un segmento de ruta

Supongamos que realizamos la siguiente peticioacuten http para el coacutedigo anterior (claseCarResource)

Servicios Rest

55

GET cochesseatleoncolor=rojo2015

Esta peticioacuten es delegada en el meacutetodo ClarResourcegetImagen() La ruta contiene 4segmentos coches seat leon y 2015 La variable _modelo tomaraacute el valor leon y la variablecolor se instanciaraacute con el valor rojo

javaxwsrsMatrixParam

La especificacioacuten JAX-RS nos permite inyectar una matriz de valores de paraacutemetros a traveacutesde la anotacioacuten javaxwsrsMatrixParam

Path(cochesmarca)public class CarResource

GET Path(modeloanyo) Produces(imagejpeg) public Response getImagen(PathParam(marca) String marca PathParam(modelo) String modelo MatrixParam(color) String color)

El uso de la anotacioacuten MatrixParam simplifica nuestro coacutedigo y lo hace algo maacutes legibleSi por ejemplo la peticioacuten de entrada es

GET cochesseatibizacolor=black2009

entonces el paraacutemetro color del meacutetodo CarResourcegetImagen() tomariacutea el valor black

javaxwsrsQueryParam

La anotacioacuten javaxwsrsQueryParam nos permite inyectar paraacutemetros de consulta(query parameters) de la URI en los valores de los paraacutemetros de los meacutetodos java denuestros recursos Por ejemplo supongamos que queremos consultar informacioacuten de nuestrosclientes y queremos recuperar un subconjunto de clientes de nuestra base de datos NuestraURI de peticioacuten podriacutea ser algo asiacute

GET clientesinicio=0amptotal=10

El paraacutemetro de consulta inicio representa el iacutendice (o posicioacuten) del primer cliente quequeremos consultar y el paraacutemetro total representa cuaacutentos clientes en total queremosobtener como respuesta Una implementacioacuten del servicio RESTful podriacutea contener elsiguiente coacutedigo

Path(clientes)public class ClienteResource GET Produces(applicationxml) public String getClientes(QueryParam(inicio) int inicio

Servicios Rest

56

QueryParam(total) int total)

En este ejemplo el paraacutemetro inicio tomariacutea el valor 0 y el paraacutemetro total tomariacutea elvalor 10 (JAX-RS convierte automaacuteticamente las cadenas de caracteres de los paraacutemetrosde consulta en enteros)

javaxwsrsFormParam

La anotacioacuten javaxwsrsFormParam se utiliza para acceder al cuerpo del mensajede la peticioacuten HTTP de entrada cuyo valor de Content-Type es applicationx-www-form-urlencoded Es decir se utiliza para acceder a entradas individuales de un formulario HTMLPor ejemplo supongamos que para registrar a nuevos clientes en el sistema tenemos querellenar el siguiente formulario

ltFORM action=httpejemplocomclientes method=postgt ltPgt Nombre ltINPUT type=text name=nombregtltBRgt Apellido ltINPUT type=text name=apellidogtltBRgt ltINPUT type=submit value=Sendgt ltPgtltFORMgt

La ejecucioacuten de este coacutedigo inyectaraacute los valores del formulario como paraacutemetros de nuestromeacutetodo Java que representa el servicio de la siguiente forma

Path(clientes)public class ClienteResource POST public void crearCliente(FormParam(nombre) String nom FormParam(apellido) String ape)

Aquiacute estamos inyectando los valores de nombre y apellidos del formulario HTML en losparaacutemetors nom y ape del meacutetodo java crearCliente() Los datos del formulario viajan atraveacutes de la red codificados como URL-encoded Cuando se utiliza la anotacioacuten FormParamJAX-RS decodifica de forma automaacutetica las entradas del fomulario antes de inyectar susvalores

Asiacute por ejemplo si tecleamos los valores Maria Luisa y_Perlado_ como valores en loscampos de texto nombre y apellido del formulario el cuerpo de nuestro mensaje HTTP seraacutenombre=Maria20Luisaapellido=Perlado Este mensaje seraacute recibido por nuestro meacutetodoque extraeraacute los valores correspondientes y los instanciaraacute en los paraacutemetros nom y ape delmeacutetodo _ClienteResourcecrearCliente()

javaxwsrsHeaderParam

La anotacioacuten javaxwsrsHeaderParam se utiliza para inyectar valores de lascabeceras de las peticiones HTTP Por ejemplo si estamos interesados en la paacutegina web quenos ha referenciado o enlazado con nuestro servicio web podriacuteamos acceder a la cabeceraHTTP Referer utilizando la anotacioacuten HeaderParam de la siguiente forma

Servicios Rest

57

Path(miservicio)public class MiServicio GET Produces(texthtml) public String get(HeaderParam(Referer) String referer)

De forma alternativa podemos acceder de forma programativa a todas las cabeceras de lapeticioacuten de entrada utilizando la interfaz javaxwsrscoreHttpHeaders

public interface HttpHeaders public ListltStringgt getRequestHeader(String name) public MultivaluedMapltString Stringgt getRequestHeaders()

El meacutetodo getRequestHeader() permite acceder a una cabecera en concreto y el meacutetodogetRequestHeaders() nos proporciona un objeto de tipo Map que representa todas lascabeceras A continuacioacuten mostramos un ejemplo que accede a todas las cabeceras de lapeticioacuten HTTP de entrada

Path(miservicio)public class MiServicio GET Produces(texthtml) public String get(Context HttpHeaders cabeceras) String referer = headersgetRequestHeader(Referer)get(0) for (String header headersgetRequestHeaders()keySet()) Systemoutprintln(Se ha utilizado esta cabecera + header)

javaxwsrscoreContext

Dentro de nuestros recursos JAX-RS podemos inyectar determinados objetos coninformacioacuten sobre el contexto de JAX-RS sobre el contexto de servlets o sobreelementos de la peticioacuten recibida desde el cliente Para ello utilizaremos la anotacioacutenjavaxwsrscoreContext

En los ejemplos de esta sesioacuten ya hemos visto como utilizarla para inyectar objetos de tipoUriInfo y HttpHeaders

A continuacioacuten mostramos un ejemplo en el que podos obtener detalles sobre el contextodel despliegue de la aplicacion asi como del contexto de peticiones individuales utilizando laanotacion Context

Implementacioacuten de un servicio que muestra informacioacuten sobre el contexto de la peticioacuten

Path(orders)

Servicios Rest

58

public class PedidoResource

Context Application app

Context UriInfo uri

Context HttpHeaders headers

Context Request request

Context SecurityContext security

Context Providers providers

GET Produces(applicationxml) public ListltOrdergt getAll(QueryParam(start)int from QueryParam(end)int to) (appgetClasses()) (urigetPath()) (headersgetRequestHeader(HttpHeadersACCEPT)) (headersgetCookies()) (requestgetMethod()) (securityisSecure())

Application proporciona acceso a la informacioacuten de la configuracioacuten de la aplicacioacuten(clase Application)UriInfo proporciona acceso a la URI de la peticioacutenHttpHeaders proporciona acceso a las cabeceras de la peticioacuten HTTP La anotacioacutenHeaderParam puede tambieacuten utilizarse para enlazar una cabecera HTTP a unparaacutemetro de un meacutetodo de nuestro recurso a un campo del mismo o a una propiedadde un beanRequest se utiliza para procesar la respuestas tiacutepicamente se usa juntamente con laclase Response para construir la respuesta de forma dinaacutemicaSecurityContext proporciona acceso a la informacioacuten de la peticioacuten actual relacionadacon la seguridadProviders proporciona informacioacuten sobre la buacutesqueda del runtime de las instancias deproveedores utilizando un conjunto de criterios de buacutesqueda

Con respecto a contexto de servlets podremos inyectar informacioacuten de ServletContextServletConfig HttpServletRequest y HttpServletResponse Debemos recordar que losrecursos JAX-RS son invocados por un servlet dentro de una aplicacioacuten web por lo quepodemos necesitar tener acceso a la informacioacuten del contexto de servlets Por ejemplo sinecesitamos acceder a la ruta en disco donde tenemos los datos de nuestra aplicacioacuten webtendremos que inyectar el objeto ServletContext

GETProduces(imagejpeg)public InputStream getImagen(Context ServletContext sc) return scgetResourceAsStream(fotos + nif + jpg)

javaxwsrsBeanParam

La anotacioacuten javaxwsrsBeanParam nos permite inyectar una clase especiacutefica cuyosmeacutetodos o atributos esteacuten anotados con alguna de las anotaciones de inyeccioacuten de paraacutemetrosxxxParam que hemos visto en esta sesioacuten Por ejemplo supongamos esta clase

Servicios Rest

59

public class ClienteInput FormParam(nombre) String nombre

FormParam(apellido) String apellido

HeaderParam(Content-Type) String contentType

public String getFirstName()

La clase ClienteInput es un simple POJO (Plain Old Java Object) que contiene el nombrey apellidos de un cliente asiacute como el tipo de contenido del mismo Podemos dejar que JAX-RScree inicialice e inyecte esta clase usando la anotacioacuten BeanParam de la siguiente forma

Path(clientes)public class ClienteResource POST public void crearCliente(BeanParam ClienteInput newCust)

El runtime de JAX-RS analizaraacute los paraacutemetros anotados con BeanParam para inyectarlas anotaciones correspondientes y asignar el valor que corresponda En este ejemplo la claseClienteInput contendraacute dos valores de un formulario de entrada y uno de los valores de lacabecera de la peticioacuten De esta forma nos podemos evitar una larga lista de paraacutemetros enel meacutetodo crearCliente() (en este caso son soacutelo tres pero podriacutean ser muchos maacutes)

Conversioacuten automaacutetica de tipos

Todas las anotaciones que hemos visto referencian varias partes de la peticioacuten HTTP Todasellas se representan como una cadena de caracteres en dicha peticioacuten HTTP JAX-RS puedeconvertir esta cadena de caracteres en cualquier tipo Java siempre y cuando se cumpla almenos uno de estos casos

1 Se trata de un tipo primitivo Los tipos int short float double byte char y booleanpertenecen a esta categoriacutea

2 Se trata de una clase Java que tiene un constructor con un uacutenico paraacutemetro de tipo String

3 Se trata de una clase Java que tiene un meacutetodo estaacutetico denominado valueOf() que tomaun uacutenico String como argumento y devuelve una instancia de la clase

4 Es una clase de tipo javautilListltTgt javautilSetltTgt o javautilSortedSetltTgt en dondeT es un tipo que satisface los criterios 2 oacute 3 o es un String Por ejemplo ListltDoublegtSetltStringgt o SortedSetltIntegergt

Si el runtime JAX-RS falla al convertir una cadena de caracteres en el tipo Java especificadose considera un error del cliente Si se produce este fallo durante el procesamiento de unainyeccioacuten de tipo MatrixParam QueryParam o PathParam se devuelve al clienteun error 404 Not found Si el fallo tiene lugar con el procesamiento de las inyecciones

Servicios Rest

60

HeaderParam o CookieParam (esta uacuteltima no la hemos visto) entonces se enviacutea al clienteel eror 400 Bad Request

Valores por defecto (DefaultValue)

Suele ser habitual que algunos de los paraacutemetros proporcionados en las peticiones a serviciosRESTful sean opcionales Cuando un cliente no proporciona esta informacioacuten opcional en lapeticioacuten JAX-RS inyectaraacute por defecto un valor null si se trata de un objeto o un valor ceroen el caso de tipos primitivos

Estos valores por defecto no siempre son los que necesitamos para nuestro servicioPara solucionar este problema podemos definir nuestro propio valor por defecto para losparaacutemetros que sean opcionales utilizando la anotacioacuten javaxwsrsDefaultValue

Consideremos el ejemplo anterior relativo a la recuperacioacuten de la informacioacuten de unsubconjunto de clientes de nuestra base de datos Para ello utilizaacutebamos dos paraacutemetros deconsulta para indicar el iacutendice del primer elemento asiacute como el nuacutemero total de elementosque estamos interesados en recuperar En este caso no queremos que el cliente tengaque especificar siempre estos paraacutemetros al realizar la peticion Usaremos la anotacioacutenDefaultValue para indicar los valores por defecto que nos interese

Path(clientes)public class ClienteResource GET Produces(applicationxml) public String getClientes( DefaultValue(0) QueryParam(inicio) int inicio DefaultValue(10) QueryParam(total) int total)

Hemos usado DefaultValue para especificar un iacutendice de comienzo con valor cero y untamantildeo del subconjunto de los datos de la respuesta JAX-RS utilizaraacute las reglas de conversioacutende cadenas de caracteres que acabamos de indicar para convertir el valor del paraacutemetro enel tipo Java que especifiquemos

25 Configuracioacuten y despliegue de aplicaciones JAX-RS

Como ya hemos visto en la sesioacuten anterior implementamos nuestros servicios REST utilizandoel API de Java JAX-RS (especificacioacuten JSR-339) Una aplicacioacuten JAX-RS consiste en unoo maacutes recursos y cero o maacutes proveedores En este apartado vamos a describir ciertosaspectos aplicados a las aplicaciones JAX-RS como un todo concretamente a la configuracioacuteny tambieacuten a la publicacioacuten de las mismas cuando utilizamos un servidor de aplicacionesJavaEE 7 o bien un contenedor de servlets 30 que incluyan una implementacioacuten del APIJAX-RS Tambieacuten indicaremos coacutemo configurar el despliegue en el caso de no disponer comomiacutenimo de un contenedor de servlets 30

Configuracioacuten mediante la clase Application

Tanto los recursos (clases anotadas con Path) como los proveedores que conforman nuestraaplicacioacuten JAX-RS pueden configurarse utilizando una subclase de Application Cuandohablamos de configuracioacuten nos estamos refiriendo en este caso a definir los mecanismospara localizar las clases que representan los recursos asiacute como a los proveedores

Servicios Rest

61

Un proveedor es una clase que implementa una o algunade las siguientes interfaces JAX-RS MesssageBodyReaderMessageBodyWriter ContextResolverltTgt y ExceptionMapperltTgt Lasdos primeras permiten crear proveedores de entidades (entity providers)la tercera es un proveedor de contexto (context provider) y la uacuteltima unproveedor de mapeado de excepciones (exception mapping provider) Lasclases que actuacutean como proveedores estaacuten anotadas con Providerpara que puedan ser identificadas automaacuteticamente por el runtime JAX-RS

El uso de una subclase de Application para configurar nuestros servicios REST constituyela forma maacutes sencilla de desplegar los servicios JAX-RS en un servidor de aplicacionescertificado como Java EE (en este caso Wildfly cumple con este requisito) o un contenedorstandalone de Servlet 3 (como por ejemplo Tomcat)

Pasemos a conocer la clase javaxwsrscoreApplication El uso de la claseApplication es la uacutenica forma portable de decirle a JAX-RS queacute servicios web (clasesanotadas con Path) asiacute como queacute otros elementos como filtros interceptoreshellip queremospublicar (desplegar)

La clase Application se define como

package javaxwsrscore

import javautilCollectionsimport javautilSet

public abstract class Application private static final SetltObjectgt emptySet = CollectionsemptySet()

public abstract SetltClassltgtgt getClasses()

public SetltObjectgt getSingletons() return emptySet

La clase Application es muy simple Como ya hemos indicado su propoacutesito es proporcionaruna lista de clases y objetos que queremos desplegar

El meacutetodo getClasses() devuelve una lista de clases de servicios web y proveedores JAX-RS Cualquier servicio JAX-RS devuelto por este meacutetodo sigue el modelo per-request queya hemos introducido en la sesioacuten anterior Cuando la implementacioacuten de JAX-RS determinaque una peticioacuten HTTP necesita ser procesada por un meacutetodo de una de estas clases secrearaacute una instancia de dicha clase durante la peticioacuten y se destruiraacute al finalizar la mismaEn este caso estamos delegando en el runtime JAX-RS la creacioacuten de los objetos Lasclases proveedoras son instanciadas por el contenedor JAX-RS y registradas una uacutenica vezpor aplicacioacuten

El meacutetodo getSingletons() devuelve una lista de servicios y proveedores web JAX-RSya instanciados Nosotros como programadores de las aplicaciones somos responsablesde crear estos objetos El runtime JAX-RS iteraraacute a traveacutes de la lista de objetos y los registraraacuteinternamente

Servicios Rest

62

Un ejemplo de uso de una subclase de Application podriacutea ser eacuteste

package orgexpertojava

import javaxwsrscoreApplicationimport javaxwsrsApplicationPath

ApplicationPath(rest)public class ComercioApplication extends Application

public SetltClassltgtgt getClasses() HashSetltClassltgtgt set = new HashSetltClassltgtgt() setadd(ClienteResourceclass) setadd(PedidoResourceclass) return set

public SetltObjectgt getSingletons() JsonWriter json = new JsonWriter() TarjetaCreditoResource servicio = new TarjetaCreditoResource()

HashSetltObjectgt set = new HashSet() setadd(json) setadd(servicio) return set

La anotacioacuten ApplicationPath define la base URL de la ruta para todos nuestrosservicios JAX-RS desplegados Asiacute por ejemplo accederemos a todos nuestros serviciosJAX-RS seraacuten desde la ruta rest cuando los ejecutemos En el ejemplo anterior estamosindicando que ClienteResource y PedidoResource son servicios per-request El meacutetodogetSingletons() devuelve el servicio de tipo TarjetaCreditoResource asiacute como el proveedorJsonWriter (que implementa la interfaz MessageBodyWriter)

Si tenemos al menos una implementacioacuten de la clase Application anotada conApplicationPath esta seraacute detectada y desplegada automaacuteticamente por el servidor deaplicaciones

Podemos aprovechar completamente esta capacidad para escanear y detectarautomaacuteticamente nuestros servicios si tenemos implementada una subclase de Applicationpero dejamos que getSingletons() devuelva el conjunto vaciacuteo y no indicamos nada en elmeacutetodo getClasses() de esta forma

package orgexpertojava

import javaxwsrsApplicationPathimport javaxwsrscoreApplication

ApplicationPath(rest)public class ComercioApplication extends Application

Servicios Rest

63

En este caso el servidor de aplicaciones se encargaraacute de buscar en el directorio WEB-INFclasses y en cualquier fichero jar dentro del directorio WEB-INFlib A continuacioacuten antildeadiraacutecualquier clase anotada con Path o Provider a la lista de cosas que necesitan serdesplegadas y registradas en el runtime JAX-RS

Los servicios REST son atendidos por un servlet que es especiacutefico de la implementacioacutenJAX-RS utilizada por el servidor de aplicaciones El servidor wildfly utiliza la implementacioacutende JAX-RS 20 denomindada resteasy (otra implementacioacuten muy utilizada es jersey porejemplo con el servidor de aplicaciones Glassfish) El runtime de JAX-RS contiene un servletinicializado con un paraacutemetro de inicializacioacuten de tipo javaxwsrsApplication cuyo valor seraacuteinstanciado automaacuteticamente por el servidor de aplicaciones con el nombre de la subclasede Application que sea detectada en el war de nuestra aplicacioacuten

Configuracioacuten mediante un fichero webxml

En la sesioacuten anterior no hemos utilizado de forma expliacutecita la clase Application para configurarel despliegue En su lugar hemos indicado esta informacioacuten en el fichero webxml

ltweb-app version=30 xmlns=httpjavasuncomxmlnsjavaee xmlnsxsi=httpwwww3org2001XMLSchema-instance xsischemaLocation=httpjavasuncomxmlnsjavaee httpjavasuncomxmlnsjavaeeweb-app_3_0xsdgt lt-- Con estas liacuteneas el servidor es el responsable de antildeadir el servlet correspondiente de forma automaacutetica Si en nuestro war tenemos clases anotadas con anotaciones JAX-RS para recibir invocaciones REST eacutestas seraacuten detectadas y registradas--gt ltservlet-mappinggt ltservlet-namegtjavaxwsrscoreApplicationltservlet-namegt lturl-patterngtrestlturl-patterngt ltservlet-mappinggtltweb-appgt

Esta configuracioacuten es equivalente a incluir una subclase de Application sin sobreescribir losmeacutetodos correspondientes En este caso se antildeade de forma dinaacutemica el servlet que sirvelas peticiones REST con el nombre javaxwsrscoreApplication de forma que se detectenautomaacuteticamente todas las clases de recursos y clases proveedoras empaquetadas en el warde la aplicacioacuten

Configuracioacuten en un contenedor que no disponga de una implementacioacuten JAX-RS

Si queremos hacer el despliegue sobre servidores de aplicaciones o servidores web queden soporte a una especificacioacuten de servlets con una versioacuten inferior a la 30 tendremosque configurar MANUALMENTE el fichero webxml para que cargue el servlet de nuestraimplementacioacuten propietaria de JAX-RS (cuyos ficheros jar deberemos incluir en el directorioWEB-INFlib de nuestro war) Un ejemplo de configuracioacuten podriacutea ser eacuteste

Configuracioacuten del fichero webxml (directorio de fuentes webappWEB-INFwebxml)

ltxml version=10gtltweb-appgt ltservletgt

Servicios Rest

64

ltservlet-namegtJAXRSltservlet-namegt ltservlet-classgt orgjbossresteasypluginsserverservletHttpServletDispatcher ltservlet-classgt ltinit-paramgt ltparam-namegt javaxwsrsApplication ltparam-namegt ltparam-valuegt orgexpertoJavaComercioApplication ltparam-valuegt ltinit-paramgt ltservletgt

ltservlet-mappinggt ltservlet-namegtJAXRSltservlet-namegt lturl-patterngtrestlturl-patterngt ltservlet-mappinggtltweb-appgt

En la configuracioacuten anterior estamos indicando de forma expliacutecita el servlet JAX-RS que recibelas peticiones REST que a su vez utilizaraacute la clase Application para detectar queacute servicios yproveedores REST seraacuten desplegados en el servidor

Tambieacuten seraacute necesario incluir la libreriacutea con la implementacioacuten JAX-RS 20 de formaexpliacutecita en el war generado (recordemos que para ello tendremos que utilizar la etiquetaltscopegtcompileltscopegt para que se antildeadan los jar correspondientes)

Libreriacutea con la implementacioacuten de JAX-RS 20

ltdependencygt ltgroupIdgtjavaxltgroupIdgt ltartifactIdgtjavaee-web-apiltartifactIdgt ltversiongt70ltversiongt ltscopegtcompileltscopegtltdependencygt

Servicios Rest

65

26 Ejercicios

Para esta sesioacuten antildeadiremos un nuevo moacutedulo en el que implementaremos un servicio restincorporando los conceptos que hemos explicado durante la sesioacuten En concreto

bull Creamos un moacutedulo Maven con IntelliJ (desde el directorio ejercicios-rest-expertojava ) con el arquetipo webapp-javaee7 tal y como hemos visto en losapuntes de la sesioacuten Las coordenadas del artefacto Maven seraacuten

GroupId orgexpertojava

ArtifactId s2-foro-nuevo

version 10-SNAPSHOT

bull Configuramos el pommxl del proyecto para poder compilar empaquetar y desplegarnuestro servicio en el servidor de aplicaciones Wildfly Consulta los apuntes para ver cuaacuteldebe ser el contenido de las etiquetas ltpropertiesgt ltdependenciesgt y ltbuildgt

bull Vamos a estructurar los fuentes (directorio srcmainjava) de nuestro proyecto en lossiguientes paquetes

orgexpertojavadatos contendraacute clases relacionadas con los datos a los que accedenuestra aplicacioacuten rest Por simplicidad almacenaremos en memoria los datos denuestra aplicacioacuten

orgexpertojavamodelo contiene las clases de nuestro modelo de objetos que seraacutenclases java con atributos y sus correspondientes getters y setters

orgexpertojavarest contiene los recursos JAX-RS que implementan nuestrosservicios rest asiacute como las clases necesarias para automatizar el despliegue de dichosrecursos

Creacioacuten de un recurso creacioacuten y consulta de temas en el foro (05 puntos)

Vamos a crear un recurso JAX-RS al que denominaremos TemasResource (en el paqueteorgexpertojavarest ) En el siguiente ejercicio al configurar la aplicacioacuten haremos que esterecurso sea un singleton Nuestro recurso gestionaraacute sus propios datos en memoria Porejemplo podemos utilizar un atributo private de tipo HashMap en el que almacenaremos lostemas cada uno con un identificador numeacuterico como clave Tambieacuten necesitaremos un atributopara generar las claves para cada uno de los temas Por ejemplo

private MapltInteger Temagt temasDB = new HashMapltInteger Temagt()private int contadorTemas = 0

Fiacutejate que si utilizamos los tipos HashMap e int podemos tener problemasde concurrencia si muacuteltiples usuarios estaacuten realizando peticiones paracrear yo consultar los temas del foro En una situacioacuten real deberiacuteamosutilizar en su lugar los tipos ConcurrentHasMap y AtomicInteger paraevitar el que dos usuarios intentaran crear un nuevo tema con la mismaclave perdieacutendose asiacute uno de los dos temas creados Al tratarse de unejercicio en el que solamente tendremos un cliente no nos plantearaacuteninguacuten problema el trabajar con HashMap e int por lo que podeacuteis elegircualquiera de las dos opciones para realizar el ejercicio

Servicios Rest

66

bull Nuestro recurso estaraacute accesible en el servidor en la ruta temas (relativa a la raiacutez delcontexto de nuestra aplicacioacuten y a la ruta de nuestro servlet JAX-RS que determinaremoscon la anotacioacuten ApplicationPath de nuestra clase Application)

bull En el paquete orgexpertojavamodelo crearemos la clase Tema con los atributosprivados

int idString nombre

y sus correspondientes getters y setters

setId() getId()setNombre() getNombre()

bull Implementamos un primer meacutetodo en el recurso TemasResource denominadocreaTema() para poder crear un nuevo tema en el foro Dicho meacutetodo atenderaacutepeticiones POST a nuestro servicio Los datos de entrada (cadena de caracteres querespresenta el nombre del tema) se pasan a traveacutes de un formulario html en el que tenemosuna uacutenica entrada denominada nombre

Puedes incluir el siguiente contenido en el fichero indexhtml para introducir los datosdesde el navegador

ltDOCTYPE htmlgtlthtmlgtltheadgt lttitlegtStart Pagelttitlegt ltmeta http-equiv=Content-Type content=texthtml charset=UTF-8gtltheadgtltbodygtlth1gtAlta de temas en el foro lth1gtltform action=s2-foro-nuevoresttemas method=postgt Nombre del tema ltinput type=text name=nombre gtltbr gt

ltinput type=submit value=Enviar gtltformgtltbodygtlthtmlgt

Cada nuevo Tema creado se antildeadiraacute a nuestra base de datos en memoria temasDB juntocon un identificador numeacuterico (que se iraacute incrementando para cada nueva instancia creada)

bull Implementamos un segundo meacutetodo para consultar los temas creados en el foro Elmeacutetodo se denominaraacute verTemasTodos() y devuelve (en formato texto) todos los temasactualmente creados Dado que puede haber un gran nuacutemero de ellos vamos a permitirque el usuario decida cuaacutentos elementos como maacuteximo quiere consultar a partir de unaposicioacuten determinada Por defecto si no se indica esta informacioacuten se mostraraacuten comomaacuteximo los primeros 8 temas registrados en el foro Si el identificador a partir del cualqueremos iniciar la consulta es mayor que el nuacutemero de temas almacenados entoncesdevolveremos la cadena No es posible atender la consulta Ejemplos de URIs que aceptadicho meacutetodo son

Servicios Rest

67

temas

en este caso y suponiendo que hayamos creado solamente los tres temas del apartadoanterior el resultado seriacutea

Listado de temas del 1 al 81 animales2 plantas3 ab

temasinicio=2amptotal=2

el resultado seriacutea

Listado de temas del 2 al 32 plantas3 ab

temasinicio=7amptotal=1

el resultado seriacutea

No es posible atender la consulta

Como ya hemos comentado las URIs indicadas en este ejercicio sonrelativas a la raiacutez del contexto de nuestra aplicacioacuten y a la ruta especificadapara nuestros servicios rest Recuerda que si has configurado el pomxmlcomo en la sesioacuten anterior la raiacutez del contexto de la aplicacioacuten vendraacutedada por el valor de la etiqueta ltfinalNamegt anidada en ltbuildgt Ennuestro caso deberiacutea ser s2-foro-nuevo Maacutes adelante fijaremos la rutade nuestros servicios rest como rest Por ejemplo la URI completa parael uacuteltimo apartado seriacutea httplocalhost8080s2-foro-nuevoresttemasinicio=7amptotal=1

Despliegue y pruebas del recurso (05 puntos)

Vamos a construir y desplegar nuestro servicio en el servidor de aplicaciones Para ello vamosa utilizar una subclase de Application que antildeadiremos en el paquete orgexpertojavarestLa ruta en la que se van a servir nuestras peticiones rest seraacute rest Fiacutejate que el recursoque hemos creado es el encargado de gestionar (crear modificarhellip) sus propios datosPor lo tanto necesitamos que nuestro recurso REST sea un singleton Implementa la claseForoApplication y realiza la construccioacuten y despliegue del proyecto A continuacioacutenprueba el servicio utilizando postman Puedes probar la insercioacuten de temas utilizando tambieacutenel formulario a traveacutes de la URI httplocalhost8080s2-foro-nuevo Podemos utilizar lasentradas del apartado anterior de forma que comprobemos que se crean correctamentelos temas animales plantas y ab y que obtenemos los listados correctos tanto si noindicamos el inicio y total de elementos como si decidimos mostrar los temas desde el 2 hastael 3

Servicios Rest

68

Cuando utilices el cliente IntelliJ para probar meacutetodos POST debesproporcionar un Request Body no vaciacuteo En este caso como en lapropia URI incluimos el contenido del mensaje que es el nombre del temaque queremos antildeadir al foro tendraacutes que seleccionar Text aunque norellenemos el campo correspondiente De no hacerlo asiacute obtendremoscomo respuesta un cuerpo de mensaje vaciacuteo y la cabecera de respuestaHTTP11 415 Unsupported Media Type

Muacuteltiples consultas de los temas del foro (05 puntos)

Implementa tres nuevas consultas de los temas del foro de forma que

bull Se pueda realizar una consulta de un tema concreto a partir de su identificador numeacuterico(el meacutetodo solamente debe admitir identificadores formados por uno o maacutes diacutegitos) Si eltema consultado no existe se debe devolver una excepcioacuten con la cabecera de respuestaHTTP11 404 Not Found Por ejemplo

temas2

Debe devolver lo siguiente

Ver el tema 2plantas

temas4

Obtenemos como respuesta un cuerpo de mensaje vaciacuteo y la cabecera de respuestaHTTP11 404 Not Found

bull Se pueda realizar una consulta de los temas que comiencen por uno de los siguientescaracteres a b c oacute d Por ejemplo teniendo en cuenta que hemos introducido los temasanteriores

temasa

Debe devolver lo siguiente

Listado de temas que comienzan por aanimales

temasd

Debe devolver Listado de temas que comienzan por d

bull Se pueda realizar una consulta de los temas que contengan una subcadena de caracteresPor ejemplo teniendo en cuenta que hemos introducido los temas anteriores

temasma + Debe devolver lo siguiente

Listado de temas que contienen la subcadena maanimales

Servicios Rest

69

Creacioacuten de subrecursos (05 puntos)

Vamos a crear el subrecurso MensajesResource (en el paquete orgexpertojavarest)de forma que este recurso gestione la creacioacuten y consulta de mensajes para cada unode los temas del foro Este subrecurso debe atender peticiones desde rutas del tipotemasidentificadorTemamensajes siendo identificadorTema la clave numeacutericaasociada a uno de los temas almacenados

bull En este caso nuestro subrecurso no seraacute un singleton por lo que necesitaremos almacenarlos mensajes en otra clase diferente (ya que crearemos una nueva instancia del recursopara cada peticioacuten) La clase DatosEnMemoria (en el paquete orgexpertojavadatos)seraacute la encargada de almacenar en memoria la informacioacuten de los mensajes publicadospara cada tema Por ejemplo puedes utilizar los siguientes campos estaacuteticos paragestionar los mensajes

public static MapltMensaje Stringgt mensajesDB = new HashMapltMensaje Stringgt()

La clave seraacute el propio mensaje (objeto Mensaje que se asociaraacute al tema correspondiente)

public static int contadorMen = 0

Como ya hemos comentado puedes usar ConcurrentHashMap yAtomicInteger en lugar de los tipos anteriores para evitar problemas deconcurrencia

bull En el paquete orgexpertojavadatos crearemos la clase Mensaje con los atributosprivados

int idString textoString autor=anonimo

y sus correspondientes getters y setters

setId() getId()setTexto() getTexto()setAutor() getAutor()

bull Vamos a crear un meacutetodo para poder realizar la publicacioacuten de un mensaje de texto en elforo en uno de los temas ya creados Independientemente del tipo de peticioacuten realizadasobre los mensajes si el tema indicado en la URI no existe lanzaremos la excepcioacutenWebApplicationException(ResponseStatusNOT_FOUND) Veamos alguacuten ejemplo

Deberemos poder realizar una peticioacuten POST a temas1mensajes con el cuerpo demensaje = Mensaje numero 1 El mensaje creado por defecto tendraacute asociado el autoranonimo

Servicios Rest

70

Si realizamos una peticioacuten para antildeadir un mensaje a la URI temas9mensajesdeberiacuteamos obtener como cabecera de respuesta HTTP11 404 Not Foundindependientemente del cuerpo del mensaje

bull Vamos a crear un meacutetodo para realizar una consulta de todos los mensajes publicados enun tema concreto Por ejemplo

Una peticioacuten GET a temas1mensajes deberiacutea dar como resultado

Lista de mensajes para el tema animales1 Mensaje anonimo

Si realizamos una peticioacuten GET a la URI temas9mensajes deberiacuteamos obtenercomo cabecera de respuesta HTTP11 404 Not Found independientemente delcuerpo del mensaje

bull Finalmente vamos a antildeadir dos nuevos meacutetodos para (a) antildeadir un nuevo mensajeen un tema concreto indicando el autor del mensaje Como restriccioacuten el nombre delautor deberaacute estar formado solamente por caracteres alfabeacuteticos utilizando mayuacutesculas ominuacutesculas y como miacutenimo tiene que tener un caracter y (b) consultar todos los mensajesque un determinado autor ha publicado en el foro en un tema determinado

Una peticioacuten POST a la URI temas1mensajespepe con el cuerpo de mensaje convalor mensaje de pepe deberiacutea crear un nuevo mensaje para el tema con identificador2 y devolver como resultado el nuevo id (yo la URI del nuevo recurso en la cabecerade respuesta Location si seguimos la ortodoxia REST) En caso de que devolvamos laURI del nuevo recurso podemos utilizar la orden

return Responsecreated(uriInfogetAbsolutePathBuilder()

segment(StringvalueOf(id))

build())

build()

Obtenemos el path absoluto de la uri que nos ha invocadoAntildeadimos el identificador id del nuevo recurso creadoConstruimos la nueva URIConstruimos el objeto Response

Veremos coacutemo manipular objetos de tipo Response en sesiones posteriores

Recuerda que para acceder al cuerpo de la peticioacuten basta con definir unparaacutemetro de tipo String JAX-RS automaacuteticamente lo instanciaraacute con elcuerpo de la peticioacuten como una cadena

bull Una peticioacuten GET a la URI temas1mensajesanonimo dariacutea como resultado

Lista de mensajes tema= animales y autor= anonimo

1 Mensaje anonimo

bull Una peticioacuten GET a la URI temas1mensajes dariacutea como resultado

Lista de mensajes para el tema animales

Servicios Rest

71

1 Mensaje anonimo2 mensaje de pepe

bull Una peticioacuten GET a la URI temas1mensajesroberto dariacutea como resultado

Lista de mensajes tema= animales y autor= roberto

Servicios Rest

72

3 Manejadores de contenidos Respuestas del servidor ymanejo de excepciones

En la sesioacuten anterior hemos hablado de coacutemo inyectar informacioacuten contenida en las cabecerasde las peticiones HTTP ahora nos detendremos en el cuerpo del mensaje tanto de la peticioacutencomo de la respuesta En el caso de las peticiones explicaremos el proceso de transformarlos datos de entrada en objetos Java para poder ser procesados por nuestros serviciosCon respecto a las respuestas proporcionadas por nuestros servicios analizaremos tanto loscoacutedigos de respuesta por defecto como la elaboracioacuten de respuestas complejas y manejo deexcepciones

31 Proveedores de entidades

JAX-RS define lo que se denominan proveedores de entidades que son clases queproporcionan servicios de mapeado entre las representaciones del cuerpo del mensaje HTTPy los correspondientes tipos java que utilizaremos en nuestros recursos (paraacutemetros en losmeacutetodos o bien como tipo de la respuesta de los mismos) Las entidades tambieacuten se conocencon el nombre de message payload o simplemente como payload y representan elcontenido del cuerpo del mensaje HTTP

ProvidersEl runtime de JAX-RS puede extenderse (ampliarse) utilizandoclases proveedoras (providers) suministradas por nuestra aplicacioacutenConcretamente JAX-RS nos proporciona un conjunto de interfaces quepodemos implementar en nuestra aplicacioacuten creando asiacute dichas clasesproveedoras de entidades (entity providers) La especificacioacuten de JAX-RS define un proveedor como una clase que implementa una o maacutesinterfaces JAX-RS (de entre un conjunto determinado) y que puedenanotarse con provider para ser descubiertas de forma automaacuteticapor el runtime de JAX-RS

Nuestra aplicacioacuten puede proporcionar su propio mapeado entre representaciones(tipos MIME) del mensaje de entrada y tipos Java implementando las interfacesMessageBodyWriter y MessageBodyReader convirtieacutendose asiacute en clases proveedorasde entidades (entity providers) Por ejemplo podemos tener nuestro propio proveedor deentidades para el formato XML o JSON de forma que utilizando las libreriacuteas de java paraprocesamiento XML o JSON (Java API for XML Processing JAXP1 y Java API for JSONProcessing JSON-P2) implementemos el serializadodeserializado del cuerpo del mensajeHTTP de entrada cuando eacuteste presente los tipos MIME applicationxml o application_jsonLas clases que realizan dichos mapeados son clases entity provider

Interfaz javaxwsrsextMessageBodyReader

La interfaz MessageBodyReader define el contrato entre el runtime de JAX-RS y loscomponentes que proporcionan servicios de mapeado desde diferentes representaciones(indicadas como tipos mime) al tipo Java correspondiente Cualquier clase que quieraproporcionar dicho servicio debe implementar la interfaz MessageBodyReader y debeanotarse con Provider para poder ser detectada de forma automaacutetica por el runtime deJAX-RS

1 httpswwwjcporgenjsrdetailsummaryid=2062 httpsjcporgenjsrdetailid=353

Servicios Rest

73

La secuencia loacutegica de pasos seguidos por una implementacioacuten de JAX-RS cuando se mapeael cuerpo de un mensaje HTTP de entrada a un paraacutemetro de un meacutetodo Java es la siguiente

1 Se obtiene el media type de la peticioacuten (valor de la cabecera HTTP Content-Type ) Sila peticioacuten no contiene una cabecera Content-Type se usaraacute applicationoctet-stream

2 Se identifica el tipo java del paraacutemetro cuyo valor seraacute mapeado desde el cuerpo delmensaje

3 Se localiza la clase MessageBodyReader que soporta el media type de la peticioacuten y seusa su meacutetodo readFrom() para mapear el contenido del cuerpo del mensaje HTTP enel tipo Java que corresponda

4 Si no es posible encontrar el MessageBodyReader adecuado se genera la excepcioacutenNotSupportedException con el coacutedigo 405

Interfaz javaxwsrsextMessageBodyWriter

La interfaz MessageBodyWriter define el contrato entre el runtime de JAX-RS ylos componentes que proporcionan servicios de mapeado desde un tipo Java a unarepresentacioacuten determinada Cualquier clase que quiera proporcionar dicho servicio debeimplementar la interfaz MessageBodyWriter y debe anotarse con Provider para poderser detectada de forma automaacutetica por el runtime de JAX-RS

La secuencia loacutegica de pasos seguidos por una implementacioacuten de JAX-RS cuando se mapeaun valor de retorno de un meacutetodo del recurso a una entidad del cuerpo de un mensaje HTTPes la siguiente

1 Se obtiene el objeto que seraacute mapeado a la entidad del cuerpo del mensaje

2 Se determina el media type de la respuesta

3 Se localiza la clase MessageBodyWriter que soporta el objeto que seraacute mapeado a laentidad del cuerpo del mensaje HTTP y se utiliza su meacutetodo writeTo() para realizardicho mapeado

4 Si no es posible encontrar el MessageBodyWriter adecuado se generala excepcioacuten InternalServerErrorException (que es una subclase deWebApplicationException ) con el coacutedigo 500

32 Proveedores de entidad estaacutendar incluidos en JAX-RS

Cualquier implementacioacuten de JAX-RS debe incluir un conjunto de implementaciones deMessageBodyReader y MessageBodyWriter de forma predeterminada para ciertascombinaciones de tipos Java y media types

Table 3 Proveedores de entidades estaacutendar de una implementacioacuten JAX-RS

Tipo Java Media Type

byte[] (Cualquier media type)

javalangString (Cualquier media type)

javaioInputStream (Cualquier media type)

Servicios Rest

74

Tipo Java Media Type

javaioReader (Cualquier media type)

javaioFile (Cualquier media type)

javaxactivationDataSource (Cualquier media type)

javaxxmltransformSource textxml applicationxml application+xml(tipos basados en xml)

javaxxmlbindJAXBElement andapplication-supplied JAXB classes

textxml applicationxml application+xml(tipos basados en xml)

MultivaluedMapltStringStringgt applicationx-www-form-urlencoded(Contenido de formularios)

StreamingOutput (Cualquier media type) (SoacuteloMessageBodyWriter )

javalangBoolean javalangCharacterjavalangNumber

textplain

A continuacioacuten comentaremos algunos de estos proveedores de entidades estaacutendar oconversores por defecto que permiten convertir el cuerpo del mensaje HTTP a objetos Javade diferentes tipos y viceversa

javaxwsrscoreStreamingOutput

StreamingOutput es una interfaz callback que implementamos cuando queremos tratar comoun flujo continuo (streaming) el cuerpo de la respuesta Constituye una alternativa ligera aluso de MessageBodyWriter

public interface StreamingOutput void write(OutputStream output) throws IOException WebApplicationException

Implementamos una instancia de esta interfaz y la utilizamos como tipo de retorno denuestros meacutetodos de recursos Cuando el runtime de JAX-RS estaacute listo para escribir elcuerpo de respuesta del mensaje se invoca al meacutetodo write() de la instancia deStreamingOutput Veamos un ejemplo

Path(miservicio) public class MiServicio GET Produces(textplain) StreamingOutput get() return new StreamingOutput() public void write(OutputStream output) throws IOException WebApplicationException outputwrite(hello worldgetBytes())

Hemos utilizado una clase interna anoacutenima que implementa la interfaz StreamingOutputen lugar de crear una clase puacuteblica separada La razoacuten de utilizar una clase interna es porque

Servicios Rest

75

en este caso al contener tan pocas liacuteneas de coacutedigo resulta beneficioso mantener dicha loacutegicadentro del meacutetodo del recurso JAX-RS de forma que el coacutedigo sea maacutes faacutecil de seguirNormalmente no tendremos necesidad de reutilizar la loacutegica implementada en otros meacutetodospor lo que no tiene demasiado sentido crear otra clase especiacutefica

iquestY por queacute no inyectamos un OutputStream directamente iquestPor queacute necesitamos un objetocallback La razoacuten es que asiacute dejamos que el runtime de JAX-RS maneje la salida de lamanera que quiera Por ejemplo por razones de rendimiento puede ser conveniente que JAX-RS utilice un thread para responder diferente del thread de peticioacuten

javaioInputStream javaioReader

Para leer el cuerpo de un mensaje de entrada podemos utilizar las clases InputStream oReader Por ejemplo

Path()public class MiServicio PUT Path(dato) public void modificaDato(InputStream is) byte[] bytes = readFromStream(is) String input = new String(bytes) Systemoutprintln(input)

private byte[] readFromStream(InputStream stream) throws IOException ByteArrayOutputStream baos = new ByteArrayOutputStream() byte[] buffer = new byte[1000] int wasRead = 0 do wasRead = streamread(buffer) if (wasRead gt 0) baoswrite(buffer 0 wasRead) while (wasRead gt -1) return baostoByteArray()

En este caso estamos leyendo bytes a partir de un javaioInputStream para convertirloen una cadena de caracteres que mostramos por pantalla

En el siguiente ejemplo creamos un javaioLineNumberReader a partir de un objetoReader e imprimimos cada liacutenea del cuerpo del mensaje de entrada

PUTPath(maslineas)public void putMasLineas(Reader reader) LineNumberReader lineReader = new LineNumberReader(reader) do String line = lineReaderreadLine() if (line = null) Systemoutprintln(line)

Servicios Rest

76

while (line = null)

No estamos limitados solamente a utilizar instancias de InputStream yo Reader paraleer el cuerpo de los mensajes de entrada Tambieacuten podemos devolver dichos objetos comorespuesta Por ejemplo

Path(fichero)public class FicheroServicio private static final String basePath =

GET Path(rutafichero ) Produces(textplain) public InputStream getFichero(PathParam(rutafichero) String path) FileInputStream is = new FileInputStream(basePath + path) return is

Aquiacute estamos inyectando un valor PathParam para crear una referencia a un fichero realde nuestro disco duro Creamos una instancia de javaioFileInputStream a partir delvalor de la ruta inyectada como paraacutemetro y la devolvemos como cuerpo de nuestro mensajede respuesta La implementacioacuten de JAX-RS leeraacute la respuesta de este stream de entrada y laalmacenaraacute en un buffer para posteriormente escribirla de forma incremental en el stream desalida de la respuesta En este caso debemos especificar la anotacioacuten Produces para quela implementacioacuten de JAX-RS conozca el valor que debe asignar a la cabecera Content-Type en la respuesta

javaioFile

Se pueden utilizar instancias de la clase javaioFile para entrada y salida decualquier MIME-TYPE (especificado en Content-Type yo Accept y en las anotacionesProduces yo Consumes ) El siguiente coacutedigo por ejemplo devuelve una referencia aun fichero en nuestro disco

Path(fichero)public class FicheroServicio private static final String baseRuta =

GET Path(rutafichero ) Produces(textplain) public File getFichero(PathParam(rutafichero) String ruta) return new File(baseRuta + ruta)

En este caso inyectamos el valor de la ruta del fichero con la anotacioacuten PathParam A partirde dicha ruta creamos un objeto javaioFile y lo devolvemos como cuerpo del mensaje

Servicios Rest

77

de respuesta La implementacioacuten JAX-RS leeraacute la informacioacuten abriendo un InputStreambasado en esta referencia al fichero y la escribiraacute en un buffer Posteriormente y de formaincremental volveraacute a escribir el contenido del buffer en el stream de salida de la respuesta Aligual que en el ejemplo anterior debemos especificar la anotacioacuten Produces para que JAX-RS sepa coacutemo rellenar la cabecera Content-Type de la respuesta

Tambieacuten podemos inyectar instancias de javaioFile a partir del cuerpo del mensaje dela peticioacuten Por ejemplo

POSTPath(masdatos)public void post(File fichero) Reader reader = new Reader(new FileInputStream(fichero)) LineNumberReader lineReader = new LineNumberReader(reader) do String line = lineReaderreadLine() if (line = null) Systemoutprintln(line) while (line = null)

En este caso la implementacioacuten de JAX-RS crea un fichero temporal en el disco para laentrada Lee la informacioacuten desde el buffer de la red y guarda los bytes leiacutedos en este ficherotemporal En el ejemplo los datos leiacutedos desde la red estaacuten representados por el el objetoFile inyectado por el runtime de JAX-RS (recuerda que soacutelo puede haber un paraacutemetro sinanotaciones en los meacutetodos del recurso y que eacuteste representa el cuerpo del mensaje de lapeticioacuten HTTP) A continuacioacuten el meacutetodo post() crea un javaioFileInputStreama partir del objeto File inyectado Finalmente utilizamos eacuteste stream de entrada para crearun objeto LineNumberReader y mostrar los datos por la consola

byte[]

Podemos utilizar un array de bytes como entrada y salida para cualquier tipo especificadocomo media-type A continuacioacuten mostramos un ejemplo

Path()public class MiServicio GET Produces(textplain) public byte[] get() return hello worldgetBytes()

POST Consumes(textplain) public void post(byte[] bytes) Systemoutprintln(new String(bytes))

Para cualquier meacutetodo de recurso JAX-RS que devuelva un array de bytes debemosespecificar la anotacioacuten Produces para que JAX-RS sepa queacute valor asignar a la cabeceraContent-Type

Servicios Rest

78

String char[]

La mayor parte de formatos en internet estaacuten basados en texto JAX-RS puede convertircualquier formato basado en texto a un String o a cualquier array de caracteres Porejemplo

Path()public class MiServicio GET Produces(applicationxml) public String get() return ltcustomergtltnamegtSergio Garcialtnamegtltcustomergt

POST Consumes(textplain) public void post(String str) Systemoutprintln(str)

Para cualquier meacutetodo de recurso JAX-RS que devuelva un Sring o un array de caracteresdebemos especificar la anotacioacuten Produces para que JAX-RS sepa que valor asignar a lacabecera Content-Type

MultivaluedMapltString Stringgt y formularios de entrada

Los formularios HTML son usados habitualmente para enviar datos a servidores web Losdatos del formulario estaacuten codificados con el media type applicationx-www-form-urlencoded Ya hemos visto como utilizar la anotacioacuten FormParam para inyectarparaacutemetros individuales de un formulario de las peticiones de entrada Tambieacuten podremosinyectar una instancia de MultivaluedMapltString Stringgt que representa todos losdatos del formulario enviado en la peticioacuten Por ejemplo

Path() public class MiServicio POST Consumes(applicationx-www-form-urlencoded) Produces(applicationx-www-form-urlencoded) public MultivaluedMapltStringStringgt post( MultivaluedMapltString Stringgt form) el formulario tiene los campos fieldName1 y fieldName2 Systemoutprintln(formgetFirst(fieldName1)) Systemoutprintln(formgetFirst(fieldName2)) return form

En este coacutedigo nuestro meacutetodo post() acepta peticiones POST y recibe unMultivaluedMapltStringStringgt que contiene todos los datos de nuestro formularioEn este caso tambieacuten devolvemos una instancia de un formulario como respuesta

Los datos del formulario pueden representarse en el cuerpo de la petcioacuten como paresnombre=valor separados por amp Por ejemplo

Servicios Rest

79

fieldName1=valor20con20espaciosampfielName2=otroValor

Los espacios en blanco se codifican como 20 No es necesario poner comillas

33 Muacuteltiples representaciones de recursos

Por defecto un recurso RESTful se produce o consume con el tipo MIME Un recursoRESTful puede restringir los media types que soporta tanto en la peticioacuten como en larespuesta utilizando las anotaciones Consumes y Produces respectivamente Estasanotaciones pueden especificarse como ya hemos visto a nivel de clase o de meacutetodo derecurso Las anotaciones especificadas sobre el meacutetodo prevalecen sobre las de la clase Laausencia de estas anotaciones es equivalente a su inclusioacuten con el tipo MIME () es decirsu ausencia implica que se soporta cualquier tipo

A continuacioacuten mostramos un ejemplo en el que un Pedido puede producirse tanto en formatoxml como en formato json

GETPath(id)Produces(applicationxml applicationjson)public Pedido getPedido(PathParam(id)int id)

El meacutetodo getPedido() puede generar ambas representaciones para el pedido El tipo exactode la respuesta viene determinado por la cabecera HTTP Accept de la peticioacuten

Otro ejemplo en el que pueden consumirse varios tipos MIME puede ser el siguiente

POSTPath(id)Consumes(applicationxml applicationjson)public Pedido addPedido(PathParam(id)int id)

En este caso el formato consumido vendraacute dado por el valor de la cabecera HTTP Content-Type de la peticioacuten

JAX-RS 20 nos permite indicar la preferencia por un media type en el lado del servidorutilizando el paraacutemetro qs (quality on service) qs toma valores entre 0000 y 1000 eindica la calidad relativa de una representacioacuten comparado con el resto de representacionesdisponibles Una representacioacuten con un valor de qs de 0000 nunca seraacute elegido Unarepresentacioacuten sin valor para el paraacutemetro qs se asume que dicho valor es 1000

Ejemplo

POSTPath(id)Consumes(applicationxml qs=075 applicationjson qs=1)public Pedido addPedido(PathParam(id)int id)

Si un cliente realiza una peticioacuten y no manifiesta ninguna preferencia por ningunarepresentacioacuten en particular o con una cabecera Accept con valor application entonces el

Servicios Rest

80

servidor seleccionaraacute la representacioacuten con el valor de qs maacutes alto (en este caso applicationjson) Los valores de qs son relativos y como tales solamente son comparables con otrosvalores qs dentro de la misma instancia de la anotacioacuten Consumes (o Produces)

Los clientes pueden indicar tambieacuten sus preferencias utilizando otro factor relativo de calidaden forma de paraacutemetro denominado q El valor del paraacutemetro q se utiliza para ordenar elconjunto de tipos aceptados q toma valores entre 0000 y 1000 (maacutexima preferencia) Al igualque antes Los valores de q son relativos y como tales solamente son comparables con otrosvalores q dentro de la misma cabecera Accept o Content-type

Las preferencias del servidor (valores de los paraacutemetros qs) soacutelo se tienenen cuenta si el cliente acepta muacuteltiples media types con el mismo valorde q

Veamos un ejemplo

GETPath(id)Produces(applicationxml qs=1 applicationjson qs=075)public Pedido getPedido(PathParam(id)int id)

Supongamos que un cliente lanza una petcioacuten GET con una valor para la cabecera Acceptde application q=05 texthtml En este caso el servidor determina que los tipos MIMEapplicationxml y applicationjson tienen la misma preferencia por parte del cliente (con valorde 05) por lo tanto el servidor elegiraacute la representacioacuten applicationjson ya que tiene un valorde qs mayor

34 Introduccioacuten a JAXB

JAXB (Java Architecture for XML Binding) es una especificacioacuten Java antigua (JSR 2223) yno estaacute definida por JAX-RS JAXB es un framework de anotaciones que mapea clases Java aXML y esquemas XML Es extremadamente uacutetil debido a que en lugar de interactuar con unarepresentacioacuten abstracta de un documento XML podemos trabajar con objetos Java realesque estaacuten maacutes cercanos al dominio que estamos modelando JAX-RS proporciona soportepara JAXB pero antes de revisar los manejadores de contenidos JAXB incuidos con JAX-RSveamos una pequentildea introduccioacuten al framework JAXB

Como ya hemos dicho si queremos mapear una clase Java existente a XML podemos utilizarJAXB a traveacutes de un conjunto de anotaciones Veaacutemoslo mejor con un ejemplo

XmlRootElement(name=cliente)XmlAccessorType(XmlAccessTypeFIELD)public class Cliente XmlAttribute protected int id

XmlElement protected String nombre

public Customer()

3 httpsjcporgaboutJavacommunityprocessmreljsr222index2html

Servicios Rest

81

public int getId() return thisid public void setId(int id) thisid = id

public String getNombre() return thisnombre public void setNombre(String nombre thisnombre = nombre

La anotacioacuten javaxxmlbindannotationXmlRootElement se utiliza en clasesjava para denotar que representan elementos XML (etiqueta XML raiacutez) En este caso estamosdiciendo que la clase Java representa un documento XML que tiene como etiqueta raiacutezltclientegt Las clases java anotadas con XmlRootElement se denomina beansJAXB

La anotacioacuten javaxxmlbindannotationXmlAttribute la hemos asociado alcampo id de nuestra clase Cliente Esta anotacioacuten indica que el campo id de la clasedebe mapearse como el atributo id del elemento raiacutez ltclientegt del documento XML Laanotacioacuten XmlAttribute tiene un atributo name de forma que podemos especificar elnombre exacto del atributo XML dentro del documento Por defecto tiene el mismo nombreque el campo anotado

Hemos utilizado la anotacioacuten javaxxmlbindannotationXmlElement en el camponombre de la clase Cliente Esta anotacioacuten indica a JAXB que debe mapearse el camponombre como el elemento ltnombregt anidado en la etiqueta raiacutez ltclientegt Igual queantes podemos especificar el nombre concreto del elememto XML Por defecto toma el mismonombre que el correspondiente campo anotado

La anotacioacuten javaxxmlbindannotationXmlAccessorType permite controlar laserializacioacuten por defecto de los atributos de la clase Esta anotacioacuten soacutelo puede ser usadaconjuntamente con XmlRootElement (y alguna otra anotacioacuten que no mostramos aquiacute)Hemos usado como valor XmlAccessTypeFIELD lo que significa que por defecto se debenserializar todos los campos (fields) de la clase (esteacuten anotados o no) y las propiedades(properties) de la clase que tengan anotaciones JAXB (a menos que la anotacioacuten seaXMLTransient)

Si alguno de los campos de la clase no tiene anotaciones JAXB asociadas por defecto seserializaraacuten como elementos (etiquetas) en el documento XML correspondiente Seguacuten ladocumentacioacuten4 de JAXB un campo es una variable de instancia no estaacutetica (normalmenteprivada)

Las propiedades de la clase vienen dadas por las combinaciones gettersetter de los atributosde la clase El coacutedigo anterior tiene dos propiedades nombre (dado por el par getNombresetNombre_) e id (par getIdsetId) Normalmente se anotan los meacutetodos getter Dichaspropiedades no estaacuten anotadas por lo que JAXB no las serializaraacute

Al proceso de serializar (convertir) un objeto Java en un documento XMLse le denomina marshalling El proceso inverso la conversioacuten de XML aobjetos Java se denomina unmarshalling

Con las anotaciones anteriores un ejemplo de una instancia de nuestra clase Cliente conun id de 42 y el valor de nombre Pablo Martinez tendriacutea el siguiente aspecto

ltcliente id=42gt

4 httpsjaxbjavanetnonav226docsapi

Servicios Rest

82

ltnombreCompletogtPablo MartinezltnombreCompletogtltclientegt

Observamos que se han serializado los campos (variables de instancia de la clase)

Si no especificamos la anotacioacuten XmlAccessorType por defecto se utilizaraacute

XmlAccessorType(XmlAccessTypePUBLIC_MEMBER)

El valor XmlAccessTypePUBLIC_MEMBER indica a JAXB que se deben serializar todoslos campos puacuteblicos de la clase todos los campos anotados y todas las propiedades (paresgettersetter) a menos que esteacuten anotadas con XMLTransient

Tambieacuten podriacuteamos utilizar

XmlAccessorType(XmlAccessTypeNONE)

En este caso la anotacioacuten XmlAccessTypeNONE indica soacutelo se deben serializar aquellaspropiedades yo campos de la clase que esteacuten anotados

A continuacioacuten indicamos en forma de tabla el uso de los diferentes XmlAccessType

Table 4 Valores utilizados para XmlAccessorType() conjuntamentecon XmlRootElement()

Valor Significado

XmlAccessTypePUBLIC_MEMBER Serializacioacuten por defecto si no se especificaXmlAccessorType() Serializa laspropiedades (pares gettersetter) y campospuacuteblicos a menos que esteacuten anotados conXMLTransient Si alguacuten campo nopuacuteblico estaacute anotado tambieacuten se serializa

XmlAccessTypeFIELD Serializa todos los campos (puacuteblicos oprivados) a menos que esteacuten anotados conXMLTransient

XmlAccessTypeNONE Solamente serializa aquellos camposy propiedades que esteacuten anotadas conanotaciones JAXB

XmlAccessTypePROPERTY Serializa cada par gettersetter a menosque esteacuten anotados con XMLTransient Si alguacuten campo (no puacuteblico) estaacute anotadotambieacuten se serializa

Podemos utilizar la anotacioacuten XmlElement para anidar otras clases anotadas con JAXBPor ejemplo supongamos que queremos antildeadir una clase Direccion a nuestra claseCliente

XmlRootElement(name=direccion)

Servicios Rest

83

XmlAccessorType(XmlAccessTypeFIELD)public class Direccion XmlElement protected String calle

XmlElement protected String cludad

XmlElement protected String codPostal

getters y setters

Simplemente tendriacuteamos que antildeadir el campo de tipo Direccion a nuestra clase Clientede la siguiente forma

XmlRootElement(name=cliente)XmlAccessorType(XmlAccessTypeFIELD)public class Cliente XmlAttribute protected int id

XmlElement protected String nombreCompleto

XmlElement protected Direccion direccion

public Customer()

getters y setters

En este caso una instancia de un Cliente con valores id =56 nombre =RicardoLopez calle =calle del oso 35 ciudad =Alicante y coacutedigo_postal =01010 seriacuteaserializado como

ltcliente id=56gt ltnombregtRicardo Lopezltnombregt ltdirecciongt ltcallegtcalle del oso 35ltcallegt ltciudadgtAlicanteltciudadgt ltcodPostalgt01010ltcodPostalgt ltdirecciongt ltclientegt

Veamos otro ejemplo Supongamos que tenemos el recurso EstadoResource con meacutetodosque responden a peticiones http GET y PUT

Path(estado)

Servicios Rest

84

public class EstadoResource

private static EstadoBean estadoBean = new EstadoBean()

GET Produces(applicationxml) public EstadoBean getEstado() return estadoBean

PUT Consumes(applicationxml) public void setEstado(EstadoBean estado) thisestadoBean = estado

En este caso la clase EstadoBean debe utilizar anotaciones JAXB para poder ser convertidaautomaacuteticamente por el runtime de JAX-RS en un documento XML y viceversa

XmlRootElement(name = estadoImpresora)public class EstadoBean

public String estado = Idle public int tonerRestante = 25 public ListltTareaBeangt tareas = new ArrayListltTareaBeangt()

Por defecto la anotacioacuten XmlRootElement realiza la serializacioacuten de la claseEstadoBean en formato xml utilizando los campos puacuteblicos y propiedades definidas en laclase ( estado tonerRestante y tareas ) Vemos que el campo tareas a su vez esuna coleccioacuten de elementos de tipo TareaBean que tambieacuten necesitan ser serializados Acontinuacioacuten mostramos la implementacioacuten de la clase TareaBeanjava

XmlRootElement(name = tarea)public class TareaBean public String nombre public String estado public int paginas

public TareaBean()

public TareaBean(String nombre String estado int paginas) thisnombre = nombre thisestado = estado thispaginas = paginas

Para serializar la clase es necesario que la clase tenga un constructor sin paraacutemetros

Servicios Rest

85

Si accedemos al servicio anterior nos devolveraacute la informacioacuten sobre el estado de la siguienteforma (resultado devuelto por el meacutetodo getEstado() anotado con GET)

ltxml version=10 encoding=UTF-8 standalone=yesgtltestadoImpresoragt ltestadogtIdleltestadogt lttonerRestantegt25lttonerRestantegt lttareasgt ltnombregttextodocltnombregt ltestadogtimprimiendoltestadogt ltpaginasgt13ltpaginasgt lttareasgt lttareasgt ltnombregttexto2docltnombregt ltestadogten esperaltestadogt ltpaginasgt5ltpaginasgt lttareasgtltestadoImpresoragt

Podemos observar que se utiliza el elemento xml lttareasgt para representar cada una de lasinstancias de TareaBean las cuales forman parte de de la lista del campo tareas de nuestraclase EstadoBean

Vamos a usar las anotaciones XmlAttribute y XmlElement de la siguiente forma

XmlRootElement(name=estadoImpresora)public class EstadoBean XmlAttribute(name=valor) public String estado = Idle XmlAttribute(name=toner) public int tonerRestante = 25 XmlElement(name=tarea) public ListltTareaBeangt tareas = new ArrayListltTareaBeangt()

En este caso el XML resultante quedariacutea de la siguiente forma

ltestadoImpresora valor=Idle toner=25gt lttareagt ltnombregttextodocltnombregt ltestadogtimprimiendoltestadogt ltpaginasgt13ltpaginasgt lttareagt lttareagt ltnombregttexto2docltnombregt ltestadogten esperaltestadogt ltpaginasgt5ltpaginasgt lttareagtltestadoImpresoragt

Servicios Rest

86

Si no se indica lo contrario por defecto se convierten los campos a elementos del XML

En caso de que los campos no sean puacuteblicos etiquetaremos los getterscorrespondiente (propiedades de la clase)

Hemos visto que para las listas el nombre que especificamos en XmlElement se utilizapara nombrar cada elemento de la lista Si queremos que ademaacutes se incluya un elemento queenvuelva a toda la lista podemos utilizar la etiqueta XmlElementWrapper

XmlRootElement(name=estadoImpresora)public class EstadoBean

XmlAttribute(name=valor) public String estado = Idle

XmlAttribute(name=toner) public int tonerRestante = 25

XmlElementWrapper(name=tareas) XmlElement(name=tarea) public ListltTareaBeangt tareas = new ArrayListltTareaBeangt()

En este caso tendremos un XML como el que se muestra a continuacioacuten

ltestadoImpresora valor=Idle toner=25gt lttareasgt lttareagt ltnombregttextodocltnombregt ltestadogtimprimiendoltestadogt ltpaginasgt13ltpaginasgt lttareagt lttareagt ltnombregttexto2docltnombregt ltestadogten esperaltestadogt ltpaginasgt5ltpaginasgt lttareagt lttareasgtltestadoImpresoragt

Para etiquetar una lista tambieacuten podemos especificar distintos tipos de elemento seguacutenel tipo de objeto contenido en la lista Por ejemplo supongamos que en el ejemploanterior la clase TareaBean fuese una clase abstracta que tiene dos posible subclasesTareaSistemaBean y TareaUsuarioBean Podriacuteamos especificar una etiqueta distintapara cada elemento de la lista seguacuten el tipo de objeto del que se trate con la etiquetaXmlElements de la siguiente forma

XmlElementWrapper(name=tareas) XmlElements( XmlElement(name=usuariotype=TareaUsuarioBeanclass

Servicios Rest

87

XmlElement(name=sistematype=TareaSistemaBeanclass) public ListltTareaBeangt tareas = new ArrayListltTareaBeangt()

De esta forma podriacuteamos tener un XML como el siguiente

ltestadoImpresora valor=Idle toner=25gt lttareasgt ltusuariogt ltnombregttextodocltnombregt ltestadogtimprimiendoltestadogt ltpaginasgt13ltpaginasgt ltusuariogt ltsistemagt ltnombregttexto2docltnombregt ltestadogten esperaltestadogt ltpaginasgt5ltpaginasgt ltsistemagt lttareasgtltestadoImpresoragt

Hemos visto que por defecto se serializan todos los campos Si queremos excluir algunode ellos de la serializacioacuten podemos hacerlo anotaacutendolo con XmlTransient Comoalternativa podemos cambiar el comportamiento por defecto de la serializacioacuten de la claseetiquetaacutendola con XmlAccessorType Por ejemplo

XmlAccessorType(NONE)XmlRootElement(name=estadoImpresora)public class EstadoBean

En este uacuteltimo caso especificando como tipo NONE no se serializaraacute por defectoninguacuten campo soacutelo aquellos que hayamos anotado expliacutecitamente con XmlElement oXmlAttribute Los campos y propiedades (getters) anotados con estas etiquetas seserializaraacuten siempre Ademaacutes de ellos tambieacuten podriacuteamos especificar que se serialicenpor defecto todos los campos puacuteblicos y los getters ( PUBLIC_MEMBER ) todos los getters( PROPERTY ) o todos los campos ya sean puacuteblicos o privados ( FIELD ) Todos los que seserialicen por defecto sin especificar ninguna etiqueta lo haraacuten como elemento

Por uacuteltimo si nos interesa que toda la representacioacuten del objeto venga dada uacutenicamente porel valor de uno de sus campos podemos etiquetar dicho campo con XmlValue

Clase JAXBContext

Para serializar clases Java a y desde XML es necesario interactuar con la clasejavaxxmlbindJAXBContext Esta clase nos permite inspeccionar las clasesJava para comprender la estructura de nuestras clases anotadas Dichas clasesse utilizan como factoriacuteas para las interfaces javaxxmlbindMarshaller yjavaxxmlbindUnmarshaller Las instancias de Marshaller se utilizan para creardocumentos XML a partir de objetos Java Las instancias de Unmarshaller se utilizan para crearobjetos Java a partir de documentos XML A continuacioacuten mostramos un ejemplo de uso de

Servicios Rest

88

JAXB para convertir una instancia de la clase Cliente que hemos definido anteriormentea formato XML para posteriormente volver a crear el objeto de tipo Cliente

Cliente cliente = new Cliente()clientesetId(42)

clientesetNombre(Lucia Arg)

JAXBContext ctx = JAXBContextnewInstance(Clienteclass) StringWriter writer = new StringWriter()

ctxcreateMarshaller()marshal(cliente writer)

String modString = writertoString()

cliente = (Cliente)ctxcreateUnmarshaller()

unmarshal(new StringReader(modString))

Creamos e inicializamos una instancia de tipo ClienteInicializamos JAXBContext para que pueda analizar la clase `ClienteUtilizamos una instancia de Marshaller para escribir el objeto Cliente como unString de Java ( StringWriter es un stream de caracteres que utilizaremos paraconstruir un String )Utilizamos una instancia de Unmarshaller para recrear el objeto Cliente a partir delString que hemos obtenido en el paso anterior

La clase JAXBContext constituye un punto en entrada al API JAXB paralos clientes de nuestros servicios RESTful Proporciona una abstraccioacutenpara gestionar el enlazado (binding) de informacioacuten XMLJava necesariapara implementar las operaciones de marshalling y unmarshalling

bull Unmarshalling La clase Unmarshaller proporciona a la aplicacioacutencliente la capacidad para convertir datos XML en un aacuterbol de objetosJava

bull Marshalling La clase Marshaller proporciona a la aplicacioacutencliente la capacidad para convertir un aacuterbol con contenidos Java denuevo en datos XML

Una vez que hemos proporcionado una visioacuten general sobre coacutemo funciona JAXB vamos aver coacutemo se integra con JAX-RS

Manejadores JAX-RS para JAXB

La especificacioacuten de JAX-RS indica que cualquier implementacioacuten debe soportar deforma automaacutetica el proceso de marshalling y unmarshalling de clases anotadas conXmlRootElement A continuacioacuten mostramos un ejemplo de la implementacioacuten de unservicio que hace uso de dichos manejadores Para ello utilizamos la clase Cliente que hemosanotado previamente con XmlRootElement y que hemos mostrado en apartados anteriores

Path(clientes)public class ClienteResource GET Path(id)

Servicios Rest

89

Produces(applicationxml) public Cliente getCliente(PathParam(id) int id) Cliente cli = findCliente(id) return cust

POST Consumes(applicationxml) public void crearCliente(Cliente cli)

Como podemos ver una vez que aplicamos las anotaciones JAXB a nuestras clases Java ( eneste caso a la clase Cliente ) es muy sencillo intercambiar documentos XML entre el clientey nuestro servicio web Los manejadores JAXB incluidos en la implementacioacuten de JAX-RSgestionaraacuten el marshallingunmarshalling de cualquier clase con anotaciones JAXB para losvalores de Content-Type applicationxml textxml o application+xml Por defecto tambieacuten se encargan de la creacioacuten e inicializacioacuten de instancias JAXBContext Debido a que la creacioacuten de las instancias JAXBContext puede ser cara la implementacioacutende JAX-RS normalmente las guarda despueacutes de la primera inicializacioacuten

JAXB y JSON

JAXB es lo suficientemente flexible como para soportar otros formatos ademaacutes de XMLAunque la especificacioacuten de JAX-RS no lo requiere muchas implementaciones de JAX-RSincluyen adaptadores de JAXB para soportar el formato JSON ademaacutes de XML JSON es unformato basado en texto que puede ser interpretado directamente por Javascript De hechoes el formato de intercambio preferido por las aplicaciones Ajax

JSON es un formato mucho maacutes simple que XML Los objetos estaacuten entre llaves ycontienen pares de clavevalor separados por comas Los valores pueden ser cadenas decaracteres booleanos ( true o false ) valores numeacutericos o arrays de los tipos anteriores

Supongamos que tenemos la siguiente descripcioacuten de un producto en formato XML

ltxml version=10 encoding=UTF-8gtltproductogt ltidgt1ltidgt ltnombregtiPadltnombregt ltdescripciongtDispositivo moacutevilltdescripciongt ltpreciogt500ltpreciogtltproductogt

La representacioacuten JSON equivalente seriacutea

id1 nombreiPad descripcionDispositivo moacutevil precio500

Servicios Rest

90

El formato JSON asociado por ejemplo al siguiente objeto Cliente

ltcliente id=56gt ltnombregtRicardo Lopezltnombregt ltdirecciongt ltcallegtcalle del oso 35ltcallegt ltciudadgtAlicanteltciudadgt ltcodPostalgt01010ltcodPostalgt ltdirecciongt ltclientegt

quedariacutea como sigue

nombre Ricardo Lopez direccion calle calle del oso 35 ciudad Alicante codPostal 01010

Como vemos en el ejemplo el formato JSON consiste baacutesicamente en objetos situados entrellaves los cuales estaacuten formados por pares clavevalor separadas por comas Cada clavey valor estaacute separado por Los valores pueden cadenas de caracteres booleanos (true ofalse) valores numeacutericos o vectores de los tipos anteriores (los vectores estaacuten delimitadospor corchetes)

Podemos antildeadir el formato applicationjson o bienMediaTypeAPPLICATION_JSON a la anotacioacuten Produces en nuestros meacutetodos derecurso para generar respuestas en formato JSON

GETPath(get)Produces(applicationxmlapplicationjson)public Producto getProducto()

En este ejemplo se elegiraacute el formato JSON en la respuesta si el cliente realiza una peticioacutenGET que incluye en la cabecera

Accept applicationjson

El tipo de respuesta es de tipo Producto En este caso Producto debe ser un bean JAXBes decir una clase anotada con XmlRootElement

Los meacutetodos de recurso pueden aceptar tambieacuten datos JSON para clases con anotacionesJAXB

POSTPath(producto)Consumes(applicationxmlapplicationjson)

Servicios Rest

91

public Response crearProducto(Producto prod)

En este caso el cliente deberiacutea incluir la siguiente cabecera cuando realice la peticioacuten POSTque incluya los datos JSON anteriores en el cuerpo del mensaje

Content-Type applicationjson

Hablaremos con maacutes detalle del formato JSON en una sesioacuten posterior

Finalmente y como resumen de lo anterior

JAXB

Para dar soporte al serializado y deserializado XML se utilizan beans JAXBPorejemplo las clases anteriores EstadoBean TareaBean Cliente y Productoson ejemplos de beans JAXB La clase que se serializaraacute como un recurso XMLo JSON se tiene que anotar con XmlRootElement Si en alguacuten elemento deun recurso se retorna un elemento de esa clase y se etiqueta con los tiposProduces(MediaTypeAPPLICATION_XMLMediatypeAPPLICATION_JSON )eacuteste se serializa automaacuteticamente utilizando el tipo de representacioacuten aceptada por elcliente Se puede consultar el artiacuteculo de Lars Vogel5 para maacutes informacioacuten

35 Respuestas del servidor

Vamos a explicar cuaacutel es el comportamiento por defecto de los meacutetodos de recursos JAX-RS en particular veremos cuaacuteles son los coacutedigos de respuesta HTTP por defecto teniendo encuenta situaciones de eacutexito asiacute como de fallo

Dado que en ocasiones vamos a tener que enviar cabeceras de respuesta especiacuteficas antecondiciones de error complejas tambieacuten vamos a explicar coacutemo podemos elaborar respuestascomplejas utilizando el API JAX-RS

Coacutedigos de respuesta por defecto

Los coacutedigos de respuesta por defecto se corresponden con el comportamiento indicado en laespecificacioacuten6 de la definicioacuten de los meacutetodos HTTP 11 Vamos a examinar dichos coacutedigosde respuesta con el siguiente ejemplo de recurso JAX-RS

Path(clientes)public class ClienteResource

Path(id) GET Produces(applicationxml) public Cliente getCliente(PathParam(id) int id)

5 httpwwwvogelladearticlesJAXBarticlehtml6 httpwwww3orgProtocolsrfc2616rfc2616-sec9html

Servicios Rest

92

POST Produces(applicationxml) Consumes(applicationxml) public Cliente crearCliente(Cliente nuevoCli)

PUT Path(id) Consumes(applicationxml) public void updateCliente(PathParam(id) int id Cliente cli)

Path(id) DELETE public void borrarCliente(PathParam(id) int id)

Respuestas que indican eacutexito

Los nuacutemeros de coacutedigo de respuestas HTTP con eacutexito se situacutean en el rango de 200 a 399

bull Para los meacutetodos crearCliente() y getCliente() se devolveraacute una respuestaHTTP con el coacutedigo 200 OK si el objeto Cliente que devuelven dichos meacutetodos noes null

bull Para los meacutetodos crearCliente() y getCliente() se devolveraacute una respuestaHTTP con el coacutedigo 204 No Content si el objeto Cliente que devuelven dichosmeacutetodos es null El coacutedigo de respuesta 204 no indica una condicioacuten de error Solamenteavisa al cliente de que todo ha ido bien pero que el mensaje de respuesta no contienenada en el cuerpo de la misma Seguacuten eacutesto si un meacutetodo de un recurso devuelve void por defecto se devuelve el coacutedigo de respuesta 204 No content Este es el caso para losmeacutetodos updateCliente() y borrarCliente() de nuestro ejemplo

La especificacioacuten HTTP es bastante consistente para los meacutetodos PUT POST GET yDELETE Si una respuesta exitosa HTTP contiene informacioacuten en el cuerpo del mensaje derespuesta entonces el coacutedigo de respuesta es 200 OK Si el cuerpo del mensaje estaacute vaciacuteoentonces se debe devolver 204 No Content

Respuestas que indican una situacioacuten de fallo

Es habitual que las respuestas fallidas se programen de forma que se lance una excepcioacutenLo veremos en un apartado posterior Aquiacute comentaremos algunas condiciones de error pordefecto

Los nuacutemeros de coacutedigo de error de respuesta estaacutendar en HTTP se situacutean en el rango entre400 y 599 En nuestro ejemplo si un cliente se equivoca tecleando la URI y eacutesta queda porejemplo como httphellipcliente entonces el servidor no encontraraacute ninguacuten meacutetodo delrecurso que pueda servir dicha peticioacuten (la URI correcta seriacutea httphellipclientes ) Eneste caso se enviaraacute como respuesta el coacutedigo 404 Not Found

Para los meacutetodos getCliente() y crearCliente() de nuestro ejemplo si el clientesolicita una respuesta con el tipo MIME texthtml entonces la implementacioacuten de JAX-RS devolveraacute automaacuteticamente 406 Not Acceptable con un mensaje de respuesta con elcuerpo de dicho mensaje vaciacuteo Esta respuesta indica que JAX-RS puede encontrar una rutade URI relativa que coincide con la peticioacuten pero no encuentra ninguacuten meacutetodo del recursoque devuelva la respuesta con ese tipo MIME

Servicios Rest

93

Si el cliente invoca una peticioacuten HTTP sobre una URI vaacutelida para la que no se puedeencontrar un meacutetodo de recurso asociado entonces el runtime de JAX-RS devolveraacute el coacutedigo405 Method Not Allowed Asiacute en nuestro ejemplo si el cliente solicita una operacioacuten PUTGET o DELETE sobre la URI clientes obtendraacute como respuesta 405 Method NotAllowed puesto que POST es el uacutenico meacutetodo HTTP que puede dar soporte a dicha URILa implementacioacuten de JAX-RS tambieacuten devolveraacute una cabecera de respuesta Allow con lalista de meacutetodos HTTP que pueden dar soporte a dicha URI Por lo tanto si nuestra aplicacioacutencliente realiza la siguiente peticioacuten de entrada

GET clientes

el servidor devolveraacute la siguiente respuesta

HTTP11 405 Method Not AllowedAllow POST

Elaboracioacuten de respuestas con la clase Response

Como ya hemos visto por ejemplo para peticiones GET si todo va bien se estaraacute devolviendoun coacutedigo de respuesta 200 (Ok) junto con el contenido especificado en el tipo de datosutilizado en cada caso Si devolvemos void el coacutedigo de respuesta seraacute 204 (No Content)

Sin embargo en ocasiones el servicio web que estamos disentildeando no puede implementarseutilizando el comportamiento por defecto de peticioacutenrespuesta inherente a JAX-RS En estoscasos necesitaremos controlar de forma expliacutecita la respuesta que se le enviacutea al cliente (cuerpodel mensaje de la respuesta HTTP) Por ejemplo cuando creamos un nuevo recurso con POSTdeberiacuteamos devolver 201 (Created) Para tener control sobre este coacutedigo nuestros recursosJAX-RS podraacuten devolver instancias de javaxwsrscoreResponse

GETProduces(MediaTypeAPPLICATION_XML)public Response getClientes() ClientesBean clientes = obtenerClientes()

return Responseok(clientes)build()

POSTConsumes(MediaTypeAPPLICATION_XML)public Response addCliente(ClienteBean cliente Context UriInfo uriInfo)

String id = insertarCliente(cliente) URI uri = uriInfo getAbsolutePathBuilder() path(id)

build(id)

return Responsecreated(uri)build()

Al crear una respuesta con Response podemos especificar una entidad que podraacute serun objeto de cualquiera de los tipos vistos anteriormente y que representa los datos a

Servicios Rest

94

devolver como contenido Por ejemplo cuando indicamos ok(clientes) estamos creandouna respuesta con coacutedigo 200 (Ok) y con el contenido generado por nuestro beanJAXB clientes Esto seraacute equivalente a haber devuelto directamente ClientesBean comorespuesta pero con la ventaja de que en este caso podemos controlar el coacutedigo de estadode la respuestaInsertamos una instancia de ClienteBean en la base de datos y obtenemos la claveasociada a dicho clienteEn la sesioacuten anterior hemos hablado de la interfaz uriInfo Es una interfaz inyectableque proporciona acceso a informacioacuten sobre la URI de la aplicacioacuten o la URI de laspeticiones recibidas En este caso estamos construyendo una nueva URI formada porla ruta absoluta de la peticioacuten de entrada http POST antildeadieacutendole la plantilla id yfinalmente sustituyendo el paraacutemetro de la plantilla por el valor id Supongamos quela peticioacuten POST contiene la uri httplocalhost8080recursosclientes Y que el valor de id es 8 El valor de la variable uri seraacute por tanto httplocalhost8080recursosclientes8Devolvemos la respuesta incluyendo la URI anterior en la cabecera HTTP `Location

Veamos con maacutes detalle la clase Response

public abstract class Response

public abstract Object getEntity()

public abstract int getStatus()

public abstract MultivaluedMapltString Objectgt getMetadata()

public abstract URI getLocation()

public abstract MediaType getMediaType()

public abstract void close()

El meacutetodo getEntity() devuelve el objeto Java correspondiente al cuerpo delmensaje HTTPEl meacutetodo getStatus() devuelve el coacutedigo de respuesta HTTPEl meacutetodo getMetadata() devuelve una instancia de tipo MultivaluedMap con lascabeceras de la respuestaEl meacutetodo getLocation() devuelve la URI de la cabecera Location de la respuestaEl meacutetodo getMediaType() devuelve el mediaType del cuerpo de la respuestaEl meacutetodo close() cierra el input stream correspondiente a la entidad asociada delcuerpo del mensaje (en el caso de que esteacute disponible y abierto) Tambieacuten liberacualquier otro recurso asociado con la respuesta (como por ejemplo datos posiblementealmacenados en un buffer)

Estos meacutetodos tiacutepicamente seraacuten invocados dese el cliente tal y como veremos maacutes adelantecuando expliquemos el API cliente

Los objetos Response no pueden crearse directamente Tienen que crearse a partir deinstancias de javaxwsrscoreResponseResponseBuilder devueltas por uno delos siguientes meacutetodos estaacuteticos de Response

public abstract class Response public abstract void close() public static ResponseBuilder status(Status status) public static ResponseBuilder status(int status) public static ResponseBuilder ok()

Servicios Rest

95

public static ResponseBuilder ok(Object entity) public static ResponseBuilder ok(Object entity MediaType type) public static ResponseBuilder ok(Object entity String type) public static ResponseBuilder ok(Object entity Variant var) public static ResponseBuilder serverError() public static ResponseBuilder created(URI location) public static ResponseBuilder noContent() public static ResponseBuilder notModified() public static ResponseBuilder notModified(EntityTag tag) public static ResponseBuilder notModified(String tag) public static ResponseBuilder seeOther(URI location) public static ResponseBuilder temporaryRedirect(URI location) public static ResponseBuilder notAcceptable(ListltVariantgt variants) public static ResponseBuilder fromResponse(Response response)

Veamos por ejemplo el meacutetodo ok()

ResponseBuilder ok(Object entity MediaType type)

Este meacutetodo recibe como paraacutemetros un objeto Java que queremos convertir en una respuestaHTTP y el Content-Type de dicha respuesta Como valor de retorno se obtiene unainstancia de tipo ResponseBuilder pre-inicializada con un coacutedigo de estado de 200 OK

El meacutetodo created() devuelve un ResponseBuilder para un recurso creado y asigna a lacabecera Location de la respuesta el valor de la URI proporionado como paraacutemetro

La clase ResponseBuilder es una factoriacutea utilizada para crear instancias individuales detipo Response

public static abstract class ResponseBuilder public abstract Response build() public abstract ResponseBuilder clone()

public abstract ResponseBuilder status(int status) public ResponseBuilder status(Status status)

public abstract ResponseBuilder entity(Object entity) public abstract ResponseBuilder type(MediaType type) public abstract ResponseBuilder type(String type)

public abstract ResponseBuilder variant(Variant variant) public abstract ResponseBuilder variants(ListltVariantgt variants)

public abstract ResponseBuilder language(String language) public abstract ResponseBuilder language(Locale language)

public abstract ResponseBuilder location(URI location) public abstract ResponseResponseBuilder header(String name Object value) public abstract ResponseResponseBuilder link(URI uri String rel)

Servicios Rest

96

public abstract ResponseResponseBuilder link(String uri String rel)

Vamos a mostrar un ejemplo sobre coacutemo crear respuestas utilizando un objeto Response En este caso el meacutetodo getLibro() devuelve un String que representa el libro en el queestaacute interesado nuestro cliente

Path(libro)public class LibroServicio GET Path(restfuljava) Produces(textplain) public Response getLibro()

String libro =

ResponseBuilder builder = Responseok(libro)

builderlanguage(fr)header(Some-Header some value)

return builderbuild()

Recuperamos los datos del libro solicitado En este caso vamos a devolver una cadenade caracteres que representa el libro en el que estamos interesadosInicializamos el cuerpo de la respuesta utilizando el meacutetodo Responseok() El coacutedigode estado de ResponseBuilder se inicializa de forma automaacutetica con 200Usamos el meacutetodo ResponseBuilderlanguage() para asignar el valorde la cabecera Content-Languaje a franceacutes Tambieacuten usamos el meacutetodoResponseBuilderheader() para asignar un valor concreto a otra cabeceraFinalmente creamos y devolvemos el objeto Response usando el meacutetodoResponseBuilderbuild()

Un detalle que es interesante destacar en este coacutedigo es que no indicamos ninguacuten valor parael Content-Type de la respuesta Debido a que ya hemos especificado esta informacioacutenen la anotacioacuten Produces del meacutetodo el runtime de JAX-RS devolveraacute el valor adecuadodel media type de la respuesta por nosotros

Inclusioacuten de cookies en la respuesta

JAX-RS proporciona la clase javaxwsrscoreNewCookie que utilizaremos para crearnuevos valores de cookies y enviarlos en las respuestas

Para poder incluir las cookies en nuestro objeto Response primero crearemoslas instancias correspondientes de tipo NewCookie las pasaremos al meacutetodoResponseBuildercookie() Por ejemplo

Path(myservice)public class MyService GET public Response get()

NewCookie cookie = new NewCookie(nombre pepe)

ResponseBuilder builder = Responseok(hola textplain) return buildercookie(cookie)build()

Servicios Rest

97

Creamos una nueva cookie con el nombre de clave nombre y le asignamos el valorpepeEn este caso al no indicar el tipo mime de la respuesta con la anotacioacuten Producesnecesitamos indicarlo (en este caso como un paraacutemetro del meacutetodo ok() )

El tipo enumerado de coacutedigos de estado

JAX-RS proporciona el tipo enumerado javaxwsrscoreStatus para representarcoacutedigos de respuesta especiacuteficos

public enum Status OK(200 OK) CREATED(201 Created) ACCEPTED(202 Accepted) NO_CONTENT(204 No Content) MOVED_PERMANENTLY(301 Moved Permanently) SEE_OTHER(303 See Other) NOT_MODIFIED(304 Not Modified) TEMPORARY_REDIRECT(307 Temporary Redirect) BAD_REQUEST(400 Bad Request) UNAUTHORIZED(401 Unauthorized) FORBIDDEN(403 Forbidden) NOT_FOUND(404 Not Found) NOT_ACCEPTABLE(406 Not Acceptable) CONFLICT(409 Conflict) GONE(410 Gone) PRECONDITION_FAILED(412 Precondition Failed) UNSUPPORTED_MEDIA_TYPE(415 Unsupported Media Type) INTERNAL_SERVER_ERROR(500 Internal Server Error) NOT_IMPLEMENTED(501 Not Implemented) SERVICE_UNAVAILABLE(503 Service Unavailable) public enum Family INFORMATIONAL SUCCESSFUL REDIRECTION CLIENT_ERROR SERVER_ERROR OTHER

public Family getFamily() public int getStatusCode() public static Status fromStatusCode(final int statusCode)

Cada valor del tipo Status se asocia con una familia especiacutefica de coacutedigos de respuestaHTTP Estas familias se identifican por el enumerado StatusFamily

bull Los coacutedigos en el rango del 100 se consideran informacionales

bull Los coacutedigos en el rango del 200 se consideran exitosos

bull Los coacutedigos en el rango del 300 son coacutedigos con eacutexito pero dentro de la categoriacutearedireccioacuten

bull Los coacutedigos de error pertenecen a los ragos 400 y 500 En el rango de 400 se consideranerrores del cliente y en el rango de 500 son errores del servidor

Servicios Rest

98

Tanto el meacutetodo Responsestatus() como ResponseBuilderstatus() puedenaceptar un valor enumerado de tipo Status Por ejemplo

DELETEResponse delete() return Responsestatus(StatusGONE)build()

En este caso estamos indicando al cliente que lo que queremos borrar ya no existe (410)

La clase javaxwsrscoreGenericEntity

Cuando estamos creando objetos de tipo Response se nos plantea un problema cuandoqueremos devolver tipos geneacutericos ya que el manejador JAXB necesita extraer la informacioacutendel tipo parametrizado de la respuesta en tiempo de ejecucioacuten Para estos casos JAX-RS proporciona la clase javaxwsrscoreGenericEntity Veamos su uso con unejemplo

GETProduces(applicationxml)public Response getListaClientes() ListltClientegt list = new ArrayListltClientegt() listadd(new Cliente())

GenericEntity entity =

new GenericEntityltListltClientegtgt(list)

return Responseok(entity)build()

La clase GenericEntity es tambieacuten una clase geneacuterica Lo que hacemos escrear una clase anoacutenima que extiende GenericEntity inicializando la plantilla deGenericEntity con el tipo geneacuterico que estemos utilizando

36 Manejadores de excepciones

Vamos a explicar coacutemo podemos tratar las excepciones en nuestros servicios RESTful

Los errores pueden enviarse al cliente bien creando y devolviendo el objeto Responseadecuado o lanzando una excepcioacuten Podemos lanzar cualquier tipo de excepcioacutentanto las denominadas checked (clases que heredan de javalangException ) comolas excepciones unchecked (clases que extienden javalangRuntimeException )Las excepciones generadas son manejadas por el runtime de JAX-RS si tenemosregistrado un mapper de excepciones Dichos mappers (o mapeadores) de excepcionespueden convertir una excepcioacuten en una respuesta HTTP Si las excepciones no estaacutengestionadas por un mapper eacutestas se propagan y se gestionan por el contenedor (deservlets) en el que se estaacute ejecutando JAX-RS JAX-RS proporciona tambieacuten la clasejavaxwsrsWebApplicationException Esta excepcioacuten puede lanzarse por elcoacutedigo de nuestra aplicacioacuten y seraacute procesado automaacuteticamente por JAX-RS sin necesidadde disponer de forma expliacutecita de ninguacuten mapper Vamos a ver coacutemo utilizar esta clase

Servicios Rest

99

La clase javaxwsrsWebApplicationException

JAX-RS incluye una excepcioacuten unchecked que podemos lanzar desde nuestraaplicacioacuten RESTful (ver la documentacioacuten del httpdocsoraclecomjavaee7apijavaxwsrsWebApplicationExceptionhtml [API]) Esta excepcioacuten se puede pre-inicializar con un objetoResponse o con un coacutedigo de estado particular

Clase javaxwsrsWebApplicationException

public class WebApplicationException extends RuntimeException Constructores public WebApplicationException()

public WebApplicationException(Response response) public WebApplicationException(int status)

public WebApplicationException(ResponseStatus status)

public WebApplicationException(String message) public WebApplicationException(Throwable cause) public WebApplicationException(Throwable cause Response response) public WebApplicationException(Throwable cause int status) public WebApplicationException(Throwable cause ResponseStatus status)

public Response getResponse() ]

Podemos crear una instancia a partir de un objeto ResponseCreacioacuten de una instancia a partir de un coacutedigo de estadoCreacioacuten de una instancia a partir de un String Por defecto se incluye el coacutedigo de estado500 El String que se pasa como paraacutemetro se almacena para su posterior recuperacioacutena traveacutes del mensaje `getMessage()

Cuando JAX-RS detecta que se ha lanzado la excepcioacuten WebApplicationException lacaptura y realiza una llamada al meacutetodo WebApplicationExceptiongetResponse()para obtener un objeto Response que enviaraacute al cliente Si la aplicacioacuten ha inicializado laexcepcioacuten WebApplicationException con un coacutedigo de estado o un objeto Response dicho coacutedigo o Response se utilizaraacuten para crear la respuesta HTTP real En otro caso laexcepcioacuten WebApplicationException devolveraacute el cliente el coacutedigo de respuesta 500Internal Server Error

Por ejemplo supongamos que tenemos un servicio web que permite a los usuarios solicitarinformacioacuten de nuestros clientes utilizando una representacioacuten XML

Path(clientes)public class ClienteResource GET Path(id) Produces(applicationxml) public Cliente getCliente(PathParam(id) int id) Cliente cli = recuperarCliente(id) if (cli == null) throw new WebApplicationException(ResponseStatusNOT_FOUND) return cli

Servicios Rest

100

En este ejemplo si no encontramos una instancia de Cliente con el id proporcionadolanzaremos una WebApplicationException que provocaraacute que se le enviacutee al clientecomo coacutedigo de respuesta 404 Not Found

Mapeado de excepciones

Normalmente las aplicaciones tienen que tratar con multitud de excepciones lanzadas desdenuestro coacutedigo de aplicacioacuten o por frameworks de terceros Dejar que el servlet JAX-RSque reside en el servidor maneje la excepcioacuten no nos proporciona demasiada flexibilidadSi capturamos y redirigimos todas estas excepciones a WebApplicationExceptionpodriacutea resultar bastante tedioso De forma alternativa podemos implementar y registrarinstancias de javaxwsrsextExceptionMapper Estos objetos saben coacutemo mapearuna excepcioacuten lanzada por la aplicacioacuten a un objeto Response

public interface ExceptionMapperltE extends Throwablegt Response toResponse(E exception)

Las clases que implementan la interfaz ExceptionMapperltTgt son proveedores demappings de excepciones (Exception Mapping Providers) y mapean una excepcioacuten runtimeo checked a una instancia de Response

Cuando un recurso JAX-RS lanza una excepcioacuten para la que existe un proveedor de mappingde excepciones eacuteste uacuteltimo se utiliza para devolver una instancia de Response Estarespuesta resultante se procesa como si hubiese sido generada por el recurso

Por ejemplo una excepcioacuten bastante utilizada por aplicaciones de bases de datos que utilizanJPA (Java Persistence Api) es javaxpersistenceEntityNotFoundException Estaexcepcioacuten se lanza cuando JPA no puede encontrar un objeto particular en la base de datosEn lugar de escribir coacutedigo que maneje dicha excepcioacuten de forma expliacutecita podemos escribirun ExceptionMapper para que gestione dicha excepcioacuten por nosotros Veaacutemos coacutemo

Provider public class EntityNotFoundMapper

implements ExceptionMapperltEntityNotFoundExceptiongt

public Response toResponse(EntityNotFoundException e)

return Responsestatus(ResponseStatusNOT_FOUND)build()

Nuestra implementacioacuten de ExceptionMapper debe anotarse con Provider Estole indica al runtime de JAX-RS que esta clase es un componente RESTLa clase que implementa ExceptionMapper debe proporcionar el tipo que se quiereparametrizar JAX-RS utiliza esta informacioacuten para emparejar las excepciones de tipoEntityNotFoundException con nuestra clase EntityNotFoundMapperEl meacutetodo toResponse() recibe la excepcioacuten lanzada ycrea un objeto Response que se utilizaraacute para construir la respuesta HTTP

Servicios Rest

101

Otro ejemplo es la excepcioacuten EJBException lanzada por aplicaciones que utilizan EJBs

Jerarquiacutea de excepciones

JAX-RS 20 proporciona una jerarquiacutea de excepciones para varias condiciones deerror para las peticiones HTTP La idea es que en lugar de crear una instancia deWebApplicationException e inicializarla con un coacutedigo de estado especiacutefico podemosuilizar en su lugar una de las excepciones de la jeraquiacutea (clases que heredan de la claseWebApplicationException) Por ejemplo podemos cambiar el coacutedigo anterior que utilizabaWebApplicationException y en su lugar usar javaxwsrsNotFoundException

Path(clientes)public class ClienteResource GET Path(id) Produces(applicationxml) public Cliente getCliente(PathParam(id) int id) Cliente cli = recuperarCliente(id) if (cli == null) throw new NotFoundException() return cli

Al igual que el resto de excepciones de la jerarquiacutea la excepcioacuten NotFoundExceptionhereda de WebApplicationException La siguiente tabla muestra algunas excepcionesque podemos utilizar todas ellas del paquete javaxwsrs Las que incluyen un coacutedigo deestado en el rango de 400 son subclases de ClientErrorException y como ya hemosindicado representan errores en las peticiones Las que presentan un coacutedigo de estado enel rango de 500 son subclases de ServerErrorException y representan errores delservidor

Table 5 Jerarquiacutea de excepciones JAX-RS

Excepcioacuten Coacutedigo de estado Descripcioacuten

BadRequestException 400 Mensaje mal formado

NotAuthorizedException 401 Fallo de autenticacioacuten

ForbiddenException 403 Acceso no permitido

NotFoundException 404 No se ha podido encontrar elrecurso

NotAllowedException 405 Meacutetodo HTTP no soportado

NotAcceptableException 406 Media type solicitado porel cliente no soportado(cabecera Accept de lapeticion)

NotSupportedException 415 El cliente ha incluido unMedia type no soportado(cabecera Content-Type dela peticion)

Servicios Rest

102

Excepcioacuten Coacutedigo de estado Descripcioacuten

InternalServerErrorException 500 Error general del servidor

ServiceUnavailableException 503 El servidor estaacute ocupadoo temporalmente fuera deservicio

bull La excepcioacuten BadRequestException se utiliza cuando el cliente enviacutea algo al servidorque eacuteste no puede interpretar Ejemplos de escenarios concretos que provocan que elruntime JAX-RS son cuando una peticioacuten PUT o POST contiene un cuerpo del mensaje conun documento XML o JSON mal formado de forma que falle el parsing del documento ocuando no puede convertir un valor especificado en la cabecera o cookie al tipo deseadoPor ejemplo

HeaderParam(Cabecera-Particular) int cabeceraCookieParam(miCookie) int cookie

Si el valor de la cabecera HTTP de la peticioacuten o el valor miCookie no puede convertirseen un entero se lanzaraacute la excepcioacuten BadRequestException

bull La excepcioacuten NotAuthorizedException (coacutedigo 401) se usa cuando queremosescribir nuestros propios protocolos de autorizacioacuten y queremos indicar al cliente que eacutestenecesita autenticarse con el servidor

bull La excepcioacuten ForbiddenException (coacutedigo 403)se usa generalmente cuando elcliente realiza una invocacioacuten para la que no tiene permisos de acceso Esto ocurrenormalmente debido a que el cliente no tiene el rol requerido

bull La excepcioacuten NotFoundException (coacutedigo 404) se usa cuando queremos comunicar alcliente que el recurso que estaacute solicitando no existe Esta excepcioacuten tambieacuten se generaraacute deforma automaacutetica por el runtime de JAX-RS cuando a eacuteste no le sea posible inyectar un valoren un PathParam QueryParam o MatrixParam Al igual que hemos comentado paraBadRequestException esto puede ocurrir si intentamos convertir el valor del paraacutemetroa un tipo que no admite esta conversioacuten

bull La excepcioacuten NotAllowedException (coacutedigo 405) se usa cuando el meacutetodo HTTP queel cliente estaacute intentando invocar no estaacute soportado por el recurso al que el cliente estaacuteaccediendo El runtime de JAX-RS lanza automaacuteticamente esta excepcioacuten si no encuentraninguacuten meacutetodo que pueda emparejar con el meacutetodo HTTP invocado

bull La excepcioacuten NotAcceptableException (coacutedigo 406) se usa cuando un cliente estaacutesolicitando un formato especiacutefico a traveacutes de la cabecera Accept El runtime de JAX-RSlanza automaacuteticamente esta excepcioacuten si no hay un meacutetodo con una anotacioacuten Producesque sea compatible con la cabecera Accept del cliente

bull La excepcioacuten NotSupportedException (coacutedigo 415)se usa cuando un cliente estaacuteenviando una representacioacuten que el servidor no comprende El runtime de JAX-RS lanzaautomaacuteticamente esta excepcioacuten si no hay un meacutetodo con una anotacioacuten Consumes quecoincida con el valor de la cabecera Content-Type de la peticioacuten

bull La excepcioacuten InternalServerErrorException (coacutedigo 500)es una excepcioacuten depropoacutesito general lanzada por el servidor Si en nuestra aplicacioacuten queremos lanzar estaexcepcioacuten deberiacuteamos hacerlo si se ha producido una condicioacuten de error que realmenteno encaja en ninguna de las situaciones que hemos visto El runtime de JAX-RS lanzaautomaacuteticamente esta excepcioacuten si falla un MessageBodyWriter o si se lanza algunaexcepcioacuten desde alguacuten ExceptionMapper

Servicios Rest

103

bull La excepcioacuten ServiceUnavailableException (coacutedigo 503)se usa cuando el servidorestaacute ocupado o temporalmente fuera de servicio En la mayoriacutea de los casos es suficientecon que el cliente vuelva a intentar realizar la llamada un tiempo maacutes tarde

Servicios Rest

104

37 Ejercicios

Servicio REST ejemplo

Para familiarizarnos con las el uso de diferentes manejadores de contenidos y manejo deexcepciones proporcionamos el moacutedulo el MOacuteDULO s3-ejemplo-rest con la implementacioacutende un servicio rest sencillo que podeacuteis probar con la herramienta postman

En el directorio srcmainresources de dicho moacutedulo teneacuteis un fichero de texto con lasinstrucciones (instruccionestxt) para construir desplegar y probar la aplicacioacuten de ejemplo

Plantillas que se proporcionan

Para esta sesioacuten proporcionamos un proyecto como plantilla con el nombre s3-filmotecaque tendraacutes utilizar como punto de partida para aplicar lo que hemos aprendido en esta sesioacuten

Se trata de una implementacioacuten parcial para gestionar una filmoteca con informacioacuten depeliacuteculas y actores

La estructura loacutegica del proyecto proporcionado es la siguiente

bull Paquete orgexpertojavadomain es la capa que contiene los objetos del dominiode la aplicacioacuten Por simplicidad no usamos una base de datos real sino que trabajamoscon datos en memoria

bull Paquete orgexpertojavaservice contiene la implementacioacuten de los servicios denuestra aplicacioacuten que seraacuten accedidos desde la capa rest

bull Paquete orgexpertojavarest constituye la capa rest de nuestra aplicacioacuten Estacapa es cliente de la capa de servicios

En la carpeta srcmainresources teneacuteis un fichero de texto (instruccionestxt) coninformacioacuten detallada sobre el API rest implementado

Uso de JAXB (05 puntos)

Utiliza las anotaciones JAXB oportunas para realizar el serializado de las entidades java a xmly json de forma que la lista de peliacuteculas de la filmoteca en formato xml sea

Peticioacuten rest GET peliculas

ltxml version=10 encoding=UTF-8 standalone=yesgtltpeliculasgt ltpelicula duracion=120 estreno=2015-12-08T000000+0100 id=1gt ltdirectorgtStanley Kubrickltdirectorgt lttitulogtEl resplandorlttitulogt ltpeliculagt ltpelicula duracion=155 estreno=2015-18-07T000000+0100 id=2gt ltdirectorgtDirector 2ltdirectorgt lttitulogtPelicula 2lttitulogt ltpeliculagtltpeliculasgt

Servicios Rest

105

Por defecto JAXB serializa los tipos Date con el formato ISO 80617

para los contenidos en el cuerpo de la petioacutenrespuesta Dicho formatoes YYYY-MM-DD (seguido de HHMMSS) Por otro lado si antildeadimosactores a las peliacuteculas eacutestos deberaacuten mostrarse bajo la etiquetaltactoresgt tal y como mostramos en el siguiente ejemplo

Los datos mostrados para una peliacutecula en formato xml tienen que presentar el siguienteaspecto

sourcejava] Peticioacuten rest GET peliculas1

ltxml version=10 encoding=UTF-8 standalone=yesgtltpelicula duracion=120 estreno=2015-12-08T000000+0100 id=1gt ltactoresgt ltactor nombre=Jack Nicholson personaje=Jack Torrancegt ltactoresgt ltdirectorgtStanley Kubrickltdirectorgt lttitulogtEl resplandorlttitulogtltpeliculagt

Puedes comprobar si has hecho bien el ejercicio utilizando Postman

Uso de manejadores de contenidos y clase Response (075 puntos)

Implementa una nueva peticioacuten POST que reciba los datos de una nueva peliacutecula desdeun formulario Recuerda que los datos de un formulario pueden ponerse en el cuerpo de lapeticioacuten HTTP como pares nombre=valor separados por amp Los espacios en blanco secodifican como 20 No es necesario poner comillas Por ejemplo

nombre1=valor20con20espaciosampnombre2=valor

Se proporciona el fichero indexhtml con un formulario para utilizarlo como alternativa apostman Para acceder al formulario usaremos httplocalhost8080s3-filmoteca

Modifica las peticiones POST sobre peliacuteculas y actores de forma que devuelvan enla cabecera Location la URI del nuevo recurso creado Por ejemplo

httplocalhost8080s3-filmotecapeliculas3

en el caso de una nueva peliacutecula y

httplocalhost8080s3-filmotecapeliculas3actoresSigourney20Weaver

si por ejemplo hemos antildeadido un nuevo actor a la peliacutecula con id=3

Modifica los meacutetodos GET para que devuelvan el estado 204 No Content en los casos enlos que la peliacutecula yo actor consultado no exista

7 httpseswikipediaorgwikiISO_8601

Servicios Rest

106

Modifica los meacutetodos GET para que devuelvan el estado 404 Not Found en los casos enlos que las listas de peliacuteculas yo actores esteacuten vaciacuteas

Implementa el coacutedigo para antildeadir un nuevo actor Tendraacutes que obtener la informacioacuten de lapeliacutecula y nombre del actor de la URI de la peticioacuten

Puedes comprobar si has hecho bien el ejercicio utilizando Postman

Manejo de excepciones (075 puntos)

Modifica el meacutetodo addPelicula de la capa de servicio (paquete orgexpertojavaservice)para que lance una excepcioacuten de tipo ServiceException con el mensaje El tiacutetulo de la peliacuteculano puede ser nulo ni vaciacuteo cuando se intente antildeadir una peliacutecula con un tiacutetulo con valor nullo vaciacuteo

El meacutetodo addPelicula debe lanzar tambieacuten una excepcioacuten de tipo ServiceException conel mensaje La peliacutecula ya existe cuando se intente antildeadir una peliacutecula con un tiacutetulo que yaexiste

Modifica el meacutetodo addActor de la capa de servicio para que lance las excepciones de tipoServiceException con los mensajes El tiacutetulo de la peliacutecula no puede ser nulo ni vaciacuteo cuandose intente antildeadir un actor a una peliacutecula cuyo tiacutetulo no existe o bien el mensaje EL actor yaexiste si intentamos antildeadir un actor a una peliacutecula que ya habiacuteamos antildeadido previamente

Implementa un mapper para capturar las excepciones de la capa de servicio de forma quese devuelva el estado 500 Internal Server error y como entidad del cuerpo de la respuestael mensaje asociado a las excepciones generadas por el servicio (El tiacutetulo de la peliacutecula nopuede ser nulo ni vaciacuteo EL actor ya existe hellip) La nueva clase pertenceraacute a la capa restPuedes ponerle el nombre ServiceExceptionMapper

Puedes comprobar si has hecho bien el ejercicio utilizando Postman

Servicios Rest

107

4 HATEOAS y Seguridad

En esta sesioacuten trataremos uno de los principios REST de obligado cumplimiento para poderhablar de un servicio RESTful Nos referimos a HATEOAS Hasta ahora hemos visto coacutemo losclientes pueden cambiar el estado de los recursos (el nombre del recurso se especifica en laURI de la peticioacuten) a traveacutes de los contenidos del cuerpo del mensaje o utilizando paraacutemetroso cabeceras de peticioacuten A su vez los servicios comunican el estado resultante de la peticioacuten alos clientes a traveacutes del contenido del cuerpo del mensaje coacutedigos de respuesta y cabecerasde respuesta Pues bien teniendo en cuenta lo anterior HATEOAS hace referencia a quecuando sea necesario tambieacuten deben incluirse los enlaces a los recursos (URI) en el cuerpode la respuesta (o en las cabeceras) para asiacute poder recuperar el recurso en cuestioacuten o losrecursos relacionados

En esta sesioacuten tambieacuten explicaremos algunos conceptos baacutesicos para poder dotar deseguridad a nuestros servicios REST

41 iquestQueacute es HATEOAS

Comuacutenmente se hace referencia a Internet como la Web (web significa red telarantildea) debidoa que la informacioacuten estaacute interconectada mediante una serie de hiperenlaces embebidosdentro de los documentos HTML Estos enlaces crean una especie de hilos o hebras entrelos sitios web relacionados en Internet Una consecuencia de ello es que los humanos puedennavegar por la Web buscando elementos de informacioacuten relacionados de su intereacutes haciendoclick en los diferentes enlaces desde sus navegadores Los motores de buacutesqueda puedentrepar o desplazarse por estos enlaces y crear iacutendices enormes de datos susceptibles deser buscados Sin ellos Internet no podriacutea tener la propiedad de ser escalable No habriacuteaforma de indexar faacutecilmente la informacioacuten y el registro de sitios web seriacutea un proceso manualbastante tedioso

Ademaacutes de los enlaces (links) otra caracteriacutestica fundamental de Internet es HTML Enocasiones un sitio web nos solicita que rellenemos alguna informacioacuten para compraralgo o registrarnos en alguacuten servicio El servidor nos indica a nosotros como clientes queacuteinformacioacuten necesitamos proporcionar para completar una accioacuten descrita en la paacutegina webque estamos viendo El navegador nos muestra la paacutegina web en un formado que podemosentender faacutecilmente Nosotros leemos la paacutegina web y rellenamos y enviamos el formulario Unformulario HTML es un formato de datos interesante debido a que auto-describe la interaccioacutenentre el cliente y el servidor

El principio arquitectoacutenico que describe el proceso de enlazado (linking) y el enviacuteo deformularios se denomina HATEOAS Las siglas del teacutermino HATEOAS significan HypermediaAs The Engine Of Application State (es decir el uso de Hipermedia como mecanismo demaacutequina de estados de la aplicacioacuten) La idea de HATEOAS es que el formato de los datosproporciona informacioacuten extra sobre coacutemo cambiar el estado de nuestra aplicacioacuten En laWeb los enlaces HTML nos permiten cambiar el estado de nuestro navegador Por ejemplocuando estamos leyendo una paacutegina web un enlace nos indica queacute posibles documentos(estados) podemos ver a continuacioacuten Cuando hacemos click sobre un enlace el estado delnavegador cambia al visitar y mostrar una nueva paacutegina web Los formularios HTML por otraparte nos proporcionan una forma de cambiar el estado de un recurso especiacutefico de nuestroservidor Por uacuteltimo cuando compramos algo en Internet por ejemplo estamos creando dosnuevos recursos en el servicio una transaccioacuten con tarjeta de creacutedito y una orden de compra

Servicios Rest

108

42 HATEOAS y Servicios Web

Cuando aplicamos HATEOAS a los servicios web la idea es incluir enlaces en nuestrosdocumentos XML o JSON La mayoriacutea de las aplicaciones RESTful basadas en XML utilizanel formato Atom Syndication Format8 para implementar HATEOAS

Enlaces Atom

Los enlaces Atom constituyen un mecanismo estaacutendar para incluir enlaces (links) en nuestrosdocumentos XML Veamos un ejemplo

ltclientesgt ltlink rel=next href=httpejemplocomclientesinicio=2amptotal=2 type=applicationxmlgt ltcliente id=123gt ltnombregtJuan Garcialtnombregt ltclientegt ltcliente id=332gt ltnombregtPablo Bozoltnombregt ltclientegtltclientesgt

El documento anterior representa una lista de clientes y el elemento ltlinkgt que contieneun enlace indica la forma de obtener los siguientes clientes de la lista

Un enlace Atom es simplemente un elemento XML (elemento ltlinkgt ) con unos atributosespeciacuteficos

bull El atributo rel Se utiliza para indicar la relacioacuten del enlace con el elemento XML en elque anidamos dicho enlace Es el nombre loacutegico utilizado para referenciar el enlace Esteatributo tiene el mismo significado para la URL que estamos enlazando que la etiquetaHTML ltagt tiene para la URL sobre la que estamos haciendo click con el ratoacuten en elnavegador Si el enlace hace referencia al propio elemento XML en el que incluimos elenlace entonces asignaremos el valor del atributo self

bull El atributo href es la URL a la que podemos acceder para obtener nueva informacioacuten ocambiar el estado de nuestra aplicacioacuten

bull El atributo type indica el media type asociado con el recurso al que apunta la URL

Cuando un cliente recibe un documento con enlaces Atom eacuteste busca la relacioacuten en la queestaacute interesado (atributo rel ) e invoca la URI indicada en el atributo href

Ventajas de utilizar HATEOAS con Servicios Web

Resulta bastante obvio por queacute los enlaces y los formularios tienen mucho que ver en laprevalencia de la Web Con un navegador tenemos una ventana a todo un mundo deinformacioacuten y servicios Las maacutequinas de buacutesqueda rastrean Internet e indexan sitios webpara que todos los datos esteacuten al alcance de nuestros dedos Esto es posible debido a quela Web es auto-descriptiva Cuando accedemos a un documento conocemos coacutemo recuperarinformacioacuten adicional siguiendo los enlaces situados en dicho documento Por ejemplo

8 httpwwww3org2005Atom

Servicios Rest

109

conocemos coacutemo realizar una compra en Amazon debido a que los formularios HTML nosindican coacutemo hacerlo

Cuando los clientes son maacutequinas en lugar de personas (los servicios Web tambieacuten seconocen como Web para maacutequinas frente a la Web para humanos proporcionada por elacceso a un servidor web a traveacutes de un navegador) el tema es algo diferente puesto que lasmaacutequinas no pueden tomar decisiones sobre la marcha cosa que los humanos siacute puedenhacer Las maacutequinas requieren que los programadores les digan coacutemo interpretar los datosrecibidos desde un servicio y coacutemo realizar transiciones entre estados como resultado de lasinteracciones entre clientes y servidores

En este sentido HATEOAS proporciona algunas ventajas importantes para contribuir a quelos clientes sepan coacutemo utilizar los servicios a la vez que acceden a los mismos Vamos acomentar algunas de ellas

Transparencia en la localizacioacuten

En un sistema RESTful gracias a HATEOAS soacutelo es necesario hacer puacuteblicas unas pocasURIs Los servicios y la informacioacuten son representados con enlaces que estaacuten embebidosen los formatos de los datos devueltos por las URIs puacuteblicas Los clientes necesitan conocerlos nombres loacutegicos de los enlaces para buscar a traveacutes de ellos pero no necesitan conocerlas ubicaciones reales en la red de los servicios a los que acceden

Los enlaces proporcionan un nivel de indireccioacuten de forma que los servicios subyacentespueden cambiar sus localizaciones en la red sin alterar la loacutegica ni el coacutedigo del cliente

Desacoplamiento de los detalles de la interaccioacuten

Consideremos una peticioacuten que nos devuelve una lista de clientes en una base de datosGET clientes Si nuestra base de datos tiene miles de datos probablemente noquerremos devolver todos ellos de una soacutela vez Lo que podemos hacer es definir una vistaen nuestra base de datos utilizando paraacutemetros de consulta por ejemplo

customersinicio=indiceInicioamptotal=numeroElementosDevueltos

El paraacutemetro inicio identifica el iacutendice inicial de nuestra lista de clientes El paraacutemetrototal especifica cuaacutentos clientes queremos que nos sean devueltos como respuesta

Lo que estamos haciendo en realidad es incrementar la cantidad de conocimiento que elcliente debe tener predefinido para interactuar con el servicio (es decir no soacutelo necesita saberla URI sino ademaacutes conocer la existencia de estos paraacutemetros) Supongamos que en el futuroel servidor decide que necesita cambiar la forma en la que se accede al nuacutemero de datossolicitados por el cliente Si el servidor cambia la interfaz los clientes antiguos dejaraacuten defuncionar a menos que cambien su coacutedigo

En lugar de publicar la interfaz REST anterior para obtener datos de los clientes podemosincluir dicha informacioacuten en el documento de respuesta por ejemplo

ltclientesgt ltlink rel=next href=httpejemplocomclientesinicio=2amptotal=2 type=applicationxmlgt ltcliente id=123gt

Servicios Rest

110

ltnombregtJuan Garcialtnombregt ltclientegt ltcliente id=332gt ltnombregtPablo Bozoltnombregt ltclientegtltclientesgt

Cuando incluimos un enlace Atom en un documento estamos asignando un nombre loacutegicoa una transicioacuten de estados En el ejemplo anterior la transicioacuten de estados es el siguienteconjunto de clientes a los que podemos acceder En lugar de tener que recordar cuaacuteles sonlos paraacutemetros de la URI que tenemos que utilizar en la siguiente invocacioacuten para obtener maacutesclientes lo uacutenico que tenemos que hacer es seguir el enlace proporcionado El cliente notiene que contabilizar en ninguacuten sitio la interaccioacuten ni tiene que recordar queacute seccioacuten de labase de datos estamos consultando actualmente

Ademaacutes el XML devuelto es auto-contenido iquestQueacute pasa si tenemos que pasar estedocumento a un tercero Tendriacuteamos que decirle que se trata de una vista parcial de la basede datos y especificar el iacutedice de inicio Al incluir el enlace en el documento ya no es necesarioproporcionar dicha informacioacuten adicional ya que forma parte del propio documento

Reduccioacuten de errores de transicioacuten de estados

Los enlaces no se utilizan solamente como un mecanismo para agregar informacioacuten denavegacioacuten Tambieacuten se utilizan para cambiar el estado de los recursos Pensemos en unaaplicacioacuten de comercio web a la que podemos acceder con la URI pedidos333

ltpedido id=333gt ltcliente id=123gtltclientegt ltimportegt9999ltimportegt ltlineas-pedidogt ltlineas-pedidogtltpedidogt

Supongamos que un cliente quiere cancelar su pedido Podriacutea simplemente invocar la peticioacutenHTTP DELETE pedidos333 Esta no es siempre la mejor opcioacuten ya que normalmenteel sistema necesitaraacute retener el pedido para propoacutesitos de almacenaje Por ello podriacuteamosconsiderar una nueva representacioacuten del pedido con un elemento cancelado a true

PUT pedidos333 HTTP11Content-Type applicationxmlltpedido id=333gt ltcliente id=123gtltclientegt ltimportegt9999ltimportegt ltcanceladogttrueltcanceladogt ltlineas-pedidogt ltlineas-pedidogtltpedidogt

Pero iquestqueacute ocurre si el pedido no puede cancelarse Podemos tener un cierto estado ennuestro proceso de pedidos en donde esta accioacuten no estaacute permitida Por ejemplo si el pedido

Servicios Rest

111

ya ha sido enviado entonces no puede cancelarse En este caso realmente no hay niguacutencoacutedigo de estado HTTP de respuesta que represente esta situacioacuten Una mejor aproximacioacutenes incluir un enlace para poder realizar la cancelacioacuten

ltpedido id=333gt ltcliente id=123gtltclientegt ltimportegt9999ltimportegt ltcanceladogtfalseltcanceladogt ltlink rel=cancelar href=httpejemplocompedidos333canceladogt ltlineas-pedidogt ltlineas-pedidogtltpedidogt

El cliente podriacutea invocar la orden GET pedidos333 y obtener el documento XMLque representa el pedido Si el documento contiene el enlace cancelar entonces el clientepuede cambiar el estado del pedido a cancelado enviando una orden PUT vaciacutea a la URIreferenciada en el enlace Si el documento no contiene el enlace el cliente sabe que estaoperacioacuten no es posible Esto permite que el servicio web controle en tiempo real la forma enla que el cliente interactua con el sistema

Enlaces en cabeceras frente a enlaces Atom

Una alternativa al uso de enlaces Atom en el cuerpo de la respuesta es utilizar enlaces enlas cabeceras de la respuesta (httptoolsietforghtmlrfc5988) Vamos a explicar eacutesto con unejemplo

Consideremos el ejemplo de cancelacioacuten de un pedido que acabamos de ver En lugar deutilizar un enlace Atom para especificar si se permite o no la cancelacioacuten del pedido podemosutilizar la cabecera Link (es uno de los posibles campos que podemos incluir como cabeceraen una respuesta HTTP)) De esta forma si un usuario enviacutea la peticioacuten GET pedidos333 recibiraacute la siguiente respuesta HTTP

HTTP11 200 OKContent-Type applicationxmlLink lthttpejemplocompedidos333canceladogt rel=cancel

ltpedido id=333gt ltpedidogt

La cabecera Link tiene las mismas caracteriacutesticas que un enlace Atom La URI estaacute entrelos signos lt y gt y estaacute seguida por uno o maacutes atributos delimitados por El atributo reles obligatorio y tiene el mismo significado que el correspondiente atributo Atom com el mismonombre En el ejemplo no se muestra pero podriacuteamos especificar el media type utilizando elatributo type

43 HATEOAS y JAX-RS

JAX-RS no proporciona mucho soporte para implementar HATEOAS HATEOAS se definepor la aplicacioacuten por lo que no hay mucho que pueda aportar ninguacuten framework Lo que siacute

Servicios Rest

112

proporciona JAX-RS son algunas clases que podemos utilizar para construir las URIs de losenlaces HATEOAS

Construccioacuten de URIs con UriBuilder

Una clase que podemos utilizar es javaxwsrscoreUriBuilder Esta clase nospermite construir URIs elemento a elemento y tambieacuten permite incluir plantillas de paraacutemetros(segmentos de ruta variables)

Clase UriBuilder meacutetodos para instanciar objetos de la clase

public abstract class UriBuilder public static UriBuilder fromUri(URI uri) throws IllegalArgumentException public static UriBuilder fromUri(String uri) throws IllegalArgumentException public static UriBuilder fromPath(String path) throws IllegalArgumentException public static UriBuilder fromResource(Classltgt resource) throws IllegalArgumentException public static UriBuilder fromLink(Link link) throws IllegalArgumentException

Las instancias de UriBuilder se obtienen a partir de meacutetodos estaacuteticos con la formafromXXX() Podemos inicializarlas a partir de una URI una cadena de caracteres o laanotacioacuten Path de una clase de recurso

Para extraer modificar yo componer una URI se pueden utilizar meacutetodos como

Clase UriBuilder meacutetodos para manipular las URIs

public abstract UriBuilder clone() crea una copia

crea una copia con la informacioacuten de un objeto URIpublic abstract UriBuilder uri(URI uri) throws IllegalArgumentException

meacutetodos para asignarmodificar valores de los atributos de los objetos UriBuilderpublic abstract UriBuilder scheme(String scheme) throws IllegalArgumentExceptionpublic abstract UriBuilder userInfo(String ui)public abstract UriBuilder host(String host) throws IllegalArgumentExceptionpublic abstract UriBuilder port(int port) throws IllegalArgumentExceptionpublic abstract UriBuilder replacePath(String path)

meacutetodos que antildeaden elementos a nuestra URIpublic abstract UriBuilder path(String path)public abstract UriBuilder segment(String segments)public abstract UriBuilder matrixParam(String name Object values)public abstract UriBuilder queryParam(String name Object values)

Servicios Rest

113

meacutetodo que instancia el valor de una plantilla de la URIpublic abstract UriBuilder resolveTemplate(String name Object value)

Los meacutetodos build() construyen la URI Eacutesta puede contener plantillas de paraacutemetros( segmentos de ruta variables) que deberemos inicializar utilizando pares nombrevalor o bienuna lista de valores que reemplazaraacuten a los paraacutemetros de la plantilla en el orden en el queaparezcan

Clase UriBuilder meacutetodos buildXXX() para construir las URIs

public abstract URI buildFromMap(MapltString extends Objectgt values) throws IllegalArgumentException UriBuilderException

public abstract URI build(Object values) throws IllegalArgumentException UriBuilderException

Veamos alguacuten ejemplo que muestra coacutemo crear inicializar componer y construir una URIutilizando un UriBuilder

UriBuilder builder = UriBuilderfromPath(clientesid)builderscheme(http) host(hostname) queryParam(param=param)

Con este coacutedigo estamos definiendo una URI como

httphostnameclientesidparam=param

Puesto que tenemos plantillas de paraacutemetros necesitamos inicializarlos con valores quepasaremos como argumentos para crear la URI final Si queremos reutilizar la URI quecontiene las plantillas deberiacuteamos realizar una llamada a clone() antes de llamar al meacutetodobuild() ya que eacuteste reemplazaraacute los paraacutemetros de las plantillas en la estructura internadel objeto

UriBuilder clone = builderclone()URI uri = clonebuild(ejemplocom 333 valor)

El coacutedigo anterior dariacutea lugar a la siguiente URI

httpejemplocomclientes333param=valor

Tambieacuten podemos definir un objeto de tipo Map que contenga los valores de las plantillas

MapltString Objectgt map = new HashMapltString Objectgt()

Servicios Rest

114

mapput(hostname ejemplocom)mapput(id 333)mapput(param valor)UriBuilder clone = builderclone()URI uri = clonebuildFromMap(map)

Otro ejemplo interesante es el de crear una URI a partir de las expresiones Path definidasen las clases JAX-RS anotadas A continuacioacuten mostramos el coacutedigo

Path(clientes)public class ServicioClientes

Path(id) public Cliente getCliente(PathParam(id) int id)

Podemos referenciar esta clase y el meacutetodo getCliente() a traveacutes de la claseUriBuilder de la siguiente forma

UriBuilder builder = UriBuilderfromResource(ServicioClientesclass)builderhost(hostname)builderpath(ServicioClientesclass getCliente)

El coacutedigo anterior define la siguiente plantilla para la URI

httphostnameclientesid

A partir de esta plantilla podremos construir la URI utilizando alguno de los meacutetodos`buildXXX()

Tambieacuten podemos querer utilizar UriBuilder para crear URIS a partir de plantillas Para ellodisponemos de meacutetodos resolveTemplateXXX() que nos facilitan el trabajo

Clase UriBuilder meacutetodos resolveTemplateXXX() para crear URIs a partir de plantillas

public abstract UriBuilder resolveTemplate(String name Object value)public abstract UriBuilder resolveTemplate(String name Object value boolean encodeSlashInPath)public abstract UriBuilder resolveTemplateFromEncoded(String nameObject value)public abstract UriBuilder resolveTemplates(MapltString Objectgt templateValues)public abstract UriBuilder resolveTemplates( MapltStringObjectgt templateValues boolean encodeSlashInPath) throws IllegalArgumentExceptionpublic abstract UriBuilder resolveTemplatesFromEncoded( MapltString Objectgt templateValues) Devuelve la URI de la plantilla como una cadena de caracterespublic abstract String toTemplate()

Servicios Rest

115

Funcionan de forma similar a los meacutetodos build() y se utilizan para resolver parcialmentelas plantillas contenidas en la URI Cada uno de los meacutetodos devuelve una nueva instanciade UriBuilder de forma que podemos encadenar varias llamadas para resolver todas lasplantillas de la URI Finalmente usaremos el meacutetodo toTemplate() para obtener la nuevaplantilla en forma de String

String original = httphostidString nuevaPlantilla = UriBuilderfromUri(original) resolveTemplate(host localhost) toTemplate()

El valor de nuevaPlantilla para el coacutedigo anterior seriacutea httplocalhostid

URIs relativas mediante el uso de UriInfo

Cuando estamos escribiendo servicios que distribuyen enlaces hay cierta informacioacuten queprobablemente no conozcamos cuando estamos escribiendo el coacutedigo Por ejemplo podemosno conocer todaviacutea los hostnames de los enlaces o incluso los base paths de las URIs en elcaso de que estemos enlazando con otros servicios REST

JAX-RS proporciona una forma sencilla de solucionar estos problemas utilizando la interfazjavaxwsrscoreUriInfo Ya hemos introducido algunas caracteriacutesticas de estainterfaz en sesiones anteriores Ademaacutes de poder consultar informacioacuten baacutesica de la rutatambieacuten podemos obtener instancias de UriBuilder preinicializadas con la URI base utilizadapara definir los servicios JAX-RS o la URI utilizada para invocar la peticioacuten HTTP actual

public interface UriInfo public URI getRequestUri() public UriBuilder getRequestUriBuilder() public URI getAbsolutePath() public UriBuilder getAbsolutePathBuilder() public URI getBaseUri() public UriBuilder getBaseUriBuilder()

Por ejemplo supongamos que tenemos un servicio que permite acceder a Clientes desdeuna base de datos En lugar de tener una URI base que devuelva todos los clientes en unuacutenico documento podemos incluir los enlaces previo y sigiente de forma que podamosnavegar por los datos Vamos a mostrar coacutemo crear estos enlaces utilizando la URI parainvocar la peticioacuten

Path(clientes)public class ServicioClientes GET Produces(applicationxml)

public String getCustomers(Context UriInfo uriInfo)

UriBuilder nextLinkBuilder = uriInfogetAbsolutePathBuilder() nextLinkBuilderqueryParam(inicio 5) nextLinkBuilderqueryParam(total 10) URI next = nextLinkBuilderbuild() rellenar el resto del documento

Servicios Rest

116

Para acceder a la instancia UriInfo que representa al peticioacuten usamos la anotacioacutenjavaxwsrscoreContext para inyectarla como un paraacutemetro del meacutetodo delrecurso RESTObtenemos un UriBuilder preininicializado con la URI utilizada para acceder alservicio

Para el coacutedigo anterior y dependiendo de coacutemo se despliegue el servicio la URI creada podriacuteaser

httporgexpertojavajaxrsclientesinicio=5amptotal=10

Construccioacuten de enlaces (Links) en documentos XML y en cabeceras HTTP

JAX-RS proporciona cierto soporte para construir los enlaces y devolverlos en las cabecerasde respuesta o bien incluirlos en los documentos XML Para ello podemos utilizar las clasesjavawsrscoreLink y javawsrscoreLinkBuilder

Clase abstracta javaxwsrscoreLink

public abstract class Link public abstract URI getUri() public abstract UriBuilder getUriBuilder() public abstract String getRel() public abstract ListltStringgt getRels() public abstract String getTitle() public abstract String getType() public abstract MapltString Stringgt getParams() public abstract String toString()

Link es una clase abstracta que representa todos los metadatos contenidos en una cabecerao en un enlace Atom El meacutetodo getUri() representa el atributo href del enlace Atom Elmeacutetodo getRel() representa el atributo rel y asiacute sucesivamente Podemos referenciar atodos los atributos a traveacutes del meacutetodo getParams() Finalmente el meacutetodo toString()convertiraacute la instancia Link en una cadena de caracteres con el formato de una cabeceraLink

Para crear instancias de Link utilizaremos un LinkBuilder que crearemos con algunode estos meacutetodos

public abstract class Link public static Builder fromUri(URI uri) public static Builder fromUri(String uri) public static Builder fromUriBuilder(UriBuilder uriBuilder) public static Builder fromLink(Link link) public static Builder fromPath(String path) public static Builder fromResource(Classltgt resource) public static Builder fromMethod(Classltgt resource String method)

Servicios Rest

117

Los meacutetodos fromXXX() funcionan de forma similar a UriBuilderfromXXX() Todosinicializan el UriBuilder subyacente que utilizaremos para construir el atributo href delenlace

Los meacutetodos link() uri() y uriBuilder() nos permiten sobreescribir la URIsubyacente del enlace que estamos creando

public abstract class Link interface Builder public Builder link(Link link) public Builder link(String link) public Builder uri(URI uri) public Builder uri(String uri) public Builder uriBuilder(UriBuilder uriBuilder)

Los siguientes meacutetodos nos permiten asignar valores a varios atributos del enlace que estamosconstruyendo

public Builder rel(String rel) public Builder title(String title) public Builder type(String type) public Builder param(String name String value)

Finalmente eacutel meacutetodo build() nos permitiraacute construir el enlace

public Link build(Object values)

El objeto LinkBuilder tiene asociado una UriBuilder subyacente Los valorespasados como paraacutemetros del meacutetodo build() son utilizados por el UriBuilder paracrear una URI para el enlace Veamos un ejemplo

Link link = LinkfromUri(httphostraizclientesid) rel(update)type(textplain) build(localhost 1234)

Si realizamos una llamada a toString() sobre la instancia del enlace ( link )obtendremos lo siguiente

httplocalhostraizclientes1234gt rel=update type=textplain

A continuacioacuten mostramos dos ejemplos que muestran coacutemo crear instancias Link en lascabeceras y en el cuerpo de la respuesta como un enlace Atom

Escritura de enlaces en cabeceras HTTP

Servicios Rest

118

PathGETResponse get() Link link = LinkfromUri(abc)build() Response response = ResponsenoContent() links(link) build() return response

Inclusioacuten de un enlace Atom en el documento XMl de respuesta

import javaxwsrscoreLink

XmlRootElementpublic class Cliente private String nombre private ListltLinkgt enlaces = new ArrayListltLinkgt()

XmlElement public String getNombre() return nombre

public void setNombre(String nom) thisnombre = nom

XmlElement(name = enlace)

XmlJavaTypeAdapter(LinkJaxbAdapterclass) public ListltLinkgt getEnlaces() return enlaces

La clase Link contiene tambieacuten un JaxbAdapter con una implementacioacuten de la claseJAXB XmlAdapter que mapea los objetos JAX-RS de tipo Link a un valor quepuede ser serializado y deserializado por JAXB

El coacutedigo de este ejemplo permite construir cualquier enlace y antildeadirlo a la clase Clientede nuestro dominio Los enlaces seraacuten convertidos a elementos XML que se incluiraacuten en eldocumento XML de respuesta

44 Seguridad

Es importante que los servicios rest permitan un acceso seguro a los datos y funcionalidadesque proporcionan Especialmente para servicios que permiten la realizacioacuten de actualizacionesen los datos Tambieacuten es interesante asegurarnos de que terceros no lean nuestros mensajese incluso permitir que ciertos usuarios accedan a determinadas funcionalidades pero a otrasno

Ademaacutes de la especificacioacuten JAX-RS podemos aprovechar los servicios de seguridad quenos ofrece la web y Java EE y utilizarla en nuestros servicios REST Estos incluyen

AutentificacioacutenHace referencia a la validacioacuten de la identidad del cliente que accede a los serviciosNormalmente implica la comprobacioacuten de si el cliente ha proporcionado unos credenciales

Servicios Rest

119

vaacutelidos tales como el password En este sentido podemos utilizar los mecanismos quenos proporciona la web y las facilidades del contenedor de servlets de Java EE paraconfigurar los protocolos de autentificacioacuten

AutorizacioacutenUna vea que el cliente se ha autenticado (ha validado su identidad) querraacute interactuarcon nuestro servicio REST La autorizacioacuten hace referencia a decidir si un cierto usuariopuede acceder e invocar un determinado meacutetodo sobre una determinada URI Por ejemplopodemos habilitar el acceso a operaciones PUTPOSTDELETE para ciertos usuarios peropara otros no En este caso utilizaremos las facilidades que nos propociona el contenedorde servlets de Java EE para realizar autorizaciones

EncriptadoCuando un cliente estaacute interaccionando con un servicio REST es posible que alguienintercepte los mensajes y los lea si la conexioacuten HTTP no es segura Los datos sensiblesdeberiacutean protegerse con servicios criptograacuteficos tales como SSL

Autentificacioacuten en JAX-RS

Hay varios protocolos de autentificacioacuten En este caso vamos a ver coacutemo realizar unaautenticacioacuten baacutesica sobre HTTP (y que ya habeacuteis utilizado para servlets) Este tipo deautentificacioacuten requiere enviar un nombre de usuario y password codificados como Base-64en una cabecera de la peticioacuten al servidor El servidor comprueba si existe dicho usuario en elsistema y verifica el password enviado Veaacutemoslo con un ejemplo

Supongamos que un cliente no autorizado quiere acceder a nuestros servicios REST

GET clientes333 HTTP11

Ya que la peticioacuten no contiene informacioacuten de autentificacioacuten el servidor deberiacutea responderla siguiente respuesta

HTTP11 401 UnauthorizedWWW-Autenticate Basic realm=Cliente Realm

La respuesta 401 nos indica que el cliente no estaacute autorizado a acceder a dicha URI Lacabecera WWW-Autenticate especifica queacute protocolo de autentificacioacuten se deberiacutea usarEn este caso Basic significa que se deberiacutea utilizar una autentificacioacuten de tipo Basic Elatributo realm identifica una coleccioacuten de recursos seguros en un sitio web En este ejemploindica que solamente estaacuten autorizados a acceder al meacutetodo GET a traveacutes de la URI anteriortodos aquellos uarios que pertenezcan al realm Cliente Realm y seraacuten autentificados porel servidor mediante una autentificacioacuten baacutesica

Para poder realizar la autentificacioacuten el cliente debe enviar una peticioacuten que incluya lacabecera Authorization cuyo valor sea Basic seguido de la siguiente cadena decaracteres loginpassword codificada en Base64 (el valor de login y password representael login y password del usuario) Por ejemplo supongamos que el nombre del usuario esfelipe y el password es locking la cadena felipelocking codificada como Base64es ZmVsaXBlOmxvY2tpbmc= Por lo tanto nuestra peticioacuten deberiacutea ser la siguiente

GET clientes333 HTTP11

Servicios Rest

120

Authorization Basic ZmVsaXBlOmxvY2tpbmc=

El cliente deberiacutea enviar esta cabecera con todas y cada una de las peticiones que haga alservidor

El inconveniente de esta aproximacioacuten es que si la peticioacuten es interceptada por alguna entidadhostil en la red el hacker puede obtner faacutecilmente el usuario y el passwork y utilizarlos parahacer sus propias peticiones Utilizando una conexioacuten HTTP encriptada (HTTPS) se solucionaeste problema

Creacioacuten de usuarios y roles

Para poder utilizar la autentificacioacuten baacutesica necesitamos tener creados previamente los realmsen el servidor de aplicaciones Wildfly y registrar los usuarios que pertenecen a dichos realmsLa forma de hacerlo es ideacutentica a lo que ya habeacuteis visto en la asignatura de ComponentesWeb (a traveacutes del comando add-usersh )

Utilizaremos el realm por defecto ApplicationRealm de Wildfly que nos permitiraacute ademaacutescontrolar la autorizacioacuten mediante la asignacioacuten de roles a usuarios

Lo uacutenico que tendremos que hacer es antildeadir los usuarios a dicho realm a traveacutes de laherramienta $WILDFLY_HOMEbinadd-usersh

Al ejecutarla desde liacutenea de comandos deberemos elegir el ream ApplicationRealm eintroducir los datos para cada nuevo usuario que queramos antildeadir indicando su loginpassword y el grupo (rol) al que queremos que pertenezca dicho usuario

Los datos sobre los nuevos usuarios creados se almacenan en los ficheros application-usersproperties y application-rolesproperties tanto en el directorio$WILDFLY_HOMEstandaloneconfiguration como en $WILDFLY_HOMEdomainconfiguration

Una vez creados los usuarios tendremos que incluir en el fichero de configuracioacuten webxml la siguiente informacioacuten

ltweb-appgt

ltlogin-configgt ltauth-methodgtBASICltauth-methodgt

ltrealm-namegtApplicationRealmltrealm-namegt ltlogin-configgt

ltsecurity-constraintgt ltweb-resource-collectiongt ltweb-resource-namegtcustomer creationltweb-resource-namegt

lturl-patterngtrestresourceslturl-patterngt

lthttp-methodgtPOSTlthttp-methodgt ltweb-resource-collectiongt ltsecurity-constraintgt ltweb-appgt

Servicios Rest

121

El elemento ltlogin-configgt define coacutemo queremos autentificar nuestro despliegueEl subelemento ltauth-methodgt puede tomar los valores BASIC DIGEST orCLIENT_CERT correspondieacutendose con la autentificacioacuten Basic Digest y ClientCertificate respectivamenteEl valor de la etiqueta ltrealm-namegt es el que se mostraraacute como valor del atributorealm de la cabecera WWW-Autenticate si intentamos acceder al recurso sin incluirnuestras credenciales en la peticioacutenEl elemento ltlogin-configgt realmente NO activa la autentificacioacuten Por defectocualquier cliente puede acceder a cualquier URL proporcionada por nuestra aplicacioacutenweb sin restricciones Para forzar la autentificacioacuten debemos especificar el patroacuten URLque queremos asegurar (elemento lturl-patterngt )El elemento lthttp-methodgt nos indica que solamente queremos asegurar laspeticiones POST sobre esta URL Si no incluimos el elemento lthttp-methodgt todoslos meacutetodos HTTP seraacuten seguros En este ejemplo solamente queremos asegurar losmeacutetodos POST dirigidos a la URL restresources

Autorizacioacuten en JAX-RS

Mientras que la autentificacioacuten hacer referencia a establecer y verificar la identidad del usuariola autorizacioacuten tiene que ver con los permisos iquestEl usuario X estaacute autorizado para acceder aun determinado recurso REST

JAX-RS se basa en las especificaciones Java EE y de servlets para definir la forma de autorizara los usuarios En Java EE la autorizacioacuten se realiza asociando uno o maacutes roles con un usuariodado y a continuacioacuten asignando permisos basados en dicho rol Ejemplos de roles puedenser administrador empleado Cada rol tiene asignando unos permisos de acceso adeterminados recursos por lo que asignaremos los permisos utilizando cada uno de los roles

Para poder realizar la autorizacioacuten tendremos que incluir determinadas etiquetas en el ficherode configuracioacuten webxml (tal y como ya habeacuteis visto en la asignatura de ComponentesWeb) Veaacutemoslo con un ejemplo (en el que tambieacuten incluiremos autentificacioacuten)

Volvamos a nuestra aplicacioacuten de venta de productos por internet En esta aplicacioacutenes posible crear nuevos clientes enviando la informacioacuten en formato XML a un recursoJAX-RS localizado por la anotacioacuten Path(clientes) El servicio REST esdesplegado y escaneado por la clase Application anotada con ApplicationPath(servicios) de forma que la URI completa es serviciosclientes Queremosproporcionar seguridad a nuestro servicio de clientes de forma que solamente losadministradores puedan crear nuevos clientes Veamos cuaacutel seriacutea el contenido del ficherowebxml

ltxml version=10gtltweb-appgt ltsecurity-constraintgt ltweb-resource-collectiongt ltweb-resource-namegtcreacion de clientesltweb-resource-namegt lturl-patterngtserviciosclienteslturl-patterngt lthttp-methodgtPOSTlthttp-methodgt ltweb-resource-collectiongt

ltauth-constraintgt ltrole-namegtadminltrole-namegt ltauth-constraintgt ltsecurity-constraintgt

Servicios Rest

122

ltlogin-configgt ltauth-methodgtBASICltauth-methodgt ltrealm-namegtApplicationRealmltrealm-namegt ltlogin-configgt

ltsecurity-rolegt ltrole-namegtadminltrole-namegt ltsecurity-rolegtltweb-appgt

Especificamos queacute roles tienen permiso para acceder mediante POST a la URLservicescustomers Para ello utilizamos el elemento ltauth-constraintgtdentro de ltsecurity-constraintgt Este elemento tiene uno o maacutes subelementosltrole-namegt que definen queacute roles tienen permisos de acceso definidos porltsecurity-constraintgt En nuestro ejemplo estamos dando al rol adminpermisos para acceder a la URL servicescustomers con el meacutetodo POST Si ensu lugar indicamos un ltrole-namegt con el valor cualquier usuario podriacutea accedera dicha URL En otras palabras un ltrole-namegt con el valor significa que cualquierusuario que sea capaz de autentificarse puede acceder al recursoPara cada ltrole-namegt que usemos en nuestras declaraciones ltauth-constraintsgt debemos definir el correspondiente ltsecurity-rolegt en eldescriptor de despliegue

Una limitacioacuten cuando estamos declarando las ltsecurity-contraintsgt para los recursosJAX-RS es que el elemento lturl-patterngt solamente soporta el uso de en el patroacutenurl especificado Por ejemplo rest txt

Encriptacioacuten

Por defecto la especificacioacuten de servlets no requiere un acceso a traveacutes de HTTPSSi queremos forzar un acceso HTTPS podemos especificar un elemento ltuser-data-constraintgt como parte de nuestra definicioacuten de restricciones de seguridad ( ltsecurity-constraintgt ) Vamos a modificar nuestro ejemplo anterior para forzar un acceso a traveacutesde HTTPS

ltweb-appgt ltsecurity-constraintgt

ltweb-resource-collectiongt ltweb-resource-namegtcreacion de clientesltweb-resource-namegt lturl-patterngtserviciosclienteslturl-patterngt lthttp-methodgtPOSTlthttp-methodgt ltweb-resource-collectiongt

ltauth-constraintgt ltrole-namegtadminltrole-namegt ltauth-constraintgt

ltuser-data-constraintgt

lttransport-guaranteegtCONFIDENTIALlttransport-guaranteegt ltuser-data-constraintgt ltsecurity-constraintgt

Servicios Rest

123

ltweb-appgt

Todo lo que tenemos que hacer es declarar un elemento lttransport-guaranteegtdentro de ltuser-data-constraintgt con el valor CONFIDENTIAL Si un usuariointenta acceder a una URL con el patroacuten especificado a traveacutes de HTTP seraacute redirigidoa una URL basada en HTTPS

Anotaciones JAX-RS para autorizacioacuten

Java EE define un conjunto de anotaciones para definir metadatos de autorizacioacuten Laespecificacioacuten JAX-RS sugiere aunque no es obligatorio que las implementaciones pordiferentes vendedores den soporte a dichas anotaciones Eacutestas se encuentran en elpaquete javaxannotationsecurity y son RolesAllowed DenyAll PermitAll yRunAs

La anotacioacuten RolesAllowed define los roles permitidos para ejecutar una determinadaoperacioacuten Si anotamos una clase JAX-RS define el acceso para todas las operacionesHTTP definidas en la clase JAX-RS Si anotamos un meacutetodo JAX-RS la restriccioacuten se aplicasolamente al meacutetodo que se estaacute anotando

La anotacioacuten PermitAll especifica que cualquier usuario autentificado puede invocar anuestras operaciones Al igual que RolesAllowed esta anotacioacuten puede usarse en la clasepara definir el comportamiento por defecto de toda la clase o podemos usarla en cada unode los meacutetodos Veamos un ejemplo

Path(clientes)

RolesAllowed(ADMIN CLIENTE) public class ClienteResource

GET Path(id) Produces(applicationxml) public Cliente getClienter(PathParam(id) int id)

RolesAllowed(ADMIN) POST Consumes(applicationxml) public void crearCliente(Customer cust)

PermitAll GET Produces(applicationxml) public Customer[] getClientes()

Por defecto solamente los usuarios con rol ADMIN y CLIENTE pueden ejecutar losmeacutetodos HTTP definidos en la clase ClienteResourceSobreescribimos el comportamiento por defecto Para el meacutetodo crearCliente()solamente permitimos peticiones de usuarios con rol ADMINSobreescribimos el comportamiento por defecto Para el meacutetodo getClientes() deforma que cualquier usuario autentificado puede acceder a esta operacioacuten a traveacutes de laURI correspondiente con el meacutetodo GET

Servicios Rest

124

La ventaja de utilizar anotaciones es que nos permite una mayor flexibilidad que el usodel fichero de configuracioacuten webxml pudiendo definir diferentes autorizaciones a nivel demeacutetodo

Seguridad programada

Hemos visto como utilizar una seguridad declarativa es decir basaacutendonos en meta-datosdefinidos estaacuteticamente antes de que la aplicacioacuten se ejecute JAX-RS proporciona unaforma de obtener informacioacuten de seguridad que nos permite implementar seguridad de formaprogramada en nuestras aplicaciones

Podemos utilizar la interfaz javaxwsrscoreSecurityContext para determinar laidentidad del usuario que realiza la invocacioacuten al meacutetodo proporcionando sus credencialesTambieacuten podemos comprobar si el usuario pertenece o no a un determinado rol Esto nospermite implementar seguridad de forma programada en nuestras aplicaciones

public interface SecurityContext public Principal getUserPrincipal() public boolean isUserInRole(String role) public boolean isSecure() public String getAuthenticationScheme()

El meacutetodo getUserPrincipal() devuelve un objeto de tipojavaxsecurityPrincipal que representa al usuario que actualmente estaacute realizandola peticioacuten HTTP

El meacutetodo isUserInRole() nos permite determinar si el usuario que realiza la llamadaactual pertenece a un determinado rol

El meacutetodo isSecure() devuelve cierto si la peticioacuten actual es una conexioacuten segura

El meacutetodo getAuthenticationScheme() nos indica queacute mecanismo de autentificacioacuten seha utilizado para asegurar la peticioacuten (valores tiacutepicos devueltos por el meacutetodo son BASIC DIGEST CLIENT_CERT y FORM )

Podemos acceder a una instancia de SecurityContext inyectaacutendola en un campo meacutetodosetter o un paraacutemetro de un recurso utilizando la anotacioacuten Context Veamos un ejemploSupongamos que queremos obtener un fichero de log con todos los accesos a nuestra basede datos de clientes hechas por usuarios que no son administradores

Path(clientes)public class CustomerService GET Produces(applicationxml) public Cliente[] getClientes(Context SecurityContext sec) if (secisSecure() ampamp secisUserInRole(ADMIN)) loggerlog(secgetUserPrincipal() + ha accedido a la base de datos de clientes)

Servicios Rest

125

En este ejemplo inyectamos una instancia de SecurityContext como un paraacutemetro delmeacutetodo getClientes() Utilizamos el meacutetodo SecurityContextisSecure() paradeterminar si se trata de una peticioacuten realizada a traveacutes de un canal seguro (como HTTPS) Acontinuacioacuten utilizamos el meacutetodo SecurityContextisUserInRole() para determinarsi el usuario que realiza la llamada tiene el rol ADMIN o no Finalmente imprimimos el resultadoen nuestro fichero de logs

Con la introduccioacuten del API de filtros en JAX-RS 20 podemos implementar la interfazSecurityContext y sobreescribir la peticioacuten actual sobre SecurityContext utilizandoel meacutetodo ContainerRequestContextsetSecurityContext() Lo interesante deesto es que podemos implementar nuestros propios protocolos de seguridad Por ejemplo

import javaxwsrscontainerContainerRequestContextimport javaxwsrscontainerContainerRequestFilterimport javaxwsrscontainerPreMatchingimport javaxwsrscoreSecurityContextimport javaxwsrscoreHttpHeaders

PreMatchingpublic class CustomAuth implements ContainerRequestFilter protected MyCustomerProtocolHandler customProtocol =

public void filter(ContainerRequestContext requestContext) throws IOException String authHeader = requestgetHeaderString(HttpHeadersAUTHORIZATION) SecurityContext newSecurityContext = customProtocolvalidate(authHeader) requestContextsetSecurityContext(authHeader)

Este filtro no muestra todos los detalles pero siacute la idea Extrae la cabecera Authorizationde la peticioacuten y la pasa a nuestro propio servicio customerProtocol Eacuteste devuelve unaimplementacioacuten de SecurityContext Finalmente sobreescribimos el SecurityContext pordefecto utilizando la nueva implementacioacuten

No vamos a explicar el API de filtros de JAS-RS 20 Como ya habeacuteis visto en la asignaturade Componentes Web los filtros son objetos que se interponen entre el procesamiento delas peticiones tanto del servidor como del cliente

El filtro mostrado en el ejemplo es un filtro de peticioacuten en la parte del servidor Este tipo defiltros se ejecuta antes de que se invoque a un meacutetodo JAX-RS

Servicios Rest

126

45 Ejercicios

Para los ejercicios de esta sesioacuten proporcionamos el MOacuteDULO s4-foroAvanzado quetendreacuteis que usar como plantilla para realizar las tareas planteadas

El proyecto estaacute estructurado loacutegicamente en los siguientes paquetes

bull orgexpertojavanegocio

bull orgexpertojavarest

A su vez cada uno de ellos contiene los subpaquetes api y modelo con las clasesrelacionadas con los servicios proporcionados y los datos utilizados por los serviciosrespectivamente

El API rest implementado es el siguiente

bull Recurso UsuariosResourcejava

GET usuarios proporciona un listado con los usuarios del foro

bull Subrecurso UsuarioResourcejava

GET usuarioslogin proporciona informacioacuten sobre el usuario cuyo login es login

PUT usuarioslogin actualiza los datos de un usuario

DELETE usuarioslogin borra los datos de un usuario

GET usuariosloginmensajes obtiene un listado de los mensajes de un usuario

bull Recurso MensajesResourcejava

GET mensajes proporciona un listado con los mensajes del foro

POST mensajes antildeade un mensaje nuevo en el foro

GET mensajesid proporciona informacioacuten sobre el mensaje cuyo id es id

PUT mensajesid modifica un mensaje

DELETE mensajesid borra un mensaje

Una vez desplegada la aplicacioacuten podeacuteis antildeadir datos a la base de datos del foro utilizandolos datos del fichero srcmainresourcesforosql Para ello simplemente tendreacuteis que invocarla goal Maven correspondiente desde la ventana Maven Projects gt s4-foroAvanzado gt Pluginsgt sql gt sqlexecute

En el directorio srcmainresources teneacuteis un fichero de texto ( instruccionestxt )con informacioacuten adicional sobre la implementacioacuten proporcionada

A partir de las plantillas se pide

Uso de Hateoas (1 puntos)

Vamos a antildeadir a los servicios enlaces a las operaciones que podemos realizar con cadarecurso siguiendo el estilo Hateoas

a Para los usuarios

Servicios Rest

127

bull En el listado de usuarios antildeadir a cada usuario un enlace con relacioacuten self que apuntea la direccioacuten a la que estaacute mapeado el usuario individual

bull En la operacioacuten de obtencioacuten de un usuario individual incluir los enlaces para ver elpropio usuario (self) modificarlo (usuariomodificar) borrarlo (usuarioborrar) o ver losmensajes que envioacute el usuario (usuariomensajes)

b Para los mensajes

bull En el listado de mensajes antildeadir a cada mensaje un enlace con relacioacuten self queapunte a la direccioacuten a la que estaacute mapeado el mensaje individual

bull En la operacioacuten de obtencioacuten de un mensaje individual incluir los enlaces para ver elpropio mensaje (self) modificarlo (mensajemodificar) borrarlo (mensajeborrar) o verlos datos del usuario que envioacute el mensaje (mensajeusuario)

Utiliza postman para comprobar las modificaciones realizadas

Ejercicio seguridad (1 punto)

Vamos ahora a restringir el acceso al servicio para que soacutelo usuarios registrados puedanrealizar modificaciones Se pide

a Antildeadir al usuario pepe en el ApplicationRealm de wildfly con la contrasentildea pepe yperteneciente al grupo (rol) registrado

b Configurar mediante seguridad declarativa para que las operaciones de modificacioacuten(POST PUT y DELETE) soacutelo la puedan realizar los usuarios con rol registrado Utilizarautentificacioacuten de tipo BASIC

c Ahora vamos a hacer que la modificacioacuten o borrado de usuarios soacutelo puedarealizarlas el mismo usuario que va a modificarse o borrarse Para ello utilizaremosseguridad programada En el caso de que el usuario que va a realizar lamodificacioacuten o borrado quiera borrarmodificar otros usuarios lanzaremos la excepcioacutenWebApplicationException(StatusFORBIDDEN)

d Vamos a hacer lo mismo con los mensajes Soacutelo podraacute modificar y borrar mensajes elmismo usuario que los creoacute y al publicar un nuevo mensaje forzaremos que el login delmensaje sea el del usuario que hay autentificado en el sistema

Utiliza postman para comprobar las modificaciones realizadas

Servicios Rest

128

5 Api cliente Procesamiento JSON y Pruebas

Hasta ahora hemos hablado sobre la creacioacuten de servicios web RESTful y hemos probadonuestros servicios utilizando el cliente que nos proporciona IntelliJ curl o Postman pararealizar peticiones y observar las respuestas JAX-RS 20 proporciona un API cliente parafacilitar la programacioacuten de clientes REST que presentaremos en esta sesioacuten

En sesiones anteriores hemos trabajado con representaciones de texto y xmlfundamentalmente Aquiacute hablaremos con maacutes detalle de JSON que constituye otra forma derepresentar los datos de las peticiones y respuestas de servicios REST muy extendida

Finalmente veremos coacutemo implementar pruebas sobre nuestro servicio utilizando el APIcliente y el framework junit

51 API cliente Visioacuten general

La especificacioacuten JAX-RS 20 incorpora un API cliente HTTP que facilita enormemente laimplementacioacuten de nuestros clientes RESTful y constituye una clara alternativa al uso declases Java como javanetURL libreriacuteas externas (como la de Apache) u otras solucionespropietarias

El API cliente estaacute contenido en el paquete javaxwsrsclient y estaacute disentildeado paraque se pueda utilizar de forma fluida (fluent) Esto significa como ya hemos visto que loutilizaremos encadenando una sucesioacuten de llamadas a meacutetodos del API permitieacutendonos asiacuteescribir menos liacuteneas de coacutedigo Baacutesicamente estaacute formado por tres clases principales ClientWebTarget y Response (ya hemos hablado de esta uacuteltima en sesiones anteriores)

Para acceder a un recurso REST mediante el API cliente es necesario seguir los siguientespasos

1 Obtener una instancia de la interfaz Client

2 Configurar la instancia Client a traveacutes de un target (instancia de WebTarget )

3 Crear una peticioacuten basada en el target anterior

4 Invocar la peticioacuten

Vamos a mostrar un coacutedigo ejemplo para ilustrar los pasos anteriores En este caso vamos ainvocar peticiones POST y GET sobre una URL (target) para crear y posteriormente consultarun objeto Cliente que representaremos en formato XML

Client client = ClientBuildernewClient()

WebTarget target =

clienttarget(httpexpertojavaorgclientes)

Response response = target

request()

post(Entityxml(new Cliente(Alvaro Gomez)))

responseclose()

Servicios Rest

129

Cliente cliente = targetqueryParam(nombre Alvaro Gomez) request()

get(Clienteclass) clientclose()

Obtenemos una instancia javaxwsrsclientClientCreamos un WebTargetCreamos la peticioacutenRealizamos una invocacioacuten POSTCerramos (liberamos) el input stream para esta respuesta (en el caso de que esteacutedisponible y abierto) Es una operacioacuten idempotente es decir podemos invocarlamuacuteltiples veces con el mismo efectoA partir de un WebTarget establecemos los valores de los queryParams de la URIde la peticioacuten creamos la peticioacuten y realizamos una invocacioacuten GET

A continuacioacuten explicaremos con detalle los pasos a seguir para implementar un clienteutilizando el API de JAX-RS

Obtenemos una instancia Client

La interfaz javaxwsrsclientClient es el principal punto de entrada del API ClienteDicha interfaz define las acciones e infraestructura necesarias requeridas por un cliente RESTpara consumir un servicio web RESTful Los objetos Client se crean a partir de la claseClientBuilder

Clase ClientBuilder utilizada para crear objetos Client

public abstract class ClientBuilder implements ConfigurableltClientBuildergt public static Client newClient() public static Client newClient(final Configuration configuration)

public static ClientBuilder newBuilder()

public abstract ClientBuilder sslContext(final SSLContext sslContext) public abstract ClientBuilder keyStore(final KeyStore keyStore final char[] password) public ClientBuilder keyStore(final KeyStore keyStore final String password) public abstract ClientBuilder trustStore(final KeyStore trustStore) public abstract ClientBuilder hostnameVerifier(final HostnameVerifier verifier)

public abstract Client build()

La forma maacutes sencilla de crear un objeto Client es medianteClientBuildernewClient() Este meacutetodo proporciona una instancia pre-inicializada detipo Client lista para ser usada La clase ClientBuilder nos proporciona meacutetodos adicionalescon los que podremos configurar diferentes propiedades del objeto

Veamos un ejemplo de uso de ClientBuildernewBuilder() utilizando ademaacutes algunode los meacutetodos proporcionados para configurar nuestra instancia de tipo Client que vamos

Servicios Rest

130

a crear Los meacutetodos register() y property() son meacutetodos de la interfaz Configurable(y que son implementados por ClientBuilder)

Ejemplo de uso de ClientBuilder

Client cliente = ClientBuildernewBuilder()

property(connectiontimeout 100)

sslContext(sslContext)

register(JacksonJsonProviderclass)

build()

Creamos un ClientBuilder invocando al meacutetodo estaacuteticoClientBuildernewBuilder()Asignamos una propiedad especiacutefica de la implementacioacuten concreta de JAX-RS queestemos utilizando que controla el timeout de las conexiones de los socketsEspecificamos el sslContext que queremos utilizar para gestionar las conexiones HTTPRegistramos a traveacutes del meacutetodo register() (de la interfaz Configurable) una claseanotada con Provider Dicha clase conoce coacutemo serializar objetos Java a JSONy viceversaFinalmente realizamos una llamada a build() para crear la instancia Client

Las instancias de Client gestionan conexiones con el cliente utilizando sockets y sonobjetos bastante pesados Se deberiacutean reutilizar las instancias de esta interfaz en la medida delo posible ya que la inicializacioacuten y destruccioacuten de dichas instancias consume mucho tiempoPor lo tanto por razones de rendimiento debemos limitar el nuacutemero de instancias Clienten nuestra aplicacioacuten

Client client = ClientBuildernewClient()

clientclose()

Obtenemos una instancia de tipo Client invocando al meacutetodoClientBuildernewClient()Utilizamos el meacutetodo close() para cerrar la instancia Client despueacutes de realizartodas las invocaciones sobre el target del recurso De esta forma cerramos la conexioacutende forma que se liberan sus recursos y ya no podremos seguir usaacutendola

Recuerda siempre invocar el meacutetodo close() sobre nuestros objetosClient despueacutes de que hayamos realizado todas las invocacionessobre el target dellos recursos REST A menudo los objetos Clientreutilizan conexiones por razones de rendimiento Si no los cerramosdespueacutes de utilizarlos estaremos desaprovechando recursos del sistemamuy valiosos Cerrar la conexioacuten implica cerrar el socket

Igualmente si el resultado de una invocacioacuten a un servicio rest es unainstancia de Response debemos invocar el meacutetodo close() sobredichos objetos Response para liberar la conexioacuten Liberar una conexionsignifica permitir que eacutesta esteacute disponible para otro uso por una instanciaClient Liberar la conexioacuten no implica cerrar el socket

La interfaz Client es una sub-interfaz de Configurable Esto nos permitiraacute utililizarlos meacutetodos property() y register() para cambiar la configuracioacuten y registrarcomponentes en la parte del cliente en tiempo de ejecucioacuten

Servicios Rest

131

Interfaz Client (es una subinterfaz de Configurable)

public interface Client extends ConfigurableltClientgt

public void close()

public WebTarget target(String uri) public WebTarget target(URI uri) public WebTarget target(UriBuilder uriBuilder) public WebTarget target(Link link)

Sin embargo el principal propoacutesito de Client es crear instancias de WebTarget comoveremos a continuacioacuten

Configuramos el target del cliente (URI)

La interfaz javaxwsrsclientWebTarget representa la URI especiacutefica quequeremos invocar para acceder a un recurso REST particular

Interfaz WebTarget (es una subinterfaz de Configurable)

public interface WebTarget extends ConfigurableltWebTargetgt

public URI getUri() public UriBuilder getUriBuilder()

public WebTarget path(String path) public WebTarget resolveTemplate(String name Object value) public WebTarget resolveTemplates(MapltString Objectgt templateValues) public WebTarget matrixParam(String name Object values) public WebTarget queryParam(String name Object values)

La interfaz WebTarget tiene meacutetodos para extender la URI inicial que hayamosconstruido Podemos antildeadir por ejemplo segmentos de path o paraacutemetros deconsulta invocando a los meacutetodos WebTargetpath() o WebTargetqueryParam() respectivamente Si la instancia de WebTarget contiene plantillas de paraacutemetros losmeacutetodos WebTargetresolveTemplate() pueden asignar valores a las variablescorrespondientes Por ejemplo

Ejemplo para crear la URI httpejemplocomclientes123verboso=true

WebTarget target = client

target(httpejemplocomclientesid)

resolveTemplate(id 123)

queryParam(verboso true)

Servicios Rest

132

Inicializamos un WebTarget con una URI que contiene una plantilla con un paraacutemetroid El objeto client es una instancia de la clase `ClientEl meacutetodo resolveTemplate() rellena la expresioacuten id con el valor 123Finalmente antildeadimos a la URI un paraacutemetro de consulta verboso=true

Las instancias de WebTarget son inmutables con respecto a la URI que contienen Estosignifica que los meacutetodos para especificar segmentos de path adicionales y paraacutemetrosdevuelven una nueva instancia de WebTarget Sin embargo las instancias de WebTargetson mutables respecto a su configuracioacuten Por lo tanto la configuracioacuten de objetosWebTarget no crea nuevas instancias

Veamos otro ejemplo

WebTarget base = clientetarget(httpexpertojavaorg)

WebTarget clienteURI = basepath(cliente)

clienteURIregister(MyProviderclass)

base es una instancia de WebTarget con el valor de URI httpexertojavaorgclienteURI es una instancia de WebTarget con el valor de URI httpexertojavaorgclienteConfiguramos clienteURI registrando la clase MyProvider

En este ejemplo creamos dos instancias de WebTarget La instancia clienteURI heredala configuracioacuten de base y posteriormente modificamos la configuramos registrando unaclase Provider Los cambios sobre la configuracioacuten de clienteURI no afectan a laconfiguracioacuten de base ni tampoco se crea una nueva instancia de WebTarget

Los beneficios del uso de WebTarget se hacen evidentes cuando construimos URIscomplejas por ejemplo cuando extendemos nuestra URI base con segmentos de pathadicionales o plantillas El siguiente ejemplo ilustra estas situaciones

WebTarget base = clientetarget(httpexpertojavaorg)

WebTarget saludo = basepath(hola)path(quien) Response res = saludoresolveTemplate(quien mundo)request()get()

base representa la URI httpexpertojavaorgsaludo representa la URI httpexpertojavaholaquien

En el siguiente ejemplo utilizamos una URI base y a partir de ella construimos otras URIs querepresentan servicios diferentes proporcionados por nuestro recurso REST

Client cli = ClientBuildernewClient()WebTarget base = clienttarget(httpejemplowebapi)

WebTarget lectura = basepath(leer)

WebTarget escritura = basepath(escribir)

lectura representa la uri httpejemplowebapileerescritura representa la uri httpejemplowebapiescribir

El meacutetodo WebTargetpath() crea una nueva instancia de WebTarget antildeadiendo a laURI actual el segmento de ruta que se pasa como paraacutemetro

Servicios Rest

133

Construimos y Realizamos la peticioacuten

Una vez que hemos creado y configurado convenientemente el WebTarget que representala URI que queremos invocar tenemos que construir la peticioacuten y finalmente realizarla

Para construir la peticioacuten podemos Utilizar uno de los meacutetodos WebTargetrequest() que mostramos a continuacioacuten

Interfaz WebTarget meacutetodos para comenzar a construir la peticioacuten

public interface WebTarget extends ConfigurableltWebTargetgt public InvocationBuilder request() public InvocationBuilder request(String acceptedResponseTypes) public InvocationBuilder request(MediaType acceptedResponseTypes)

Normalmente invocaremos WebTargetrequest() pasando como paraacutemetro el mediatype aceptado como respuesta en forma de String o utilizando una de las constantes dejavaxwsrscoreMediaType Los meacutetodos WebTargetrequest() devuelven unainstancia de InvocationBuilder una interfaz que proporciona meacutetodos para prepararla peticioacuten del cliente y tambieacuten para invocarla

La interface InvocationBuilder Contiene un conjunto de meacutetodos que nos permitenconstruir diferentes tipos de cabeceras de peticiones Asiacute por ejemplo proporciona variosmeacutetodos acceptXXX() para indicar diferentes tipos MIME lenguajes o encodingaceptados Tambieacuten proporciona meacutetodos cookie() para especificar cookies para enviaral servidor Finalmente proporciona meacutetodos header() para especificar diferentes valoresde cabeceras

Ejemplos de uso de esta interfaz para construir la peticioacuten

Client cli = ClientBuildernewClient()cliinvocation(LinkvalueOf(httpejemplorest))accept(applicationjson)get()si no utilizamos el meacutetodo invocation podemos hacerlo asiacuteclitarget(httpejemplorest)request(applicationjson)get()

Client cliente = ClientBuildernewClient()WebTarget miRecurso = clienttarget(httpejemplowebapimensaje) request(MediaTypeTEXT_PLAIN)

El uso de una constante MediaType es equivalente a utilizar el String que define el tipoMIME

InvocationBuilder builder = miRecursorequest(textplain)

Hemos visto que WebTarget implementa meacutetodos request() cuyosparaacutemetros especifican el tipo MIME de la cabecera Accept de la peticioacutenEl coacutedigo puede resultar maacutes legible si usamos en su lugar el meacutetodo

Servicios Rest

134

InvocationBuilderaccept() En cualquier caso es una cuestioacuten de gustospersonales

Despueacutes de determinar el media type de la respuesta invocamos la peticioacuten realizando unallamada a uno de los meacutetodos de la instancia de InvocationBuilder que se correspondecon el tipo de peticioacuten HTTP esperado por el recurso REST al que va dirigido dicha peticioacutenEstos meacutetodos son

bull get()

bull post()

bull delete()

bull put()

bull head()

bull options()

La interfaz InvocationBuilder es una subinterfaz de la interfaz SyncInvoker y es la queespecifica los meacutetodos anteriores (get post hellip) para realizar peticiones siacutencronas es decirque hasta que no nos conteste el servidor no podremos continuar procesando el coacutedigo enla parte del cliente

Las peticiones GET tienen los siguientes prototipos

Interface SyncInvoker peticiones GET siacutencronas

public interface SyncInvoker ltTgt T get(ClassltTgt responseType) ltTgt T get(GenericTypeltTgt responseType) Response get()

Los primeros dos meacutetodos geneacutericos convertiraacuten una respuesta HTTP con eacutexito a tipos Javaespeciacuteficos indicados como paraacutemetros del meacutetodo El tercero devuelve una instancia de tipoResponse Por ejemplo

Ejemplos de peticiones GET utilizando el API cliente jasx-rx 20

Client cli = ClientBuildernewClient()

peticioacuten get que devuelve una instancia de ClienteCliente cliRespuesta = clitarget(httpejemploclientes123) request(applicationjson)

get(Clienteclass)

peticioacuten get que devuelve una lista de objetos ClienteListltClientegt cliRespuesta2 = clitarget(httpejemploclientes) request(applicationxml)

get(new GenericTypeltListltClientegtgt() )

peticioacuten get que devuelve un objeto de tipo ResponseResponse respuesta = clitarget(httpejemploclientes245)

Servicios Rest

135

request(applicationjson)

get() try if (respuestagetStatus() == 200) Cliente cliRespuesta =

respuestareadEntity(Clienteclass) finally respuestaclose()

En la primera peticioacuten queremos que el servidor nos devuelva la respuesta en formatoJSON y posteriormente la convertiremos en el tipo Cliente utilizando un de loscomponentes MessageBodyReader registradosEn la segunda peticioacuten utilizamos la clase javaxwsrscoreGenericType parainformar al correspondiente MessageBodyReader del tipo de objetos de nuestra ListaPara ello creamos una clase anoacutenima a la que le pasamos como paraacutemetro el tipogeneacuterico que queremos obtenerEn la tercera peticioacuten obtenemos una instancia de Response a partir de la cual podemosobtener el cuerpo del mensaje de respuesta del servidorEl meacutetodo readEntity() asocia el tipo Java solicitado (en este caso el tipo java Cliente) yel contenido de la respuesta recibida con el correspondiente proveedor de entidades (detipo MessageBodyReader) para obtener dicho tipo Java a partir de la respuesta HTTPrecibida En sesiones anteriores hemos utilizado la clase Response desde el servicioREST para construir la respuesta que se enviacutea al cliente

Recordemos algunos de los meacutetodos que podemos utilizar desde el cliente para analizar larespuesta que hemos obtenido

Meacutetodos de la clase Response que utilizaremos desde el cliente

public abstract class Response

public abstract Object getEntity()

public abstract int getStatus()

public abstract ResponseStatusType getStatusInfo()

public abstract MultivaluedMapltString Objectgt getMetadata()

public abstract URI getLocation()

public abstract MediaType getMediaType()

public MultivaluedMapltStringObjectgt getHeaders()

public abstract ltTgt T readEntity(ClassltTgt entityType)

public abstract ltTgt T readEntity(GenericTypeltTgt entityType)

public abstract void close()

El meacutetodo getEntity() devuelve el objeto Java correspondiente al cuerpo delmensaje HTTPEl meacutetodo getStatus() devuelve el coacutedigo de respuesta HTTPEl meacutetodo getStatusInfo() devuelve la informacioacuten de estado asociada con larespuestaEl meacutetodo getMetadata() devuelve una instancia de tipo MultivaluedMap con lascabeceras de la respuesta

Servicios Rest

136

El meacutetodo getLocation() devuelve la URI de la cabecera Location de la respuestaEl meacutetodo getMediaType() devuelve el mediaType del cuerpo de la respuestaEl meacutetodo getHeaders() devuelve las cabeceras de respuesta con sus valorescorrespondientesEl meacutetodo readEntity() devuelve la entidad del cuerpo del mensaje utilizando unMessageBodyReader que soporte el mapeado del inputStream de la entidad a la claseJava especificada como paraacutemetroEl meacutetodo readEntity() tambieacuten puede devolver una clase geneacuterica si se disponedel MessageBodyReader correspondienteEl meacutetodo close() cierra el input stream correspondiente a la entidad asociada delcuerpo del mensaje (en el caso de que esteacute disponible y abierto) Tambieacuten liberacualquier otro recurso asociado con la respuesta (como por ejemplo datos posiblementealmacenados en un buffer)

Veamos otro ejemmplo Si el recurso REST espera una peticioacuten HTTP GET invocaremosel meacutetodo InvocationBuilderget() El tipo de retorno del meacutetodo deberiacuteacorresponderse con la entidad devuelta por el recurso REST que atenderaacute la peticioacuten

Client cliente = ClientBuildernewClient()WebTarget miRecurso = clientetarget(httpejemplowebapilectura)String respuesta = miRecursorequest(MediaTypeTEXT_PLAIN) get(Stringclass)

O tambieacuten podriacuteamos codificarlo como

Client cliente = ClientBuildernewClient()String respuesta = cliente target(httpejemplowebapilectura) request(MediaTypeTEXT_PLAIN) get(Stringclass)

Si el tipo de retorno de la peticioacuten GET es una coleccioacuten usaremosjavaxwsrscoreGenericTypeltTgt como paraacutemetro del meacutetodo en donde T es eltipo de la coleccioacuten

ListltPedidoAlmacengt pedidos = client target(httpejemplowebapilectura) path(pedidos) request(MediaTypeAPPLICATION_XML) get(new GenericTypeltListltPedidoAlmacengtgt() )

Si el recurso REST destinatario de la peticioacuten espera una peticioacuten de tipo HTTP POSTinvocaremos el meacutetodo InvocationBuilderpost()

Las peticiones POST tienen los siguientes prototipos

Interface SyncInvoker peticiones POST siacutencronas

public interface SyncInvoker ltTgt T post(Entityltgt entity ClassltTgt responseType)

Servicios Rest

137

ltTgt T post(Entityltgt entity GenericTypeltTgt responseType) Response post(Entityltgt entity)

Los primeros dos meacutetodos geneacutericos enviacutean una entidad (clase java + tipo MIME asociado)indicada como primer paraacutemetro del meacutetodo y como segundo paraacutemetro se indica el tipo javaal que se convertiraacute la respuesta recibida El tercero enviacutea una entidad y devuelve una instanciade tipo Response Por ejemplo

Veamos un ejemplo de invocacioacuten de peticiones POST

Ejemplo de peticioacuten POST utilizando el API cliente jasx-rx 20

Client cli = ClientBuildernewClient()Pedido pe = new PedidoAlmacen()Pedido peRespuesta = cli target() request() post(Entityentity(new Pedido() applicationjson) Pedidoclass)

En este caso estamos realizando una peticioacuten POST Como payload del mensaje enviamosun objeto Pedido representado en formato json La entidad esperada como respuesta debeser de tipo Pedido

Esto implica que en el lado del servidor el meacutetodo que atiende la peticioacuten Post tendraacute unparaacutemetro de tipo Pedido y se deberaacuten serializar los objetos de tipo Pedido a json ya que esel tipo MIME asociado a esta entidad ( especificado en la cabera Content-Type de la peticioacutenHTTP)

La clase Entity encapsula los objetos Java que queremos enviar con las peticiones GET oPOST No tiene un constructor puacuteblico En su lugar tenemos que invocar uno de sus meacutetodosestaacuteticos

Clase javaxwsrsclientEntity

public final class EntityltTgt

public static ltTgt EntityltTgt entity(T entity String mediaType)

public static ltTgt EntityltTgt entity(T entity MediaType mediaType)

public static ltTgt EntityltTgt xml(final T entity)

public static ltTgt EntityltTgt json(final T entity)

public static ltTgt EntityltTgt text(T entity)

public static EntityltFormgt form(final Form form)

El meacutetodo estaacutetico entity() crea una entidad (clase Java) con un tipo MIME asociado dadopor la cadena de caracteres mediaTypeEl meacutetodo estaacutetico entity() crea una entidad (clase Java) con un tipo MIME indicado enmediaTypeEl meacutetodo xml crea una entidad (clase Java) con el tipo MIME applicationxml

Servicios Rest

138

El meacutetodo json crea una entidad (clase Java) con el tipo MIME applicationjsomEl meacutetodo text crea una entidad (clase Java) con el tipo MIME textplainEl meacutetodo form crea una entidad (clase Java) con el tipo MIME applicationx-www-form-urlencoded

Veamos otro ejemplo de invocacioacuten POST que utiliza la clase Entity

Ejemplo de peticioacuten POST y uso de clase Entity

NumSeguimiento numSeg = client target(httpejemplowebapiescritura)

request(MediaTypeAPPLICATION_XML)

post(Entityxml(pedido) NumeroSeguimientoclass)

Especificamos como paraacutemetro de la peticioacuten request() el tipo MIME que aceptamos enla respuesta (cabecera HTTP Accept)Realizamos una peticioacuten POST El cuerpo del mensaje se crea con la llamadaEntityxml(pedido) El tipo Entity encapsula la entidad del mensaje (tipo JavaPedido) y el tipo MIME asociado (tipo MIME applicationxml)

Veamos un ejemplo en el que enviamos paraacutemetros de un formulario en una peticioacuten POST

Ejemplo de enviacuteo de datos de un formulario en una peticioacuten POST

Form form = new Form()param(nombre Pedro) param(apellido Garcia)Response response = clienttarget(httpejemploclientes) request() post(Entityform(form))responseclose()

La peticioacuten POST del coacutedigo anterior enviacutea los datos del formulario y espera recibir comorespuesta una entidad de tipo Response

El coacutedigo en el lado del servidor seraacute similar a eacuteste

Servicio rest que sirve una peticioacuten POST a partir de datos de un formulario

POSTPath(clientes)Produces(texthtml)public Response crearCliente(FormParam(nombre)String nom FormParam(apellido)String ape) creamos el nuevo cliente return Responseok(RESPONSE_OK)build()

Manejo de excepciones

Veamos queacute ocurre si se produce una excepcioacuten cuando utilizamos una forma de invocacioacutenque automaacuteticamente convierte la respuesta en el tipo especificado Supongamos el siguienteejemplo

Servicios Rest

139

Cliente cli = clienttarget(httptiendacomclientes123) request(applicationjson) get(Clienteclass)

En este escenario el framework del cliente convierte cualquier coacutedigo de error HTTP en unade las excepciones que antildeade JAX-RS 20 (BadRequesException ForbiddenExceptionhellip) yque ya hemos visto Podemos capturar dichas excepciones en nuestro coacutedigo para tratarlasadecuadamente

try Cliente cli = clienttarget(httptiendacomclientes123) request(applicationjson) get(Clienteclass) catch (NotAcceptableException notAcceptable) catch (NotFoundException notFound)

Si el servidor responde con un error HTTP no cubierto por alguna excepcioacutenespeciacutefica JAX-RS entonces se lanza una excepcioacuten de propoacutesito general La claseClientErrorException cubre cualquier coacutedigo de error en la franja del 400 La claseServerErrorException cubre cualquier coacutedigo de error en la franja del 500

Si el servidor enviacutea alguna de los coacutedigos de respuesta HTTP 3xx (clasificados como coacutedigosde la categoriacutea redireccioacuten) el API cliente lanza una RedirectionException a partir de lacual podemos obtener la URL para poder tratar la redireccioacuten nosotros mismos Por ejemplo

WebTarget target = clienttarget(httptiendacomclientes123)boolean redirected = false

Cliente cli = nulldo try cli = targetrequest(applicationjson) get(Clienteclass) catch (RedirectionException redirect) if (redirected) throw redirect redirected = true target = clienttarget(redirectgetLocation()) while (cli == null)

En este ejemplo volvemos a iterar si recibimos un coacutedigo de respuesta 3xx El coacutedigo seasegura de que soacutelo permitimos un coacutedigo de este tipo cambiando el valor de la variableredirect en el bloque en el que capturamos la exceptioacuten A continuacioacuten cambiamos elWebTarget (en el bloque catch ) al valor de la cabecera Location de la respuesta delservidor

Los coacutedigos de estado HTTP 3xx indican que es neceario realizar algunaaccioacuten adicional para que el servidor pueda completar la peticioacuten Laaccioacuten requerida puede llevarse a cabo sin necesidad de interactuar con el

Servicios Rest

140

cliente soacutelo si el meacutetodo utilizado en la segunda peticioacuten es GET o HEAD(ver httpwwww3orgProtocolsrfc2616rfc2616-sec10html)

52 Procesamiento JSON

JSON (JavaScript Object Notation) es un formato para el intercambio de datos basado en textoderivado de Javascript (Javascript disponde de una funcioacuten nativa eval() para convertirstreams JSON en objetos con propiedades que son accesibles sin necesidad de manipularninguna cadena de caracteres)

La especificacioacuten JSR 3539 proporciona un API para el procesamiento de datos JSON(parsing transformacioacuten y consultas)

La gramaacutetica de los objetos JSON es bastante simple Soacutelo se requieren dos estructurasobjetos y arrays Un objeto es un conjunto de pares nombre-valor y un array es una lista devalores JSON define siete tipos de valores string number object array true false y null

El siguiente ejemplo muestra datos JSON para un objeto que contiene pares nombre-valor

Ejemplo formato JSON

nombre John apellidos Smith edad 25 direccion calle 21 2nd Street ciudad New York codPostal 10021 telefonos [ tipo fijo numero 212 555-1234 tipo movil numero 646 555-4567 ]

El objeto anterior tiene cinco pares nombre-valor

bull Los dos primeros son nombre y apellidos con el valor de tipo String

bull El tercero es edad con el valor de tipo number

bull El cuarto es direccion con el valor de tipo object

bull El quinto es telefonos cuyo valor es de tipo array con dos objetos

JSON tiene la siguiente sintaxis

bull Los objetos estaacuten rodeados por llaves sus pares de elementos nombre-valor estaacutenseparados por una coma y el nombre y el valor de cada par estaacuten separados por dospuntos Los nombres en un objeto son de tipo String mientras que sus valores

9 httpsjcporgaboutJavacommunityprocessfinaljsr353indexhtml

Servicios Rest

141

pueden ser cualquiera de los siete tipos que ya hemos indicado incluyendo a otro objetou otro array

bull Los arrays estaacuten rodeados por corchetes [] y sus valores estaacuten separados por una coma Cada valor en un array puede ser de un tipo diferente incluyendo a otro objeto o array

bull Cuando los objetos y arrays contienen otros objetos yo arrays los datos adquieren unaestructura de aacuterbol

Los servicios web RESTful utilizan JSON habitualmente tanto en las peticiones como en lasrespuestas La cabecera HTTP utilizada para indicar que el contenido de una peticioacuten o unarespuesta es JSON es la siguiente

Content-Type applicationjson

La representacioacuten JSON es normalmente maacutes compacta que las representaciones XML debidoa que JSON no tiene etiquetas de cierre A diferencia de XML JSON no tiene un esquemade definicioacuten y validacioacuten de datos ampliamente aceptado

Actualmente las aplicaciones Java utilizan diferentes libreriacuteas para producirconsumir JSONque tienen que incluirse junto con el coacutedigo de la aplicacioacuten incrementando asiacute el tamantildeo delarchivo desplegado El API de Java para procesamiento JSON proporciona un API estaacutendarpara analizar y generar JSON de forma que las aplicaciones que utilicen dicho API sean maacutesligeras y portables

Para generar y parsear datos JSON hay dos modelos de programacioacuten que son similares alos usados para documentos XML

bull El modelo de objetos crea un aacuterbol en memoria que representa los datos JSON

bull El modelo basado en streaming utiliza un parser que lee los datos JSON elemento aelemento (uno cada vez)

Java EE incluye soporte para JSR 353 de forma que el API de java para procesamiento JSONse encuentra en los siguientes paquetes

bull El paquete javaxjson contiene interfaces para leer escribir y construir datos JSONseguacuten el modelo de objetos asiacute como otras utilidades

bull El paquete javaxjsonstream contiene una interfaz para parsear y generar datosJSON para el modelo streaming

Vamos a ver coacutemo producir y consumir datos JSON utilizando cada uno de los modelos

53 Modelo de procesamiento basado en el modelo de objetos

En este caso se crea un aacuterbol en memoria que representa los datos JSON (todos losdatos) Una vez construido el aacuterbol se puede navegar por eacutel analizarlo o modificarlo Estaaproximacioacuten es muy flexible y permite un procesamiento que requiera acceder al contenidocompleto del aacuterbol En contrapartida normalmente es maacutes lento que el modelo de streamingy requiere utilizar maacutes memoria El modelo de objetos genera una salida JSON navegandopor el aacuterbol entero de una vez

El siguiente coacutedigo muestra coacutemo crear un modelo de objetos a partir de datos JSON desdeun fichero de texto

Creacioacuten de un modelos de objetos a partir de datos JSON

Servicios Rest

142

import javaioFileReaderimport javaxjsonJsonimport javaxjsonJsonReaderimport javaxjsonJsonStructureJsonReader reader = JsoncreateReader(new FileReader(datosjsontxt))JsonStructure jsonst = readerread()

El objeto jsonst puede ser de tipo JsonObject o de tipo JsonArray dependiendo delos contenidos del fichero JsonObject y JsonArray son subtipos de JsonStructure Este objeto representa la raiacutez del aacuterbol y puede utilizarse para navegar por el aacuterbol o escribirloen un stream como datos JSON

Vamos a mostrar alguacuten ejemplo en el que utilicemos un StringReader

Objeto JSON con dos pares nombre-valor

jsonReader = JsoncreateReader(new StringReader( + manzanaroja + plaacutetanoamarillo + ))JsonObject json = jsonReaderreadObject()

jsongetString(manzana) jsongetString(plaacutetano)

El meacutetodo getString() devuelve el valor del string para la clave especificadacomo paraacutemetro Pueden utilizarse otros meacutetodos getXXX() para acceder al valorcorrespondiente de la clave en funcioacuten del tipo de dicho objeto

Un array con dos objetos cada uno de ellos con un par nombre-valor puede leerse como

Array con dos objetos

jsonReader = JsoncreateReader(new StringReader([ + manzanarojo + plaacutetanoamarillo + ]))

JsonArray jsonArray = jsonReaderreadArray()

La interfaz JsonArray tambieacuten tiene meacutetodos get para valores de tipo boolean integer y String en el iacutendice especificado (esta interfaz hereda de javautilList)

Creacioacuten de un modelos de objetos desde el coacutedigo de la aplicacioacuten

A continuacioacuten mostramos un ejemplo de coacutedigo para crear un modelo de objetos medianteprogramacioacuten

Ejemplo de creacioacuten de un modelo de objetos JSON desde programacioacuten

import javaxjsonJsonimport javaxjsonJsonObjectJsonObject modelo =

JsoncreateObjectBuilder() add(nombre Duke)

Servicios Rest

143

add(apellidos Java) add(edad 18) add(calle 100 Internet Dr) add(ciudad JavaTown) add(codPostal 12345) add(telefonos

JsoncreateArrayBuilder() add(JsoncreateObjectBuilder() add(tipo casa) add(numero 111-111-1111)) add(JsoncreateObjectBuilder() add(tipo movil) add(numero 222-222-2222))) build()

El tipo JsonObject representa un objeto JSON El meacutetodoJsoncreateObjectBuilder() crea un modelo de objetos en memoria antildeadiendoelementos desde el coacutedigo de nuestra aplicacioacutenEl meacutetodo JsoncreateArrayBuilder() crea un modelo de arrays en memoriaantildeadiendo elementos desde el coacutedigo de nuestra aplicacioacuten

El objeto modelo de tipo JsonObject representa la raiacutez del aacuterbol que es creado anidandollamadas a meacutetodos acuteadd()acute y construyendo el aacuterbol a traveacutes del meacutetodo build()

La estructura JSON generada es la siguiente

Ejemplo formato JSON generado mediante programacioacuten

nombre Duke apellidos Java edad 18 calle 100 Internet Dr ciudad JavaTown codPostal 12345 telefonos [ tipo casa numero 111-111-1111 tipo movil numero 222-222-2222 ]

Navegando por el modelo de objetos

A continuacioacuten mostramos un coacutedigo de ejemplo para navegar por el modelo de objetos

import javaxjsonJsonValueimport javaxjsonJsonObjectimport javaxjsonJsonArrayimport javaxjsonJsonNumberimport javaxjsonJsonStringpublic static void navegarPorElArbol(JsonValue arbol String clave)

Servicios Rest

144

if (clave = null) Systemoutprint(Clave + clave + ) switch(arbolgetValueType()) case OBJECT Systemoutprintln(OBJETO) JsonObject objeto = (JsonObject) arbol for (String nombre objectkeySet()) navegarPorElArbol(objectget(nombre) name) break case ARRAY Systemoutprintln(ARRAY) JsonArray array = (JsonArray) arbol for (JsonValue val array) navegarPorElArbol(val null) break case STRING JsonString st = (JsonString) arbol Systemoutprintln(STRING + stgetString()) break case NUMBER JsonNumber num = (JsonNumber) arbol Systemoutprintln(NUMBER + numtoString()) break case TRUE case FALSE case NULL Systemoutprintln(arbolgetValueType()toString()) break

El meacutetodo navegarPorElArbol() podemos usarlo con el ejemplo anterior de la siguienteforma

navegarPorElArbol(modelo OBJECT)

El meacutetodo navegarPorElArbol() tiene dos argumentos un elemento JSON y una claveLa clave se utiliza para imprimir los pares clave-valor dentro de los objetos Los elementos enel aacuterbol se representan por el tipo JsonValue Si el elemento es un objeto o un array serealiza una nueva llamada a este meacutetodo es invocada para cada elemento contenido en elobjeto o el array Si el elemento es un valor eacuteste se imprime en la salida estaacutendar

El meacutetodo JsonValuegetValueType() identifica el elemento como un objeto un arrayo un valor Para los objetos el meacutetodo JsonObjectkeySet() devuelve un conjunto deStrings que contienene las claves de los objetos y el meacutetodo JsonObjectget(Stringnombre) devuelve el valor del elemento cuya clave es nombre Para los arraysJsonArray implementa la interfaz ListltJsonValuegt Podemos utilizar bucles formejorados con el valor de SetltStringgt devuelto por JsonObjectkeySet() y coninstancias de JsonArray tal y como hemos mostrado en el ejemplo

Escritura de un modelo de objetos en un stream

Los modelos de objetos creados en los ejemplos anteriores pueden escribirse en un streamutilizando la clase JsonWriter de la siguiente forma

Servicios Rest

145

import javaioStringWriterimport javaxjsonJsonWriterStringWriter stWriter = new StringWriter()

JsonWriter jsonWriter = JsoncreateWriter(stWriter)

jsonWriterwriteObject(modelo)

jsonWriterclose()

String datosJson = stWritertoString()Systemoutprintln(datosJson)

El meacutetodo JsoncreateWriter() toma como paraacutemetro un OutputStreamEl meacutetodo JsonWriterwriteObject() escribe el objeto JsonObject en elstreamEl meacutetodo JsonWriterclose() cierra el stream de salida

Modelo de procesamiento basado en streaming

El modelo de streaming utiliza un parser basado en eventos que va leyendo los datos JSONde uno en uno El parser genera eventos y detiene el procesamiento cuando un objeto o arraycomienza o termina cuando encuentra una clave o encuentra un valor Cada elemento puedeser procesado o rechazado por el coacutedigo de la aplicacioacuten y a continuacioacuten el parser continuacuteacon el siguiente evento Esta aproximacioacuten es adecuada para un procesamiento local en elcual el procesamiento de un elemento no requiere informacioacuten del resto de los datos El modelode streaming genera una salida JSON para un determinado stream realizando una llamada auna funcioacuten con un elemento cada vez

A continuacioacuten veamos con ejemplos coacutemo utilizar el API para el modelo de streaming

bull Para leer datos JSON utilizando un parser (JsonParser)

bull Para escribir datos JSON utilizando un generador (JsonGenerator)

Lectura de datos JSON

El API para el modelo streaming es la aproximacioacuten maacutes eficiente para parsear datos JSONutilizando eventos

import javaxjsonJsonimport javaxjsonstreamJsonParserJsonParser parser = JsoncreateParser(new StringReader(datosJson))while (parserhasNext()) JsonParserEvent evento = parsernext() switch(evento) case START_ARRAY case END_ARRAY case START_OBJECT case END_OBJECT case VALUE_FALSE case VALUE_NULL case VALUE_TRUE Systemoutprintln(eventotoString())

Servicios Rest

146

break case KEY_NAME Systemoutprint(eventotoString() + + parsergetString() + - ) break case VALUE_STRING case VALUE_NUMBER Systemoutprintln(eventotoString() + + parsergetString()) break

El ejemplo consta de tres pasos

1 Obtener una instancia de un parser invocando el meacutetodo estaacuteticoJsoncreateParser()

2 Iterar sobre los eventos del parser utilizando los meacutetodos JsonParserhasNext() yJsonParsernext()

3 Realizar un procesamiento local para cada elemento

El ejemplo muestra los diez posibles tipos de eventos del parser El meacutetodoJsonParsernext() avanza al siguiente evento Para los tipos de eventos KEY_NAME VALUE_STRING y VALUE_NUMBER podemos obtener el contenido del elemento invocandoal meacutetodo JsonParsergetString() Para los eventos VALUE_NUMBER podemostambieacuten usar los siguientes meacutetodos

bull JsonParserisIntegralNumber

bull JsonParsergetInt

bull JsonParsergetLong

bull JsonParsergetBigDecimal

El parser genera los eventos START_OBJECT y END_OBJECT para un objeto JSON vaciacuteo

Para un objeto con dos pares nombre-valor

manzajaroja plaacutetanoamarillo

Mostramos los eventos generados

START_OBJECT manzajaKEY_NAMErojaVALUE_STRING plaacutetanoKEY_NAMEamarilloVALUE_STRINGEND_OBJECT

Los eventos generados para un array con dos objetos JSON seriacutean los siguientes

[START_ARRAY START_OBJECT manzajaKEY_NAMErojaVALUE_STRING END_OBJECT

Servicios Rest

147

START_OBJECT plaacutetanoKEY_NAMEamarilloVALUE_STRING END_OBJECT]END_ARRAY

Escritura de datos JSON

El siguiente coacutedigo muestra coacutemo escribir datos JSON en un fichero utilizando el API para elmodelo de streaming

Ejemplo de escritura de datos JSON con el modelo de streaming

FileWriter writer = new FileWriter(testtxt)JsonGenerator gen = JsoncreateGenerator(writer)genwriteStartObject() write(nombre Duke) write(apellidos Java) write(edad 18) write(calle 100 Internet Dr) write(ciudad JavaTown) write(codPostal 12345) writeStartArray(telefonos) writeStartObject() write(tipo casa) write(numero 111-111-1111) writeEnd() writeStartObject() write(tipo movil) write(numero 222-222-2222) writeEnd() writeEnd() writeEnd()genclose()

Este ejemplo obtiene un generador JSON invocando al meacutetodo estaacuteticoJsoncreateGenerator() que toma como paraacutemetro un output stream o un writerstream El ejemplo escribe los datos JSON en el fichero texttxt anidando llamadas a losmeacutetodos write() writeStartArray() writeStartObject() and writeEnd() El meacutetodo JsonGeneratorclose() cierra el output stream o writer stream subyacente

54 Pruebas de servicios REST

Hasta ahora hemos visto varias formas de probar nuestros servicios REST desde liacutenea decomandos con Curl desde IntelliJ con la herramienta Test RESTFul Web Service y desde elnavegador Chrome con Postman (siendo esta uacuteltima la que maacutes hemos utilizado)

Vamos a ver coacutemo implementar tests para nuestros servicios REST utilizando Maven y JUnitPara ello repasaremos algunas cuestiones baacutesicas sobre los ciclos de vida de Maven10

Ciclo de vida de Maven y tests JUnit

Un ciclo de vida en Maven es una secuencia de acciones determinada que defineel proceso de construccioacuten de un proyecto en concreto Como resultado del proceso deconstruccioacuten de un proyecto obtendremos un artefacto (fichero) de un cierto tipo (porejemplo jar war earhellip) Por lo tanto podriacuteamos decir que un ciclo de vida estaacute formado por

10 httpsmavenapacheorgref333maven-corelifecycleshtml

Servicios Rest

148

las acciones necesarias para convertir nuestros archivos fuente que constituyen el proyectoen por ejemplo un jar un warhellip

Maven propone 3 ciclos de vida es decir tres posibles secuencias de acciones que podemosutilizar (y modificar a nuestra conveniencia) para construir nuestro proyecto Dichos ciclos devida son clean site y el denominado default-lifecycle

Cada ciclo de vida estaacute formado por fases Una fase es un concepto abstracto y define el tipode acciones que se deberiacutean llevar a cabo Por ejemplo una fase del ciclo de vida por defectoes compile para referirse a las acciones que nos permiten convertir los ficheros java en losficheros class correspondientes

Cada fase estaacute formada por un conjunto de goals que son las acciones que se llevaraacuten a caboen cada una de las fases Las goals no viven de forma independiente sino que cualquiergoal siempre forma parte de un plugin Maven Podriacuteamos decir que un plugin por lo tantoes una agrupacioacuten loacutegica de una serie de goals relacionadas Por ejemplo el plugin wildflycontiene una serie de goals para desplegar re-desplegar deshacer-el-despliegue arrancarel servidor etc es decir agrupa las acciones que podemos realizar sobre el servidor wildflyUna goal se especifica siempre anteponiendo el nombre del plugin al que pertenece seguidode dos puntos por ejemplo wildflydeploy indica que se trata de la goal deploy que perteneceal plugin wildfly de maven

Pues bien por defecto Maven asocia ciertas goals a las fases de los tres ciclos de vidaCuando se ejecuta una fase de un ciclo de vida por ejemplo mvn package se ejecutan todaslas goals asociadas a todas las fases anteriores a la fase package en orden y finalmentelas goals asociadas a la fase package Por supuesto podemos alterar en cualquier momentoeste comportamiento por defecto incluyendo los plugins y goals correspondientes dentro dela etiqueta ltbuildgt en nuestro fichero de configuracioacuten pomxml

Vamos a implementar tests JUnit Los tests como ya habeacuteis visto en sesiones anterioresen el directorio srctest Algunas normas importantes son que los tests pertenezcan almismo paquete loacutegico al que pertencen las clases Java que estamos probando Por ejemplosi estamos haciendo pruebas sobre las clases del paquete orgexpertojavarest los testsdeberiacutean pertenecer al mismo paquete aunque fiacutesicamente el coacutedigo fuente y sus pruebasestaraacuten separados (el coacutedigo fuente estaraacute en srcmain y los tests en srctest)

Para realizar pruebas sobre nuestros servicios REST necesitamos que el servidor Wilfly esteacuteen marcha Tambieacuten necesitamos empaquetar el coacutedigo en un fichero war y desplegarlo enel servidor todo esto ANTES de ejecutar los tests

Las acciones para arrancar el servidor Wilfly y desplegar nuestra aplicacioacuten en eacutel NO formanparte de las acciones (o goals) incluidas por defecto en el ciclo de vida por defecto de Mavencuando nuestro proyecto tiene que empaquetarse como un war (etiqueta ltpackaginggt denuestro pomxml) Podeacuteis consultar aquiacute11 la lista de goals asociadas a las fases del ciclo devida por defecto de Maven

Por otro lado en el ciclo de vida por defecto se incluye una goal para ejecutar los testsasociada a la fase test Dicha goal es surefiretest El problema es que por defecto la fasetest se ejecuta ANTES de la fase package y por lo tanto antes de empaquetar y desplegarnuestra aplicacioacuten en Wildfly

Por lo tanto tendremos que alterar convenientemente este comportamiento por defectopara que se ejecuten las acciones de nuestro proceso de construccioacuten que necesitemos

11 httpsmavenapacheorgref333maven-coredefault-bindingshtmlPlugin_bindings_for_war_packaging

Servicios Rest

149

y en el orden en el que lo necesitemos Como ya hemos indicado antes esto lo haremosincluyendo dichas acciones en la etiqueta ltbuildgt de nuestro pomxml y configurandolasconvenientemente para asegurarnos que el orden en el que se ejecutan es el que queremos

La siguiente figura muestra parte de la secuencia de fases llevadas a cabo por Maven ensu ciclo de vida por defecto Para conseguir nuestros propoacutesitos simplemente antildeadiremosla goals wildflydeploy y la asociaremos a la fase pre-integration-test y cambiaremos lafase a la que estaacute asociada la goal surefiretest para que los tests se ejecuten DESPUEacuteS dehaber desplegado el war en Wildfly

A continuacioacuten mostramos los cambios que tenemos que realizar en el fichero de configuracioacutenpomxml

Adicioacuten de las goals wildflydeploy y surefiretest a las fases pre-integration-test ysurefiretest respectivamente

lt-- forzamos el despliegue del war generado durante la fase pre-integration-test justo despueacutes de obtener dicho war--gtltplugingt ltgroupIdgtorgwildflypluginsltgroupIdgt ltartifactIdgtwildfly-maven-pluginltartifactIdgt ltversiongt102Finalltversiongt ltconfigurationgt lthostnamegtlocalhostlthostnamegt ltportgt9990ltportgt ltconfigurationgt

Servicios Rest

150

ltexecutionsgt ltexecutiongt ltidgtwildfly-deployltidgt ltphasegtpre-integration-testltphasegt ltgoalsgt ltgoalgtdeployltgoalgt ltgoalsgt ltexecutiongt ltexecutionsgtltplugingt

lt--ejecutaremos los test JUnit en la fase integration-test inmediatamente despueacutes de la fase pre-integration-test y antes de la fase verify--gtltplugingt ltgroupIdgtorgapachemavenpluginsltgroupIdgt ltartifactIdgtmaven-surefire-pluginltartifactIdgt ltversiongt218ltversiongt ltconfigurationgt ltskipgttrueltskipgt ltconfigurationgt ltexecutionsgt ltexecutiongt ltidgtsurefire-itltidgt ltphasegtintegration-testltphasegt ltgoalsgt ltgoalgttestltgoalgt ltgoalsgt ltconfigurationgt ltskipgtfalseltskipgt ltconfigurationgt ltexecutiongt ltexecutionsgtltplugingt

Tambieacuten necesitamos incluir en el pomxml las libreriacuteas de las que depende el coacutedigo depruebas de nuestro proyecto (clases XXXXTest situadas en srctest) libreriacutea JUnit JAXB yel API cliente de JAX-RS Por lo que antildeadimos en el las dependencias correspondientes

Dependencias del coacutedigo srctest con JUnit y API cliente de JAX-RS

ltdependencygt ltgroupIdgtjunitltgroupIdgt ltartifactIdgtjunitltartifactIdgt ltversiongt412ltversiongt ltscopegttestltscopegtltdependencygtltdependencygt ltgroupIdgtorgjbossresteasyltgroupIdgt ltartifactIdgtresteasy-clientltartifactIdgt ltversiongt3013Finalltversiongt ltscopegttestltscopegtltdependencygtltdependencygt ltgroupIdgtorgjbossresteasyltgroupIdgt

Servicios Rest

151

ltartifactIdgtresteasy-jaxb-providerltartifactIdgt ltversiongt3013Finalltversiongtltdependencygt

Dado que vamos a trabajar con el API Json y dado que ejecutaremos los tests desde lamaacutequina virtual de Java y no dentro del servidor WildFly necesitamos antildeadir tambieacuten lassiguientes libreriacuteas

Dependencias del coacutedigo srctest con el API Json de jaxrs

lt--Libreriacuteas para serializardeserializar json --gtltdependencygt ltgroupIdgtorgjbossresteasyltgroupIdgt ltartifactIdgtresteasy-jackson-providerltartifactIdgt ltversiongt3013Finalltversiongtltdependencygt

lt--Jaxrs API json --gtltdependencygt ltgroupIdgtorgjbossresteasyltgroupIdgt ltartifactIdgtresteasy-json-p-providerltartifactIdgt ltversiongt3013Finalltversiongtltdependencygt

No hemos incluido en el pomxml la orden para arrancar Wildfly Vamosa hacer esto desde IntelliJ en un perfil de ejecucioacuten como ya habeacuteishecho en sesiones anteriores De esta forma podremos ver desde IntelliJla consola de logs del servidor En este caso podemos crear un perfilsolamente para arrancar el servidor Wildfly (no es necesario que se incluyael despliegue del war generado puesto que lo haremos desde la ventanaMaven Projects) Antes de iniciar el proceso de construccioacuten por lo tantotendremos que asegurarnos de que hemos arrancado Wildlfly

Con estos cambios en el pomxml y ejecutando el comando mvn verify se llevaraacuten a cabolas siguientes acciones en este orden

bull Despueacutes de compilar el proyecto obtenemos el war (fase package )

bull El war generado se despliega en el servidor de aplicaciones Wilfly (fase pre-integration-test )

bull Se ejecutan los test JUnit sobre la aplicacioacuten desplegada en el servidor (faseintegration-test )

Anotaciones JUnit y aserciones AssertThat

JUnit 4 proporciona anotaciones para forzar a que los meacutetodos anotados con Test seejecuten en el orden que nos interese (por defecto no estaacute garantizado que se ejecuten enel orden en el que se escriben)

En principio debemos programar los tests para que sean totalmente independientes unos deotros y por lo tanto el orden de ejecucioacuten no influya para nada en el resultado de su ejecucioacutentanto si se ejecuta el primero como a mitad o el uacuteltimo El no hacer los tests independientes

Servicios Rest

152

hace que el proceso de testing se alargue y complique innecesariamente ya que puede serque unos tests enmascaren en resultado de otros o que no podamos saber si ciertas partesdel coacutedigo estaacuten bien o mal implementadas hasta que los tests de los que dependemos sehayan superado con eacutexito

Auacuten asiacute y dado que muchas veces se obtienen errores por hacer asunciones en el ordende la ejecucioacuten de los tests JUnit nos permite fijar dicho orden Para ello utilizaremosla anotacioacuten FixMethodOrder indicando el tipo de ordenacioacuten como por ejemploMethodSortersNAME_ASCENDING de forma que se ejecutaraacuten los tests por ordenlexicograacutefico

Por ejemplo

Ejemplo para forzar el orden de ejecucioacuten de los test (orden lexicograacutefico)

FixMethodOrder(MethodSortersNAME_ASCENDING)public class TestMethodOrder

Test public void testB() Systemoutprintln(second)

Test public void testA() Systemoutprintln(first)

Test public void testC() Systemoutprintln(third)

En ese caso el orden de ejecucioacuten seraacute testA() a continuacioacuten testB() y finalmentetestC()

Otra aportacioacuten de JUnit 4 es la incorporacioacuten de aserciones de tipoassertThat En sesiones anteriores habeacuteis utilizado aserciones con meacutetodosAssertassertEquals(resultado_esperado resultado_real) Los nuevosmeacutetodos AssertassertThat() permiten una mayor flexibilidad a la hora de expresar lasaserciones realizadas en nuestros tests asiacute como una mayor legibilidad de los mismos Elprototipo general de las aserciones de este tipo es

assertThat([value] [matcher statement])

en donde [value] es el resultado real (valor sobre el que se quiere afirmar algo)y [matcher statement] es un Matcher u objeto que realiza operaciones deemparejamiento sobre una secuencia de caracteres seguacuten un determinado patroacuten

Por ejemplo

Ejemplos de sentencias assertThat

assertThat(x is(not(4)))

Servicios Rest

153

assertThat(responseStringJson

either(containsString(nombre))and(containsString(apellido)))

assertThat(myList hasItem(3))

Aquiacute utilizamos un matcher con el patroacuten 4 esta sentencia devuelve false si x = 4Podemos combinar varios matchers de forma que se tengan que satisfacer maacutes de unoEn este caso aplicamos el matcher sobre un conjunto de elementos

Hay varias libreriacuteas que implementan Matchers JUnit incluye parte de los matchers deHamcrest (Hamcrest es un framework para escribir objetos matcher permitiendo definir reglasde matching de forma declarativa) Otra libreriacutea interesante para realizar testing de serviciosrest que utilizan representaciones Json es la libreriacutea hamcrest-json que podemos utilizarpara realizar aserciones sobre dos objetos Json

Por ejemplo supongamos que nuestro objeto Json contiene una lista de enlaces Hateoas detipo Link Los objetos Link seraacuten serializadosdeserializados (Wildfly utiliza Jackson pararealizar estas tareas) convenientemente Cuando serializamos un objeto Link (obtenemossu representacioacuten Json) veremos ademaacutes de los objetos uri valor type valor y relvalor que son los que baacutesicamente utilizamos al crear los enlaces Hateoas otros comouriBuilder hellip paramshellip que puede que no nos interese consultar o incluso que no leshayamos asignado ninguacuten valor

Si en nuestro test queremos comprobar que el objeto Json que nos devuelve el servicio(resultado real) se corresponde con el valor esperado tendremos que comparar ambasrepresentaciones Ahora bien puede que solamente nos interese comparar ciertos valorescontenidos en el objeto Json no el objeto completo

Hacer esta comprobacioacuten elemento a elemento es bastante tediosoLa libreriacutea hamcrest-json nos proporciona lo que estamos buscandocon los meacutetodos sameJSONAs() allowingExtraUnexpectedFields() yallowingAnyArrayOrdering() de la siguiente forma

Meacutetodo para comparar dos representaciones Json ClaseukcodatumedgehamcrestjsonSameJSONAs

AssertassertThat(age43 friend_ids[16 52 23] sameJSONAs(friend_ids[52 23 16]) allowingExtraUnexpectedFields() allowingAnyArrayOrdering())

En este coacutedigo tenemos una representacioacuten formada por dos objetos uno de los cuales tienecomo valor un array de enteros Si el servicio rest devuelve un objeto Json con maacutes elementoso en otro orden en este caso el resultado de la sentencia assertThat es true Volviendo alejemplo anterior de un objeto Json que contiene enlaces Hatehoas podriacuteamos realizar lasiguiente comparacioacuten

Comparamos dos objetos Json que contienen hiperenlaces Hateoas (objetos Link)

JsonObject json_object = clienttarget(httplocalhost8080forousuarios) request(MediaTypeAPPLICATION_JSON)

get(JsonObjectclass)

String json_string = json_objecttoString()

Servicios Rest

154

JsonObject usuarios = JsoncreateObjectBuilder() add(usuarios JsoncreateArrayBuilder() add(JsoncreateObjectBuilder() add(nombre Pepe Lopez) add(links JsoncreateArrayBuilder() add(JsoncreateObjectBuilder() add(uri httplocalhost8080forousuariospepe) add(type applicationxmlapplicationjson) add(rel self)))) add(JsoncreateObjectBuilder() add(nombre Ana Garcia) add(links JsoncreateArrayBuilder() add(JsoncreateObjectBuilder() add(uri httplocalhost8080forousuariosana) add(type applicationxmlapplicationjson) add(rel self)))))

build()

AssertassertThat(json_string sameJSONAs(usuariostoString()) allowingExtraUnexpectedFields()

allowingAnyArrayOrdering())

Realizamos la llamada al servicio REST y recibimos como respuesta un objeto Json Eneste caso nuestro objeto Json estaacute formado por una lista de objetosObtenemos la representacioacuten de nuestro objeto Json (resultado real) en forma de cadenade caracteresCreamos un nuevo objeto Json con el resultado esperadoComparamos ambos objetos Si el resultado real incluye maacutes elementos que loscontenidos en json_string o en otro orden consideraremos que hemos obtenido larespuesta correcta

Para utilizar esta libreriacutea en nuestro proyecto simplemente tendremos que antildeadirla comodependencia en la configuracioacuten de nuestro pomxml

Libreriacutea para comparar objetos Json en los tests

lt--Hamcrest Json --gtltdependencygt ltgroupIdgtukcodatumedgeltgroupIdgt ltartifactIdgthamcrest-jsonltartifactIdgt ltversiongt02ltversiongtltdependencygt

Observaciones sobre los tests y algunos ejemplos de tests

Recuerda que para utilizar el API cliente necesitas utilizar instanciasjavaxwsrsclientClient que debemos cerrar siempre despueacutes de su uso paracerrar el socket asociado a la conexioacuten

Servicios Rest

155

Para ello podemos optar por Crear una uacutenica instancia Client antes de ejecutar cualquiertest (meacutetodo BeforeClass) y cerrar el socket despueacutes de ejecutar todos los tests (meacutetodoAfterClass) Crear una uacutenica instancia Client antes de ejecutar CADA test (meacutetodoBefore) y cerrar el socket despueacutes de ejecutar CADA tests (meacutetodo After)

Si el resultado de una invocacioacuten sobre la instancia Client es de tipojavaxwsrscoreResponse debemos liberar de forma expliacutecita la conexioacuten para quepueda ser usada de nuevo por dicha instancia Client

Por ejemplo supongamos que queremos realizar un test en el que realizamos una operacioacutenPOST y a continuacioacuten una operacioacuten GET para verificar que el nuevo recurso se ha antildeadididocorrectamente

Ejemplo de Test que utiliza una instancia Client para todos los tests

public class TestRESTServices private static final String BASE_URL = httplocalhost8080rest private static URI uri = UriBuilderfromUri(BASE_URL)build() private static Client client

BeforeClass public static void initClient()

client = ClientBuildernewClient()

AfterClass public static void closeClient()

clientclose()

Test public void createAndRetrieveACustomer()

Customer customer = Creamos un nuevo cliente Response response = clienttarget(uri) request() post(Entityentity(customer MediaTypeAPPLICATION_JSON)) assertEquals(ResponseStatusCREATED responsegetStatusInfo()) URI referenceURI = responsegetLocation()

responseclose()

Obtenemos el recurso que hemos antildeadido response = clienttarget(referenceURI)request()get()

Customer retrieved_customer = responsereadEntity(Customerclass) assertEquals(ResponseStatusOK responsegetStatusInfo()) assertEquals(retreivedRefgetName() rgetName())

responseclose()

Creamos una instancia Client ANTES de ejecutar cualquier testCerramos el socket asociado a la conexioacuten DESPUEacuteS de ejecutar TODOS los testsLiberamos la conexioacuten para poder reutilizarla

Servicios Rest

156

Liberamos la conexioacuten para poder reutilizarlaAhora veamos otro ejemplo en el que utilizamos una instancia Client para cada test

Ejemplo de Test que utiliza una instancia Client para CADA test

public class TestRESTServices

private Client client

Before public void setUp()

thisclient = ClientBuildernewClient()

After public void tearDown()

thisclientclose()

Test public void getAllCustomersAsJson() String uriString = httplocalhost8080restcustomers JsonArray json_array = client target(uriString) request(MediaTypeAPPLICATION_JSON) accept(MediaTypeAPPLICATION_JSON) get(JsonArrayclass)

AssertassertEquals(2 json_arraysize())

Test public void getAllCustomers() String uriString = httplocalhost8080restcustomers Consultamos los datos de todos los customers ListltCustomergt lista_usuarios = clienttarget(uriString) request(applicationjson) get(new GenericTypeltListltCustomergtgt() ) AssertassertEquals(2 lista_usuariossize())

Creamos una instancia Client ANTES de ejecutar CADA testCerramos el socket asociado a la conexioacuten DESPUEacuteS de ejecutar CADA los tests

Podemos ver en este uacuteltimo ejemplo que no es necesario liberar la conexioacuten entre usossucesivos de la instancia Client si no utilizamos la clase Response En este caso el procesose realiza de forma automaacutetica por el sistema

Finalmente comentaremos que debido a un bug en la especificacioacuten JAX-RS eldeserializado del de los objetos Link no se realiza por lo que obendremos una listade Links vaciacutea (ver httpkingsfleetblogspotcomes201405reading-and-writing-jax-rs-link-objectshtml) Podemos comprobar que si obtenemos la representacioacuten en formato texto dela entidad del mensaje dicha lista de objetos tendraacute el valor correcto

Si no utilizamos la solucioacuten propuesta en el enlace anterior deberemos usar la anotacioacutenJsonIgnoreProperties(ignoreUnknown = true) De esta forma ignoraremos el

Servicios Rest

157

deserializado de los objetos Link pero tendremos que utilizar la representacioacuten en formato decadena de caracteres del recurso json en lugar del objeto java Link asociado

Asiacute por ejemplo si nuestro recurso Customer tiene asociado una lista de objetos Link parapoder utilizar el API Cliente y acceder a la lista de enlaces usaremos la anotacioacuten anterior enla implementacioacuten de la clase Customer

JsonIgnoreProperties(ignoreUnknown = true)XmlRootElement(name=customer)public class Customer int id String name ListltLinkgt links

Servicios Rest

158

55 Ejercicios

Tests utilizando el API cliente y un mapeador de excepciones (1 punto)

Se proporciona como plantilla el MOacuteDULO IntelliJ s5-tienda con una implementacioacuten parcialde una tienda de clientes on-line Este proyecto ya contiene varios tests implementados amodo de ejemplo

Los recursos rest implementados lanzan excepciones de tipo RestException si porejemplo se intenta realizar una consulta sobre un producto yo usuario que no existe

Se ha implementado un mapeador de excepciones RestExceptionMapper quecaptura excepciones de tipo RuntimeException y devuelve una respuesta de tipoErrorMensajeBean que seraacute serializada a formato json yo formato xml (dependiendo delvalor de la cabecera Accept de la peticioacuten) con informacioacuten sobre el error producido

Implementa los siguientes dos tests test7recuperarTodosLosUsuarios() en el querealizamos una invocacioacuten GET sobre httplocalhost8080s5-tiendarestclientes Esta URIpodriacutea corresponderse con un meacutetodo anotado con GET y que devolviese una lista de todoslos clientes de la tienda Sin embargo no existe tal meacutetodo en nuestro recursos rest Verificaque dicha invocacioacuten devuelve el coacutedigo de estado 500 (Internal Server Error) y que en elcuerpo del mensaje se recibe Servicio no disponible

bull test8recuperarClienteQueNoExiste() en el que intentamos recuperar lainformacioacuten de un cliente que no exista en nuestra base de datos En este caso debemosverificar que el mensaje obtenido en formato json es el siguiente

status Not Found code 404 message El producto no se encuentra en la base de datos developerMessage error

Tests utilizando el API Json y JUnit (1 punto)

Vamos a seguir usando el proyecto s4-foroAvanzado con el que hemos trabajado en la sesioacutenanterior

Vamos a implementar algunos tests con JUnit en los que utilizaremos ademaacutes del API clienteel API Json que nos proporciona jaxrs

Para ejecutar los tests necesitamos modificar el pomxml antildeadiendo las dependenciascorrespondientes que hemos visto a lo largo de la sesioacuten y antildeadiendo las goals para que seejecuten los tests despueacutes de desplegar la aplicacioacuten en Wildfly

Proporcionamos el contenido del pomxml con las libreriacuteas y plugins que necesitaraacutes(aunque como ejercicio deberiacuteas intentar modificar la configuracioacuten tuacute mismo y luego puedescomprobar el resultado con el pomxml que se proporciona) El contenido del nuevo pomxmllo tienes en srctestresourcesnuevo-pommxl

Inicializacioacuten de los datos para los tests

Vamos a utilizar DBUnit para inicializar la BD para realizar los tests Para ello tendraacutes queantildeadir en el pomxml las dependencias necesarias (ya estaacuten antildeadidas en el fichero de

Servicios Rest

159

configuracioacuten proporcionado) En el fichero srctestresourcesforo-inicialxml encontrareacuteis elconjunto de datos con el que inicializaremos la base de datos para ejecutar nuestros tests

No es necesario (aunque es una muy buena praacutectica) que inicialicemos la BD para cada test

Implementacioacuten de los tests

Vamos a implementar los siguientes tests (que se ejecutaraacuten en en este mismo orden)

bull test1ConsultaTodosUsuarios() recuperamos los datos de todos los usuarios delforo Recuerda que previamente tienes que haber inicializado la BD con los datos del ficheroforo-inicialxml Recupera los datos en forma de JsonObject y comprueba que el nuacutemerode usuarios es el correcto Tambieacuten debes comprobar que tanto el login como los enlaceshatehoas para cada usuario estaacuten bien creados En concreto para cada usuario debesverificar que la uri (uri) el tipo mime (type) y el tipo de enlace (rel) son los correctos

bull test2CreamosMensajeDePepe() crearemos un nuevo mensaje del usuario con loginpepe Recuerda que este usuario tiene el rol registrado El mensaje tendraacute el asuntocena y el texto seraacute Mejor me voy al cine En este caso deberaacutes comprobar el valorde estado (debe ser 201) y debes recuperar (consultar con una peticioacuten REST) el mensajepara comprobar que la operacioacuten de insertar el mensaje ha tenido eacutexito

bull test3CreamosMensajeDeUsuarioNoAutorizado() creamos un nuevo mensaje deun usuario que no estaacute autorizado (por ejemplo de un usuario con login juan) En estecaso el mensaje tendraacute el asunto cena y el mensaje puede ser Pues yo tampoco voyEl resultado debe ser el coacutedigo de estado 401 ( Unauthorized)

bull test4ConsultaUsuario() Consultamos los datos del usuario pepe Recuperaremoslos datos como un JsonObject y comprobaremos que el valor de la uri para el tipo derelacioacuten self del enlace Link asociado es el correcto

  • Servicios Rest
  • Table of Contents
  • 1 Introduccioacuten a REST Disentildeo y creacioacuten de servicios RESTful
    • 11 iquestQueacute es un servicio Web
      • Servicios Web RESTful
        • 12 Fundamentos de REST
          • Recursos
          • Representacioacuten de los recursos
          • Direccionabilidad de los recursos URI
          • Uniformidad y restricciones de las interfaces
            • 13 Disentildeo de servicios Web RESTful
            • 14 Un primer servicio JAX-RS
              • Modelo de objetos
              • Modelado de URIs
              • Definicioacuten del formato de datos
                • Formato de datos para operaciones de lectura y modificacioacuten de los recursos
                • Formato de datos para operaciones de creacioacuten de los recursos
                  • Asignacioacuten de meacutetodos HTTP
                    • Visualizacioacuten de todos los Pedidos Clientes o Productos
                    • Obtencioacuten de Pedidos Clientes o Productos individuales
                    • Creacioacuten de un Pedido Cliente o Producto
                    • Actualizacioacuten de un Pedido Cliente o Producto
                    • Borrado de un Pedido Cliente o Producto
                    • Cancelacioacuten de un Pedido
                      • Implementacioacuten del servicio Creacioacuten del proyecto Maven
                      • Implementacioacuten del servicio Recursos JAX-RS
                        • Clases de nuestro dominio (entidades) Clientejava
                        • Clases de nuestro servicio RESTful ClienteResourcejava
                        • Creacioacuten de clientes
                        • Consulta de clientes
                        • Modificacioacuten de clientes
                          • Construccioacuten y despliegue del servicio
                          • Probando nuestro servicio
                            • 15 Ejercicios
                              • Servicio REST ejemplo (0 puntos)
                              • Servicio REST saludo (1 punto)
                              • Servicio REST foro (1 punto)
                                  • 2 Anotaciones baacutesicas JAX-RS El modelo de despliegue
                                    • 21 iquestCoacutemo funciona el enlazado de meacutetodos HTTP
                                    • 22 La anotacioacuten Path
                                      • Expresiones Path
                                        • Expresiones regulares
                                        • Reglas de precedencia
                                          • Paraacutemetros matrix (Matrix parameters)
                                          • Subrecursos (Subresource Locators)
                                            • Caraacutecter dinaacutemico del dispatching de peticiones
                                                • 23 Usos de las anotaciones Produces y Consumes
                                                  • Anotacioacuten Consumes
                                                  • Anotacioacuten Produces
                                                    • 24 Inyeccioacuten de paraacutemetros JAX-RS
                                                      • javaxwsrsPathParam
                                                      • Interfaz UriInfo
                                                      • javaxwsrsMatrixParam
                                                      • javaxwsrsQueryParam
                                                      • javaxwsrsFormParam
                                                      • javaxwsrsHeaderParam
                                                      • javaxwsrscoreContext
                                                      • javaxwsrsBeanParam
                                                      • Conversioacuten automaacutetica de tipos
                                                      • Valores por defecto (DefaultValue)
                                                        • 25 Configuracioacuten y despliegue de aplicaciones JAX-RS
                                                          • Configuracioacuten mediante la clase Application
                                                          • Configuracioacuten mediante un fichero webxml
                                                          • Configuracioacuten en un contenedor que no disponga de una implementacioacuten JAX-RS
                                                            • 26 Ejercicios
                                                              • Creacioacuten de un recurso creacioacuten y consulta de temas en el foro (05 puntos)
                                                              • Despliegue y pruebas del recurso (05 puntos)
                                                              • Muacuteltiples consultas de los temas del foro (05 puntos)
                                                              • Creacioacuten de subrecursos (05 puntos)
                                                                  • 3 Manejadores de contenidos Respuestas del servidor y manejo de excepciones
                                                                    • 31 Proveedores de entidades
                                                                      • Interfaz javaxwsrsextMessageBodyReader
                                                                      • Interfaz javaxwsrsextMessageBodyWriter
                                                                        • 32 Proveedores de entidad estaacutendar incluidos en JAX-RS
                                                                          • javaxwsrscoreStreamingOutput
                                                                          • javaioInputStream javaioReader
                                                                          • javaioFile
                                                                          • byte[]
                                                                          • String char[]
                                                                          • MultivaluedMapltString Stringgt y formularios de entrada
                                                                            • 33 Muacuteltiples representaciones de recursos
                                                                            • 34 Introduccioacuten a JAXB
                                                                              • Clase JAXBContext
                                                                              • Manejadores JAX-RS para JAXB
                                                                              • JAXB y JSON
                                                                                • 35 Respuestas del servidor
                                                                                  • Coacutedigos de respuesta por defecto
                                                                                    • Respuestas que indican eacutexito
                                                                                    • Respuestas que indican una situacioacuten de fallo
                                                                                      • Elaboracioacuten de respuestas con la clase Response
                                                                                        • Inclusioacuten de cookies en la respuesta
                                                                                        • El tipo enumerado de coacutedigos de estado
                                                                                        • La clase javaxwsrscoreGenericEntity
                                                                                            • 36 Manejadores de excepciones
                                                                                              • La clase javaxwsrsWebApplicationException
                                                                                              • Mapeado de excepciones
                                                                                              • Jerarquiacutea de excepciones
                                                                                                • 37 Ejercicios
                                                                                                  • Servicio REST ejemplo
                                                                                                  • Plantillas que se proporcionan
                                                                                                  • Uso de JAXB (05 puntos)
                                                                                                  • Uso de manejadores de contenidos y clase Response (075 puntos)
                                                                                                  • Manejo de excepciones (075 puntos)
                                                                                                      • 4 HATEOAS y Seguridad
                                                                                                        • 41 iquestQueacute es HATEOAS
                                                                                                        • 42 HATEOAS y Servicios Web
                                                                                                          • Enlaces Atom
                                                                                                          • Ventajas de utilizar HATEOAS con Servicios Web
                                                                                                            • Transparencia en la localizacioacuten
                                                                                                            • Desacoplamiento de los detalles de la interaccioacuten
                                                                                                            • Reduccioacuten de errores de transicioacuten de estados
                                                                                                              • Enlaces en cabeceras frente a enlaces Atom
                                                                                                                • 43 HATEOAS y JAX-RS
                                                                                                                  • Construccioacuten de URIs con UriBuilder
                                                                                                                  • URIs relativas mediante el uso de UriInfo
                                                                                                                  • Construccioacuten de enlaces (Links) en documentos XML y en cabeceras HTTP
                                                                                                                    • 44 Seguridad
                                                                                                                      • Autentificacioacuten en JAX-RS
                                                                                                                        • Creacioacuten de usuarios y roles
                                                                                                                          • Autorizacioacuten en JAX-RS
                                                                                                                          • Encriptacioacuten
                                                                                                                            • Anotaciones JAX-RS para autorizacioacuten
                                                                                                                              • Seguridad programada
                                                                                                                                • 45 Ejercicios
                                                                                                                                  • Uso de Hateoas (1 puntos)
                                                                                                                                  • Ejercicio seguridad (1 punto)
                                                                                                                                      • 5 Api cliente Procesamiento JSON y Pruebas
                                                                                                                                        • 51 API cliente Visioacuten general
                                                                                                                                          • Obtenemos una instancia Client
                                                                                                                                          • Configuramos el target del cliente (URI)
                                                                                                                                          • Construimos y Realizamos la peticioacuten
                                                                                                                                          • Manejo de excepciones
                                                                                                                                            • 52 Procesamiento JSON
                                                                                                                                            • 53 Modelo de procesamiento basado en el modelo de objetos
                                                                                                                                              • Creacioacuten de un modelos de objetos desde el coacutedigo de la aplicacioacuten
                                                                                                                                              • Navegando por el modelo de objetos
                                                                                                                                              • Escritura de un modelo de objetos en un stream
                                                                                                                                              • Modelo de procesamiento basado en streaming
                                                                                                                                                • Lectura de datos JSON
                                                                                                                                                • Escritura de datos JSON
                                                                                                                                                    • 54 Pruebas de servicios REST
                                                                                                                                                      • Ciclo de vida de Maven y tests JUnit
                                                                                                                                                      • Anotaciones JUnit y aserciones AssertThat
                                                                                                                                                      • Observaciones sobre los tests y algunos ejemplos de tests
                                                                                                                                                        • 55 Ejercicios
                                                                                                                                                          • Tests utilizando el API cliente y un mapeador de excepciones (1 punto)
                                                                                                                                                          • Tests utilizando el API Json y JUnit (1 punto)
                                                                                                                                                            • Inicializacioacuten de los datos para los tests
                                                                                                                                                              • Implementacioacuten de los tests
Page 4: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones

Servicios Rest

4

1 Introduccioacuten a REST Disentildeo y creacioacuten de serviciosRESTful

En esta sesioacuten vamos a introducir los conceptos de servicio Web y servicio Web RESTfulque es el tipo de servicios con los que vamos a trabajar Explicaremos el proceso de disentildeodel API de un servicio Web RESTful y definiremos las URIs que constituiraacuten los puntos deentrada de nuestra aplicacioacuten REST Finalmente ilustraremos los pasos para implementardesplegar y probar un servicio REST utilizando Maven IntelliJ y el servidor de aplicacionesWildfly Tambieacuten nos familiarizaremos con Postman una herramienta para poder probar deforma sencilla los servicios web directamente desde el navegador

11 iquestQueacute es un servicio Web

El disentildeo del software tiende a ser cada vez maacutes modular Las aplicaciones estaacuten formadaspor una serie de componentes reutilizables (servicios) que pueden encontrarse distribuidosa lo largo de una serie de maacutequinas conectadas en red

El WC3 (World Wide Web Consortium) define un servicio Web como un sistema softwaredisentildeado para soportar interacciones maacutequina a maacutequina a traveacutes de la red Dicho de otromodo los servicios Web proporcionan una forma estaacutendar de interoperar entre aplicacionessoftware que se ejecutan en diferentes plataformas Por lo tanto su principal caracteriacutesticasu gran interoperabilidad y extensibilidad asiacute como por proporcionar informacioacuten faacutecilmenteprocesable por las maacutequinas gracias al uso de XML Los servicios Web pueden combinarsecon muy bajo acoplamiento para conseguir la realizacioacuten de operaciones complejas De estaforma las aplicaciones que proporcionan servicios simples pueden interactuar con otras paraentregar servicios sofisticados antildeadidos

Historia de los servicios Web

Los servicios Web fueron inventados para solucionar el problema de lainteroperabilidad entre las aplicaciones Al principio de los 90 con el desarrollo deInternetLANWAN aparecioacute el gran problema de integrar aplicaciones diferentes Unaaplicacioacuten podiacutea haber sido desarrollada en C++ o Java y ejecutarse bajo Unix unPC o un computador mainframe No habiacutea una forma faacutecil de intercomunicar dichasaplicaciones Fueacute el desarrollo de XML el que hizo posible compartir datos entreaplicaciones con diferentes plataformas hardware a traveacutes de la red o incluso a traveacutesde Internet La razoacuten de que se llamasen servicios Web es que fueron disentildeadospara residir en un servidor Web y ser llamados a traveacutes de Internet tiacutepicamente viaprotocolos HTTP o HTTPS De esta forma se asegura que un servicio puede serllamado por cualquier aplicacioacuten usando cualquier lenguaje de programacioacuten y bajocualquier sistema operativo siempre y cuaacutendo por supuesto la conexioacuten a Internetesteacute activa y tenga un puerto abierto HTTPHTTPS lo cual es cierto para casi cualquiercomputador que disponga de acceso a Internet

A nivel conceptual un servicio es un componente software proporcionado a traveacutes de unendpoint accesible a traveacutes de la red Los servicios productores y consumidores utilizanmensajes para intercambiar informacioacuten de invocaciones de peticioacuten y respuesta en formade documentos auto-contenidos que hacen muy pocas asunciones sobre las capacidadestecnoloacutegicas de cada uno de los receptores

Servicios Rest

5

iquestQueacute es un endpointLos servicios pueden interconectarse a traveacutes de la red En unaarquitectura orientada a servicios cualquier interaccioacuten punto a puntoimplica dos endpoints uno que proporciona un servicio y otro de loconsume Es decir que un endpoint es cada uno de los elementosen nuestro caso nos referimos a servicios que se situacutean en ambosextremos de la red que sirve de canal de comunicacioacuten entre ellosCuando hablamos de servicios Web un endpoint se especifica medianteuna URI

A nivel teacutecnico los servicios pueden implementarse de varias formas En estesentido podemos distinguir dos tipos de servicios Web los denominados servicios Webgrandes (big Web Services) los llamaremos servicios Web SOAP y servicios ligeros oservicios Web RESTful

Los servicios Web SOAP se caracterizan por utilizar mensajes XML que siguen el estaacutendarSOAP (Simple Object Access Protocol) Ademaacutes contienen una descripcioacuten de las operacionesproporcionadas por el servicio escritas en WSDL (Web Services Description Language) unlenguaje basado en XML

Los servicios Web RESTful por el contrario pueden intercambiar mensajes escritos endiferentes formatos y no requieren el publicar una descripcioacuten de las operaciones queproporcionan por lo que necesitan una menor infraestructura para su implementacioacutenNosotros vamos a centrarnos en el uso de estos servicios

Servicios Web RESTful

Son un tipo de Servicios Web que se adhieren a una serie de restricciones arquitectoacutenicasenglobadas bajo las siglas de REST y que utilizan estaacutendares Web tales como URIs HTTPXML y JSON

El API Java para servicios Web RESTful (JAX-RS) permite desarrollar servicios Web RESTfulde forma sencilla La versioacuten maacutes reciente del API es la 20 cuya especificacioacuten estaacute publicadaen el documento JSR-339 y que podemos descargar desde httpsjcporgenjsrdetailid=339 A lo largo de estas sesiones veremos coacutemo utilizar JAX-RS para desarrollar serviciosWeb RESTful Dicho API utiliza anotaciones Java para reducir los esfuerzos de programacioacutende los servicios

12 Fundamentos de REST

El teacutermino REST proviene de la tesis doctoral de Roy Fielding publicada en el antildeo 2000y significa REpresentational State Transfer (podemos acceder a la tesis original en httpwwwicsuciedu~fieldingpubsdissertationtophtm) REST es un conjunto de restriccionesque cuando son aplicadas al disentildeo de un sistema crean un estilo arquitectoacutenico de softwareDicho estilo arquitectoacutenico se caracteriza por seguir los siguientes principios

bull Debe ser un sistema cliente-servidor

bull Tiene que ser sin estado es decir no hay necesidad de que los servicios guarden lassesiones de los usuarios (cada peticioacuten al servicio tiene que ser independiente de lasdemaacutes)

bull Debe soportar un sistema de cacheacutes la infraestructura de la red deberiacutea soportar cacheacuteen diferentes niveles

Servicios Rest

6

bull Debe ser un sistema uniformemente accesible (con una interfaz uniforme) Estarestriccioacuten define coacutemo debe ser la interfaz entre clientes y servidores La idea es simplificary desacoplar la arquitectura permitiendo que cada una de sus partes puede evolucionarde forma independiente Una interfaz uniforme se debe caracterizar por

Estar basada en recursos La abstraccioacuten utilizada para representar la informacioacuten ylos datos en REST es el recurso y cada recurso debe poder ser accedido medianteuna URI (Uniform Resource Identifier)

Orientada a representaciones La interaccioacuten con los servicios tiene lugar atraveacutes de las representaciones de los recursos que conforman dicho servicio Unrecurso referenciado por una URI puede tener diferentes formatos (representaciones)Diferentes plataformas requieren formatos diferentes Por ejemplo los navegadoresnecesitan HTML JavaScript requiere JSON (JavaScript Object Notation) y unaaplicacioacuten Java puede necesitar XML

Interfaz restringida Se utiliza un pequentildeo conjunto de meacutetodos bien definidos paramanipular los recursos

Uso de mensajes auto-descriptivos cada mensaje debe incluir la suficienteinformacioacuten como para describir coacutemo procesar el mensaje Por ejemplo se puedeindicar coacutemo parsear el mensaje indicando el tipo de contenido del mismo (xml htmltextohellip)

Uso de Hipermedia como maacutequina de estados de la aplicacion (HATEOAS) Lospropios formatos de los datos son los que dirigen las transiciones entre estados dela aplicacioacuten Como veremos maacutes adelante con maacutes detalle el uso de HATEOAS(Hypermedia As The Engine Of Application State) va a permitir transferir de formaexpliacutecita el estado de la aplicacion en los mensajes intercambiados y por lo tantorealizar interacciones con estado

bull Tiene que ser un sistema por capas un cliente no puede discernir si estaacute accediendodirectamente al servidor o a alguacuten intermediario Las capas intermedias van a permitirsoportar la escalabilidad asiacute como reforzar las poliacuteticas de seguridad

A continuacioacuten analizaremos algunas de las abstracciones que constituyen un sistemaRESTful recursos representaciones URIs y los tipos de peticiones HTTP que constituyen lainterfaz uniforme utilizada en las transferencias clienteservidor

Recursos

Un recurso REST es cualquier cosa que sea direccionable (y por lo tanto accesible) a traveacutesde la Web Por direccionable nos referimos a recursos que puedan ser accedidos y transferidosentre clientes y servidores Por lo tanto un recurso es una correspondencia loacutegica ytemporal con un concepto en el dominio del problema para el cual estamos implementandouna solucioacuten

Algunos ejemplos de recursos REST son

bull Una noticia de un perioacutedico

bull La temperatura de Alicante a las 400pm

bull Un valor de IVA almacenado en una base de datos

bull Una lista con el historial de las revisiones de coacutedigo en un sistema CVS

bull Un estudiante en alguna aula de alguna universidad

Servicios Rest

7

bull El resultado de una buacutesqueda de un iacutetem particular en Google

Aun cuando el mapeado de un recurso es uacutenico diferentes peticiones a un recursopueden devolver la misma representacioacuten binaria almacenada en el servidor Por ejemploconsideremos un recurso en el contexto de un sistema de publicaciones En este casouna peticioacuten de la uacuteltima revisioacuten publicada y la peticioacuten de la revisioacuten nuacutemero 12 enalguacuten momento de tiempo pueden devolver la misma representacioacuten del recurso cuandola uacuteltima revisioacuten sea efectivamente la 12 Por lo tanto cuando la uacuteltima revisioacuten publicadase incremente a la versioacuten 13 una peticioacuten a la uacuteltima revisioacuten devolveraacute la versioacuten 13 yuna peticioacuten de la revisioacuten 12 continuaraacute devolviendo la versioacuten 12 En definitiva cada unode los recursos puede ser accedido directamente y de forma independiente pero diferentespeticiones podriacutean apuntar al mismo dato

Debido a que estamos utilizando HTTP para comunicarnos podemos transferir cualquier tipode informacioacuten que pueda transportarse entre clientes y servidores Por ejemplo si realizamosuna peticioacuten de un fichero de texto de la CNN nuestro navegador mostraraacute un fichero de textoSi solicitamos una peliacutecula flash a YouTube nuestro navegador recibiraacute una peliacutecula flash Enambos casos los datos son transferidos sobre TCPIP y el navegador conoce coacutemo interpretarlos streams binarios debido a la cabecera de respuesta del protocolo HTTP Content-Type Porlo tanto en un sistema RESTful la representacioacuten de un recurso depende del tipo deseado porel cliente (tipo MIME) el cual estaacute especificado en la peticioacuten del protocolo de comunicaciones

Representacioacuten de los recursos

La representacioacuten de los recursos es lo que se enviacutea entre los servidores y clientes Unarepresentacioacuten muestra el estado temporal del dato real almacenado en alguacuten dispositivo dealmacenamiento en el momento de la peticioacuten En teacuterminos generales es un stream binariojuntamente con los metadatos que describen coacutemo dicho stream debe ser consumido por elcliente yo servidor (los metadatos tambieacuten puden contener informacioacuten extra sobre el recursocomo por ejemplo informacioacuten de validacioacuten y encriptacioacuten o coacutedigo extra para ser ejecutadodinaacutemicamente)

A traveacutes del ciclo de vida de un servicio web pueden haber varios clientes solicitando recursosClientes diferentes son capaces de consumir diferentes representaciones del mismo recursoPor lo tanto una representacioacuten puede tener varias formas como por ejemplo una imagen untexto un fichero XML o un fichero JSON pero tienen que estar disponibles en la misma URL

Para respuestas generadas para humanos a traveacutes de un navegador una representacioacutentiacutepica tiene la forma de paacutegina HTML Para respuestas automaacuteticas de otros servicios web lalegibilidad no es importante y puede utilizarse una representacioacuten mucho maacutes eficiente comopor ejemplo XML

El lenguaje para el intercambio de informacioacuten con el servicio queda a eleccioacuten deldesarrollador A continuacioacuten mostramos algunos formatos comunes que podemos utilizarpara intercambiar esta informacioacuten

Table 1 Ejemplos de formatos utilizados por los servicios REST

Formato Tipo MIME

Texto plano textplain

HTML texthtml

XML applicationxml

JSON applicationjson

Servicios Rest

8

De especial intereacutes es el formato JSON Se trata de un lenguaje ligero de intercambio deinformacioacuten que puede utilizarse en lugar de XML (que resulta considerablemente maacutespesado) para aplicaciones AJAX De hecho en Javascript puede leerse este tipo de formatosimplemente utilizando el meacutetodo eval()

Direccionabilidad de los recursos URI

Una URI o Uniform Resource Identifier en un servicio web RESTful es un hiper-enlace aun recurso y es la uacutenica forma de intercambiar representaciones entre clientes y servidoresUn servicio web RESTful expone un conjunto de recursos que identifican los objetivos de lainteraccioacuten con sus clientes

El conjunto de restricciones REST no impone que las URIs deban ser hiper-enlacesSimplemente hablamos de hiper-enlaces porque estamos utilizando la Web para crearservicios web Si estuvieacutesemos utilizando un conjunto diferente de tecnologiacuteas soportadasuna URI RESTful podriacutea ser algo completamente diferente Sin embargo la idea dedireccionabilidad debe permanecer

En un sistema REST la URI no cambia a lo largo del tiempo ya que la implementacioacutende la arquitectura es la que gestiona los servicios localiza los recursos negocia lasrepresentaciones y enviacutea respuestas con los recursos solicitados Y lo que es maacutes importantesi hubiese un cambio en la estructura del dispositivo de almacenamiento en el lado del servidor(por ejemplo un cambio de servidores de bases de datos) nuestras URIs seguiraacuten siendo lasmismas y seraacuten vaacutelidas mientras el servicio web siga estando en marcha o el contexto delrecurso no cambie

Sin las restricciones REST los recursos se acceden por su localizacioacutenlas direcciones web tiacutepicas son URIs fijas Si por ejemplo renombramosun fichero en el servidor la URI seraacute diferente si movemos el fichero a undirectorio diferente la URI tambieacuten seraacute diferente

El formato de una URI se estandariza como sigue

schemehostportpathqueryStringfragment

En donde

bull scheme es el protocolo que estamos utilizando para comunicarnos con el servidor Paraservicios REST normalmente el protocolo seraacute http o https

bull El teacutermino host es un nombre DNS o una direccioacuten IP

bull A continuacioacuten se puede indicar de forma opcional un puerto (mediante port ) que es unvalor numeacuterico El host y el port representan la localizacioacuten de nuestro recurso en la red

bull Seguidamente aparece una expresioacuten path que es un conjunto de segmentos de textodelimitados por el caraacutecter (pensemos en la expresioacuten path como en una lista dedirectorios de un fichero en nuestra maacutequina)

bull Esta expresioacuten puede ir seguida opcionalmente por una queryString El caraacutecter )separa el path de la queryString Esta uacuteltima es una lista de paraacutemetros representadoscomo pares nombrevalor Cada par estaacute delimitado por el caraacutecter amp

La uacuteltima parte de la URI es el fragment delimitado por el caraacutecter Normalmente seutiliza para apuntar a cierto lugar del documento al que estamos accediendo

Servicios Rest

9

En una URI no todos los caracteres estaacuten permitidos de forma que algunos caracteres secodificaraacuten de acuerdo a las siguientes reglas

bull Los caracteres a-z A-Z 0-9 - y _ permanecen igual

bull El caracter espacio se convierte en el caraacutecter +

bull El resto de caracteres se codifican como una secuencia de bits siguiendo un esquema decodificacioacuten hexadecimal de forma que cada dos diacutegitos hexadecimales van precedidospor el caraacutecter

Un ejemplo de URI podriacutea ser eacuteste

httpexpertojavauaesrecursosclientesapellido=MartinezampcodPostal=02115

En el ejemplo anterior el host viene dado por expertojavauaes el path o ruta de acceso alrecurso es recursosclientes y hemos especificado los paraacutemetros apellido y codPostal conlos valores Martinez y 02115 respectivamente

Si por ejemplo en nuestra aplicacioacuten tenemos informacioacuten de clientes podriacuteamos acceder ala lista correspondiente mediante una URL como la siguiente

httpexpertojavauaesrecursosclientes

Esto nos devolveraacute la lista de clientes en el formato que el desarrollador del servicio hayadecidido Hay que destacar por lo tanto que en este caso debe haber un entendimiento entreel consumidor y el productor del servicio de forma que el primero comprenda el lenguajeutilizado por el segundo

La URL anterior nos podriacutea devolver un documento como el siguiente

ltxml version=10gtltclientesgt ltclientegthttpexpertojavauaesrecursosclientes1ltclientegt ltclientegthttpexpertojavauaesrecursosclientes2ltclientegt ltclientegthttpexpertojavauaesrecursosclientes4ltclientegt ltclientegthttpexpertojavauaesrecursosclientes6ltclientegtltclientesgt

En este documento se muestra la lista de clientes registrados en la aplicacioacuten cada uno deellos representado tambieacuten por una URL Accediendo a estas URLs a su vez podremosobtener informacioacuten sobre cada curso concreto o bien modificarlo

Uniformidad y restricciones de las interfaces

Ya hemos introducido los conceptos de recursos y sus representaciones Hemos dichoque los recursos son correspondencias (mappings) de los estados reales de las entidadesque son intercambiados entre los clientes y servidores Tambieacuten hemos dicho que lasrepresentaciones son negociadas entre los clientes y servidores a traveacutes del protocolo decomunicacioacuten en tiempo de ejecucioacuten (a traveacutes de HTTP) A continuacioacuten veremos con detalle

Servicios Rest

10

lo que significa el intercambio de estas representaciones y lo que implica para los clientes yservidores el realizar acciones sobre dichos recursos

El desarrollo de servicios web REST es similar al desarrollo de aplicaciones web Sin embargola diferencia fundamental entre el desarrollo de aplicaciones web tradicionales y las maacutesmodernas es coacutemo pensamos sobre las acciones a realizar sobre nuestras abstraccionesde datos De forma maacutes concreta el desarrollo moderno estaacute centrado en el concepto denombres (intercambio de recursos) el desarrollo tradicional estaacute centrado en el conceptode verbos (acciones remotas a realizar sobre los datos) Con la primera forma estamosimplementando un servicio web RESTful con la segunda un servicio similar a una llamada aprocedimiento remoto- RPC) Y lo que es maacutes un servicio RESTful modifica el estado delos datos a traveacutes de la representacioacuten de los recursos (por el contrario una llamada aun servicio RPC oculta la representacioacuten de los datos y en su lugar enviacutea comandos paramodificar el estado de los datos en el lado del servidor) Finalmente en el desarrollo modernode aplicaciones web limitamos la ambiguumledad en el disentildeo y la implementacioacuten debido aque tenemos cuatro acciones especiacuteficas que podemos realizar sobre los recursos CreateRetrieve Update y Delete (CRUD) Por otro lado en el desarrollo tradicional de aplicacionesweb podemos tener otras acciones con nombres o implementaciones no estaacutendar

A continuacioacuten indicamos la correspondencia entre las acciones CRUD sobre los datos y losmeacutetodos HTTP asociados

Table 2 Operaciones REST sobre los recursos

Accioacuten sobre los datos Protocolo HTTP equivalente

CREATE POST

RETRIEVE GET

UPDATE PUT

DELETE DELETE

El principio de uniformidad de la interfaz de acceso a recursos es fundamental y quizaacute el maacutesdifiacutecil de seguir por los programadores acostumbrados al modelo RPC (Remote ProcedureCall) La idea subyacente es utilizar uacutenicamente un conjunto finito y claramente establecidode operaciones para la interaccioacuten con los servicios Esto significa que no tendremos unparaacutemetro accioacuten en nuestra URI y que soacutelo utilizaremos los meacutetodos HTTP para accedera nuestros servicios Cada uno de los meacutetodos tiene un propoacutesito y significado especiacuteficosque mostramos a continuacioacuten

GETGET es una operacioacuten soacutelo de lectura Se utiliza para recuperar informacioacuten especiacuteficadel servidor Tambieacuten se trata de una operacioacuten idempotente y segura Idempotentesignifica que no importa cuaacutentas veces invoquemos esta operacioacuten el resultado (queobservaremos como usuarios) debe ser siempre el mismo Segura significa que unaoperacioacuten GET no cambia el estado del servidor en modo alguno es decir no debe exhibirninguacuten efecto lateral en el servidor Por ejemplo el hecho de leer un documento HTMLno deberiacutea cambiar el estado de dicho documento

PUTLa operacioacuten PUT solicita al servidor el almacenar el cuerpo del mensaje enviado condicha operacioacuten en la direccioacuten proporcionada en el mensaje HTTP Normalmente semodela como una insercioacuten o actualizacioacuten (nosotros la utilizaremos solamente comoactualizacioacuten) Es una propiedad idempotente Cuando se utiliza PUT el cliente conoce

Servicios Rest

11

la identidad del recurso que estaacute creando o actualizando Es idempotente porque enviar elmismo mensaje PUT maacutes de una vez no tiene ninguacuten efecto sobre el servicio subyacenteUna analogiacutea podriacutea ser un documento de texto que estemos editando No importa cuaacutentasveces pulsemos el botoacuten de grabar el fichero que contiene el documento loacutegicamenteseraacute el mismo documento

DELETEEsta operacioacuten se utiliza para eliminar recursos Tambieacuten es idempotente

POSTPost es la uacutenica operacioacuten HTTP que no es idempotente ni segura Cada peticioacuten POSTpuede modificar el servicio de forma exclusiva Se puede enviar o no informacioacutencon la peticioacuten Tambieacuten podemos recibir o no informacioacuten con la respuesta Paraimplementar servicios REST es deseable enviar informacioacuten con la peticioacuten y tambieacutenrecibir informacioacuten con la respuesta

Adicionalmente podemos utilizar otras dos operaciones HTTP

HEADEs una operacioacuten exactamente igual que GET excepto que en lugar de devolver un cuerpode mensaje solamente devuelve un coacutedigo de respuesta y alguna cabecera asociada conla peticioacuten

OPTIONSSe utiliza para solicitar informacioacuten sobre las opciones disponibles sobre un recurso en elque estamos interesados Esto permite al cliente determinar las capacidades del servidory del recurso sin tener que realizar ninguna peticioacuten que provoque una accioacuten sobre elrecurso o la recuperacioacuten del mismo

PATCHSe utiliza para para realiza reemplazos (actualizaciones) parciales de un documento yaque la operacioacuten PUT soacutelo permite una actualizacioacuten completa del recurso (y requiereindicar una representacioacuten completa del mismo) Es uacutetil cuando el recurso a modificares complejo y solamente queremos actualizar parte de su contenido En este caso solonecesitamos indicar la parte que queremos cambiar

13 Disentildeo de servicios Web RESTful

El disentildeo de servicios RESTful no es muy diferente del disentildeo de aplicaciones webtradicionales tenemos requerimientos de negocio tenemos usuarios que quieren realizaroperaciones sobre los datos y tenemos restricciones hardware que van a condicionar nuestraarquitectura software La principal diferencia reside en el hecho de que tenemos que buscara partir de los requerimientos cuaacuteles van a ser los recursos que van a ser accedidos a traveacutesde los servicios sin preocuparnos de queacute operaciones o acciones especiacuteficas van a poderserealizar sobre dichos recursos (el proceso de disentildeo depende de los nombres no de losverbos)

Podemos resumir los principios de disentildeo de servicios web RESTful en los siguientes cuatropasos

1 Elicitacioacuten de requerimientos y creacioacuten del modelo de objetos Este paso es similar aldisentildeo orientado a objetos El resultado del proceso puede ser un modelo de clases UML

2 Identificacioacuten de recursos Este paso consiste en identificar los objetos de nuestromodelo sin preocuparnos de las operaciones concretas a realizar sobre dichos objetos

Servicios Rest

12

3 Definicioacuten de las URIs Para satisfacer el principio de direccionabilidad de los recursostendremos que definir las URIs que representaraacuten los endpoints de nuestros servicios yque constituiraacuten los puntos de entrada de los mismos

4 Definicioacuten de la representacioacuten de los recursos Puesto que los sistemas REST estaacutenorientados a la representacioacuten tendremos que definir el formato de los datos queutilizaremos para intercambiar informacioacuten entre nuestros servicios y clientes

5 Definicioacuten de los meacutetodos de acceso a los recursos Finalmente tendremos que decidirqueacute meacutetodos HTTP nos permitiraacuten acceder a las URIs que queremos exponer asiacutecomo queacute haraacute cada meacutetodo Es muy importante que en este paso nos cintildeamos alas restricciones que definen los principios RESTful que hemos indicado en apartadosanteriores

14 Un primer servicio JAX-RS

Vamos a ilustrar los pasos anteriores con un ejemplo concretamente definiremos una interfazRESTful para un sistema sencillo de gestioacuten de pedidos de un hipoteacutetico comercio por internetLos potenciales clientes de nuestro sistema podraacuten realizar compras modificar pedidosexistentes en nuestro sistema asiacute como visualizar sus datos personales o la informacioacuten sobrelos productos que son ofertados por el comercio

Modelo de objetos

A partir de los requerimientos del sistema obtenemos el modelo de objetos El modelode objetos de nuestro sistema de ventas por internet es bastante sencillo Cada pedidoen el sistema representa una uacutenica transaccioacuten de compra y estaacute asociada con un clienteparticular Los pedidos estaraacuten formados por una o maacutes liacuteneas de pedido Las liacuteneas de pedidorepresentan el tipo y el nuacutemero de unidades del producto adquirido

Basaacutendonos en esta descripcioacuten de nuestro sistema podemos extraer que los objetos denuestro modelo son Pedido Cliente LineaPedido y Producto Cada objeto de nuestromodelo tiene un identificador uacutenico representado por la propiedad id dada por un valor detipo entero La siguiente figura muestra un diagrama UML de nuestro modelo

Estamos interesados en consultar todos los pedidos realizados asiacute como cada pedido deforma individual Tambieacuten queremos poder realizar nuevos pedidos asiacute como actualizar

Servicios Rest

13

pedidos existentes El objeto ServicioPedidos representa las operaciones que queremosrealizar sobre nuestos objetos Pedido Cliente LineaPedido y Producto

Modelado de URIs

Lo primero que haremos para crear nuestra interfaz distribuida es definir y poner nombre acada uno de los endpoints de nuestro sistema En un sistemam RESTful los endpoints seraacutenlos recursos del sistema que identificaremos mediante URIs

En nuestro modelo de objetos queremos poder interactuar con Pedidos Clientes y ProductosEacutestos seraacuten por lo tanto nuestros recursos de nivel maacutes alto Por otro lado estamosinteresados en obtener una lista de cada uno de estos elementos de alto nivel asiacute comointeractuar con los elementos indiduales de cada tipo El objeto LineaPedido es un objetoagregado del objeto Pedido por lo que no lo consideraremos com un recurso de nivel superiorMaacutes adelante veremos que podremos exponerlo como un subrecurso de un Pedido particularpero por ahora asumiremos que estaacute oculto por el formato de nuestros datos Seguacuten estouna posible lista de URIs que expondraacute nuestro sistema podriacutea ser

bull pedidos

bull pedidosid

bull productos

bull productosid

bull clientes

bull clientesid

Fiacutejate que hemos representado como URIs los nombres en nuestromodelo de objetos Recuerda que las URIS no deberiacutean utilizarse comomini-mecanismos de RPC ni deberiacutean identificar operaciones En vez deeso tenemos que utilizar una combinacioacuten de meacutetodos HTTP y de datos(recursos) para modelar las operaciones de nuestro sistema RESTful

Definicioacuten del formato de datos

Una de las cosas maacutes importantes que tenemos que hacer cuando definimos la interfazRESTful es determinar coacutemo se representaraacuten los recursos que seraacuten accedidos por losusuarios de nuestro API REST Quizaacute XML sea el formato maacutes popular de la web y puede serprocesado por la mayor parte de los lenguajes de programacioacuten Como veremos maacutes adelanteJSON es otro formato popular menos verboso que XML y que puede ser interpretadodirectamente por JavaScript (lo cual es perfecto para aplicaciones Ajax por ejemplo) Porahora utilizaremos el formato XML en nuestro ejemplo

Generalmente tendriacuteamos que definir un esquema XML para cada representacioacuten quequeramos transimitir a traves de la red Un esquema XML define la gramaacutetica del formato dedatos Por simplicidad vamos a omitir la creacioacuten de esquemas asumiendo que los ejemplosque proporcionamos se adhieren a sus correspondientes esquemas

A continuacioacuten distinguiremos entre dos formatos de datos uno para las operaciones delectura y actualizacioacuten y otro para la operacioacuten de creacioacuten de recursos

Formato de datos para operaciones de lectura y modificacioacuten de los recursos

Las representaciones de los recursos Pedido Cliente y Producto tendraacuten un elemento XMLen comuacuten al que denominaremos link

Servicios Rest

14

ltlink rel=self href=httporgexpertojavagt

El elemento (o etiqueta) link indica a los clientes que obtengan un documento XML comorepresentacioacuten del recurso doacutende pueden interaccionar en la red con dicho recurso enparticular El atributo self le indica al cliente queacute relacioacuten tiene dicho enlace con la URIdel recurso al que apunta (informacioacuten contenida en el atributo href ) El valor self indicaque estaacute apuntando a siacute mismo Maacutes adelante veremos la utilidad del elemento link cuandoagreguemos informacioacuten en documentos XML maacutes grandes

El formato de representacioacuten del recurso Cliente podriacutea ser

ltcliente id=8gt ltlink rel=self href=httporgexpertojavaclientes8gt ltnombregtPedroltnombregt ltapellidosgtGarcia Perezltapellidosgt ltdirecciongtCalle del Pino 5ltdirecciongt ltcodPostalgt08888ltcodPostalgt ltciudadgtMadridltciudadgtltclientegt

El formato de representacioacuten del recurso Producto podriacutea ser

ltproducto id=34gt ltlink rel=self href=httporgexpertojavaproductos34gt ltnombregtiPhone 6ltnombregt ltpreciogt800ltpreciogt ltcantidadgt1ltcantidadgtltproductogt

Finalmente el formato de la representacioacuten del recurso Pedido podriacutea ser

ltpedido id=233gt ltlink rel=self href=httporgexpertojavapedidos233gt lttotalgt800lttotalgt ltfechagtDecember 22 2014 0656ltfechagt ltcliente id=8gt ltlink rel=self href=httporgexpertojavaclientes8gt ltnombregtPedroltnombregt ltapellidosgtGarcia Perezltapellidosgt ltdirecciongtCalle del Pino 5ltdirecciongt ltcodPostalgt08888ltcodPostalgt ltciudadgtMadridltciudadgt ltclientegt ltlineasPedidogt ltlineaPedido id=1gt ltproducto id=34gt ltlink rel=self href=httporgexpertojavaproductos34gt ltnombregtiPhone 6ltnombregt

Servicios Rest

15

ltpreciogt800ltpreciogt ltcantidadgt1ltcantidadgt ltproductogt ltlineaPedidogt ltlineasPedidogtltpedidogt

El formato de datos de un Pedido tiene en un primer nivel la informacioacuten del total conel importe total del pedido asiacute como la fecha en la que se hizo dicho pedido Pedido es unbuen ejemplo de composicioacuten de datos ya que un pedido incluye informacioacuten sobre el Clientey el Productos Aquiacute es donde el elemento ltlinkgt puede resultar particularmente uacutetil Si elusuario estaacute interesado en interaccionar con el Cliente que ha realizado el pedido o en uno delos productos del mismo se proporciona la URI necesaria para interactuar con cada uno dedichos recursos De esta forma cuando el usuario del API consulte un pedido podraacute ademaacutesacceder a informacioacuten adicional relacionada con la consulta realizada

Formato de datos para operaciones de creacioacuten de los recursos

Cuando estamos creando nuevos Pedidos Clientes o Productos no tiene mucho sentidoincluir un atributo id y un elemento link en nuestro documento XML El servidor seraacute elencargado de crear los ids cuando inserte nuestro nuevo objeto en la base de datos Tampococonocemos la URI del nuevo objeto creado ya que seraacute el servidor el encargado de generarloPor lo tanto para crear un nuevo Producto el formato de la informacioacuten podriacutea ser el siguiente

ltproductogt ltlink rel=self href=httporgexpertojavaclientes8gt ltnombregtiPhoneltnombregt ltpreciogt800ltpreciogtltproductogt

Asignacioacuten de meacutetodos HTTP

Finalmente tendremos que decidir queacute meacutetodos HTTP expondremos en nuestro servicio paracada uno de los recursos asiacute como definir queacute haraacuten dichos meacutetodos Es muy importanteno asignar funcionaldad a un meacutetodo HTTP que sobrepase los liacutemites impuestos por laespecificacioacuten de dicho meacutetodo Por ejemplo una operacioacuten GET sobre un recurso concretodeberiacutea ser de soacutelo lectura No deberiacutea cambiar el estado del recurso cuando invoquemos laoperacioacuten GET sobre eacutel Si no seguimos de forma estricta la semaacutentica de los meacutetodos HTTPlos clientes asiacute como cualquier otra herramienta administrativa no pueden hacer asuncionessobre nuestros servicios de forma que nuestro sistema se vuelve maacutes complejo

Veamos para cada uno de los meacutetodos de nuestro modelo de objetos cuales seraacuten las URIsy meacutetodos HTTP que usaremos para representarlos

Visualizacioacuten de todos los Pedidos Clientes o Productos

Los tres objetos de nuestro modelo Pedidos Clientes y Productos son accedidos ymanipulados de forma similar Los usuarios pueden estar interesados en ver todos losPedidos Clientes o Productos en el sistema Las siguientes URIs representan dichos objetoscomo un grupo

bull pedidos

Servicios Rest

16

bull productos

bull clientes

Para obtener una lista de Pedidos Clientes o Productos el cliente remoto realizara unallamada al meacutetodo HTTP GET sobre la URI que representa el grupo de objetos Un ejemplode peticioacuten podriacutea ser la siguiente

GET productos HTTP11

Nuestro servicio responderaacute con los datos que representan todos los Pedidos de nuestrosistema Una respuesta podriacutea ser eacutesta

HTTP11 200 OKContent-Type applicationxml

ltproductosgt ltproducto id=111gt ltlink rel=self href=httporgexpertojavaproductos111gt ltnombregtiPhoneltnombregt ltpreciogt64899ltpreciogt ltproductogt ltproducto id=222gt ltlink rel=self href=httporgexpertojavaproductos222gt ltnombregtMacbookltnombregt ltpreciogt159999ltpreciogt ltproductogt ltproductosgt

Un problema que puede darse con esta peticioacuten es que tengamos miles de Pedidos Clienteso Productos en nuestro sistema por lo que podemos sobrecargar a nuestro cliente y afectarnegativamente a los tiempos de respuesta Para mitigar esta problema permitiremos que elusuario especifique unos paraacutemetros en la URI para limitar el tamantildeo del conjunto de datosque se va a devolver

GET pedidosstartIndex=0ampsize=5 HTTP11GET productosstartIndex=0ampsize=5 HTTP11GET clientesstartIndex=0ampsize=5 HTTP11

En las oacuterdenes anteriores hemos definido dos paraacutemetros de peticioacuten startIndex ysize El primero de ellos es un iacutendice numeacuterico que representa a partir de queacute posicioacuten enla lista de Pedidos Clientes o Productos comenzaremos a enviar la informacioacuten al clienteEl paraacutemetro size especifica cuaacutentos de estos objetos de la lista queremos que nos seandevueltos

Estos paraacutemetros seraacuten opcionales de forma que el cliente no tiene que especificarlos en suURI

Obtencioacuten de Pedidos Clientes o Productos individuales

Ya hemos comentado previamente que podriacuteamos utilizar las siguientes URIs para obtenerPedidos Clientes o Productos

Servicios Rest

17

bull pedidosid

bull productosid

bull clientesid

En este caso usaremos el meacutetodo HTTP GET para recuperar objetos individuales en elsistema Cada invocacioacuten GET devolveraacute la informacioacuten del correspondiente objeto Porejemplo

GET pedidos233 HTTP11

Para esta peticioacuten el cliente estaacute interesado en obtener una representacioacuten del Pedido conidentificador 233 Las peticiones GET para Productos y Clientes podriacutean funcionar de formasimilar El mensaje de respuesta podriacutea parecerse a algo como esto

HTTP11 200 OKContent-Type applicationxml

ltpedido id=233gtltpedidogt

El coacutedigo de respuesta es 200 OK indicando que la peticioacuten ha tenido eacutexito La cabeceraContent-Type especifica el formato del cuerpo de nuestro mensaje como XML y finalmenteobtenemos la representacioacuten del Pedido solicitado

Creacioacuten de un Pedido Cliente o Producto

Para crear un Pedido Cliente o Producto utilizaremos el meacutetodo POST En este caso el clienteenviacutea una representacioacuten del nuevo objeto que se prentende crear a la URI padre de surepresentacioacuten y por lo tanto podremos omitir el identificador del recurso Por ejemplo

Peticioacuten POST para crear un pedidio

POST pedidos HTTP11Content-Type applicationxml

ltpedidogt lttotalgt19902lttotalgt ltfechagtDecember 22 2008 0656ltfechagt ltpedidogt

El servicio recibe el mensaje POST procesa la XML y crea un nuevo pedido en la basede datos utilizando un identificador generado de forma uacutenica Si bien esta aproximacioacutenfunciona perfectamente se le pueden plantear varias cuestiones al usuario iquestQueacute ocurre siel usuario quiere visualizar modificar o eliminar el pedido que acaba de crear iquestCuaacutel es elidentificador del nuevo recurso iquestCuaacutel es la URI que podemos utilizar para interactuar con elnuevo recurso Para resolver estas cuestiones antildeadiremos alguna informacioacuten al mensajede respuesta HTTP El cliente podriacutea recibir un mensaje similar a eacuteste

Respuesta de una peticioacuten POST para crear un pedido

HTTP11 201 Created

Servicios Rest

18

Content-Type applicationxmlLocation httporgexpertojavapedidos233

ltpedido id=233gt ltlink rel=self href=httporgexpertojavapedidos233gt lttotalgt19902lttotalgt ltfechagtDecember 22 2008 0656ltfechagt ltpedidogt

HTTP requiere que si POST crea un nuevo recurso se debe responder con un coacutedigo 201Created Tambieacuten se requieer que la cabecera Location en el mensaje de respuestaproporcione una URI al usuario que ha hecho la peticioacuten para que eacuteste pueda interactuar conla Peticioacuten que acaba de crear (por ejemplo para modificar dicho Pedido) Es opcional porparte del servidor devolver en la respuesta la representacioacuten del nuevo recurso creado Ennuestro ejemplo optamos por devolver una representacioacuten XML de la Peticion creada con elidentificador del atributo asiacute como el elemento link

Actualizacioacuten de un Pedido Cliente o Producto

Para realizar modificaciones sobre los recursos que ya hemos creado utilizaremos el meacutetodoPUT En este caso un ejemplo de peticioacuten podriacutea ser eacutesta

Peticioacuten PUT para modificar un pedidio

PUT pedidos233 HTTP11Content-Type applicationxml

ltproducto id=111gt ltnombregtiPhoneltnombregt ltpreciogt64999ltpreciogtltproductogt

Tal y como he hemos indicado anteriormente la operacioacuten PUT es idempotente Lo quesignifica que no importa cuaacutentas veces solicitemos la peticioacuten PUT el producto subyacentesigue permaneciendo con el mismo estado final

Cuando un recurso se modifica mediante PUT la especificacioacuten HTTP requiere que el servidorenviacutee un coacutedigo de respuesta 200 OK y un cuerpo de mensaje de respuesta o bien el coacutedigo204 No Content sin ninguacuten cuerpo de mensaje en la respuesta

En nuestro caso devolveremos un coacutedigo de estado 204 y un mensaje sin cuerpo derespuesta

RECUERDA Es importante NO confundir POST con PUTMuchas veces se confunden los meacutetodos PUT y POST El significado deestos meacutetodos es el siguiente

bull POST Publica datos en un determinado recurso El recurso debe existirpreviamente y los datos enviados son antildeadidos a eacutel Por ejemplo paraantildeadir nuevos pedidos con POST hemos visto que debiacuteamos hacerlocon el recurso lista de pedidos (pedidos) ya que la URI del nuevopedido todaviacutea no existe La operacioacuten NO es idempotente es decir si

Servicios Rest

19

antildeadimos varias veces el mismo alumno apareceraacute repetido en nuestralista de pedidos con URIs distintas

bull PUT Hace que el recurso indicado tome como contenido los datosenviados El recurso podriacutea no existir previamente y en caso de queexistiese seriacutea sobrescrito con la nueva informacioacuten A diferencia dePOST PUT es idempotente Muacuteltiples llamadas ideacutenticas a la mismaaccioacuten PUT siempre dejaraacuten el recurso en el mismo estado Laaccioacuten se realiza sobre la URI concreta que queremos establecer (porejemplo pedidos215) de forma que varias llamadas consecutivas conlos mismos datos tendraacuten el mismo efecto que realizar soacutelo una deellas

Borrado de un Pedido Cliente o Producto

Modelaremos el borrado de los recursos utilizando el meacutetodo HTTP DELETE El usuariosimplemente invocaraacute el meacutetodo DELETE sobre la URI que representa el objeto que queremoseliminar Este meacutetodo haraacute que dicho recurso ya no exista en nuestro sistema

Cuando eliminamos un recurso con DELETE la especificacioacuten requiere que se enviacutee un coacutedigode respuesta 200 OK y un cuerpo de mensaje de respuesta o bien un coacutedigo de respuesta204 No Content sin un cuerpo de mensaje de respuesta

En nuestro caso devolveremos un coacutedigo de estado 204 y un mensaje sin cuerpo derespuesta

Cancelacioacuten de un Pedido

Hasta ahora las operaciones de nuestro modelo de objetos encajan bastante bien enla especificacioacuten de los correspondientes meacutetodos HTTP Hemos utilzado GET para leerdatos PUT para realizar modificaciones POST para crear nuevos recursos y DELETE paraeliminarlos En nuestro sistema los Pedidos pueden eliminarse o tambieacuten cancelarse Yahemos comentado que el borrado de un recurso lo elimina completamente de nuestra basede datos La operacioacuten de cancelacioacuten solamente cambia el estado del Pedido y lo siguemanteniendo en el sistema iquestCoacutemo podriacuteamos modelar esta operacioacuten

Cuando modelamos una interfaz RESTful para las operaciones de nuestro modelo de objetosdeberiacuteamos plantearnos la siguiente pregunta iquestla operacioacuten es un estado del recurso Sila respuesta es siacute entonces deberiacuteamos modelar esta operacioacuten dentro del formato de losdatos

La cancelacioacuten de un pedido es un ejemplo perfecto de esto que acabamos de decir La claveestaacute en que esta operacioacuten en realidad es un estado especiacutefico del Pedido eacuteste puede estarcancelado o no Cuando un usuario accede a un Pedido puede desear conocer si el Pedidoha sido o no cancelado Por lo tanto la informacioacuten sobre la cancelacioacuten deberiacutea formar partedel formato de datos de un Pedido Asiacute antildeadiremos un nuevo elemento a la informacioacuten delPedido

ltpedido id=233gt ltlink rel=self href=httporgexpertojavapedidos233gt lttotalgt19902lttotalgt ltfechagtDecember 22 2008 0656ltfechagt ltcanceladogtfalseltcanceladogt

Servicios Rest

20

ltpedidogt

Ya que el estado cancelado se modela en el propio formato de datos modelaremos la accioacutende cancelacioacuten con una operacioacuten HTTP PUT que ya conocemos

PUT pedidos233 HTTP11Content-Type applicationxml

ltpedido id=233gt ltlink rel=self href=httporgexpertojavapedidos233gt lttotalgt19902lttotalgt ltfechagtDecember 22 2008 0656ltfechagt ltcanceladogttrueltcanceladogt ltpedidogt

En este ejemplo modificamos la representacioacuten del Pedido con el elemento ltcanceladogtcon valor true

Este patroacuten de modelado no siempre sirve en todos los casos Por ejemplo imaginemosque queremos ampliar el sistema de forma que borremos del sistema todos los pedidoscancelados No podemos modelar esta operacioacuten de la misma manera que la de cancelacioacutenya que esta operacioacuten no cambia el estado de nuestra aplicacioacuten (no es en siacute misma un estadode la aplicacioacuten)

Para resolver este problema podemos modelar esta nueva operacioacuten como un subrecursode pedidos y realizar un borrado de los pedidos cancelados mediante el meacutetodo HTTP POSTde dicho subrecurso de la siguiente forma

POST pedidoseliminacion HTTP11

Un efecto interesante de lo que acabamos de hacer es que puesto que ahora eliminaciones una URI podemos hacer que la interfaz de nuestro servicios RESTful evolucionen conel tiempo Por ejemplo la orden GET pedidoseliminacion podriacutea devolver la uacuteltima fechaen la que se procedioacute a eliminar todos los pedidos cancelados asiacute como queacute pedidosfueron cancelados iquestY si queremos antildeadir alguacuten criterio a la hora de realizar el borradode pedidos cancelados Podriacuteamos introducir paraacutemetros para indicar que soacutelo queremoseliminar aquellos pedidos que esteacuten cancelados en una fecha anterior a una dada Comovemos podemos mantener una interfaz uniforme y centildeirnos a las operaciones HTTP tal ycomo estaacuten especificadas y a la vez dotar de una gran flexiblidad a la interfaz de nuestrosistema RESTful Hablaremos con maacutes detalle de los subrecursos en la siguiente sesioacuten

Implementacioacuten del servicio Creacioacuten del proyecto Maven

Vamos a utilizar Maven para crear la estructura del proyecto que contendraacute la implementacioacutende nuestro servicio Rest Inicialmente podemos utilizar el mismo arquetipo con el que habeacuteistrabajado en sesiones anteriores Y a continuacioacuten modificaremos la configuracioacuten del ficheropomxml para implementar nuestros servicios

Una opcioacuten es generar la estructura del proyecto directamente desde liacutenea de comandos Elcomando es el siguiente (recuerda que debes escribirlo en una misma liacutenea Los caracteres

Servicios Rest

21

que aparecen en el comando no forman parte del mismo simplemente indican que no sedebe pulsar el retorno de carro)

mvn --batch-mode archetypegenerate -DarchetypeGroupId=orgcodehausmojoarchetypes -DarchetypeArtifactId=webapp-javaee7 -DgroupId=orgexpertojava -DartifactId=ejemplo-rest

En donde

bull archetypeGroupId y archetypeArtifactId son los nombres del groupId yartifactId del arquetipo Maven que nos va a generar la plantilla para nuestro proyecto

bull groupId y artifactId son los nombres que asignamos como groupId y artifactId denuestro proyecto En este caso hemos elegido los valores orgexpertojava y ejemplo-restrespectivamente

Si utilizamos IntelliJ para crear el proyecto tenemos que

1 Crear un nuevo proyecto (New Project)

2 Elegir el tipo de proyecto Maven

3 Crear el proyecto Maven a partir de un arquetipo con las siguientes coordenadas

bull GroupId orgcodehausmojoarchetypes

bull ArtifactId webapp-javaee7

bull Version 11

4 Indicar las coordenadas de nuestro proyecto

bull GroupId orgexpertojava

bull ArtifactId ejemplo-rest

bull Version 10-SNAPSHOT

5 Confirmamos los datos introducidos

6 Para finalizar especificamos el nombre de nuestro proyecto en IntelliJ

bull Project Name ejemplo-rest (este valor tambieacuten identificaraacute el nombre del moacutedulo enIntelliJ)

7 Por comodidad marcaremos Enable autoimport para importar automaacuteticamente cualquiercambio en el proyecto

Una vez que hemos creado el proyecto con IntelliJ el paso siguiente es cambiar laconfiguracioacuten del pommxl que nos ha generado el arquetipo para incluir las propiedadesdependencias pluginshellip que necesitaremos para implementar nuestros recursos REST

Como ya sabemos el fichero pomxml contiene la configuracioacuten que utiliza Maven paraconstruir el proyecto A continuacioacuten indicamos las modificaciones en el fichero pomxmlgenerado inicialmente para adecuarlo a nuestras necesidades particulares

bull Cambiamos las propiedades del proyecto (etiqueta ltpropertiesgt ) por

Servicios Rest

22

Propiedades del proyecto

ltpropertiesgt ltprojectbuildsourceEncodinggtUTF-8ltprojectbuildsourceEncodinggtltpropertiesgt

bull Indicamos las dependencias del proyecto (etiqueta ltdependenciesgt en donde seincluyen las libreriacuteas necesarias para la construccioacuten del proyecto) En nuestro casonecesitamos incluir la libreriacutea javaxjavaee-web-api70 que contiene el apiestaacutendar de javaee 7 Marcamos el aacutembito de la libreriacutea (etiqueta ltscopegt ) comoprovided Con esto estamos indicando que soacutelo necesitaremos el jar correspondientepara compilar el proyecto y por lo tanto no incluiremos dicho jar en el fichero wargenerado para nuestra aplicacioacuten ya que dicha libreriacutea ya estaraacute disponible desde elservidor de aplicaciones en el que residiraacute nuestra aplicacioacuten

Libreriacuteas utilizadas para construir el proyecto (dependencias)

ltdependenciesgt ltdependencygt ltgroupIdgtjavaxltgroupIdgt ltartifactIdgtjavaee-web-apiltartifactIdgt ltversiongt70ltversiongt ltscopegtprovidedltscopegt ltdependencygtltdependenciesgt

bull A continuacioacuten configuramos la construccioacuten del proyecto (etiqueta ltbuildgt ) de lasiguiente forma (cambiamos la configuracioacuten original por la que mostramos a continuacioacuten)

Configuracioacuten de la construccioacuten del proyecto

ltbuildgt lt-- Especificamos el nombre del war que seraacute usado como context root cuando despleguemos la aplicacioacuten --gt ltfinalNamegt$projectartifactIdltfinalNamegt

ltpluginsgt lt-- Compilador de java Utilizaremos la versioacuten 17 --gt ltplugingt ltgroupIdgtorgapachemavenpluginsltgroupIdgt ltartifactIdgtmaven-compiler-pluginltartifactIdgt ltversiongt31ltversiongt ltconfigurationgt ltsourcegt17ltsourcegt lttargetgt17lttargetgt ltconfigurationgt ltplugingt

lt-- Servidor de aplicaciones wildfly --gt ltplugingt ltgroupIdgtorgwildflypluginsltgroupIdgt ltartifactIdgtwildfly-maven-pluginltartifactIdgt ltversiongt102Finalltversiongt ltconfigurationgt lthostnamegtlocalhostlthostnamegt

Servicios Rest

23

ltportgt9990ltportgt ltconfigurationgt ltplugingt

lt-- Cuando generamos el war no es necesario que el fichero webxml esteacute presente --gt ltplugingt ltgroupIdgtorgapachemavenpluginsltgroupIdgt ltartifactIdgtmaven-war-pluginltartifactIdgt ltversiongt23ltversiongt ltconfigurationgt ltfailOnMissingWebXmlgtfalseltfailOnMissingWebXmlgt ltconfigurationgt ltplugingt ltpluginsgtltbuildgt

Implementacioacuten del servicio Recursos JAX-RS

Una vez que tenemos la estructura del proyecto implementaremos los recursos de nuestraaplicacioacuten que seraacuten clases Java que utilizaraacuten anotaciones JAX-RS para enlazar y mapearpeticiones HTTP especiacuteficas a meacutetodos java los cuales serviraacuten dichas peticiones Eneste caso vamos a ilustrar con un ejemplo una posible implementacioacuten para el recursoCliente Tenemos que diferenciar entre las clases java que representaraacuten entidades denuestro dominio (objetos java que representan elementos de nuestro negocio y que seraacutenalmacenados tiacutepicamente en una base de datos) de nuestros recursos JAX-RS que tambieacutenseraacuten clases java anotadas y que utilizaraacuten objetos de nuestro dominio para llevar a cabo lasoperaciones expuestas en el API RESTful que hemos disentildeado

Asiacute por ejemplo implementaremos las clases

bull Clientejava representa una entidad del dominio Contiene atributos y suscorrespondientes getters y setters

bull ClienteResourcejava representa las operaciones RESTful sobre nuestro recurso Clienteque hemos definido en esta sesioacuten Es una clase java con anotaciones JAX-RS que nospermitiraacute insertar modificar borrar consultar un cliente asiacute como consultar la lista declientes de nuestro sistema

Clases de nuestro dominio (entidades) Clientejava

La clase que representa nuestra entidad del dominio Cliente es una clase java plana con suscorrespondientes atributos y getters y setters

Implementacioacuten del dominio clientejava

package orgexpertojavadomain

XmlRootElement(name=cliente)XmlAccessorType(XmlAccessTypeFIELD)public class Cliente private int id private String nombre private String apellidos private String direccion

Servicios Rest

24

private String codPostal private String ciudad

public int getId() return id public void setId(int id) thisid = id

public String getNombre() return nombre public void setNombre(String nom) thisnombre = nom

public String getApellidos() return apellidos public void setApellidos(String apellidos) thisapellidos = apellidos

public String getDireccion() return direccion public void setDireccion(String dir) thisdireccion = dir

public String getCodPostal() return codPostal public void setCodPostal(String cp) thiscodPostal = cp

public String getCiudad() return ciudad public void setCiudad(String ciudad) thisciudad = ciudad

Hemos anotado la clase Cliente con XmlRootElement y XmlAccessorType Hablaremos de estas anotaciones en sesiones posteriores las cuales se encargan delserializadodeserializado del cuerpo del mensaje (en formato xml o json) a nuestra clase javaCliente

Clases de nuestro servicio RESTful ClienteResourcejava

Una vez definido el objeto de nuestro dominio que representaraacute un objeto Cliente vamos a vercoacutemo implementar nuestros servicio JAX-RS para que diferentes usuarios de forma remotapuedan interactuar con nuestra base de datos de clientes

La implementacioacuten del servicio es lo que se denomina una resource class que no es maacutesque una clase java que utiliza anotaciones JAX-RS

Por defecto una nueva instancia de nuestra clase de recursos se crea para cada peticioacuten aese recurso Es lo que se conoce como un objeto per-request Esto implica que se crea unobjeto Java para procesar cada peticioacuten de entrada y se destruye automaacuteticamente cuandola peticioacuten se ha servido Per-request tambieacuten implica sin estado ya que no se guarda elestado del servicio entre peticiones

Comencemos con la implementacioacuten del servicio

package orgexpertojavaservices

import

Path(clientes)public class ClienteResource

private static MapltInteger Clientegt clienteDB = new ConcurrentHashMapltInteger Clientegt()

Servicios Rest

25

private static AtomicInteger idContador = new AtomicInteger()

Podemos observar que ClientResource es una clase java planay que no implementaninguna interfaz JAX-RS particular La anotacioacuten javaxwsrsPath indica que laclase ClienteResource es un servicio JAX-RS Todas las clases que queramos que seanreconocidas como servicios JAX-RS tienen que tener esta anotacioacuten Fiacutejate que estaanotacioacuten tiene el valor clientes Este valor representa la raiacutez relativa de la URI de nuestroservicio RESTful Si la URI absoluta de nuestro servidor es por ejemplo httpexpertojavaorglos meacutetodos expuestos por nuestra clase ClienteResource estariacutean disponibles bajo la URIhttpexpertojavaorgclientes

En nuestra clase definimos un Mapa para el campo ClienteDB quealmacenaraacute en memoria a los objetos Cliente de nuestro sistema Utilizamos unjavautilconcurrentConcurrentHashMap como tipo de clienteDB ya que nuestro recursoseraacute accedido concurrentemente por los usuarios de nuestro servicio rest El campoidContador lo utilizaremos para generar nuevos identificadores de nuestros objetos Clientecreados El tipo de este campo es javautilconcurrentatomicAtomicInteger para garantizarque siempre generaremos un identificador uacutenico aunque tengamos peticiones concurrentes

Justificacioacuten del caracter static de los atributosComo nuestros objetos seraacuten de tipo per-request el runtime de JAX-RScrearaacute una instancia de ClienteResource para cada pecioacuten que se realicesobre nuestro servicio La maacutequina virtual de java ejecutaraacute cada peticioacutena nuestro servicio en un hilo (thread) diferente permitiendo asiacute el accesoconcurrente a nuestro recurso Puesto que hemos decidido almacenaren memoria la informacioacuten de los clientes necesitamos que los atributosclienteDB y idContador sean static para que todas las instancias deClienteResource tengan acceso a la lista de clientes en memoria y nohaya problemas de concurrencia En realidad lo que estamos haciendocon eacutesto es permitir que el servicio guarde el estado entre peticiones Enun sistema real ClienteResource probablemente interactuacutee con una basede datos para almacenar y recuperar la informacioacuten de los clientes y porlo tanto no necesitaremos guardar el estado entre peticiones

Una mejor solucioacuten seriacutea no utilizar variables estaacuteticas y definir nuestroservicio como singleton Si hacemos eacutesto solamente se creariacutea unainstancia de clienteResource y estariacuteamos manteniendo el estado delas peticiones En la siguiente sesioacuten explicaremos coacutemo configurar unservicio como singleton Por simplicidad de momento optaremos por laopcioacuten de que los objetos RESTful sean per-request

Creacioacuten de clientes

Para implementar la creacioacuten de un nuevo cliente utilizamos el mismo modelo que hemosdisentildeado previamente Una peticioacuten HTTP POST enviacutea un documento XML que representaal cliente que queremos crear

El coacutedigo para crear nuevos clientes en nuestro sistema podriacutea ser eacuteste

POST

Consumes(applicationxml)

public Response crearCliente(Cliente cli)

Servicios Rest

26

el paraacutemetro cli se instancia con los datos del cliente del body del mensaje HTTP idContador++ clisetId(idContadorincrementAndGet())

clienteDBput(cligetId() cli)

Systemoutprintln(Cliente creado + cligetId()) return Responsecreated(URIcreate(clientes

+ cligetId()))build()

se recibe una peticioacuten POSTel cuerpo de la peticioacuten debe tener formato xmlcontiene la informacioacuten del documento xml del cuerpo de la peticioacuten de entradase antildeade el nuevo objeto Cliente a nuestro mapa de clientes (clienteDB)este meacutetodo se ejectua en el servidor por lo que el mensaje soacutelo seraacute visible por ejemplosi consultamos los mensajes generados por el servidor durante la ejecucioacuten el meacutetododevuelve un coacutedigo de respuesta 201 Created junto con una cabecera Locationapuntando a la URI absoluta del cliente que acabamos de crear

Vamos a explicar la implementacioacuten con maacutes detalle

Para enlazar peticiones HTTP POST con el meacutetodo crearCliente() lo anotamos conla anotacioacuten javaxwsrsPOST La anotacioacuten Path combinada con la anotacioacutenPOST enlaza todas las peticiones POST dirigidas a la URI relativa clientes al meacutetodo JavacrearCliente()

La anotacioacuten javaxwsrsConsumes aplicada a crearCliente() especifica queacute media typeespera el meacutetodo en el cuerpo del mensaje HTTP de entrada Si el cliente incluye en su peticioacutenPOST un media type diferente de XML se enviacutea un coacutedigo de error al cliente

El meacutetodo crearCliente() tiene un paraacutemetro de tipo Cliente En JAX-RS cualquier paraacutemetrono anotado con anotaciones JAX-RS se considera que es una representacioacuten del cuerpodel mensaje de la peticioacuten de entrada HTTP Las anotaciones que hemos introducido en laclase Cliente de nuestro dominio realizan el trabajo de convertir el documento xml contenidoen el cuerpo de la peticioacuten htpp de entrada en una instancia de nuestra clase Cliente

Solamente UNO de los paraacutemetros del meacutetodo Java puede representar elcuerpo del mensaje de la peticioacuten HTTP Esto significa que el resto deparaacutemetros deben anotarse con alguna anotacioacuten JAX-RS que veremosmaacutes adelante

El meacutetodo crearCliente() devuelve una respuesta de tipo javaxwsrscoreResponse El meacutetodo estaacutetico Responsecreated() crea un objeto Response que contiene un coacutedigode estado 201 Created Tambieacuten antildeade una cabecera Location con un valor similar ahttpexpertojavaorgclientes123 dependiendo del valor del valor de base de la raiacutez dela URI del servidor y el identificador generado para el objeto Cliente (en este caso se habriacuteagenerado el identificador 123) Maacutes adelante explicaremos con detalle el uso de la claseResponse

Consulta de clientes

A continuacioacuten mostramos un posible coacutedigo para consultar la informacioacuten de un cliente

GET

Servicios Rest

27

Path(id)Produces(applicationxml)public Cliente recuperarClienteId(PathParam(id) int id) final Cliente cli = clienteDBget(id) if (cli == null) throw new WebApplicationException(ResponseStatusNOT_FOUND) return new Cliente(cligetId() cligetNombre() cligetApellidos() cligetDireccion() cligetCodPostal() cligetCiudad())

En este caso anotamos el meacutetodo recuperarClienteId() con la anotacioacutenjavaxwsrsGET para enlazar las operaciones HTTP GET con este meacutetodo Java

Tambieacuten anotamos recuperarClienteId() con la anotacioacuten javaxwsrsPRODUCES Estaanotacioacuten indica a JAX-RS que valor tiene la cabecera HTTP Content-Type en la respuestaproporcionada por la operacioacuten GET En este caso estamos indicando que seraacute de tipoapplicationxml

En la implementacioacuten del meacutetodo utilizamos el paraacutemetro id para consultar si existe unobjeto Cliente en nuestro mapa clienteDB Si dicho cliente no existe lanzaremos la excepcioacutenjavaxwsrsWebApplictionException Esta excepcioacuten provocaraacute que el coacutedigo derespuesta HTTP sea 404 Not Found y significa que el recurso cliente requerido no existeDiscutiremos maacutes adelante el tema del manejo de excepciones

Modificacioacuten de clientes

Vamos a mostrar coacutemo seriacutea el coacutedigo para modificar un cliente

PUTPath(id)Consumes(applicationxml)public void modificarCliente(PathParam(id) int id Cliente nuevoCli)

Cliente actual = clienteDBget(id) if (actual == null) throw new WebApplicationException(ResponseStatusNOT_FOUND)

actualsetNombre(nuevoCligetNombre()) actualsetApellidos(nuevoCligetApellidos()) actualsetDireccion(nuevoCligetDireccion()) actualsetCodPostal(nuevoCligetCodPostal()) actualsetCiudad(nuevoCligetCiudad())

Anotamos el meacutetodo modificarCliente() con javaxwsrsPUT para enlazar laspeticiones HTTP PUT a este meacutetodo Al igual que hemos hecho con recuperarClienteId() elmeacutetodo modificarCliente() estaacute anotado adicionalmente con Path de forma que podamosatender peticiones a traveacutes de las URIs clientesid

El meacutetodo modificarCliente() tiene dos paraacutemetros El primero es un paraacutemetro id querepresenta el objeto Cliente que estamos modificando Al igual que ocurriacutea con el meacutetodorecuperarClienteId() utilizamos la anotacioacuten PathParam para extraer el identificador a

Servicios Rest

28

partir de la URI de la peticioacuten de entrada El segundo paraacutemetro es un objeto Cliente querepresenta el cuerpo del mensaje de entrada ya que no tiene ninguna anotacioacuten JAX-RS

El meacutetodo intenta encontrar un objeto Cliente en nuestro mapa clienteDB Si no existeprovocamos una WebApplicationException que enviaraacute una respuesta al usuario con elcoacutedigo 404 Not Found Si el objeto Cliente existe modificamos nuestro objeto Clienteexistente con los nuevos valores que obtenemos de la peticioacuten de entrada

Construccioacuten y despliegue del servicio

Una vez implementado nuestro servicio RESTful necesitamos poner en marcha el procesode construccioacuten El proceso de construccioacuten compilaraacutehellip empaquetaraacute hellip y finalmente nospermitiraacute desplegar nuestro servicio en el servidor de aplicaciones

Para poder empaquetar nuestro servicio RESTful como un war que se desplegaraacute en elservidor de aplicaciones vamos a incluir un proveedor de servicios JAX-RS en el descriptorde despliegue de nuestra aplicacioacuten (fichero webxml) En la siguiente sesioacuten justificaremos laexistencia de dicho proveedor (que seraacute un servlet) y explicaremos el modelo de desplieguede los servicios JAX-RS Los pasos a seguir desde IntelliJ para configurar el despliegue denuestro servicio son

bull Antildeadimos el directorio WEB-INF como subdirectorio de webapp

bull Nos vamos a FileProject StructurehellipFacetsWeb y antildeadimos el fichero webxml (enel panel Deployment descriptors pulsamos sobre + y seleccionamos webxml) Editamoseste fichero para antildeadir el servlet que serviraacute las peticiones de nuestros servicios RESTindicando cuaacutel seraacute la ruta en la que estaraacuten disponibles dichos servicios (en nuestroejemplo indicaremos la ruta rest) Dicha ruta es relativa a la ruta del contexto de nuestraaplicacioacuten y que por defecto es el nombre del artefacto war desplegado que hemosindicado en la etiqueta ltfinalNamegt dentro del ltbuildgt del fichero de configuracioacuten deMaven (pomxml)

Contenido del fichero webxml

ltweb-app version=30 xmlns=httpjavasuncomxmlnsjavaee xmlnsxsi=httpwwww3org2001XMLSchema-instance xsischemaLocation=httpjavasuncomxmlnsjavaee httpjavasuncomxmlnsjavaeeweb-app_3_0xsdgt lt-- One of the way of activating REST Services is adding these lines the server is responsible for adding the corresponding servlet automatically if the src folder has the Annotations to receive REST invocation--gt ltservlet-mappinggt ltservlet-namegtjavaxwsrscoreApplicationltservlet-namegt lturl-patterngtrestlturl-patterngt ltservlet-mappinggtltweb-appgt

A continuacioacuten ya estamos en disposicioacuten de iniciar la construccioacuten del proyecto con Mavenpara compilar empaquetar y desplegar nuestro servicio en Wildfly

Si utilizamos el terminal la secuencia de pasos para empaquetar y desplegar nuestroproyecto seriacutean

cd ejemplo-rest

Servicios Rest

29

mvn package

usrlocalwildfly-821Finalbinstandalonesh

mvn wildflydeploy

Nos situamos en el directorio que contiene el pomxml de nuestro proyectoEmpaquetamos el proyecto (obtendremos el war)Arrancamos el servidor wildflyDesplegamos el war generado en el servidor wildfly

Secuencia correcta de acciones

En ejecuciones posteriores despueacutes de realizar modificaciones en nuestro coacutedigo esrecomendable ejecutar mvn clean previamente al empaquetado del proyecto

Por lo tanto y suponiendo que el servidor de aplicaciones ya estaacute en marcha lasecuencia de acciones (comandos maven) que deberiacuteamos realizar para asegurarnosde que vamos a ejecutar exactamente la aplicacioacuten con los uacuteltimos cambios quehayamos introducido son

bull mvn wildflyundeploy

bull mvn clean

bull mvn package

bull mvn wildflydeploy

Tambieacuten podriacuteamos realizar todas estas acciones con un uacutenico comando maven

bull mvn wildflyundeploy clean package wildflydeploy

Si utilizamos IntelliJ antildeadiremos un nuevo elemento de configuracioacuten de ejecucioacuten desdeRunEdit Configurations Pulsamos el icono + y antildeadimos la configuracioacuten de tipo JBosssServerLocal Podemos ponerle por ejemplo como nombre Wilfdly start A continuacioacutenconfiguramos la ruta del servidor wildfly como usrlocalwildfly-821Final

Cuando lancemos este elemento de ejecucioacuten desde IntelliJ automaacuteticamente seconstruiraacute el proyecto (obtendremos el war) y arrancaremos wildfly Para desplegarel war utlizaremos la ventana Maven Projects y haremos doble click sobre ejemplo-restPluginswildflywildflydeploy

Probando nuestro servicio

Podemos probar nuestro servicio de varias formas Vamos a mostrar como hacerlodirectamente desde liacutenea de comandos utilizando IntelliJ o bien utilizando la herramientaPostman (que teneacuteis disponible desde el navegador Chrome)

Invocacioacuten del servicio desde liacutenea de comandosUtilizaremos la herramienta curl Por ejemplo para realizar una insercioacuten de un cliente elcomando seriacutea

Servicios Rest

30

curl -i -H Accept applicationxml -H Content-Type applicationxml -X POST -d clientexml httplocalhost8080ejemplo-restrestclientes

En donde

-iTambieacuten se puede utilizar la opcioacuten equivalente --include Indica que se debe incluirlas cabeceras HTTP en la respuesta recibida Recuerda que la peticioacuten POST devuelveen la cabecera Location el enlace del nuevo recurso creado (puede hacerlo en unacabedera Location o como un campo ltlinkgt del elemento creado en el cuerpo delmensaje lo veremos maacutes adelante) Esta informacioacuten seraacute necesaria para poder consultarla informacioacuten del nuevo cliente creado

-HIndica un par cabecera_valor_ En nuestro caso lo utilizamos para especificar los valoresde las cabeceras HTTP Accept y Content-Type

-XIndica el meacutetodo a invocar (GET POST PUThellip)

-dTambieacuten se puede utilizar --data Indica cuaacuteles son los datos enviados en el mensajede entrada en una peticioacuten POST Si los datos especificados van precedidos por estamos indicando que dichos datos estaacuten en un fichero Por ejemplo en la orden anteriorescribimos en el fichero clientexml los datos del cliente que queremos antildeadir en nuestrosistema

El contenido del fichero clientexml podriacutea ser eacuteste

ltxml version=10 encoding=UTF-8gtltclientesgt ltclientegt ltnombregtPepe ltnombregt ltapellidosgtGarcia Lopezltapellido1gt ltdirecciongtCalle del pino 3ltapellido2gt ltcodPostalgt0001ltcodPostalgt ltciudadgtAlicanteltciudadgt ltclientegtltclientesgt

Finalmente en la orden indicamos la URI a la que queremos acceder en este caso

httplocalhost8080ejemplo-restrestclientes

Una vez insertado el cliente podemos recuperar el cliente utilizando el enlace que se incluyeen la cabecera de respuesta Location

curl -i -H Accept applicationxml -H Content-Type applicationxml -X GET httplocalhost8080ejemplo-restrestclientes1

Servicios Rest

31

Invocacioacuten del servicio desde IntelliJIntelliJ nos proporciona una herramienta para probar servicios REST desde ToolsTestRESTful Web Service Desde esta nueva ventana podremos invocar al servicio RESTindicando el tipo de peticioacuten HTTP asiacute como las cabeceras y cuerpo de la peticioacuten

La siguiente figura muestra la elaboracioacuten de una peticioacuten POST a nuestro servicio REST

A continuacioacuten mostramos la ejecucioacuten de una peticioacuten GET

Cuando realizamos una peticioacuten POST debemos indicar el contenido del cuerpo del mensajeEn la siguiente figura observamos que tenemos varias opciones disponibles como por ejemploteclear directamente dicho contenido (opcioacuten Text) o bien subir dicha informacioacuten desdeun fichero en nuestro disco duro (opcioacuten File Contents) Podemos ver que hemos elegido estauacuteltima opcioacuten para probar nuestro servicio

Invocacioacuten del servicio desde PostmanOtra alternativa sencilla para probar nuestro servicio REST es la herramienta postmanque podemos lanzar desde el navegador en nuestro caso Chrome

Accederemos a la aplicacioacuten desde la barra de marcadores seleccionando Aplicaciones yy a continuacioacuten pulsaremos sobre el icono Postman

El aspecto de la herramienta es el que mostramos a continuacioacuten

Servicios Rest

32

Postman a diferencia de las alternativas anteriores nos permitiraacute guardar un historial depeticiones de forma que podamos repetir la ejecucioacuten de nuestros tests exactamente de lamisma forma aunque no de forma automaacutetica sino que tenemos que lanzar manualmentecada test que queramos volver a ejecutar

Tambieacuten podemos crear colecciones que no son maacutes que carpetas que contienen unconjunto de peticiones de nuestro historial Por ejemplo podemos crear la coleccioacuten s1-rest-ejercicio1 en donde guardaremos todas las peticiones que hayamos hecho sobre el ejercicio1 de la primera sesioacuten de rest

Podeacuteis crearos una cuenta gratuita para almacener y gestionar vuestras peticiones rest Unavez que tengaacuteis creadas varias colecciones Postman nos permite guardarlas en nuestrodisco duro en formato json

Servicios Rest

33

15 Ejercicios

Antes de empezar a crear los proyectos debes descargarte el repositorio git java_uaejercicios-rest-expertojava en el que vas a implementar los ejercicios relativos a laasignatura de Servicios REST El proceso es el mismo que el seguido en sesiones anteriores

1 Accedemos al repositorio y realizamos un Fork en nuestra cuenta personal (asiacute podremostener una copia con permisos de escritura)

2 Realizamos un Clone en nuestra maacutequina

$ git clone httpsbitbucketorgltalumnogtejercicios-rest-expertojava

De esta forma se crea en nuestro ordenador el directorio ejercicios-rest-expertojavay se descarga en eacutel un proyecto IntelliJ en el que iremos antildeadiendo MOacuteDULOS para cada unode los ejercicios Contiene tambieacuten el fichero gitignore asiacute como diferentes moacutedulos conlas plantillas que vayamos a necesitar para realizar los ejercicios

A partir de este momento se puede trabajar con dicho proyecto y realizar Commit y Pushcuando sea oportuno

$ cd ejercicios-rest-expertojava$ git add $ git commit -a -m Mensaje de commit$ git push origin master

Los MOacuteDULOS IntelliJ que iremos antildeadiendo tendraacuten todos el prefijo sx- siendo x elnuacutemero de la sesioacuten correspondiente (por ejemplo s1-ejercicio s2-otroEjerciciohellip)

Servicio REST ejemplo (0 puntos)

Para familiarizarnos con las peticiones http POST PUT GET DELETE se proporciona elMOacuteDULO s1-ejemplo-rest con la implementacioacuten de un servicio rest que podeacuteis probar biendesde liacutenea de comandos con la utilidad curl desde el navegador con la herramienta postman o bien desde IntelliJ con el cliente REST incluido en el IDE

En el directorio srcmainresources de dicho moacutedulo teneacuteis un fichero de texto(instruccionestxt) con las instrucciones para construir desplegar y probar la aplicacioacuten deejemplo

Servicio REST saludo (1 punto)

Vamos a implementar un primer servicio RESTful muy sencillo Para ello seguiremos lassiguientes indicaciones

bull Creamos un MOacuteDULO Maven con IntelliJ (desde el directorio ejercicios-rest-expertojava ) con el arquetipo webapp-javaee7 tal y como hemos visto en losapuntes de la sesioacuten Las coordenadas del artefacto Maven seraacuten

GroupId orgexpertojava

ArtifactId s1-saludo-rest

Servicios Rest

34

version 10-SNAPSHOT

bull Configuramos el pommxl del proyecto para poder compilar empaquetar y desplegarnuestro servicio Consulta los apuntes para ver cuaacutel debe ser el contenido de las etiquetasltpropertiesgt ltdependenciesgt y ltbuildgt

bull Creamos la carpeta WEB-INF y antildeadimos el fichero de configuracioacuten webxml tal y comohemos visto en los apuntes (esto seraacute necesario para configurar el despliegue) En estecaso queremos mapear los servicios REST contenidos en el paquete orgexpertojavaal directorio recursos dentro de nuestro contexto (recuerda que el contexto de nuestraaplicacioacuten web vendraacute dado por el valor de la etiqueta ltfinalNamegt anidada dentro deltbuildgt)

bull Creamos un recurso de nombre HolaMundoResource que se mapee a la direccioacuten holamundo Implementar un meacutetodo de forma que al acceder a eacutel por GET nos devuelvaen texto plano (textplain) el mensaje Hola mundo Una vez desplegada la aplicacioacutenen el servidor WildFly prueba el servicio mediante la utilidad Postman desde ChromeComprobar que la invocacioacuten

GET httplocalhost8080saludo-restholamundo

Devuelve como cuerpo del mensaje Hola mundo

bull Vamos a antildeadir un segmento variable a la ruta Implementa un meacutetodo GET nuevode forma que si accedemos a holamundonombre antildeade el nombre indicado al saludo(separado por un espacio en blanco y seguido por )

Una vez desplegada la aplicacioacuten en el servidor WildFly prueba el servicio mediante la utilidadTest RESTFul Web Service de IntelliJ o con Postman Comprobar que la invocacioacuten

GET httplocalhost8080saludo-restholamundopepe

Devuelve como cuerpo del mensaje Hola mundo pepe

bull Hacer que se pueda cambiar el saludo mediante un meacutetodo PUT El nuevo saludo llegaraacutetambieacuten como texto plano en el cuerpo de la peticioacuten y posteriores invocaciones a losmeacutetodos GET utilizaraacuten el nuevo saludo Almacenaremos el nuevo saludo en una variableestaacutetica de nuestro recurso iquestQueacute pasa si no lo es (lo hemos explicado en los apuntespuedes hacer la prueba para ver queacute ocurre si la variable no es estaacutetica)

Una vez desplegada la aplicacioacuten en el servidor WildFly prueba el servicio con Postmano bien mediante la utilidad Test RESTFul Web Service de IntelliJ Realizar las siguientesinvocaciones (en este orden)

PUT httplocalhost8080saludo-restholamundoy en el cuerpo del mensaje Buenos diacuteas

GET httplocalhost8080saludo-restholamundoGET httplocalhost8080saludo-restholamundopepe

Servicios Rest

35

La segunda invocacioacuten debe devolver como cuerpo del mensaje Buenos dias Al ejecutarla tercera invocacioacuten el cuerpo del mensaje de respuesta deberiacutea ser Buenos diasPepe

Servicio REST foro (1 punto)

Vamos a implementar un servicio RESTful que contemple las cuatro operaciones baacutesicas(GET PUT POST y DELETE) Se trata de un foro con en el que los usuarios pueden intervenirde forma anoacutenima en diferentes conversaciones

Primero debes crear un nuevo moacutedulo Maven configurar el pomxml asiacute como el ficherowebxml de la misma forma que hemos hecho en el ejercicio anterior pero para esteejercicio

bull Las coordenadas del moacutedulo Maven seraacuten

GroupId orgexpertojava

ArtifactId s1-foro-rest

version 10-SNAPSHOT

bull Nuestros servicios REST estaraacuten disponibles en la URI httplocalhost8080s1-foro-rest

El foro estaraacute formado por diferentes mensajes Por lo tanto el modelo del dominio de nuestraaplicacioacuten estaraacute formado por la clase Mensaje que contendraacute un identificador y una cadenade caracteres que representaraacute el contenido del mensaje (recuerda que debes implementarlos correspondientes getters y setters)

Por simplicidad vamos a almacenar los mensajes de nuestro foro en memoria Estos estaraacutendisponibles desde la clase DatosEnMemoria que contendraacute la variable estaacutetica

static MapltInteger Mensajegt datos = new HashMapltInteger Mensajegt()

Los servicios que proporcionaraacute el foro estaraacuten implementados en la claseMensajeResource Se accederaacute a ellos traveacutes de la ruta relativa a la raiacutez de nuestrosservicios mensajes Concretamente podremos realizar las siguientes operaciones

bull Antildeadir un nuevo mensaje al foro con la URI relativa a la raiacutez de nuestros servicios mensajes El texto del mensaje estaraacute en el cuerpo de la peticioacuten y el tipo MIME asociadoseraacute textplain (contenido de la cabecera Content-type de la peticioacuten HTTP)Nuestra respuesta debe incluir en la cabecera Location de la respuesta HTTP la URIdel nuevo recurso creado Utiliza para ello la clase Response tal y como hemos mostradoen el coacutedigo de ejemplo proporcionado para el ejercicio anterior Hablaremos con detallesobre esta clase en sesiones posteriores

Recuerda que para acceder al cuerpo de la peticioacuten basta con definir unparaacutemetro de tipo String JAX-RS automaacuteticamente lo instanciaraacute a partirdel cuerpo de la peticioacuten y lo convertiraacute en un objeto de tipo String

bull Modificar un mensaje determinado con un identificador con valor id a traveacutes de la URIrelativa a la raiacutez de nuestros servicios mensajesid ( id debe ser por tanto unsegmento de ruta variable) Si no existe ninguacuten mensaje con el identificador id se lanzaraacutela excepcioacuten WebApplicationException(ResponseStatusNOT_FOUND)

Servicios Rest

36

bull Borrar un mensaje determinado con un identificador con valor id atraveacutes de la URI relativa a la raiacutez de nuestros servicios mensajesid Igual que en el caso anterior si el identificador proporcionado no secorresponde con el de ninguacuten mensaje del foro se lanzaraacute la excepcioacutenWebApplicationException(ResponseStatusNOT_FOUND)

bull Consultar todos los mensajes del foro (la URI relativa seraacute mensajes ) El resultado semostraraacute en tantas liacuteneas como mensajes Cada mensaje iraacute precedido de su identificadorTambieacuten se informaraacute del nuacutemero total de mensajes en el foro (La respuesta seraacute unacadena de caracteres Al final del ejercicio mostramos un ejemplo de mensaje de respuestapara esta operacioacuten)

bull Consultar un mensaje determinado con un identificador con valor id a traveacutes dela URI relativa a la raiacutez de nuestros servicios mensajesid Si el identificadorproporcionado no se corresponde con el de ninguacuten mensaje del foro se lanzaraacute laexcepcioacuten WebApplicationException(ResponseStatusNOT_FOUND)

Prueba el servicio utilizando Postman o el cliente de IntelliJ para servicios REST con lassiguientes entradas

bull Crea los mensajes Mensaje numero 1 Mensaje numero 2 Mensaje numero 3 en esteorden

bull Consulta los mensajes del foro El resultado debe ser

1 Mensaje numero 1

2 Mensaje numero 2

3 Mensaje numero 3

Numero total de mensajes = 3

bull Cambia el mensaje con identificador 2 por Nuevo mensaje numero 2

bull Consulta los mensajes del foro El resultado debe ser

1 Mensaje numero 1

2 Nuevo Mensaje numero 2

3 Mensaje numero 3

Numero total de mensajes = 3

bull Borra el mensaje con identificador 3

bull Consulta el mensaje con el identificador 3 Se debe obtener una respuesta 404 NotFound

bull Consulta los mensajes del foro El resultado debe ser

1 Mensaje numero 1

2 Nuevo Mensaje numero 2

Numero total de mensajes = 2

bull Antildeade el mensaje Mensaje final Vuelve a consultar los mensajes el resultado debe ser

1 Mensaje numero 1

Servicios Rest

37

2 Nuevo Mensaje numero 2

4 Mensaje final

Numero total de mensajes = 3

Para evitar problemas con el id generado si hemos borrado mensajeslo maacutes sencillo es que el identificador vaya incrementaacutendose siemprecon cada nuevo mensaje Esto puede hacer que queden huecos en lanumeracioacuten como en el ejemplo anterior

Servicios Rest

38

2 Anotaciones baacutesicas JAX-RS El modelo de despliegue

Ya hemos visto como crear un servicio REST baacutesico Ahora se trata de analizar con maacutesdetalle aspectos fundamentales sobre la implementacioacuten de los servicios Comenzaremos pordetallar los usos de la anotacioacuten Path que es la que nos permite etiquetar una clase Javacomo un recurso REST sobre el que podremos realizar las operaciones que hemos identificadoen la sesioacuten anterior Tambieacuten hablaremos algo maacutes sobre las anotaciones Produces yConsumes que ya hemos utilizado para implementar nuestro primer servicio

En segundo lugar hablaremos sobre la extraccioacuten de informacioacuten de las peticiones HTTP ycoacutemo podemos inyectar esa informacioacuten en nuestro coacutedigo java Esto nos permitiraacute servir laspeticiones sin tener que escribir demasiado coacutedigo adicional

Finalmente explicaremos maacutes detenidamente coacutemo configurar el despliegue de nuestraaplicacioacuten REST de forma que sea portable

21 iquestCoacutemo funciona el enlazado de meacutetodos HTTP

JAX-RS define cinco anotaciones que se corresponden con operaciones HTTP especiacuteficas

bull javaxwsrsGET

bull javaxwsrsPUT

bull javaxwsrsPOST

bull javaxwsrsDELETE

bull javaxwsrsHEAD

En la sesioacuten anterior ya hemos utilizado estas anotaciones para hacer corresponder (enlazar)peticiones HTTP GET con un meacutetodo Java concreto

Por ejemplo

Path(clientes)public class ServicioCliente

GET Produces(applicationxml) public String getTodosLosClientes()

En este coacutedigo la anotacioacuten GET indica al runtime JAX-RS que el meacutetodo javagetTodosLosClientes() atiende peticiones HTTP GET dirigidas a la URI clientes

Soacutelamente se puede utilizar una de las anotaciones anteriores para unmismo meacutetodo Si se aplica maacutes de uno se produce un error durante eldespliegue de la aplicacioacuten

Es interesante conocer que cada una de estas anotaciones a su vez estaacute anotada con otrasanotaciones (podriacuteamos llamarlas meta anotaciones) Por ejemplo la implementacioacuten de laanotacioacuten GET tiene este aspecto

package javaxwsrsimport

Servicios Rest

39

Target(ElementTypeMETHOD)Retention(RetentionPolicyRUNTIME)HttpMethod(HttpMethodGET)public interface GET

GET en siacute mismo no tiene ninguacuten significado especial para el proveedor JAX-RS (runtimede JAX-RS) Lo que hace que la anotacioacuten GET sea significativo para el runtime de JAX-RSes el valor de la meta anotacioacuten javaxwsrsHttpMethod (en este caso HttpMethodGET )Este valor es el que realmente decide que un determinado meacutetodo Java se enlace con undeterminado meacutetodo HTTP

iquestCuaacuteles son las implicaciones de eacutesto Pues que podemos crear nuevas anotaciones quepodemos enlazar a otros meacutetodos HTTP que no sean GET POST PUT DELETE o HEAD Deesta forma podriacuteamos permitir que diferentes tipos de clientes que hacen uso de la operacioacutenHTTP LOCK puedan ser atendidos por nuestro servicio REST (como por ejemplo un clienteWebDAV )

22 La anotacioacuten Path

La anotacioacuten Path identifica la plantilla de path para la URI del recurso al que se accede yse puede especificar a nivel de clase o a nivel de meacutetodo de dicho recurso

El valor de una anotacioacuten Path es una expresioacuten que denota una URI relativa a la URIbase del servidor en el que se despliega el recurso a la raiz del contexto de la aplicacioacuten yal patroacuten URL al que responde el runtime de JAX-RS

Un segmento de la URI es cada una de las subcadenas delimitadas por que aparecenen dicha URI Por ejemplo la URI httpejemploclientescomclientesviprecientes contiene 4segmentos de ruta ejemploclientescom clientes vip y recientes

La anotacioacuten Path no es necesario que contenga una ruta que empieceo termine con el caraacutecter El runtime de JAX-RS analiza igualmente laexpresioacuten indicada como valor de Path

Para que una clase Java sea identificada como una clase que puede atender peticiones HTTPeacutesta tiene que estar anotada con al menos la expresioacuten Path() Este tipo de clasesse denominan recursos JAX-RS raiacutez

Para recibir una peticioacuten un meacutetodo Java debe tener al menos una anotacioacuten de meacutetodoHTTP como por ejemplo javaxwsrsGET Este meacutetodo no requiere tener ninguna anotacioacutenPath adicional Por ejemplo

Path(pedidos)public class PedidoResource GET public String getTodosLosPedidos()

Una peticioacuten HTTP GET pedidos se delegaraacute en el meacutetodo getTodosLosPedidos()

Servicios Rest

40

Podemos aplicar tambieacuten Path a un meacutetodo Java Si hacemos esto la expresioacuten de laanotacioacuten Path de la clase se concatenaraacute con la expresioacuten de la anotacioacuten Path delmeacutetodo Por ejemplo

Path(pedidos)public class PedidoResource

GET Path(noPagados) public String getPedidosNoPagados()

De esta forma una peticioacuten GET pedidosnoPagados se delegaraacute en el meacutetodogetPedidosNoPagados()

Podemos tener anotaciones Path para cada meacutetodo que seraacuten relativos a la ruta indicadaen la anotacioacuten Path de la definicioacuten de la clase Por ejemplo la siguiente clase de recursosirve peticiones a la URI pedidos

Path(pedidos)public class PedidoResource

GET public String getPedidos()

Si quisieacuteramos proporcionar el servicio en la URI pedidosincidencias por ejemplono necesitamos una nueva definicioacuten de clase y podriacuteamos anotar un nuevo meacutetodogetIncidenciasPedidos() de la siguiente forma

Path(pedidos)public class PedidoResource

GET public String getPedidos()

GET Path(incidencias) public String getIncidenciasPedidos()

Ahora tenemos una clase de recurso que gestiona peticiones para pedidos y para pedidosincidencias

Expresiones Path

El valor de una anotacioacuten Path puede ser una cadena de caracteres o tambieacutenpuede contener expresiones maacutes complejas si es necesario nos referiremos a ellas comoexpresiones Path

Servicios Rest

41

Una expresioacuten Path puede incluir variables que se indican entre llaves que seraacuten sustituidasen tiempo de ejecucioacuten dependiendo del valor que se indique en la llamada al recurso Asiacutepor ejemplo si tenemos la siguiente anotacioacuten

GETPath(clientesid)

y el usuario realiza la llamada

GET httporgexpertojavacontextorestclientesPedro

la peticioacuten se delegaraacute en el meacutetodo que esteacute anotado con las anotaciones anteriores y el valorde id seraacute instanciado en tiempo de ejecucioacuten a Pedro

Para obtener el valor del nombre del cliente utilizaremos la anotacioacuten PathParam en losparaacutemetros del meacutetodo de la siguiente forma

GETPath(clientesnombre)public String getClientePorNombre(PathParam(nombre) String nombre)

Una expresioacuten Path puede tener maacutes de una variable cada una figuraraacute entre llaves Porejemplo si utilizamos la siguiente expresioacuten Path

Path(nombre1nombre2)public class MiResource

podremos atender peticiones dirigidas a URIs que respondan a la plantilla

httporgexpertojavacontextorecursosnombre1nombre2

como por ejemplo

httporgexpertojavacontextorecursosPedroLopez

Las expresiones Path pueden incluir maacutes de una variable para referenciar un segmento deruta Por ejemplo

Path()public class ClienteResource GET Path(clientesapellido1-apellido2) public String getCliente(PathParam(apellido1) String ape1 PathParam(apellido2) String ape2)

Servicios Rest

42

Una peticioacuten del tipo

GET httporgexpertojavacontextoclientesPedro-Lopez

seraacute procesada por el meacutetodo getCliente()

Expresiones regulares

Las anotaciones Path pueden contener expresiones regulares (asociadas a las variables)Por ejemplo si nuestro meacutetodo getClienteId() tiene un paraacutemetro de tipo entero podemosrestringir las peticiones para tratar solamente aquellas URIs que contengan diacutegitos en elsegmento de ruta que nos interese

Path(clientes)public class ClienteResource GET Path(id d+) solo soporta diacutegitos public String getClienteId(PathParam(id) int id)

Si la URI de la peticioacuten de entrada no satisface ninguna expresioacuten regular de ninguno de losmetodos del recurso entonces se devolveraacute el coacutedigo de error 404 Not Found

El formato para especificar expresiones regulares para las variables del path es

nombre-variable [ expresion-regular ]

El uso de expresiones regulares es opcional Si no se proporciona una expresioacuten regularpor defecto se admite cualquier caraacutecter En teacuterminos de una expresioacuten regular la expresioacutenregular por defecto seriacutea

[^]+

Por ejemplo si queremos aceptar solamente nombres que comiencen por una letra y acontinuacioacuten puedan contener una letra o un diacutegito lo expresariacuteamos como

Path(clientes)public class ClienteResource GET Path(nombre [a-zA-Z][a-zA-Z_0-9]) public String getClienteNombre(PathParam(nombre) string nom)

Servicios Rest

43

De esta forma la URI clientesaaa no seriacutea vaacutelida la URI clientesa9 activariacutea elmeacutetodo getClienteNombre() y la URI clientes89 activariacutea el meacutetodo getClienteId()

Las expresiones regulares no se limitan a un soacutelo segmento de la URI Por ejemplo

Path(clientes)public class ClienteResource GET Path(id +) public String getCliente(PathParam(id) String id)

GET Path(id +direccion) public String getDireccion(PathParam(id) String id)

La expresioacuten regular + indica que estaacuten permitidos cualquier nuacutemero de caracteres Asiacutepor ejemplo la peticioacuten GET clientespedrolopez podriacutea delegarse en el meacutetodogetClientes()

El meacutetodo getDireccion() tiene asociada una expresioacuten maacutes especiacutefica la cual puedemapearse con cualquier cadena de caracteres que termine con direccion Seguacuten eacutestola peticioacuten GET clientespedrolopezdireccion podriacutea delegarse en el meacutetodogetDireccion()

Reglas de precedencia

En el ejemplo anterior acabamos de ver que las expresiones Path para getCliente() ygetDireccion() son ambiguas Una peticioacuten GET clientespedrolopezdireccionpodriacutea mapearse con cualquiera de los dos meacutetodos La especificacioacuten JAX-RS define lassiguientes reglas para priorizar el mapeado de expresiones regulares

bull El primer criterio para ordenar las acciones de mapeado es el nuacutemero de caracteres literalesque contiene la expresioacuten Path teniendo prioridad aquellas con un mayor nuacutemero decaracteres literales El patroacuten de la URI para el meacutetodo getCliente() tiene 10 caraacutecteresliterales clientes El patroacuten para el meacutetodo getDireccion() tiene 19 clientesdireccion Por lo tanto se elegiriacutea primero el meacutetodo getDireccion()

bull El segundo criterio es el nuacutemero de variables en expresiones Path (por ejemplo id oid +) Teniendo precedencia las patrones con un mayor nuacutemero de variables

bull El tercer criterio es el nuacutemero de variables que tienen asociadas expresiones regulares(tambieacuten en orden descendente)

A continuacioacuten mostramos una lista de expresiones Path ordenadas en orden descendentede prioridad

1 clientesidnombredireccion

2 clientesid +direccion

3 clientesiddireccion

4 clientesid +

Servicios Rest

44

Las expresiones 13 se analizariacutean primero ya que tienen maacutes caracteres literales que laexpresioacuten nuacutemero 4 Si bien las expresiones 13 tienen el mismo nuacutemero de caracteresliterales La expresioacuten 1 se analizariacutea antes que las otras dos debido a la segunda regla (tienemaacutes variables) Las expresiones 2 y 3 tienen el mismo nuacutemero de caracteres literales y elmismo nuacutemero de variables pero la expresioacuten 2 tiene una variable con una expresioacuten regularasociada

Estas reglas de ordenacioacuten no son perfectas Es posible que siga habiendo ambiguumledadespero cubren el 90 de los casos Si el disentildeo de nuestra aplicacioacuten presenta ambiguumledadesaplicando estas reglas es bastante probable que hayamos complicado dicho disentildeo y seriacuteaconveniente revisarlo y refactorizar nuestro esquema de URIs

Paraacutemetros matrix (Matrix parameters)

Los paraacutemetros matrix con pares nombre-valor incluidos como parte de la URI Aparecen alfinal de un segmento de la URI (segmento de ruta) y estaacuten delimitados por el caraacutecter Por ejemplo

httpejemplocochescomseatibizacolor=black2006

En la ruta anterior el paraacutemetro matrix aparece despueacutes del segmento de ruta ibiza Su nombrees color y el valor asociado es black

Un paraacutemetro matrix es diferente de lo que denominamos paraacutemetro de consulta (queryparameter) ya que los paraacutemetros matrix representan atributos de ciertos segmentos de laURI y se utilizan para propoacutesitos de identificacioacuten Pensemos en ellos como adjetivos Losparaacutemetros de consulta por otro lado siempre aparecen al final de la URI y siemprepertenecen al recurso completo que estemos referenciando

Los paraacutemetros matrix son ignorados cuando el runtime de JAX-RS realiza el matching de laspeticiones de entrada a meacutetodos de recursos REST De hecho es ilegal incluir paraacutemetrosmatrix en las expresiones Path Por ejemplo

Path(seat)public class SeatService

GET Path(ibizaanyo) Produces(imagejpeg) public Response getIbizaImagen(PathParam(anyo) String anyo)

Si la peticioacuten de entrada es GET seatibizacolor=black2009 el meacutetodo getIbizaImagen()seriacutea elegido por el proveedor de JAX-RS para servir la peticioacuten de entrada y seriacutea invocadoLos paraacutemetros matrix NO se consideran parte del proceso de matching debido a quenormalmente son atributos variables de la peticioacuten

Subrecursos (Subresource Locators)

Acabamos de ver la capacidad de JAX-RS para hacer corresponder de forma estaacuteticaa traveacutes de la anotacioacuten Path URIs especificadas en la entrada de la peticioacuten conmeacutetodos Java especiacuteficos JAX-RS tambieacuten nos permitiraacute de forma dinaacutemica servir nosotros

Servicios Rest

45

mismos las peticiones a traveacutes de los denominados subresource locators (localizadores desubrecursos)

Los subresource locators son meacutetodos Java anotados con Path pero sin anotacionesGET PUT hellip Este tipo de meacutetodos devuelven un objeto que es en siacute mismo un servicioJAX-RS que sabe coacutemo servir el resto de la peticioacuten Vamos a describir mejor este conceptocon un ejemplo

Supongamos que queremos extender nuestro servicio que proporciona informacioacuten sobre losclientes Disponemos de diferentes bases de datos de clientes seguacuten regiones geograacuteficasQueremos antildeadir esta informacioacuten en nuestro esquema de URIs pero desacoplando labuacutesqueda del servidor de base de datos de la consulta particular de un cliente en concretoAntildeadiremos la informacioacuten de la zona geograacutefica en la siguiente expresioacuten Path

clienteszona-dbclienteId

A continuacioacuten definimos la clase ZonasClienteResource que delegaraacute en la claseClienteResource que ya teniacuteamos definida

Path(clientes)public class ZonasClienteResource

Path(zona-db) public ClienteResource getBaseDeDatos(PathParam(zona) String db) devuelve una instancia dependiendo del paraacutemetro db ClienteResource resource = localizaClienteResource(db) return resource

protected ClienteResource localizaClienteResource(String db)

La clase ZonasClienteResource es nuestro recurso raiacutez Dicha clase no atiende ningunapeticioacuten HTTP directamente Nuestro recurso raiacutez procesa el segmento de URI que hacereferencia a la base de datos en donde buscar a nuestro cliente y devuelve una instancia dedicha base de datos (o maacutes propiamente dicho del objeto con en que accederemos a dichabase de datos) El proveedor de JAX-RS utiliza dicha instancia para servir el resto de lapeticioacuten

public class ClienteResource private MapltInteger Clientegt clienteDB = new ConcurrentHashMapltInteger Clientegt() private AtomicInteger idContador = new AtomicInteger()

public ClienteResource(MapltInteger Clientegt clienteDB) thisclienteDB = clienteDB

POST Consumes(applicationxml)

Servicios Rest

46

public Response crearCliente(InputStream is)

GET Path(id) Produces(applicationxml) public Cliente recuperarClienteId(PathParam(id) int id)

PUT Path(id) Consumes(applicationxml) public void modificarCliente(PathParam(id) int id Cliente cli)

Si un usuario enviacutea la peticioacuten GET clientesnorteamerica-db333 el proveedorJAX-RS primero realizaraacute un matching de la expresioacuten sobre el meacutetodoZonasClienteResourcegetBaseDeDatos() A continuacioacuten procesaraacute el resto de la peticioacuten(333) a traveacutes del meacutetodo ClienteResourcerecuperarClienteId()

Podemos observar que la nueva clase ClienteResource ademaacutes de tener un nuevoconstructor ya no estaacute anotada con Path Esto implica que ya no es un recurso de nuestrosistema es un subrecurso y no debe ser registrada en el runtime de JAX-RS a traveacutes de laclase Application (como veremos maacutes adelante)

Veamos otro ejemplo Supongamos que tenemos un conjunto de alumnos del que podemosobtener el listado completo de alumnos y antildeadir nuevos alumnos pero ademaacutes queremos quecada alumno individual pueda consultarse modificarse o borrarse Una forma sencilla de trataresto es dividir el coacutedigo en un recurso (lista de alumnos) y un subrecurso (alumno individual)de la siguiente forma

Path(alumnos)public class AlumnosResource

Context UriInfo uriInfo

GET Produces(MediaTypeAPPLICATION_XML MediaTypeAPPLICATION_JSON) public ListltAlumnoBeangt getAlumnos() return FactoriaDaosgetAlumnoDao()getAlumnos()

POST Consumes(MediaTypeAPPLICATION_JSON) public void addAlumno(AlumnoBean alumno) throws IOException String dni = FactoriaDaosgetAlumnoDao()addAlumno(alumno) URI uri = uriInfogetAbsolutePathBuilder()path(dni)build(dni) Responsecreated(uri)build()

Path(alumno) public AlumnoResource getAlumno( PathParam(alumno) String dni)

Servicios Rest

47

return new AlumnoResource(uriInfo dni)

Vemos que en este recurso inyectamos informacioacuten sobre la URI solicitada como variable deinstancia (utilizando la anotacioacuten Context de la que hablaremos maacutes adelante) Para elconjunto de alumnos ofrecemos dos operaciones obtener la lista de alumnos y antildeadir unnuevo alumno a la lista la cual devuelve como respuesta la URI que nos da acceso al recursoque acabamos de antildeadir

Sin embargo lo maacutes destacable es el uacuteltimo meacutetodo Eacuteste se ejecutaraacute cuando antildeadamos a laruta el identificador de un alumno (por ejemplo alumnos15 ) En este caso lo que hace esdevolver un subrecurso (AlumnoResource) para asiacute tratar un alumno individual (destacamosque el nombre estaacute en singular para distinguirlo del recurso anterior que representa elconjunto)

Cuando hacemos esto estamos delegando en el nuevo Recurso para tratar la peticioacuten

public class AlumnoResource

UriInfo uriInfo

String dni

public AlumnoResource(UriInfo uriInfo String dni) thisuriInfo = uriInfo thisdni = dni

GET Produces(MediaTypeAPPLICATION_XMLMediaTypeAPPLICATION_JSON) public AlumnoBean getAlumno() AlumnoBean alumno = FactoriaDaosgetAlumnoDao()getAlumno(dni) if(alumno==null) throw new WebApplicationException(StatusNOT_FOUND) return alumno

PUT Consumes(MediaTypeAPPLICATION_XML) public Response setAlumno(AlumnoBean alumno) El DNI del alumno debe coincidir con el de la URI alumnosetDni(dni)

if(FactoriaDaosgetAlumnoDao()getAlumno(dni) = null) FactoriaDaosgetAlumnoDao()updateAlumno(alumno) return ResponsenoContent()build() else FactoriaDaosgetAlumnoDao()addAlumno(alumno) return Responsecreated(uriInfogetAbsolutePath())build()

Servicios Rest

48

DELETE public void deleteAlumno() FactoriaDaosgetAlumnoDao()deleteAlumno(dni)

Este recurso ya no es un recurso raiacutez mapeado a una ruta determinada (podemos ver que laclase no lleva la anotacioacuten Path) sino que es creado desde otro recurso Es por lo tantoun subrecurso

Como ya hemos visto los subrecursos nos permiten simplificar la forma de trabajar conconjuntos de recursos definiendo en un uacutenico meacutetodo la ruta de acceso a un recurso individualen lugar de tenerlo que hacer de forma independiente para cada operacioacuten

Ademaacutes este disentildeo modular de los recursos nos va a permitir reutilizar determinadosrecursos dentro de otros Por ejemplo dentro del recurso de un alumno podriacuteamos ver la listade asignaturas en las que se ha matriculado y reutilizar el subrecurso encargado de acceder alas asignaturas para poder acceder a sus datos a partir del recurso del alumno No deberemosabusar de esta caracteriacutestica ya que si creamos relaciones ciacuteclicas perdemos la caracteriacutesticadeseable de los servicios REST de que cada recurso estaacute asignado a una uacutenica URI

En un subrecurso NO podemos inyectar objetos de contexto mediantela anotacioacuten Context ya que no estamos en un recurso raiacutez Porese motivo en el ejemplo hemos proporcionado el objeto UriInfo enel constructor del subrecurso De forma alternativa tambieacuten podriacuteamoshaber inyectado este objeto como paraacutemetro de sus meacutetodos en ese casosi que habriacutea sido posible la inyeccioacuten

Caraacutecter dinaacutemico del dispatching de peticiones

En los ejemplos anteriores hemos ilustrado el concepto de subresource locator aunque nohemos mostrado completamente su caraacutecter dinaacutemico Asiacute si volvemos al primero de ellosel meacutetodo ZonasClienteResourcegetBaseDeDatos() puede devolver cualquier instancia decualquier clase En tiempo de ejecucioacuten el proveedor JAX-RS buscaraacute el interior de estainstancia meacutetodos de recurso que puedan gestionar la peticioacuten

Supongamos que tenemos dos bases de datos de clientes con diferentes tipos deidentificadores Una de ellas utiliza una clave numeacuterica La otra utiliza una clave formada porel nombre y apellidos Necesitamos tener dos clases diferentes para extraer la informacioacutenadecuada de la URI de la peticioacuten Cambiaremos la implementacioacuten de la siguiente forma

Path(clientes)public class ZonasClienteResourceResource protected ClienteResource europa = new ClienteResource() protected OtraClaveClienteResource norteamerica = new OtraClaveClienteResource()

Path(zona-db) public Object getBaseDeDatos(PathParam(zona) String db) if (dbequals(europa)) return europa else if (dbequals(norteamerica)) return northamerica

Servicios Rest

49

else return null

En lugar de devolver una instancia de ClienteResource el meacutetodo getBaseDeDatos() devuelveuna instancia de javalangObject JAX-RS analizaraacute la instancia devuelta para ver coacutemoprocesar el resto de la peticioacuten

Ahora si un usuario enviacutea la peticioacuten GET clienteseuropa-db333 se utilizaraacute la claseClienteResource para servir el resto de la peticioacuten Si la peticioacuten es GET clientesnorteamerica-dbjohn-smith utilizaremos el nuevo subrecurso OtraClaveClienteResource

public class OtraClaveClienteResource private MapltString Clientegt clienteDB = new ConcurrentHashMapltString Clientegt() GET Path(nombre-apellidos) Produces(applicationxml) public Cliente getCliente(PathParam(nombre) String nombre PathParam(apellidos) String apelllidos)

PUT Path(nombre-apellidos) Consumes(applicationxml) public void actualizaCliente()PathParam(nombre) String nombre PathParam(apellidos) String apelllidos Cliente cli)

23 Usos de las anotaciones Produces y Consumes

La informacioacuten enviada a un recurso y posteriormente devuelta al cliente que realizoacute la peticioacutense especifica con la cabecera HTTP Media-Type tanto en la peticioacuten como en la respuestaComo ya hemos visto podemos especificar que representaciones de los recursos (valor deMedia_Type) son capaces de aceptar yo producir nuestros servicios mediante las siguientesanotaciones

bull javaxwsrsConsumes

bull javaxwsrsProduces

La ausencia de dichas anotaciones es equivalente a incluirlas con el valor de media type es decir su ausencia implica que se soporta (acepta) cualquier tipo de representacioacuten

Anotacioacuten Consumes

Esta anotacioacuten funciona conjuntamente con POST y PUT Le indica al framework (libreriacuteasJAX-RS) a queacute meacutetodo se debe delegar la peticioacuten de entrada Especiacuteficamente el clientefija la cabecera HTTP Content-Type y el framework delega la peticioacuten al correspondientemeacutetodo capaz de manejar dicho contenido Un ejemplo de anotacioacuten con PUT es lasiguiente

Servicios Rest

50

Path(pedidos)public class PedidoResource PUT Consumes(applicationxml) public void modificarPedido(Pedido representation)

Si Consumes se aplica a la clase por defecto los meacutetodos correspondientes aceptan lostipos especificados de tipo MIME Si se aplica a nivel de meacutetodo se ignora cualquier anotacioacutenConsumes a nivel de clase para dicho meacutetodo

En este ejemplo le estamos indicando al framework que el meacutetodo modificarPedido() aceptaun recurso cuya representacioacuten (tipo MIME) es applicationxml (y que se almacenaraacute en lavariable representation hablaremos de ello en la siguiente sesioacuten) Por lo tanto un clienteque se conecte al servicio web a traveacutes de la URI pedidos debe enviar una peticioacuten HTTPPUT conteniendo el valor de applicationxml como tipo MIME de la cabecera HTTPContent-Type y el cuerpo (body) del mensaje HTTP debe ser por tanto un documentoxml vaacutelido

Si no hay meacutetodos de recurso que puedan responder al tipo MIME solicitado (tipo MIMEespecificado en la anotacioacuten Consumes del servicio) se le devolveraacute al cliente un coacutedigoHTTP 415 (Unsupported Media Type) Si el meacutetodo que consume la representacioacutenindicada como tipo MIME no devuelve ninguna representacioacuten se enviaraacute un el coacutedigo HTTP204 (No content) A continuacioacuten mostramos un ejemplo en el que sucede eacutesto

POSTConsumes(applicationxml)public void creaPedido(Pedido pedido) Crea y almacena un nuevo _Pedido_

Podemos ver que el meacutetodo consume una representacioacuten en texto plano pero devuelvevoid es decir no devuelve ninguna representacioacuten En este caso se enviacutea el coacutedigo de estadoHTTP 204 No content en la respuesta

Un recurso puede aceptar diferentes tipos de entradas Asiacute podemos utilizar la anotacioacutenPUT con maacutes de un meacutetodo para gestionar las repuestas con tipos MIME diferentes Porejemplo podriacuteamos tener un meacutetodo para aceptar estructuras XML y otro para aceptarestructuras JSON

Path(pedidos)public class PedidoResource PUT Consumes(applicationxml) public void modificarPedidoXML(InputStream pedido) PUT Consumes(applicationjson) public void modificarPedidoJson(InputStream pedido)

Servicios Rest

51

Anotacioacuten Produces

Esta anotacioacuten funciona conjuntamente con GET POST y PUT Indica al frameworkqueacute tipo de representacioacuten se enviacutea de vuelta al cliente

De forma maacutes especiacutefica el cliente enviacutea una peticioacuten HTTP junto con una cabecera HTTPAccept que se mapea directamente con el Content-Type que el meacutetodo produce Por lo tantosi el valor de la cabecera Accept HTTP es applicationxml el meacutetodo que gestiona la peticioacutendevuelve un stream de tipo MIME applicationxml Esta anotacioacuten tambieacuten puede utilizarse enmaacutes de un meacutetodo en la misma clase de recurso Un ejemplo que devuelve representacionesXML y JSON seriacutea el siguiente

Path(pedidos)public class PedidoResource

GET Produces(applicationxml) public String getPedidoXml()

GET Produces(applicationjson) public String getPedidoJson()

Si un cliente solicita una peticioacuten a una URI con un tipo MIME no soportadopor el recurso el framework JAX-RS lanza la excepcioacuten adecuadaconcretamente el runtime de JAX-RS enviacutea de vuelta un error HTTP 406Not acceptable

Se puede declarar maacutes de un tipo en la misma declaracioacuten Produces como por ejemplo

Produces(applicationxml applicationjson)public String getPedidosXmlOJson()

El meacutetodo getPedidosXmlOJson() seraacute invocado si cualquiera de los dos tipos MIMEespecificados en la anotacioacuten Produces son aceptables (la cabecera Accept de la peticioacutenHTTP indica queacute representacioacuten es aceptable) Si ambas representaciones son igualmenteaceptables se elegiraacute la primera

En lugar de especificar los tipos MIME como cadenas detexto en Consumes y Produces podemos utilizar lasconstantes definidas en la clase javaxwsrscoreMediaTypecomo por ejemplo MediaTypeAPPLICATION_XML oMediaTypeAPPLICATION_JSON en lugar de applicationxml yapplicationjson

24 Inyeccioacuten de paraacutemetros JAX-RS

Buena parte del trabajo de JAX-RS es el extraer informacioacuten de una peticioacuten HTTP einyectarla en un meacutetodo Java Podemos estar interesados en un fragmento de la URI de

Servicios Rest

52

entrada en los paraacutemetros de peticioacutenhellip El cliente tambieacuten podriacutea enviar informacioacuten en lascabeceras de la peticioacuten A continuacioacuten indicamos una lista con algunas de las anotacionesque podemos utilizar para inyectar informacioacuten de las peticiones HTTP

bull javaxwsrsPathParam

bull javaxwsrsMatrixParam

bull javaxwsrsQueryParam

bull javaxwsrsFormParam

bull javaxwsrsHeaderParam

bull javaxwsrsContext

bull javaxwsrsBeanParam

Habitualmente estas anotaciones se utilizan en los paraacutemetros de un meacutetodo de recurso JAX-RX Cuando el proveedor de JAX-RS recibe una peticioacuten HTTP busca un meacutetodo Java quepueda servir dicha peticioacuten Si el meacutetodo Java tiene paraacutemetros anotados con alguna de estasanotaciones extraeraacute la informacioacuten de la peticioacuten HTTP y la pasaraacute como un paraacutemetrocuando se invoque el meacutetodo

javaxwsrsPathParam

Ya la hemos utilizado en la sesioacuten anterior PathParam nos permite inyectar el valor de losparaacutemetros de la URI definidos en expresiones Path Recordemos el ejemplo

Path(clientes)public class ClienteResource

GET Path(id) Produces(applicationxml) public Cliente recuperarClienteId(PathParam(id) int id)

Podemos referenciar maacutes de un paraacutemetro en el path de la URI en nuestros meacutetodo javaPor ejemplo supongamos que estamos utilizando el nombre y apellidos para identificar a uncliente en nuestra clase de recurso

Path(clientes)public class ClienteResource

GET Path(nombre-apellidos) Produces(applicationxml) public Cliente recuperarClienteId(PathParam(nombre) String nom PathParam(apellidos) String ape)

Servicios Rest

53

En ocasiones un parameacutetro de path de la URI puede repetirse en diferentes expresionesPath que conforman el patroacuten de matching completo para un meacutetodo de un recurso (porejemplo puede repetirse en la expresioacuten Path de la clase y de un meacutetodo) En estos casos laanotacioacuten PathParam siempre referencia el paraacutemetro path final Asiacute en el siguiente coacutedigo

Path(clientesid)public class ClienteResource

GET Path(direccionid) Produces(textplain) public String getDireccion(PathParam(id) String direccionId)

Si nuestra peticioacuten HTTP es GET clientes123direccion456 el paraacutemetro direccionIddel meacutetodo getDireccion() tendriacutea el valor inyectado de 456

Interfaz UriInfo

Podemos disponer ademaacutes de un API maacutes general para consultar y extraer informacioacuten sobrelas peticiones URI de entrada Se trata de la interfaz javaxwsrscoreUriInfo

public interface UriInfo public javanetURI getAbsolutePath() public UriBuilder getAbsolutePathBuilder()

public javanetURI getBaseUri() public UriBuilder getBaseUriBuilder()

public String getPath() public ListltPathSegmentgt getPathSegments() public MultivaluedMapltString Stringgt getPathParameters()

Los meacutetodos getAbsolutePathBuilder() y getAbsolutePath() devuelven la ruta absoluta dela peticioacuten HTTP en forma de UriBuilder y URI respectivamente

Los meacutetodos getBaseUri() y getBaseUriBuilder() devuelven la ruta base de la aplicacioacuten(ruta raiz de nuestros servicios rest) en forma de UriBuilder y URI respectivamente

El meacutetodo UriInfogetPath() permite obtener la ruta relativa de nuestros servicios RESTutilizada para realizar el matching con nuestra peticioacuten de entrada (es la ruta de la peticioacutenactual relativa a la ruta base de la peticioacuten rest)

El meacutetodo UriInfogetPathSegments() divide la ruta relativa de nuestro servicio REST enuna serie de objetos PathSegment (segmentos de ruta delimitados por )

El meacutetodo UriInfogetPathParameters() devuelve un objeto de tipo MultivaluedMap con todoslos paraacutemetros del path definidos en todas las expresiones Path de nuestra peticioacuten rest

Servicios Rest

54

Por ejemplo si la ruta de nuestra petcioacuten http es httplocalhost8080contextorestclientes2(siendo contexto la ruta raiacutez del war desplegado y rest la ruta de servicio de jax-rs)

bull la ruta absoluta (meacutetodo getAbsolutePath()) seriacutea httplocalhost8080contextorestclientes2

bull la ruta base (meacutetodo getBaseUri) seriacutea httplocalhost8080contextorest

bull la ruta relativa a la ruta base (meacutetodo getPath()) seriacutea clientes2

bull el nuacutemero de segmentos de la peticioacuten rest (meacutetodo getPathSegments()) seriacutean 2 clientesy 2

Podemos inyectar una instancia de la interfaz UriInfo utilizando la anotacioacutenjavaxwsrscoreContext A continuacioacuten mostramos un ejemplo

Path(cochesmarca)public class CarResource

GET Path(modeloanyo) Produces(imagejpeg) public Response getImagen(Context UriInfo info) String fabricado = infogetPathParameters()getFirst(marca) PathSegment modelo = infogetPathSegments()get(2) String color = modelogetMatrixParameteres()getFirst(color)

En este ejemplo inyectamos una instancia de UriInfo como paraacutemetro del meacutetodo getImagen()A continuacioacuten hacemos uso de dicha instancia para extraer informacioacuten de la URI

Recuerda que tambieacuten podriacuteamos inyectar una instancia de UriInfo en unavariable de instancia de la clase raiacutez de nuestro recurso

El meacutetodo CarResourcegetImagen() utiliza la interfazjavaxwsrscorePathSegment que como ya hemos indicado representa unsegmento de ruta

package javaxwsrscorepublic interface PathSegment String getPath() MultivaluedMapltString Stringgt getMatrixParameters()

El meacutetodo PathSegmentgetPath() devuelve el valor de la cadena de caracteres del segmentode ruta actual sin considerar niguacuten paraacutemetro matrix que pudiese contener

El meacutetodo PathSegmentgetMatrixParameters() devuelve un mapa con todos losparaacutemetros matrix aplicados a un segmento de ruta

Supongamos que realizamos la siguiente peticioacuten http para el coacutedigo anterior (claseCarResource)

Servicios Rest

55

GET cochesseatleoncolor=rojo2015

Esta peticioacuten es delegada en el meacutetodo ClarResourcegetImagen() La ruta contiene 4segmentos coches seat leon y 2015 La variable _modelo tomaraacute el valor leon y la variablecolor se instanciaraacute con el valor rojo

javaxwsrsMatrixParam

La especificacioacuten JAX-RS nos permite inyectar una matriz de valores de paraacutemetros a traveacutesde la anotacioacuten javaxwsrsMatrixParam

Path(cochesmarca)public class CarResource

GET Path(modeloanyo) Produces(imagejpeg) public Response getImagen(PathParam(marca) String marca PathParam(modelo) String modelo MatrixParam(color) String color)

El uso de la anotacioacuten MatrixParam simplifica nuestro coacutedigo y lo hace algo maacutes legibleSi por ejemplo la peticioacuten de entrada es

GET cochesseatibizacolor=black2009

entonces el paraacutemetro color del meacutetodo CarResourcegetImagen() tomariacutea el valor black

javaxwsrsQueryParam

La anotacioacuten javaxwsrsQueryParam nos permite inyectar paraacutemetros de consulta(query parameters) de la URI en los valores de los paraacutemetros de los meacutetodos java denuestros recursos Por ejemplo supongamos que queremos consultar informacioacuten de nuestrosclientes y queremos recuperar un subconjunto de clientes de nuestra base de datos NuestraURI de peticioacuten podriacutea ser algo asiacute

GET clientesinicio=0amptotal=10

El paraacutemetro de consulta inicio representa el iacutendice (o posicioacuten) del primer cliente quequeremos consultar y el paraacutemetro total representa cuaacutentos clientes en total queremosobtener como respuesta Una implementacioacuten del servicio RESTful podriacutea contener elsiguiente coacutedigo

Path(clientes)public class ClienteResource GET Produces(applicationxml) public String getClientes(QueryParam(inicio) int inicio

Servicios Rest

56

QueryParam(total) int total)

En este ejemplo el paraacutemetro inicio tomariacutea el valor 0 y el paraacutemetro total tomariacutea elvalor 10 (JAX-RS convierte automaacuteticamente las cadenas de caracteres de los paraacutemetrosde consulta en enteros)

javaxwsrsFormParam

La anotacioacuten javaxwsrsFormParam se utiliza para acceder al cuerpo del mensajede la peticioacuten HTTP de entrada cuyo valor de Content-Type es applicationx-www-form-urlencoded Es decir se utiliza para acceder a entradas individuales de un formulario HTMLPor ejemplo supongamos que para registrar a nuevos clientes en el sistema tenemos querellenar el siguiente formulario

ltFORM action=httpejemplocomclientes method=postgt ltPgt Nombre ltINPUT type=text name=nombregtltBRgt Apellido ltINPUT type=text name=apellidogtltBRgt ltINPUT type=submit value=Sendgt ltPgtltFORMgt

La ejecucioacuten de este coacutedigo inyectaraacute los valores del formulario como paraacutemetros de nuestromeacutetodo Java que representa el servicio de la siguiente forma

Path(clientes)public class ClienteResource POST public void crearCliente(FormParam(nombre) String nom FormParam(apellido) String ape)

Aquiacute estamos inyectando los valores de nombre y apellidos del formulario HTML en losparaacutemetors nom y ape del meacutetodo java crearCliente() Los datos del formulario viajan atraveacutes de la red codificados como URL-encoded Cuando se utiliza la anotacioacuten FormParamJAX-RS decodifica de forma automaacutetica las entradas del fomulario antes de inyectar susvalores

Asiacute por ejemplo si tecleamos los valores Maria Luisa y_Perlado_ como valores en loscampos de texto nombre y apellido del formulario el cuerpo de nuestro mensaje HTTP seraacutenombre=Maria20Luisaapellido=Perlado Este mensaje seraacute recibido por nuestro meacutetodoque extraeraacute los valores correspondientes y los instanciaraacute en los paraacutemetros nom y ape delmeacutetodo _ClienteResourcecrearCliente()

javaxwsrsHeaderParam

La anotacioacuten javaxwsrsHeaderParam se utiliza para inyectar valores de lascabeceras de las peticiones HTTP Por ejemplo si estamos interesados en la paacutegina web quenos ha referenciado o enlazado con nuestro servicio web podriacuteamos acceder a la cabeceraHTTP Referer utilizando la anotacioacuten HeaderParam de la siguiente forma

Servicios Rest

57

Path(miservicio)public class MiServicio GET Produces(texthtml) public String get(HeaderParam(Referer) String referer)

De forma alternativa podemos acceder de forma programativa a todas las cabeceras de lapeticioacuten de entrada utilizando la interfaz javaxwsrscoreHttpHeaders

public interface HttpHeaders public ListltStringgt getRequestHeader(String name) public MultivaluedMapltString Stringgt getRequestHeaders()

El meacutetodo getRequestHeader() permite acceder a una cabecera en concreto y el meacutetodogetRequestHeaders() nos proporciona un objeto de tipo Map que representa todas lascabeceras A continuacioacuten mostramos un ejemplo que accede a todas las cabeceras de lapeticioacuten HTTP de entrada

Path(miservicio)public class MiServicio GET Produces(texthtml) public String get(Context HttpHeaders cabeceras) String referer = headersgetRequestHeader(Referer)get(0) for (String header headersgetRequestHeaders()keySet()) Systemoutprintln(Se ha utilizado esta cabecera + header)

javaxwsrscoreContext

Dentro de nuestros recursos JAX-RS podemos inyectar determinados objetos coninformacioacuten sobre el contexto de JAX-RS sobre el contexto de servlets o sobreelementos de la peticioacuten recibida desde el cliente Para ello utilizaremos la anotacioacutenjavaxwsrscoreContext

En los ejemplos de esta sesioacuten ya hemos visto como utilizarla para inyectar objetos de tipoUriInfo y HttpHeaders

A continuacioacuten mostramos un ejemplo en el que podos obtener detalles sobre el contextodel despliegue de la aplicacion asi como del contexto de peticiones individuales utilizando laanotacion Context

Implementacioacuten de un servicio que muestra informacioacuten sobre el contexto de la peticioacuten

Path(orders)

Servicios Rest

58

public class PedidoResource

Context Application app

Context UriInfo uri

Context HttpHeaders headers

Context Request request

Context SecurityContext security

Context Providers providers

GET Produces(applicationxml) public ListltOrdergt getAll(QueryParam(start)int from QueryParam(end)int to) (appgetClasses()) (urigetPath()) (headersgetRequestHeader(HttpHeadersACCEPT)) (headersgetCookies()) (requestgetMethod()) (securityisSecure())

Application proporciona acceso a la informacioacuten de la configuracioacuten de la aplicacioacuten(clase Application)UriInfo proporciona acceso a la URI de la peticioacutenHttpHeaders proporciona acceso a las cabeceras de la peticioacuten HTTP La anotacioacutenHeaderParam puede tambieacuten utilizarse para enlazar una cabecera HTTP a unparaacutemetro de un meacutetodo de nuestro recurso a un campo del mismo o a una propiedadde un beanRequest se utiliza para procesar la respuestas tiacutepicamente se usa juntamente con laclase Response para construir la respuesta de forma dinaacutemicaSecurityContext proporciona acceso a la informacioacuten de la peticioacuten actual relacionadacon la seguridadProviders proporciona informacioacuten sobre la buacutesqueda del runtime de las instancias deproveedores utilizando un conjunto de criterios de buacutesqueda

Con respecto a contexto de servlets podremos inyectar informacioacuten de ServletContextServletConfig HttpServletRequest y HttpServletResponse Debemos recordar que losrecursos JAX-RS son invocados por un servlet dentro de una aplicacioacuten web por lo quepodemos necesitar tener acceso a la informacioacuten del contexto de servlets Por ejemplo sinecesitamos acceder a la ruta en disco donde tenemos los datos de nuestra aplicacioacuten webtendremos que inyectar el objeto ServletContext

GETProduces(imagejpeg)public InputStream getImagen(Context ServletContext sc) return scgetResourceAsStream(fotos + nif + jpg)

javaxwsrsBeanParam

La anotacioacuten javaxwsrsBeanParam nos permite inyectar una clase especiacutefica cuyosmeacutetodos o atributos esteacuten anotados con alguna de las anotaciones de inyeccioacuten de paraacutemetrosxxxParam que hemos visto en esta sesioacuten Por ejemplo supongamos esta clase

Servicios Rest

59

public class ClienteInput FormParam(nombre) String nombre

FormParam(apellido) String apellido

HeaderParam(Content-Type) String contentType

public String getFirstName()

La clase ClienteInput es un simple POJO (Plain Old Java Object) que contiene el nombrey apellidos de un cliente asiacute como el tipo de contenido del mismo Podemos dejar que JAX-RScree inicialice e inyecte esta clase usando la anotacioacuten BeanParam de la siguiente forma

Path(clientes)public class ClienteResource POST public void crearCliente(BeanParam ClienteInput newCust)

El runtime de JAX-RS analizaraacute los paraacutemetros anotados con BeanParam para inyectarlas anotaciones correspondientes y asignar el valor que corresponda En este ejemplo la claseClienteInput contendraacute dos valores de un formulario de entrada y uno de los valores de lacabecera de la peticioacuten De esta forma nos podemos evitar una larga lista de paraacutemetros enel meacutetodo crearCliente() (en este caso son soacutelo tres pero podriacutean ser muchos maacutes)

Conversioacuten automaacutetica de tipos

Todas las anotaciones que hemos visto referencian varias partes de la peticioacuten HTTP Todasellas se representan como una cadena de caracteres en dicha peticioacuten HTTP JAX-RS puedeconvertir esta cadena de caracteres en cualquier tipo Java siempre y cuando se cumpla almenos uno de estos casos

1 Se trata de un tipo primitivo Los tipos int short float double byte char y booleanpertenecen a esta categoriacutea

2 Se trata de una clase Java que tiene un constructor con un uacutenico paraacutemetro de tipo String

3 Se trata de una clase Java que tiene un meacutetodo estaacutetico denominado valueOf() que tomaun uacutenico String como argumento y devuelve una instancia de la clase

4 Es una clase de tipo javautilListltTgt javautilSetltTgt o javautilSortedSetltTgt en dondeT es un tipo que satisface los criterios 2 oacute 3 o es un String Por ejemplo ListltDoublegtSetltStringgt o SortedSetltIntegergt

Si el runtime JAX-RS falla al convertir una cadena de caracteres en el tipo Java especificadose considera un error del cliente Si se produce este fallo durante el procesamiento de unainyeccioacuten de tipo MatrixParam QueryParam o PathParam se devuelve al clienteun error 404 Not found Si el fallo tiene lugar con el procesamiento de las inyecciones

Servicios Rest

60

HeaderParam o CookieParam (esta uacuteltima no la hemos visto) entonces se enviacutea al clienteel eror 400 Bad Request

Valores por defecto (DefaultValue)

Suele ser habitual que algunos de los paraacutemetros proporcionados en las peticiones a serviciosRESTful sean opcionales Cuando un cliente no proporciona esta informacioacuten opcional en lapeticioacuten JAX-RS inyectaraacute por defecto un valor null si se trata de un objeto o un valor ceroen el caso de tipos primitivos

Estos valores por defecto no siempre son los que necesitamos para nuestro servicioPara solucionar este problema podemos definir nuestro propio valor por defecto para losparaacutemetros que sean opcionales utilizando la anotacioacuten javaxwsrsDefaultValue

Consideremos el ejemplo anterior relativo a la recuperacioacuten de la informacioacuten de unsubconjunto de clientes de nuestra base de datos Para ello utilizaacutebamos dos paraacutemetros deconsulta para indicar el iacutendice del primer elemento asiacute como el nuacutemero total de elementosque estamos interesados en recuperar En este caso no queremos que el cliente tengaque especificar siempre estos paraacutemetros al realizar la peticion Usaremos la anotacioacutenDefaultValue para indicar los valores por defecto que nos interese

Path(clientes)public class ClienteResource GET Produces(applicationxml) public String getClientes( DefaultValue(0) QueryParam(inicio) int inicio DefaultValue(10) QueryParam(total) int total)

Hemos usado DefaultValue para especificar un iacutendice de comienzo con valor cero y untamantildeo del subconjunto de los datos de la respuesta JAX-RS utilizaraacute las reglas de conversioacutende cadenas de caracteres que acabamos de indicar para convertir el valor del paraacutemetro enel tipo Java que especifiquemos

25 Configuracioacuten y despliegue de aplicaciones JAX-RS

Como ya hemos visto en la sesioacuten anterior implementamos nuestros servicios REST utilizandoel API de Java JAX-RS (especificacioacuten JSR-339) Una aplicacioacuten JAX-RS consiste en unoo maacutes recursos y cero o maacutes proveedores En este apartado vamos a describir ciertosaspectos aplicados a las aplicaciones JAX-RS como un todo concretamente a la configuracioacuteny tambieacuten a la publicacioacuten de las mismas cuando utilizamos un servidor de aplicacionesJavaEE 7 o bien un contenedor de servlets 30 que incluyan una implementacioacuten del APIJAX-RS Tambieacuten indicaremos coacutemo configurar el despliegue en el caso de no disponer comomiacutenimo de un contenedor de servlets 30

Configuracioacuten mediante la clase Application

Tanto los recursos (clases anotadas con Path) como los proveedores que conforman nuestraaplicacioacuten JAX-RS pueden configurarse utilizando una subclase de Application Cuandohablamos de configuracioacuten nos estamos refiriendo en este caso a definir los mecanismospara localizar las clases que representan los recursos asiacute como a los proveedores

Servicios Rest

61

Un proveedor es una clase que implementa una o algunade las siguientes interfaces JAX-RS MesssageBodyReaderMessageBodyWriter ContextResolverltTgt y ExceptionMapperltTgt Lasdos primeras permiten crear proveedores de entidades (entity providers)la tercera es un proveedor de contexto (context provider) y la uacuteltima unproveedor de mapeado de excepciones (exception mapping provider) Lasclases que actuacutean como proveedores estaacuten anotadas con Providerpara que puedan ser identificadas automaacuteticamente por el runtime JAX-RS

El uso de una subclase de Application para configurar nuestros servicios REST constituyela forma maacutes sencilla de desplegar los servicios JAX-RS en un servidor de aplicacionescertificado como Java EE (en este caso Wildfly cumple con este requisito) o un contenedorstandalone de Servlet 3 (como por ejemplo Tomcat)

Pasemos a conocer la clase javaxwsrscoreApplication El uso de la claseApplication es la uacutenica forma portable de decirle a JAX-RS queacute servicios web (clasesanotadas con Path) asiacute como queacute otros elementos como filtros interceptoreshellip queremospublicar (desplegar)

La clase Application se define como

package javaxwsrscore

import javautilCollectionsimport javautilSet

public abstract class Application private static final SetltObjectgt emptySet = CollectionsemptySet()

public abstract SetltClassltgtgt getClasses()

public SetltObjectgt getSingletons() return emptySet

La clase Application es muy simple Como ya hemos indicado su propoacutesito es proporcionaruna lista de clases y objetos que queremos desplegar

El meacutetodo getClasses() devuelve una lista de clases de servicios web y proveedores JAX-RS Cualquier servicio JAX-RS devuelto por este meacutetodo sigue el modelo per-request queya hemos introducido en la sesioacuten anterior Cuando la implementacioacuten de JAX-RS determinaque una peticioacuten HTTP necesita ser procesada por un meacutetodo de una de estas clases secrearaacute una instancia de dicha clase durante la peticioacuten y se destruiraacute al finalizar la mismaEn este caso estamos delegando en el runtime JAX-RS la creacioacuten de los objetos Lasclases proveedoras son instanciadas por el contenedor JAX-RS y registradas una uacutenica vezpor aplicacioacuten

El meacutetodo getSingletons() devuelve una lista de servicios y proveedores web JAX-RSya instanciados Nosotros como programadores de las aplicaciones somos responsablesde crear estos objetos El runtime JAX-RS iteraraacute a traveacutes de la lista de objetos y los registraraacuteinternamente

Servicios Rest

62

Un ejemplo de uso de una subclase de Application podriacutea ser eacuteste

package orgexpertojava

import javaxwsrscoreApplicationimport javaxwsrsApplicationPath

ApplicationPath(rest)public class ComercioApplication extends Application

public SetltClassltgtgt getClasses() HashSetltClassltgtgt set = new HashSetltClassltgtgt() setadd(ClienteResourceclass) setadd(PedidoResourceclass) return set

public SetltObjectgt getSingletons() JsonWriter json = new JsonWriter() TarjetaCreditoResource servicio = new TarjetaCreditoResource()

HashSetltObjectgt set = new HashSet() setadd(json) setadd(servicio) return set

La anotacioacuten ApplicationPath define la base URL de la ruta para todos nuestrosservicios JAX-RS desplegados Asiacute por ejemplo accederemos a todos nuestros serviciosJAX-RS seraacuten desde la ruta rest cuando los ejecutemos En el ejemplo anterior estamosindicando que ClienteResource y PedidoResource son servicios per-request El meacutetodogetSingletons() devuelve el servicio de tipo TarjetaCreditoResource asiacute como el proveedorJsonWriter (que implementa la interfaz MessageBodyWriter)

Si tenemos al menos una implementacioacuten de la clase Application anotada conApplicationPath esta seraacute detectada y desplegada automaacuteticamente por el servidor deaplicaciones

Podemos aprovechar completamente esta capacidad para escanear y detectarautomaacuteticamente nuestros servicios si tenemos implementada una subclase de Applicationpero dejamos que getSingletons() devuelva el conjunto vaciacuteo y no indicamos nada en elmeacutetodo getClasses() de esta forma

package orgexpertojava

import javaxwsrsApplicationPathimport javaxwsrscoreApplication

ApplicationPath(rest)public class ComercioApplication extends Application

Servicios Rest

63

En este caso el servidor de aplicaciones se encargaraacute de buscar en el directorio WEB-INFclasses y en cualquier fichero jar dentro del directorio WEB-INFlib A continuacioacuten antildeadiraacutecualquier clase anotada con Path o Provider a la lista de cosas que necesitan serdesplegadas y registradas en el runtime JAX-RS

Los servicios REST son atendidos por un servlet que es especiacutefico de la implementacioacutenJAX-RS utilizada por el servidor de aplicaciones El servidor wildfly utiliza la implementacioacutende JAX-RS 20 denomindada resteasy (otra implementacioacuten muy utilizada es jersey porejemplo con el servidor de aplicaciones Glassfish) El runtime de JAX-RS contiene un servletinicializado con un paraacutemetro de inicializacioacuten de tipo javaxwsrsApplication cuyo valor seraacuteinstanciado automaacuteticamente por el servidor de aplicaciones con el nombre de la subclasede Application que sea detectada en el war de nuestra aplicacioacuten

Configuracioacuten mediante un fichero webxml

En la sesioacuten anterior no hemos utilizado de forma expliacutecita la clase Application para configurarel despliegue En su lugar hemos indicado esta informacioacuten en el fichero webxml

ltweb-app version=30 xmlns=httpjavasuncomxmlnsjavaee xmlnsxsi=httpwwww3org2001XMLSchema-instance xsischemaLocation=httpjavasuncomxmlnsjavaee httpjavasuncomxmlnsjavaeeweb-app_3_0xsdgt lt-- Con estas liacuteneas el servidor es el responsable de antildeadir el servlet correspondiente de forma automaacutetica Si en nuestro war tenemos clases anotadas con anotaciones JAX-RS para recibir invocaciones REST eacutestas seraacuten detectadas y registradas--gt ltservlet-mappinggt ltservlet-namegtjavaxwsrscoreApplicationltservlet-namegt lturl-patterngtrestlturl-patterngt ltservlet-mappinggtltweb-appgt

Esta configuracioacuten es equivalente a incluir una subclase de Application sin sobreescribir losmeacutetodos correspondientes En este caso se antildeade de forma dinaacutemica el servlet que sirvelas peticiones REST con el nombre javaxwsrscoreApplication de forma que se detectenautomaacuteticamente todas las clases de recursos y clases proveedoras empaquetadas en el warde la aplicacioacuten

Configuracioacuten en un contenedor que no disponga de una implementacioacuten JAX-RS

Si queremos hacer el despliegue sobre servidores de aplicaciones o servidores web queden soporte a una especificacioacuten de servlets con una versioacuten inferior a la 30 tendremosque configurar MANUALMENTE el fichero webxml para que cargue el servlet de nuestraimplementacioacuten propietaria de JAX-RS (cuyos ficheros jar deberemos incluir en el directorioWEB-INFlib de nuestro war) Un ejemplo de configuracioacuten podriacutea ser eacuteste

Configuracioacuten del fichero webxml (directorio de fuentes webappWEB-INFwebxml)

ltxml version=10gtltweb-appgt ltservletgt

Servicios Rest

64

ltservlet-namegtJAXRSltservlet-namegt ltservlet-classgt orgjbossresteasypluginsserverservletHttpServletDispatcher ltservlet-classgt ltinit-paramgt ltparam-namegt javaxwsrsApplication ltparam-namegt ltparam-valuegt orgexpertoJavaComercioApplication ltparam-valuegt ltinit-paramgt ltservletgt

ltservlet-mappinggt ltservlet-namegtJAXRSltservlet-namegt lturl-patterngtrestlturl-patterngt ltservlet-mappinggtltweb-appgt

En la configuracioacuten anterior estamos indicando de forma expliacutecita el servlet JAX-RS que recibelas peticiones REST que a su vez utilizaraacute la clase Application para detectar queacute servicios yproveedores REST seraacuten desplegados en el servidor

Tambieacuten seraacute necesario incluir la libreriacutea con la implementacioacuten JAX-RS 20 de formaexpliacutecita en el war generado (recordemos que para ello tendremos que utilizar la etiquetaltscopegtcompileltscopegt para que se antildeadan los jar correspondientes)

Libreriacutea con la implementacioacuten de JAX-RS 20

ltdependencygt ltgroupIdgtjavaxltgroupIdgt ltartifactIdgtjavaee-web-apiltartifactIdgt ltversiongt70ltversiongt ltscopegtcompileltscopegtltdependencygt

Servicios Rest

65

26 Ejercicios

Para esta sesioacuten antildeadiremos un nuevo moacutedulo en el que implementaremos un servicio restincorporando los conceptos que hemos explicado durante la sesioacuten En concreto

bull Creamos un moacutedulo Maven con IntelliJ (desde el directorio ejercicios-rest-expertojava ) con el arquetipo webapp-javaee7 tal y como hemos visto en losapuntes de la sesioacuten Las coordenadas del artefacto Maven seraacuten

GroupId orgexpertojava

ArtifactId s2-foro-nuevo

version 10-SNAPSHOT

bull Configuramos el pommxl del proyecto para poder compilar empaquetar y desplegarnuestro servicio en el servidor de aplicaciones Wildfly Consulta los apuntes para ver cuaacuteldebe ser el contenido de las etiquetas ltpropertiesgt ltdependenciesgt y ltbuildgt

bull Vamos a estructurar los fuentes (directorio srcmainjava) de nuestro proyecto en lossiguientes paquetes

orgexpertojavadatos contendraacute clases relacionadas con los datos a los que accedenuestra aplicacioacuten rest Por simplicidad almacenaremos en memoria los datos denuestra aplicacioacuten

orgexpertojavamodelo contiene las clases de nuestro modelo de objetos que seraacutenclases java con atributos y sus correspondientes getters y setters

orgexpertojavarest contiene los recursos JAX-RS que implementan nuestrosservicios rest asiacute como las clases necesarias para automatizar el despliegue de dichosrecursos

Creacioacuten de un recurso creacioacuten y consulta de temas en el foro (05 puntos)

Vamos a crear un recurso JAX-RS al que denominaremos TemasResource (en el paqueteorgexpertojavarest ) En el siguiente ejercicio al configurar la aplicacioacuten haremos que esterecurso sea un singleton Nuestro recurso gestionaraacute sus propios datos en memoria Porejemplo podemos utilizar un atributo private de tipo HashMap en el que almacenaremos lostemas cada uno con un identificador numeacuterico como clave Tambieacuten necesitaremos un atributopara generar las claves para cada uno de los temas Por ejemplo

private MapltInteger Temagt temasDB = new HashMapltInteger Temagt()private int contadorTemas = 0

Fiacutejate que si utilizamos los tipos HashMap e int podemos tener problemasde concurrencia si muacuteltiples usuarios estaacuten realizando peticiones paracrear yo consultar los temas del foro En una situacioacuten real deberiacuteamosutilizar en su lugar los tipos ConcurrentHasMap y AtomicInteger paraevitar el que dos usuarios intentaran crear un nuevo tema con la mismaclave perdieacutendose asiacute uno de los dos temas creados Al tratarse de unejercicio en el que solamente tendremos un cliente no nos plantearaacuteninguacuten problema el trabajar con HashMap e int por lo que podeacuteis elegircualquiera de las dos opciones para realizar el ejercicio

Servicios Rest

66

bull Nuestro recurso estaraacute accesible en el servidor en la ruta temas (relativa a la raiacutez delcontexto de nuestra aplicacioacuten y a la ruta de nuestro servlet JAX-RS que determinaremoscon la anotacioacuten ApplicationPath de nuestra clase Application)

bull En el paquete orgexpertojavamodelo crearemos la clase Tema con los atributosprivados

int idString nombre

y sus correspondientes getters y setters

setId() getId()setNombre() getNombre()

bull Implementamos un primer meacutetodo en el recurso TemasResource denominadocreaTema() para poder crear un nuevo tema en el foro Dicho meacutetodo atenderaacutepeticiones POST a nuestro servicio Los datos de entrada (cadena de caracteres querespresenta el nombre del tema) se pasan a traveacutes de un formulario html en el que tenemosuna uacutenica entrada denominada nombre

Puedes incluir el siguiente contenido en el fichero indexhtml para introducir los datosdesde el navegador

ltDOCTYPE htmlgtlthtmlgtltheadgt lttitlegtStart Pagelttitlegt ltmeta http-equiv=Content-Type content=texthtml charset=UTF-8gtltheadgtltbodygtlth1gtAlta de temas en el foro lth1gtltform action=s2-foro-nuevoresttemas method=postgt Nombre del tema ltinput type=text name=nombre gtltbr gt

ltinput type=submit value=Enviar gtltformgtltbodygtlthtmlgt

Cada nuevo Tema creado se antildeadiraacute a nuestra base de datos en memoria temasDB juntocon un identificador numeacuterico (que se iraacute incrementando para cada nueva instancia creada)

bull Implementamos un segundo meacutetodo para consultar los temas creados en el foro Elmeacutetodo se denominaraacute verTemasTodos() y devuelve (en formato texto) todos los temasactualmente creados Dado que puede haber un gran nuacutemero de ellos vamos a permitirque el usuario decida cuaacutentos elementos como maacuteximo quiere consultar a partir de unaposicioacuten determinada Por defecto si no se indica esta informacioacuten se mostraraacuten comomaacuteximo los primeros 8 temas registrados en el foro Si el identificador a partir del cualqueremos iniciar la consulta es mayor que el nuacutemero de temas almacenados entoncesdevolveremos la cadena No es posible atender la consulta Ejemplos de URIs que aceptadicho meacutetodo son

Servicios Rest

67

temas

en este caso y suponiendo que hayamos creado solamente los tres temas del apartadoanterior el resultado seriacutea

Listado de temas del 1 al 81 animales2 plantas3 ab

temasinicio=2amptotal=2

el resultado seriacutea

Listado de temas del 2 al 32 plantas3 ab

temasinicio=7amptotal=1

el resultado seriacutea

No es posible atender la consulta

Como ya hemos comentado las URIs indicadas en este ejercicio sonrelativas a la raiacutez del contexto de nuestra aplicacioacuten y a la ruta especificadapara nuestros servicios rest Recuerda que si has configurado el pomxmlcomo en la sesioacuten anterior la raiacutez del contexto de la aplicacioacuten vendraacutedada por el valor de la etiqueta ltfinalNamegt anidada en ltbuildgt Ennuestro caso deberiacutea ser s2-foro-nuevo Maacutes adelante fijaremos la rutade nuestros servicios rest como rest Por ejemplo la URI completa parael uacuteltimo apartado seriacutea httplocalhost8080s2-foro-nuevoresttemasinicio=7amptotal=1

Despliegue y pruebas del recurso (05 puntos)

Vamos a construir y desplegar nuestro servicio en el servidor de aplicaciones Para ello vamosa utilizar una subclase de Application que antildeadiremos en el paquete orgexpertojavarestLa ruta en la que se van a servir nuestras peticiones rest seraacute rest Fiacutejate que el recursoque hemos creado es el encargado de gestionar (crear modificarhellip) sus propios datosPor lo tanto necesitamos que nuestro recurso REST sea un singleton Implementa la claseForoApplication y realiza la construccioacuten y despliegue del proyecto A continuacioacutenprueba el servicio utilizando postman Puedes probar la insercioacuten de temas utilizando tambieacutenel formulario a traveacutes de la URI httplocalhost8080s2-foro-nuevo Podemos utilizar lasentradas del apartado anterior de forma que comprobemos que se crean correctamentelos temas animales plantas y ab y que obtenemos los listados correctos tanto si noindicamos el inicio y total de elementos como si decidimos mostrar los temas desde el 2 hastael 3

Servicios Rest

68

Cuando utilices el cliente IntelliJ para probar meacutetodos POST debesproporcionar un Request Body no vaciacuteo En este caso como en lapropia URI incluimos el contenido del mensaje que es el nombre del temaque queremos antildeadir al foro tendraacutes que seleccionar Text aunque norellenemos el campo correspondiente De no hacerlo asiacute obtendremoscomo respuesta un cuerpo de mensaje vaciacuteo y la cabecera de respuestaHTTP11 415 Unsupported Media Type

Muacuteltiples consultas de los temas del foro (05 puntos)

Implementa tres nuevas consultas de los temas del foro de forma que

bull Se pueda realizar una consulta de un tema concreto a partir de su identificador numeacuterico(el meacutetodo solamente debe admitir identificadores formados por uno o maacutes diacutegitos) Si eltema consultado no existe se debe devolver una excepcioacuten con la cabecera de respuestaHTTP11 404 Not Found Por ejemplo

temas2

Debe devolver lo siguiente

Ver el tema 2plantas

temas4

Obtenemos como respuesta un cuerpo de mensaje vaciacuteo y la cabecera de respuestaHTTP11 404 Not Found

bull Se pueda realizar una consulta de los temas que comiencen por uno de los siguientescaracteres a b c oacute d Por ejemplo teniendo en cuenta que hemos introducido los temasanteriores

temasa

Debe devolver lo siguiente

Listado de temas que comienzan por aanimales

temasd

Debe devolver Listado de temas que comienzan por d

bull Se pueda realizar una consulta de los temas que contengan una subcadena de caracteresPor ejemplo teniendo en cuenta que hemos introducido los temas anteriores

temasma + Debe devolver lo siguiente

Listado de temas que contienen la subcadena maanimales

Servicios Rest

69

Creacioacuten de subrecursos (05 puntos)

Vamos a crear el subrecurso MensajesResource (en el paquete orgexpertojavarest)de forma que este recurso gestione la creacioacuten y consulta de mensajes para cada unode los temas del foro Este subrecurso debe atender peticiones desde rutas del tipotemasidentificadorTemamensajes siendo identificadorTema la clave numeacutericaasociada a uno de los temas almacenados

bull En este caso nuestro subrecurso no seraacute un singleton por lo que necesitaremos almacenarlos mensajes en otra clase diferente (ya que crearemos una nueva instancia del recursopara cada peticioacuten) La clase DatosEnMemoria (en el paquete orgexpertojavadatos)seraacute la encargada de almacenar en memoria la informacioacuten de los mensajes publicadospara cada tema Por ejemplo puedes utilizar los siguientes campos estaacuteticos paragestionar los mensajes

public static MapltMensaje Stringgt mensajesDB = new HashMapltMensaje Stringgt()

La clave seraacute el propio mensaje (objeto Mensaje que se asociaraacute al tema correspondiente)

public static int contadorMen = 0

Como ya hemos comentado puedes usar ConcurrentHashMap yAtomicInteger en lugar de los tipos anteriores para evitar problemas deconcurrencia

bull En el paquete orgexpertojavadatos crearemos la clase Mensaje con los atributosprivados

int idString textoString autor=anonimo

y sus correspondientes getters y setters

setId() getId()setTexto() getTexto()setAutor() getAutor()

bull Vamos a crear un meacutetodo para poder realizar la publicacioacuten de un mensaje de texto en elforo en uno de los temas ya creados Independientemente del tipo de peticioacuten realizadasobre los mensajes si el tema indicado en la URI no existe lanzaremos la excepcioacutenWebApplicationException(ResponseStatusNOT_FOUND) Veamos alguacuten ejemplo

Deberemos poder realizar una peticioacuten POST a temas1mensajes con el cuerpo demensaje = Mensaje numero 1 El mensaje creado por defecto tendraacute asociado el autoranonimo

Servicios Rest

70

Si realizamos una peticioacuten para antildeadir un mensaje a la URI temas9mensajesdeberiacuteamos obtener como cabecera de respuesta HTTP11 404 Not Foundindependientemente del cuerpo del mensaje

bull Vamos a crear un meacutetodo para realizar una consulta de todos los mensajes publicados enun tema concreto Por ejemplo

Una peticioacuten GET a temas1mensajes deberiacutea dar como resultado

Lista de mensajes para el tema animales1 Mensaje anonimo

Si realizamos una peticioacuten GET a la URI temas9mensajes deberiacuteamos obtenercomo cabecera de respuesta HTTP11 404 Not Found independientemente delcuerpo del mensaje

bull Finalmente vamos a antildeadir dos nuevos meacutetodos para (a) antildeadir un nuevo mensajeen un tema concreto indicando el autor del mensaje Como restriccioacuten el nombre delautor deberaacute estar formado solamente por caracteres alfabeacuteticos utilizando mayuacutesculas ominuacutesculas y como miacutenimo tiene que tener un caracter y (b) consultar todos los mensajesque un determinado autor ha publicado en el foro en un tema determinado

Una peticioacuten POST a la URI temas1mensajespepe con el cuerpo de mensaje convalor mensaje de pepe deberiacutea crear un nuevo mensaje para el tema con identificador2 y devolver como resultado el nuevo id (yo la URI del nuevo recurso en la cabecerade respuesta Location si seguimos la ortodoxia REST) En caso de que devolvamos laURI del nuevo recurso podemos utilizar la orden

return Responsecreated(uriInfogetAbsolutePathBuilder()

segment(StringvalueOf(id))

build())

build()

Obtenemos el path absoluto de la uri que nos ha invocadoAntildeadimos el identificador id del nuevo recurso creadoConstruimos la nueva URIConstruimos el objeto Response

Veremos coacutemo manipular objetos de tipo Response en sesiones posteriores

Recuerda que para acceder al cuerpo de la peticioacuten basta con definir unparaacutemetro de tipo String JAX-RS automaacuteticamente lo instanciaraacute con elcuerpo de la peticioacuten como una cadena

bull Una peticioacuten GET a la URI temas1mensajesanonimo dariacutea como resultado

Lista de mensajes tema= animales y autor= anonimo

1 Mensaje anonimo

bull Una peticioacuten GET a la URI temas1mensajes dariacutea como resultado

Lista de mensajes para el tema animales

Servicios Rest

71

1 Mensaje anonimo2 mensaje de pepe

bull Una peticioacuten GET a la URI temas1mensajesroberto dariacutea como resultado

Lista de mensajes tema= animales y autor= roberto

Servicios Rest

72

3 Manejadores de contenidos Respuestas del servidor ymanejo de excepciones

En la sesioacuten anterior hemos hablado de coacutemo inyectar informacioacuten contenida en las cabecerasde las peticiones HTTP ahora nos detendremos en el cuerpo del mensaje tanto de la peticioacutencomo de la respuesta En el caso de las peticiones explicaremos el proceso de transformarlos datos de entrada en objetos Java para poder ser procesados por nuestros serviciosCon respecto a las respuestas proporcionadas por nuestros servicios analizaremos tanto loscoacutedigos de respuesta por defecto como la elaboracioacuten de respuestas complejas y manejo deexcepciones

31 Proveedores de entidades

JAX-RS define lo que se denominan proveedores de entidades que son clases queproporcionan servicios de mapeado entre las representaciones del cuerpo del mensaje HTTPy los correspondientes tipos java que utilizaremos en nuestros recursos (paraacutemetros en losmeacutetodos o bien como tipo de la respuesta de los mismos) Las entidades tambieacuten se conocencon el nombre de message payload o simplemente como payload y representan elcontenido del cuerpo del mensaje HTTP

ProvidersEl runtime de JAX-RS puede extenderse (ampliarse) utilizandoclases proveedoras (providers) suministradas por nuestra aplicacioacutenConcretamente JAX-RS nos proporciona un conjunto de interfaces quepodemos implementar en nuestra aplicacioacuten creando asiacute dichas clasesproveedoras de entidades (entity providers) La especificacioacuten de JAX-RS define un proveedor como una clase que implementa una o maacutesinterfaces JAX-RS (de entre un conjunto determinado) y que puedenanotarse con provider para ser descubiertas de forma automaacuteticapor el runtime de JAX-RS

Nuestra aplicacioacuten puede proporcionar su propio mapeado entre representaciones(tipos MIME) del mensaje de entrada y tipos Java implementando las interfacesMessageBodyWriter y MessageBodyReader convirtieacutendose asiacute en clases proveedorasde entidades (entity providers) Por ejemplo podemos tener nuestro propio proveedor deentidades para el formato XML o JSON de forma que utilizando las libreriacuteas de java paraprocesamiento XML o JSON (Java API for XML Processing JAXP1 y Java API for JSONProcessing JSON-P2) implementemos el serializadodeserializado del cuerpo del mensajeHTTP de entrada cuando eacuteste presente los tipos MIME applicationxml o application_jsonLas clases que realizan dichos mapeados son clases entity provider

Interfaz javaxwsrsextMessageBodyReader

La interfaz MessageBodyReader define el contrato entre el runtime de JAX-RS y loscomponentes que proporcionan servicios de mapeado desde diferentes representaciones(indicadas como tipos mime) al tipo Java correspondiente Cualquier clase que quieraproporcionar dicho servicio debe implementar la interfaz MessageBodyReader y debeanotarse con Provider para poder ser detectada de forma automaacutetica por el runtime deJAX-RS

1 httpswwwjcporgenjsrdetailsummaryid=2062 httpsjcporgenjsrdetailid=353

Servicios Rest

73

La secuencia loacutegica de pasos seguidos por una implementacioacuten de JAX-RS cuando se mapeael cuerpo de un mensaje HTTP de entrada a un paraacutemetro de un meacutetodo Java es la siguiente

1 Se obtiene el media type de la peticioacuten (valor de la cabecera HTTP Content-Type ) Sila peticioacuten no contiene una cabecera Content-Type se usaraacute applicationoctet-stream

2 Se identifica el tipo java del paraacutemetro cuyo valor seraacute mapeado desde el cuerpo delmensaje

3 Se localiza la clase MessageBodyReader que soporta el media type de la peticioacuten y seusa su meacutetodo readFrom() para mapear el contenido del cuerpo del mensaje HTTP enel tipo Java que corresponda

4 Si no es posible encontrar el MessageBodyReader adecuado se genera la excepcioacutenNotSupportedException con el coacutedigo 405

Interfaz javaxwsrsextMessageBodyWriter

La interfaz MessageBodyWriter define el contrato entre el runtime de JAX-RS ylos componentes que proporcionan servicios de mapeado desde un tipo Java a unarepresentacioacuten determinada Cualquier clase que quiera proporcionar dicho servicio debeimplementar la interfaz MessageBodyWriter y debe anotarse con Provider para poderser detectada de forma automaacutetica por el runtime de JAX-RS

La secuencia loacutegica de pasos seguidos por una implementacioacuten de JAX-RS cuando se mapeaun valor de retorno de un meacutetodo del recurso a una entidad del cuerpo de un mensaje HTTPes la siguiente

1 Se obtiene el objeto que seraacute mapeado a la entidad del cuerpo del mensaje

2 Se determina el media type de la respuesta

3 Se localiza la clase MessageBodyWriter que soporta el objeto que seraacute mapeado a laentidad del cuerpo del mensaje HTTP y se utiliza su meacutetodo writeTo() para realizardicho mapeado

4 Si no es posible encontrar el MessageBodyWriter adecuado se generala excepcioacuten InternalServerErrorException (que es una subclase deWebApplicationException ) con el coacutedigo 500

32 Proveedores de entidad estaacutendar incluidos en JAX-RS

Cualquier implementacioacuten de JAX-RS debe incluir un conjunto de implementaciones deMessageBodyReader y MessageBodyWriter de forma predeterminada para ciertascombinaciones de tipos Java y media types

Table 3 Proveedores de entidades estaacutendar de una implementacioacuten JAX-RS

Tipo Java Media Type

byte[] (Cualquier media type)

javalangString (Cualquier media type)

javaioInputStream (Cualquier media type)

Servicios Rest

74

Tipo Java Media Type

javaioReader (Cualquier media type)

javaioFile (Cualquier media type)

javaxactivationDataSource (Cualquier media type)

javaxxmltransformSource textxml applicationxml application+xml(tipos basados en xml)

javaxxmlbindJAXBElement andapplication-supplied JAXB classes

textxml applicationxml application+xml(tipos basados en xml)

MultivaluedMapltStringStringgt applicationx-www-form-urlencoded(Contenido de formularios)

StreamingOutput (Cualquier media type) (SoacuteloMessageBodyWriter )

javalangBoolean javalangCharacterjavalangNumber

textplain

A continuacioacuten comentaremos algunos de estos proveedores de entidades estaacutendar oconversores por defecto que permiten convertir el cuerpo del mensaje HTTP a objetos Javade diferentes tipos y viceversa

javaxwsrscoreStreamingOutput

StreamingOutput es una interfaz callback que implementamos cuando queremos tratar comoun flujo continuo (streaming) el cuerpo de la respuesta Constituye una alternativa ligera aluso de MessageBodyWriter

public interface StreamingOutput void write(OutputStream output) throws IOException WebApplicationException

Implementamos una instancia de esta interfaz y la utilizamos como tipo de retorno denuestros meacutetodos de recursos Cuando el runtime de JAX-RS estaacute listo para escribir elcuerpo de respuesta del mensaje se invoca al meacutetodo write() de la instancia deStreamingOutput Veamos un ejemplo

Path(miservicio) public class MiServicio GET Produces(textplain) StreamingOutput get() return new StreamingOutput() public void write(OutputStream output) throws IOException WebApplicationException outputwrite(hello worldgetBytes())

Hemos utilizado una clase interna anoacutenima que implementa la interfaz StreamingOutputen lugar de crear una clase puacuteblica separada La razoacuten de utilizar una clase interna es porque

Servicios Rest

75

en este caso al contener tan pocas liacuteneas de coacutedigo resulta beneficioso mantener dicha loacutegicadentro del meacutetodo del recurso JAX-RS de forma que el coacutedigo sea maacutes faacutecil de seguirNormalmente no tendremos necesidad de reutilizar la loacutegica implementada en otros meacutetodospor lo que no tiene demasiado sentido crear otra clase especiacutefica

iquestY por queacute no inyectamos un OutputStream directamente iquestPor queacute necesitamos un objetocallback La razoacuten es que asiacute dejamos que el runtime de JAX-RS maneje la salida de lamanera que quiera Por ejemplo por razones de rendimiento puede ser conveniente que JAX-RS utilice un thread para responder diferente del thread de peticioacuten

javaioInputStream javaioReader

Para leer el cuerpo de un mensaje de entrada podemos utilizar las clases InputStream oReader Por ejemplo

Path()public class MiServicio PUT Path(dato) public void modificaDato(InputStream is) byte[] bytes = readFromStream(is) String input = new String(bytes) Systemoutprintln(input)

private byte[] readFromStream(InputStream stream) throws IOException ByteArrayOutputStream baos = new ByteArrayOutputStream() byte[] buffer = new byte[1000] int wasRead = 0 do wasRead = streamread(buffer) if (wasRead gt 0) baoswrite(buffer 0 wasRead) while (wasRead gt -1) return baostoByteArray()

En este caso estamos leyendo bytes a partir de un javaioInputStream para convertirloen una cadena de caracteres que mostramos por pantalla

En el siguiente ejemplo creamos un javaioLineNumberReader a partir de un objetoReader e imprimimos cada liacutenea del cuerpo del mensaje de entrada

PUTPath(maslineas)public void putMasLineas(Reader reader) LineNumberReader lineReader = new LineNumberReader(reader) do String line = lineReaderreadLine() if (line = null) Systemoutprintln(line)

Servicios Rest

76

while (line = null)

No estamos limitados solamente a utilizar instancias de InputStream yo Reader paraleer el cuerpo de los mensajes de entrada Tambieacuten podemos devolver dichos objetos comorespuesta Por ejemplo

Path(fichero)public class FicheroServicio private static final String basePath =

GET Path(rutafichero ) Produces(textplain) public InputStream getFichero(PathParam(rutafichero) String path) FileInputStream is = new FileInputStream(basePath + path) return is

Aquiacute estamos inyectando un valor PathParam para crear una referencia a un fichero realde nuestro disco duro Creamos una instancia de javaioFileInputStream a partir delvalor de la ruta inyectada como paraacutemetro y la devolvemos como cuerpo de nuestro mensajede respuesta La implementacioacuten de JAX-RS leeraacute la respuesta de este stream de entrada y laalmacenaraacute en un buffer para posteriormente escribirla de forma incremental en el stream desalida de la respuesta En este caso debemos especificar la anotacioacuten Produces para quela implementacioacuten de JAX-RS conozca el valor que debe asignar a la cabecera Content-Type en la respuesta

javaioFile

Se pueden utilizar instancias de la clase javaioFile para entrada y salida decualquier MIME-TYPE (especificado en Content-Type yo Accept y en las anotacionesProduces yo Consumes ) El siguiente coacutedigo por ejemplo devuelve una referencia aun fichero en nuestro disco

Path(fichero)public class FicheroServicio private static final String baseRuta =

GET Path(rutafichero ) Produces(textplain) public File getFichero(PathParam(rutafichero) String ruta) return new File(baseRuta + ruta)

En este caso inyectamos el valor de la ruta del fichero con la anotacioacuten PathParam A partirde dicha ruta creamos un objeto javaioFile y lo devolvemos como cuerpo del mensaje

Servicios Rest

77

de respuesta La implementacioacuten JAX-RS leeraacute la informacioacuten abriendo un InputStreambasado en esta referencia al fichero y la escribiraacute en un buffer Posteriormente y de formaincremental volveraacute a escribir el contenido del buffer en el stream de salida de la respuesta Aligual que en el ejemplo anterior debemos especificar la anotacioacuten Produces para que JAX-RS sepa coacutemo rellenar la cabecera Content-Type de la respuesta

Tambieacuten podemos inyectar instancias de javaioFile a partir del cuerpo del mensaje dela peticioacuten Por ejemplo

POSTPath(masdatos)public void post(File fichero) Reader reader = new Reader(new FileInputStream(fichero)) LineNumberReader lineReader = new LineNumberReader(reader) do String line = lineReaderreadLine() if (line = null) Systemoutprintln(line) while (line = null)

En este caso la implementacioacuten de JAX-RS crea un fichero temporal en el disco para laentrada Lee la informacioacuten desde el buffer de la red y guarda los bytes leiacutedos en este ficherotemporal En el ejemplo los datos leiacutedos desde la red estaacuten representados por el el objetoFile inyectado por el runtime de JAX-RS (recuerda que soacutelo puede haber un paraacutemetro sinanotaciones en los meacutetodos del recurso y que eacuteste representa el cuerpo del mensaje de lapeticioacuten HTTP) A continuacioacuten el meacutetodo post() crea un javaioFileInputStreama partir del objeto File inyectado Finalmente utilizamos eacuteste stream de entrada para crearun objeto LineNumberReader y mostrar los datos por la consola

byte[]

Podemos utilizar un array de bytes como entrada y salida para cualquier tipo especificadocomo media-type A continuacioacuten mostramos un ejemplo

Path()public class MiServicio GET Produces(textplain) public byte[] get() return hello worldgetBytes()

POST Consumes(textplain) public void post(byte[] bytes) Systemoutprintln(new String(bytes))

Para cualquier meacutetodo de recurso JAX-RS que devuelva un array de bytes debemosespecificar la anotacioacuten Produces para que JAX-RS sepa queacute valor asignar a la cabeceraContent-Type

Servicios Rest

78

String char[]

La mayor parte de formatos en internet estaacuten basados en texto JAX-RS puede convertircualquier formato basado en texto a un String o a cualquier array de caracteres Porejemplo

Path()public class MiServicio GET Produces(applicationxml) public String get() return ltcustomergtltnamegtSergio Garcialtnamegtltcustomergt

POST Consumes(textplain) public void post(String str) Systemoutprintln(str)

Para cualquier meacutetodo de recurso JAX-RS que devuelva un Sring o un array de caracteresdebemos especificar la anotacioacuten Produces para que JAX-RS sepa que valor asignar a lacabecera Content-Type

MultivaluedMapltString Stringgt y formularios de entrada

Los formularios HTML son usados habitualmente para enviar datos a servidores web Losdatos del formulario estaacuten codificados con el media type applicationx-www-form-urlencoded Ya hemos visto como utilizar la anotacioacuten FormParam para inyectarparaacutemetros individuales de un formulario de las peticiones de entrada Tambieacuten podremosinyectar una instancia de MultivaluedMapltString Stringgt que representa todos losdatos del formulario enviado en la peticioacuten Por ejemplo

Path() public class MiServicio POST Consumes(applicationx-www-form-urlencoded) Produces(applicationx-www-form-urlencoded) public MultivaluedMapltStringStringgt post( MultivaluedMapltString Stringgt form) el formulario tiene los campos fieldName1 y fieldName2 Systemoutprintln(formgetFirst(fieldName1)) Systemoutprintln(formgetFirst(fieldName2)) return form

En este coacutedigo nuestro meacutetodo post() acepta peticiones POST y recibe unMultivaluedMapltStringStringgt que contiene todos los datos de nuestro formularioEn este caso tambieacuten devolvemos una instancia de un formulario como respuesta

Los datos del formulario pueden representarse en el cuerpo de la petcioacuten como paresnombre=valor separados por amp Por ejemplo

Servicios Rest

79

fieldName1=valor20con20espaciosampfielName2=otroValor

Los espacios en blanco se codifican como 20 No es necesario poner comillas

33 Muacuteltiples representaciones de recursos

Por defecto un recurso RESTful se produce o consume con el tipo MIME Un recursoRESTful puede restringir los media types que soporta tanto en la peticioacuten como en larespuesta utilizando las anotaciones Consumes y Produces respectivamente Estasanotaciones pueden especificarse como ya hemos visto a nivel de clase o de meacutetodo derecurso Las anotaciones especificadas sobre el meacutetodo prevalecen sobre las de la clase Laausencia de estas anotaciones es equivalente a su inclusioacuten con el tipo MIME () es decirsu ausencia implica que se soporta cualquier tipo

A continuacioacuten mostramos un ejemplo en el que un Pedido puede producirse tanto en formatoxml como en formato json

GETPath(id)Produces(applicationxml applicationjson)public Pedido getPedido(PathParam(id)int id)

El meacutetodo getPedido() puede generar ambas representaciones para el pedido El tipo exactode la respuesta viene determinado por la cabecera HTTP Accept de la peticioacuten

Otro ejemplo en el que pueden consumirse varios tipos MIME puede ser el siguiente

POSTPath(id)Consumes(applicationxml applicationjson)public Pedido addPedido(PathParam(id)int id)

En este caso el formato consumido vendraacute dado por el valor de la cabecera HTTP Content-Type de la peticioacuten

JAX-RS 20 nos permite indicar la preferencia por un media type en el lado del servidorutilizando el paraacutemetro qs (quality on service) qs toma valores entre 0000 y 1000 eindica la calidad relativa de una representacioacuten comparado con el resto de representacionesdisponibles Una representacioacuten con un valor de qs de 0000 nunca seraacute elegido Unarepresentacioacuten sin valor para el paraacutemetro qs se asume que dicho valor es 1000

Ejemplo

POSTPath(id)Consumes(applicationxml qs=075 applicationjson qs=1)public Pedido addPedido(PathParam(id)int id)

Si un cliente realiza una peticioacuten y no manifiesta ninguna preferencia por ningunarepresentacioacuten en particular o con una cabecera Accept con valor application entonces el

Servicios Rest

80

servidor seleccionaraacute la representacioacuten con el valor de qs maacutes alto (en este caso applicationjson) Los valores de qs son relativos y como tales solamente son comparables con otrosvalores qs dentro de la misma instancia de la anotacioacuten Consumes (o Produces)

Los clientes pueden indicar tambieacuten sus preferencias utilizando otro factor relativo de calidaden forma de paraacutemetro denominado q El valor del paraacutemetro q se utiliza para ordenar elconjunto de tipos aceptados q toma valores entre 0000 y 1000 (maacutexima preferencia) Al igualque antes Los valores de q son relativos y como tales solamente son comparables con otrosvalores q dentro de la misma cabecera Accept o Content-type

Las preferencias del servidor (valores de los paraacutemetros qs) soacutelo se tienenen cuenta si el cliente acepta muacuteltiples media types con el mismo valorde q

Veamos un ejemplo

GETPath(id)Produces(applicationxml qs=1 applicationjson qs=075)public Pedido getPedido(PathParam(id)int id)

Supongamos que un cliente lanza una petcioacuten GET con una valor para la cabecera Acceptde application q=05 texthtml En este caso el servidor determina que los tipos MIMEapplicationxml y applicationjson tienen la misma preferencia por parte del cliente (con valorde 05) por lo tanto el servidor elegiraacute la representacioacuten applicationjson ya que tiene un valorde qs mayor

34 Introduccioacuten a JAXB

JAXB (Java Architecture for XML Binding) es una especificacioacuten Java antigua (JSR 2223) yno estaacute definida por JAX-RS JAXB es un framework de anotaciones que mapea clases Java aXML y esquemas XML Es extremadamente uacutetil debido a que en lugar de interactuar con unarepresentacioacuten abstracta de un documento XML podemos trabajar con objetos Java realesque estaacuten maacutes cercanos al dominio que estamos modelando JAX-RS proporciona soportepara JAXB pero antes de revisar los manejadores de contenidos JAXB incuidos con JAX-RSveamos una pequentildea introduccioacuten al framework JAXB

Como ya hemos dicho si queremos mapear una clase Java existente a XML podemos utilizarJAXB a traveacutes de un conjunto de anotaciones Veaacutemoslo mejor con un ejemplo

XmlRootElement(name=cliente)XmlAccessorType(XmlAccessTypeFIELD)public class Cliente XmlAttribute protected int id

XmlElement protected String nombre

public Customer()

3 httpsjcporgaboutJavacommunityprocessmreljsr222index2html

Servicios Rest

81

public int getId() return thisid public void setId(int id) thisid = id

public String getNombre() return thisnombre public void setNombre(String nombre thisnombre = nombre

La anotacioacuten javaxxmlbindannotationXmlRootElement se utiliza en clasesjava para denotar que representan elementos XML (etiqueta XML raiacutez) En este caso estamosdiciendo que la clase Java representa un documento XML que tiene como etiqueta raiacutezltclientegt Las clases java anotadas con XmlRootElement se denomina beansJAXB

La anotacioacuten javaxxmlbindannotationXmlAttribute la hemos asociado alcampo id de nuestra clase Cliente Esta anotacioacuten indica que el campo id de la clasedebe mapearse como el atributo id del elemento raiacutez ltclientegt del documento XML Laanotacioacuten XmlAttribute tiene un atributo name de forma que podemos especificar elnombre exacto del atributo XML dentro del documento Por defecto tiene el mismo nombreque el campo anotado

Hemos utilizado la anotacioacuten javaxxmlbindannotationXmlElement en el camponombre de la clase Cliente Esta anotacioacuten indica a JAXB que debe mapearse el camponombre como el elemento ltnombregt anidado en la etiqueta raiacutez ltclientegt Igual queantes podemos especificar el nombre concreto del elememto XML Por defecto toma el mismonombre que el correspondiente campo anotado

La anotacioacuten javaxxmlbindannotationXmlAccessorType permite controlar laserializacioacuten por defecto de los atributos de la clase Esta anotacioacuten soacutelo puede ser usadaconjuntamente con XmlRootElement (y alguna otra anotacioacuten que no mostramos aquiacute)Hemos usado como valor XmlAccessTypeFIELD lo que significa que por defecto se debenserializar todos los campos (fields) de la clase (esteacuten anotados o no) y las propiedades(properties) de la clase que tengan anotaciones JAXB (a menos que la anotacioacuten seaXMLTransient)

Si alguno de los campos de la clase no tiene anotaciones JAXB asociadas por defecto seserializaraacuten como elementos (etiquetas) en el documento XML correspondiente Seguacuten ladocumentacioacuten4 de JAXB un campo es una variable de instancia no estaacutetica (normalmenteprivada)

Las propiedades de la clase vienen dadas por las combinaciones gettersetter de los atributosde la clase El coacutedigo anterior tiene dos propiedades nombre (dado por el par getNombresetNombre_) e id (par getIdsetId) Normalmente se anotan los meacutetodos getter Dichaspropiedades no estaacuten anotadas por lo que JAXB no las serializaraacute

Al proceso de serializar (convertir) un objeto Java en un documento XMLse le denomina marshalling El proceso inverso la conversioacuten de XML aobjetos Java se denomina unmarshalling

Con las anotaciones anteriores un ejemplo de una instancia de nuestra clase Cliente conun id de 42 y el valor de nombre Pablo Martinez tendriacutea el siguiente aspecto

ltcliente id=42gt

4 httpsjaxbjavanetnonav226docsapi

Servicios Rest

82

ltnombreCompletogtPablo MartinezltnombreCompletogtltclientegt

Observamos que se han serializado los campos (variables de instancia de la clase)

Si no especificamos la anotacioacuten XmlAccessorType por defecto se utilizaraacute

XmlAccessorType(XmlAccessTypePUBLIC_MEMBER)

El valor XmlAccessTypePUBLIC_MEMBER indica a JAXB que se deben serializar todoslos campos puacuteblicos de la clase todos los campos anotados y todas las propiedades (paresgettersetter) a menos que esteacuten anotadas con XMLTransient

Tambieacuten podriacuteamos utilizar

XmlAccessorType(XmlAccessTypeNONE)

En este caso la anotacioacuten XmlAccessTypeNONE indica soacutelo se deben serializar aquellaspropiedades yo campos de la clase que esteacuten anotados

A continuacioacuten indicamos en forma de tabla el uso de los diferentes XmlAccessType

Table 4 Valores utilizados para XmlAccessorType() conjuntamentecon XmlRootElement()

Valor Significado

XmlAccessTypePUBLIC_MEMBER Serializacioacuten por defecto si no se especificaXmlAccessorType() Serializa laspropiedades (pares gettersetter) y campospuacuteblicos a menos que esteacuten anotados conXMLTransient Si alguacuten campo nopuacuteblico estaacute anotado tambieacuten se serializa

XmlAccessTypeFIELD Serializa todos los campos (puacuteblicos oprivados) a menos que esteacuten anotados conXMLTransient

XmlAccessTypeNONE Solamente serializa aquellos camposy propiedades que esteacuten anotadas conanotaciones JAXB

XmlAccessTypePROPERTY Serializa cada par gettersetter a menosque esteacuten anotados con XMLTransient Si alguacuten campo (no puacuteblico) estaacute anotadotambieacuten se serializa

Podemos utilizar la anotacioacuten XmlElement para anidar otras clases anotadas con JAXBPor ejemplo supongamos que queremos antildeadir una clase Direccion a nuestra claseCliente

XmlRootElement(name=direccion)

Servicios Rest

83

XmlAccessorType(XmlAccessTypeFIELD)public class Direccion XmlElement protected String calle

XmlElement protected String cludad

XmlElement protected String codPostal

getters y setters

Simplemente tendriacuteamos que antildeadir el campo de tipo Direccion a nuestra clase Clientede la siguiente forma

XmlRootElement(name=cliente)XmlAccessorType(XmlAccessTypeFIELD)public class Cliente XmlAttribute protected int id

XmlElement protected String nombreCompleto

XmlElement protected Direccion direccion

public Customer()

getters y setters

En este caso una instancia de un Cliente con valores id =56 nombre =RicardoLopez calle =calle del oso 35 ciudad =Alicante y coacutedigo_postal =01010 seriacuteaserializado como

ltcliente id=56gt ltnombregtRicardo Lopezltnombregt ltdirecciongt ltcallegtcalle del oso 35ltcallegt ltciudadgtAlicanteltciudadgt ltcodPostalgt01010ltcodPostalgt ltdirecciongt ltclientegt

Veamos otro ejemplo Supongamos que tenemos el recurso EstadoResource con meacutetodosque responden a peticiones http GET y PUT

Path(estado)

Servicios Rest

84

public class EstadoResource

private static EstadoBean estadoBean = new EstadoBean()

GET Produces(applicationxml) public EstadoBean getEstado() return estadoBean

PUT Consumes(applicationxml) public void setEstado(EstadoBean estado) thisestadoBean = estado

En este caso la clase EstadoBean debe utilizar anotaciones JAXB para poder ser convertidaautomaacuteticamente por el runtime de JAX-RS en un documento XML y viceversa

XmlRootElement(name = estadoImpresora)public class EstadoBean

public String estado = Idle public int tonerRestante = 25 public ListltTareaBeangt tareas = new ArrayListltTareaBeangt()

Por defecto la anotacioacuten XmlRootElement realiza la serializacioacuten de la claseEstadoBean en formato xml utilizando los campos puacuteblicos y propiedades definidas en laclase ( estado tonerRestante y tareas ) Vemos que el campo tareas a su vez esuna coleccioacuten de elementos de tipo TareaBean que tambieacuten necesitan ser serializados Acontinuacioacuten mostramos la implementacioacuten de la clase TareaBeanjava

XmlRootElement(name = tarea)public class TareaBean public String nombre public String estado public int paginas

public TareaBean()

public TareaBean(String nombre String estado int paginas) thisnombre = nombre thisestado = estado thispaginas = paginas

Para serializar la clase es necesario que la clase tenga un constructor sin paraacutemetros

Servicios Rest

85

Si accedemos al servicio anterior nos devolveraacute la informacioacuten sobre el estado de la siguienteforma (resultado devuelto por el meacutetodo getEstado() anotado con GET)

ltxml version=10 encoding=UTF-8 standalone=yesgtltestadoImpresoragt ltestadogtIdleltestadogt lttonerRestantegt25lttonerRestantegt lttareasgt ltnombregttextodocltnombregt ltestadogtimprimiendoltestadogt ltpaginasgt13ltpaginasgt lttareasgt lttareasgt ltnombregttexto2docltnombregt ltestadogten esperaltestadogt ltpaginasgt5ltpaginasgt lttareasgtltestadoImpresoragt

Podemos observar que se utiliza el elemento xml lttareasgt para representar cada una de lasinstancias de TareaBean las cuales forman parte de de la lista del campo tareas de nuestraclase EstadoBean

Vamos a usar las anotaciones XmlAttribute y XmlElement de la siguiente forma

XmlRootElement(name=estadoImpresora)public class EstadoBean XmlAttribute(name=valor) public String estado = Idle XmlAttribute(name=toner) public int tonerRestante = 25 XmlElement(name=tarea) public ListltTareaBeangt tareas = new ArrayListltTareaBeangt()

En este caso el XML resultante quedariacutea de la siguiente forma

ltestadoImpresora valor=Idle toner=25gt lttareagt ltnombregttextodocltnombregt ltestadogtimprimiendoltestadogt ltpaginasgt13ltpaginasgt lttareagt lttareagt ltnombregttexto2docltnombregt ltestadogten esperaltestadogt ltpaginasgt5ltpaginasgt lttareagtltestadoImpresoragt

Servicios Rest

86

Si no se indica lo contrario por defecto se convierten los campos a elementos del XML

En caso de que los campos no sean puacuteblicos etiquetaremos los getterscorrespondiente (propiedades de la clase)

Hemos visto que para las listas el nombre que especificamos en XmlElement se utilizapara nombrar cada elemento de la lista Si queremos que ademaacutes se incluya un elemento queenvuelva a toda la lista podemos utilizar la etiqueta XmlElementWrapper

XmlRootElement(name=estadoImpresora)public class EstadoBean

XmlAttribute(name=valor) public String estado = Idle

XmlAttribute(name=toner) public int tonerRestante = 25

XmlElementWrapper(name=tareas) XmlElement(name=tarea) public ListltTareaBeangt tareas = new ArrayListltTareaBeangt()

En este caso tendremos un XML como el que se muestra a continuacioacuten

ltestadoImpresora valor=Idle toner=25gt lttareasgt lttareagt ltnombregttextodocltnombregt ltestadogtimprimiendoltestadogt ltpaginasgt13ltpaginasgt lttareagt lttareagt ltnombregttexto2docltnombregt ltestadogten esperaltestadogt ltpaginasgt5ltpaginasgt lttareagt lttareasgtltestadoImpresoragt

Para etiquetar una lista tambieacuten podemos especificar distintos tipos de elemento seguacutenel tipo de objeto contenido en la lista Por ejemplo supongamos que en el ejemploanterior la clase TareaBean fuese una clase abstracta que tiene dos posible subclasesTareaSistemaBean y TareaUsuarioBean Podriacuteamos especificar una etiqueta distintapara cada elemento de la lista seguacuten el tipo de objeto del que se trate con la etiquetaXmlElements de la siguiente forma

XmlElementWrapper(name=tareas) XmlElements( XmlElement(name=usuariotype=TareaUsuarioBeanclass

Servicios Rest

87

XmlElement(name=sistematype=TareaSistemaBeanclass) public ListltTareaBeangt tareas = new ArrayListltTareaBeangt()

De esta forma podriacuteamos tener un XML como el siguiente

ltestadoImpresora valor=Idle toner=25gt lttareasgt ltusuariogt ltnombregttextodocltnombregt ltestadogtimprimiendoltestadogt ltpaginasgt13ltpaginasgt ltusuariogt ltsistemagt ltnombregttexto2docltnombregt ltestadogten esperaltestadogt ltpaginasgt5ltpaginasgt ltsistemagt lttareasgtltestadoImpresoragt

Hemos visto que por defecto se serializan todos los campos Si queremos excluir algunode ellos de la serializacioacuten podemos hacerlo anotaacutendolo con XmlTransient Comoalternativa podemos cambiar el comportamiento por defecto de la serializacioacuten de la claseetiquetaacutendola con XmlAccessorType Por ejemplo

XmlAccessorType(NONE)XmlRootElement(name=estadoImpresora)public class EstadoBean

En este uacuteltimo caso especificando como tipo NONE no se serializaraacute por defectoninguacuten campo soacutelo aquellos que hayamos anotado expliacutecitamente con XmlElement oXmlAttribute Los campos y propiedades (getters) anotados con estas etiquetas seserializaraacuten siempre Ademaacutes de ellos tambieacuten podriacuteamos especificar que se serialicenpor defecto todos los campos puacuteblicos y los getters ( PUBLIC_MEMBER ) todos los getters( PROPERTY ) o todos los campos ya sean puacuteblicos o privados ( FIELD ) Todos los que seserialicen por defecto sin especificar ninguna etiqueta lo haraacuten como elemento

Por uacuteltimo si nos interesa que toda la representacioacuten del objeto venga dada uacutenicamente porel valor de uno de sus campos podemos etiquetar dicho campo con XmlValue

Clase JAXBContext

Para serializar clases Java a y desde XML es necesario interactuar con la clasejavaxxmlbindJAXBContext Esta clase nos permite inspeccionar las clasesJava para comprender la estructura de nuestras clases anotadas Dichas clasesse utilizan como factoriacuteas para las interfaces javaxxmlbindMarshaller yjavaxxmlbindUnmarshaller Las instancias de Marshaller se utilizan para creardocumentos XML a partir de objetos Java Las instancias de Unmarshaller se utilizan para crearobjetos Java a partir de documentos XML A continuacioacuten mostramos un ejemplo de uso de

Servicios Rest

88

JAXB para convertir una instancia de la clase Cliente que hemos definido anteriormentea formato XML para posteriormente volver a crear el objeto de tipo Cliente

Cliente cliente = new Cliente()clientesetId(42)

clientesetNombre(Lucia Arg)

JAXBContext ctx = JAXBContextnewInstance(Clienteclass) StringWriter writer = new StringWriter()

ctxcreateMarshaller()marshal(cliente writer)

String modString = writertoString()

cliente = (Cliente)ctxcreateUnmarshaller()

unmarshal(new StringReader(modString))

Creamos e inicializamos una instancia de tipo ClienteInicializamos JAXBContext para que pueda analizar la clase `ClienteUtilizamos una instancia de Marshaller para escribir el objeto Cliente como unString de Java ( StringWriter es un stream de caracteres que utilizaremos paraconstruir un String )Utilizamos una instancia de Unmarshaller para recrear el objeto Cliente a partir delString que hemos obtenido en el paso anterior

La clase JAXBContext constituye un punto en entrada al API JAXB paralos clientes de nuestros servicios RESTful Proporciona una abstraccioacutenpara gestionar el enlazado (binding) de informacioacuten XMLJava necesariapara implementar las operaciones de marshalling y unmarshalling

bull Unmarshalling La clase Unmarshaller proporciona a la aplicacioacutencliente la capacidad para convertir datos XML en un aacuterbol de objetosJava

bull Marshalling La clase Marshaller proporciona a la aplicacioacutencliente la capacidad para convertir un aacuterbol con contenidos Java denuevo en datos XML

Una vez que hemos proporcionado una visioacuten general sobre coacutemo funciona JAXB vamos aver coacutemo se integra con JAX-RS

Manejadores JAX-RS para JAXB

La especificacioacuten de JAX-RS indica que cualquier implementacioacuten debe soportar deforma automaacutetica el proceso de marshalling y unmarshalling de clases anotadas conXmlRootElement A continuacioacuten mostramos un ejemplo de la implementacioacuten de unservicio que hace uso de dichos manejadores Para ello utilizamos la clase Cliente que hemosanotado previamente con XmlRootElement y que hemos mostrado en apartados anteriores

Path(clientes)public class ClienteResource GET Path(id)

Servicios Rest

89

Produces(applicationxml) public Cliente getCliente(PathParam(id) int id) Cliente cli = findCliente(id) return cust

POST Consumes(applicationxml) public void crearCliente(Cliente cli)

Como podemos ver una vez que aplicamos las anotaciones JAXB a nuestras clases Java ( eneste caso a la clase Cliente ) es muy sencillo intercambiar documentos XML entre el clientey nuestro servicio web Los manejadores JAXB incluidos en la implementacioacuten de JAX-RSgestionaraacuten el marshallingunmarshalling de cualquier clase con anotaciones JAXB para losvalores de Content-Type applicationxml textxml o application+xml Por defecto tambieacuten se encargan de la creacioacuten e inicializacioacuten de instancias JAXBContext Debido a que la creacioacuten de las instancias JAXBContext puede ser cara la implementacioacutende JAX-RS normalmente las guarda despueacutes de la primera inicializacioacuten

JAXB y JSON

JAXB es lo suficientemente flexible como para soportar otros formatos ademaacutes de XMLAunque la especificacioacuten de JAX-RS no lo requiere muchas implementaciones de JAX-RSincluyen adaptadores de JAXB para soportar el formato JSON ademaacutes de XML JSON es unformato basado en texto que puede ser interpretado directamente por Javascript De hechoes el formato de intercambio preferido por las aplicaciones Ajax

JSON es un formato mucho maacutes simple que XML Los objetos estaacuten entre llaves ycontienen pares de clavevalor separados por comas Los valores pueden ser cadenas decaracteres booleanos ( true o false ) valores numeacutericos o arrays de los tipos anteriores

Supongamos que tenemos la siguiente descripcioacuten de un producto en formato XML

ltxml version=10 encoding=UTF-8gtltproductogt ltidgt1ltidgt ltnombregtiPadltnombregt ltdescripciongtDispositivo moacutevilltdescripciongt ltpreciogt500ltpreciogtltproductogt

La representacioacuten JSON equivalente seriacutea

id1 nombreiPad descripcionDispositivo moacutevil precio500

Servicios Rest

90

El formato JSON asociado por ejemplo al siguiente objeto Cliente

ltcliente id=56gt ltnombregtRicardo Lopezltnombregt ltdirecciongt ltcallegtcalle del oso 35ltcallegt ltciudadgtAlicanteltciudadgt ltcodPostalgt01010ltcodPostalgt ltdirecciongt ltclientegt

quedariacutea como sigue

nombre Ricardo Lopez direccion calle calle del oso 35 ciudad Alicante codPostal 01010

Como vemos en el ejemplo el formato JSON consiste baacutesicamente en objetos situados entrellaves los cuales estaacuten formados por pares clavevalor separadas por comas Cada clavey valor estaacute separado por Los valores pueden cadenas de caracteres booleanos (true ofalse) valores numeacutericos o vectores de los tipos anteriores (los vectores estaacuten delimitadospor corchetes)

Podemos antildeadir el formato applicationjson o bienMediaTypeAPPLICATION_JSON a la anotacioacuten Produces en nuestros meacutetodos derecurso para generar respuestas en formato JSON

GETPath(get)Produces(applicationxmlapplicationjson)public Producto getProducto()

En este ejemplo se elegiraacute el formato JSON en la respuesta si el cliente realiza una peticioacutenGET que incluye en la cabecera

Accept applicationjson

El tipo de respuesta es de tipo Producto En este caso Producto debe ser un bean JAXBes decir una clase anotada con XmlRootElement

Los meacutetodos de recurso pueden aceptar tambieacuten datos JSON para clases con anotacionesJAXB

POSTPath(producto)Consumes(applicationxmlapplicationjson)

Servicios Rest

91

public Response crearProducto(Producto prod)

En este caso el cliente deberiacutea incluir la siguiente cabecera cuando realice la peticioacuten POSTque incluya los datos JSON anteriores en el cuerpo del mensaje

Content-Type applicationjson

Hablaremos con maacutes detalle del formato JSON en una sesioacuten posterior

Finalmente y como resumen de lo anterior

JAXB

Para dar soporte al serializado y deserializado XML se utilizan beans JAXBPorejemplo las clases anteriores EstadoBean TareaBean Cliente y Productoson ejemplos de beans JAXB La clase que se serializaraacute como un recurso XMLo JSON se tiene que anotar con XmlRootElement Si en alguacuten elemento deun recurso se retorna un elemento de esa clase y se etiqueta con los tiposProduces(MediaTypeAPPLICATION_XMLMediatypeAPPLICATION_JSON )eacuteste se serializa automaacuteticamente utilizando el tipo de representacioacuten aceptada por elcliente Se puede consultar el artiacuteculo de Lars Vogel5 para maacutes informacioacuten

35 Respuestas del servidor

Vamos a explicar cuaacutel es el comportamiento por defecto de los meacutetodos de recursos JAX-RS en particular veremos cuaacuteles son los coacutedigos de respuesta HTTP por defecto teniendo encuenta situaciones de eacutexito asiacute como de fallo

Dado que en ocasiones vamos a tener que enviar cabeceras de respuesta especiacuteficas antecondiciones de error complejas tambieacuten vamos a explicar coacutemo podemos elaborar respuestascomplejas utilizando el API JAX-RS

Coacutedigos de respuesta por defecto

Los coacutedigos de respuesta por defecto se corresponden con el comportamiento indicado en laespecificacioacuten6 de la definicioacuten de los meacutetodos HTTP 11 Vamos a examinar dichos coacutedigosde respuesta con el siguiente ejemplo de recurso JAX-RS

Path(clientes)public class ClienteResource

Path(id) GET Produces(applicationxml) public Cliente getCliente(PathParam(id) int id)

5 httpwwwvogelladearticlesJAXBarticlehtml6 httpwwww3orgProtocolsrfc2616rfc2616-sec9html

Servicios Rest

92

POST Produces(applicationxml) Consumes(applicationxml) public Cliente crearCliente(Cliente nuevoCli)

PUT Path(id) Consumes(applicationxml) public void updateCliente(PathParam(id) int id Cliente cli)

Path(id) DELETE public void borrarCliente(PathParam(id) int id)

Respuestas que indican eacutexito

Los nuacutemeros de coacutedigo de respuestas HTTP con eacutexito se situacutean en el rango de 200 a 399

bull Para los meacutetodos crearCliente() y getCliente() se devolveraacute una respuestaHTTP con el coacutedigo 200 OK si el objeto Cliente que devuelven dichos meacutetodos noes null

bull Para los meacutetodos crearCliente() y getCliente() se devolveraacute una respuestaHTTP con el coacutedigo 204 No Content si el objeto Cliente que devuelven dichosmeacutetodos es null El coacutedigo de respuesta 204 no indica una condicioacuten de error Solamenteavisa al cliente de que todo ha ido bien pero que el mensaje de respuesta no contienenada en el cuerpo de la misma Seguacuten eacutesto si un meacutetodo de un recurso devuelve void por defecto se devuelve el coacutedigo de respuesta 204 No content Este es el caso para losmeacutetodos updateCliente() y borrarCliente() de nuestro ejemplo

La especificacioacuten HTTP es bastante consistente para los meacutetodos PUT POST GET yDELETE Si una respuesta exitosa HTTP contiene informacioacuten en el cuerpo del mensaje derespuesta entonces el coacutedigo de respuesta es 200 OK Si el cuerpo del mensaje estaacute vaciacuteoentonces se debe devolver 204 No Content

Respuestas que indican una situacioacuten de fallo

Es habitual que las respuestas fallidas se programen de forma que se lance una excepcioacutenLo veremos en un apartado posterior Aquiacute comentaremos algunas condiciones de error pordefecto

Los nuacutemeros de coacutedigo de error de respuesta estaacutendar en HTTP se situacutean en el rango entre400 y 599 En nuestro ejemplo si un cliente se equivoca tecleando la URI y eacutesta queda porejemplo como httphellipcliente entonces el servidor no encontraraacute ninguacuten meacutetodo delrecurso que pueda servir dicha peticioacuten (la URI correcta seriacutea httphellipclientes ) Eneste caso se enviaraacute como respuesta el coacutedigo 404 Not Found

Para los meacutetodos getCliente() y crearCliente() de nuestro ejemplo si el clientesolicita una respuesta con el tipo MIME texthtml entonces la implementacioacuten de JAX-RS devolveraacute automaacuteticamente 406 Not Acceptable con un mensaje de respuesta con elcuerpo de dicho mensaje vaciacuteo Esta respuesta indica que JAX-RS puede encontrar una rutade URI relativa que coincide con la peticioacuten pero no encuentra ninguacuten meacutetodo del recursoque devuelva la respuesta con ese tipo MIME

Servicios Rest

93

Si el cliente invoca una peticioacuten HTTP sobre una URI vaacutelida para la que no se puedeencontrar un meacutetodo de recurso asociado entonces el runtime de JAX-RS devolveraacute el coacutedigo405 Method Not Allowed Asiacute en nuestro ejemplo si el cliente solicita una operacioacuten PUTGET o DELETE sobre la URI clientes obtendraacute como respuesta 405 Method NotAllowed puesto que POST es el uacutenico meacutetodo HTTP que puede dar soporte a dicha URILa implementacioacuten de JAX-RS tambieacuten devolveraacute una cabecera de respuesta Allow con lalista de meacutetodos HTTP que pueden dar soporte a dicha URI Por lo tanto si nuestra aplicacioacutencliente realiza la siguiente peticioacuten de entrada

GET clientes

el servidor devolveraacute la siguiente respuesta

HTTP11 405 Method Not AllowedAllow POST

Elaboracioacuten de respuestas con la clase Response

Como ya hemos visto por ejemplo para peticiones GET si todo va bien se estaraacute devolviendoun coacutedigo de respuesta 200 (Ok) junto con el contenido especificado en el tipo de datosutilizado en cada caso Si devolvemos void el coacutedigo de respuesta seraacute 204 (No Content)

Sin embargo en ocasiones el servicio web que estamos disentildeando no puede implementarseutilizando el comportamiento por defecto de peticioacutenrespuesta inherente a JAX-RS En estoscasos necesitaremos controlar de forma expliacutecita la respuesta que se le enviacutea al cliente (cuerpodel mensaje de la respuesta HTTP) Por ejemplo cuando creamos un nuevo recurso con POSTdeberiacuteamos devolver 201 (Created) Para tener control sobre este coacutedigo nuestros recursosJAX-RS podraacuten devolver instancias de javaxwsrscoreResponse

GETProduces(MediaTypeAPPLICATION_XML)public Response getClientes() ClientesBean clientes = obtenerClientes()

return Responseok(clientes)build()

POSTConsumes(MediaTypeAPPLICATION_XML)public Response addCliente(ClienteBean cliente Context UriInfo uriInfo)

String id = insertarCliente(cliente) URI uri = uriInfo getAbsolutePathBuilder() path(id)

build(id)

return Responsecreated(uri)build()

Al crear una respuesta con Response podemos especificar una entidad que podraacute serun objeto de cualquiera de los tipos vistos anteriormente y que representa los datos a

Servicios Rest

94

devolver como contenido Por ejemplo cuando indicamos ok(clientes) estamos creandouna respuesta con coacutedigo 200 (Ok) y con el contenido generado por nuestro beanJAXB clientes Esto seraacute equivalente a haber devuelto directamente ClientesBean comorespuesta pero con la ventaja de que en este caso podemos controlar el coacutedigo de estadode la respuestaInsertamos una instancia de ClienteBean en la base de datos y obtenemos la claveasociada a dicho clienteEn la sesioacuten anterior hemos hablado de la interfaz uriInfo Es una interfaz inyectableque proporciona acceso a informacioacuten sobre la URI de la aplicacioacuten o la URI de laspeticiones recibidas En este caso estamos construyendo una nueva URI formada porla ruta absoluta de la peticioacuten de entrada http POST antildeadieacutendole la plantilla id yfinalmente sustituyendo el paraacutemetro de la plantilla por el valor id Supongamos quela peticioacuten POST contiene la uri httplocalhost8080recursosclientes Y que el valor de id es 8 El valor de la variable uri seraacute por tanto httplocalhost8080recursosclientes8Devolvemos la respuesta incluyendo la URI anterior en la cabecera HTTP `Location

Veamos con maacutes detalle la clase Response

public abstract class Response

public abstract Object getEntity()

public abstract int getStatus()

public abstract MultivaluedMapltString Objectgt getMetadata()

public abstract URI getLocation()

public abstract MediaType getMediaType()

public abstract void close()

El meacutetodo getEntity() devuelve el objeto Java correspondiente al cuerpo delmensaje HTTPEl meacutetodo getStatus() devuelve el coacutedigo de respuesta HTTPEl meacutetodo getMetadata() devuelve una instancia de tipo MultivaluedMap con lascabeceras de la respuestaEl meacutetodo getLocation() devuelve la URI de la cabecera Location de la respuestaEl meacutetodo getMediaType() devuelve el mediaType del cuerpo de la respuestaEl meacutetodo close() cierra el input stream correspondiente a la entidad asociada delcuerpo del mensaje (en el caso de que esteacute disponible y abierto) Tambieacuten liberacualquier otro recurso asociado con la respuesta (como por ejemplo datos posiblementealmacenados en un buffer)

Estos meacutetodos tiacutepicamente seraacuten invocados dese el cliente tal y como veremos maacutes adelantecuando expliquemos el API cliente

Los objetos Response no pueden crearse directamente Tienen que crearse a partir deinstancias de javaxwsrscoreResponseResponseBuilder devueltas por uno delos siguientes meacutetodos estaacuteticos de Response

public abstract class Response public abstract void close() public static ResponseBuilder status(Status status) public static ResponseBuilder status(int status) public static ResponseBuilder ok()

Servicios Rest

95

public static ResponseBuilder ok(Object entity) public static ResponseBuilder ok(Object entity MediaType type) public static ResponseBuilder ok(Object entity String type) public static ResponseBuilder ok(Object entity Variant var) public static ResponseBuilder serverError() public static ResponseBuilder created(URI location) public static ResponseBuilder noContent() public static ResponseBuilder notModified() public static ResponseBuilder notModified(EntityTag tag) public static ResponseBuilder notModified(String tag) public static ResponseBuilder seeOther(URI location) public static ResponseBuilder temporaryRedirect(URI location) public static ResponseBuilder notAcceptable(ListltVariantgt variants) public static ResponseBuilder fromResponse(Response response)

Veamos por ejemplo el meacutetodo ok()

ResponseBuilder ok(Object entity MediaType type)

Este meacutetodo recibe como paraacutemetros un objeto Java que queremos convertir en una respuestaHTTP y el Content-Type de dicha respuesta Como valor de retorno se obtiene unainstancia de tipo ResponseBuilder pre-inicializada con un coacutedigo de estado de 200 OK

El meacutetodo created() devuelve un ResponseBuilder para un recurso creado y asigna a lacabecera Location de la respuesta el valor de la URI proporionado como paraacutemetro

La clase ResponseBuilder es una factoriacutea utilizada para crear instancias individuales detipo Response

public static abstract class ResponseBuilder public abstract Response build() public abstract ResponseBuilder clone()

public abstract ResponseBuilder status(int status) public ResponseBuilder status(Status status)

public abstract ResponseBuilder entity(Object entity) public abstract ResponseBuilder type(MediaType type) public abstract ResponseBuilder type(String type)

public abstract ResponseBuilder variant(Variant variant) public abstract ResponseBuilder variants(ListltVariantgt variants)

public abstract ResponseBuilder language(String language) public abstract ResponseBuilder language(Locale language)

public abstract ResponseBuilder location(URI location) public abstract ResponseResponseBuilder header(String name Object value) public abstract ResponseResponseBuilder link(URI uri String rel)

Servicios Rest

96

public abstract ResponseResponseBuilder link(String uri String rel)

Vamos a mostrar un ejemplo sobre coacutemo crear respuestas utilizando un objeto Response En este caso el meacutetodo getLibro() devuelve un String que representa el libro en el queestaacute interesado nuestro cliente

Path(libro)public class LibroServicio GET Path(restfuljava) Produces(textplain) public Response getLibro()

String libro =

ResponseBuilder builder = Responseok(libro)

builderlanguage(fr)header(Some-Header some value)

return builderbuild()

Recuperamos los datos del libro solicitado En este caso vamos a devolver una cadenade caracteres que representa el libro en el que estamos interesadosInicializamos el cuerpo de la respuesta utilizando el meacutetodo Responseok() El coacutedigode estado de ResponseBuilder se inicializa de forma automaacutetica con 200Usamos el meacutetodo ResponseBuilderlanguage() para asignar el valorde la cabecera Content-Languaje a franceacutes Tambieacuten usamos el meacutetodoResponseBuilderheader() para asignar un valor concreto a otra cabeceraFinalmente creamos y devolvemos el objeto Response usando el meacutetodoResponseBuilderbuild()

Un detalle que es interesante destacar en este coacutedigo es que no indicamos ninguacuten valor parael Content-Type de la respuesta Debido a que ya hemos especificado esta informacioacutenen la anotacioacuten Produces del meacutetodo el runtime de JAX-RS devolveraacute el valor adecuadodel media type de la respuesta por nosotros

Inclusioacuten de cookies en la respuesta

JAX-RS proporciona la clase javaxwsrscoreNewCookie que utilizaremos para crearnuevos valores de cookies y enviarlos en las respuestas

Para poder incluir las cookies en nuestro objeto Response primero crearemoslas instancias correspondientes de tipo NewCookie las pasaremos al meacutetodoResponseBuildercookie() Por ejemplo

Path(myservice)public class MyService GET public Response get()

NewCookie cookie = new NewCookie(nombre pepe)

ResponseBuilder builder = Responseok(hola textplain) return buildercookie(cookie)build()

Servicios Rest

97

Creamos una nueva cookie con el nombre de clave nombre y le asignamos el valorpepeEn este caso al no indicar el tipo mime de la respuesta con la anotacioacuten Producesnecesitamos indicarlo (en este caso como un paraacutemetro del meacutetodo ok() )

El tipo enumerado de coacutedigos de estado

JAX-RS proporciona el tipo enumerado javaxwsrscoreStatus para representarcoacutedigos de respuesta especiacuteficos

public enum Status OK(200 OK) CREATED(201 Created) ACCEPTED(202 Accepted) NO_CONTENT(204 No Content) MOVED_PERMANENTLY(301 Moved Permanently) SEE_OTHER(303 See Other) NOT_MODIFIED(304 Not Modified) TEMPORARY_REDIRECT(307 Temporary Redirect) BAD_REQUEST(400 Bad Request) UNAUTHORIZED(401 Unauthorized) FORBIDDEN(403 Forbidden) NOT_FOUND(404 Not Found) NOT_ACCEPTABLE(406 Not Acceptable) CONFLICT(409 Conflict) GONE(410 Gone) PRECONDITION_FAILED(412 Precondition Failed) UNSUPPORTED_MEDIA_TYPE(415 Unsupported Media Type) INTERNAL_SERVER_ERROR(500 Internal Server Error) NOT_IMPLEMENTED(501 Not Implemented) SERVICE_UNAVAILABLE(503 Service Unavailable) public enum Family INFORMATIONAL SUCCESSFUL REDIRECTION CLIENT_ERROR SERVER_ERROR OTHER

public Family getFamily() public int getStatusCode() public static Status fromStatusCode(final int statusCode)

Cada valor del tipo Status se asocia con una familia especiacutefica de coacutedigos de respuestaHTTP Estas familias se identifican por el enumerado StatusFamily

bull Los coacutedigos en el rango del 100 se consideran informacionales

bull Los coacutedigos en el rango del 200 se consideran exitosos

bull Los coacutedigos en el rango del 300 son coacutedigos con eacutexito pero dentro de la categoriacutearedireccioacuten

bull Los coacutedigos de error pertenecen a los ragos 400 y 500 En el rango de 400 se consideranerrores del cliente y en el rango de 500 son errores del servidor

Servicios Rest

98

Tanto el meacutetodo Responsestatus() como ResponseBuilderstatus() puedenaceptar un valor enumerado de tipo Status Por ejemplo

DELETEResponse delete() return Responsestatus(StatusGONE)build()

En este caso estamos indicando al cliente que lo que queremos borrar ya no existe (410)

La clase javaxwsrscoreGenericEntity

Cuando estamos creando objetos de tipo Response se nos plantea un problema cuandoqueremos devolver tipos geneacutericos ya que el manejador JAXB necesita extraer la informacioacutendel tipo parametrizado de la respuesta en tiempo de ejecucioacuten Para estos casos JAX-RS proporciona la clase javaxwsrscoreGenericEntity Veamos su uso con unejemplo

GETProduces(applicationxml)public Response getListaClientes() ListltClientegt list = new ArrayListltClientegt() listadd(new Cliente())

GenericEntity entity =

new GenericEntityltListltClientegtgt(list)

return Responseok(entity)build()

La clase GenericEntity es tambieacuten una clase geneacuterica Lo que hacemos escrear una clase anoacutenima que extiende GenericEntity inicializando la plantilla deGenericEntity con el tipo geneacuterico que estemos utilizando

36 Manejadores de excepciones

Vamos a explicar coacutemo podemos tratar las excepciones en nuestros servicios RESTful

Los errores pueden enviarse al cliente bien creando y devolviendo el objeto Responseadecuado o lanzando una excepcioacuten Podemos lanzar cualquier tipo de excepcioacutentanto las denominadas checked (clases que heredan de javalangException ) comolas excepciones unchecked (clases que extienden javalangRuntimeException )Las excepciones generadas son manejadas por el runtime de JAX-RS si tenemosregistrado un mapper de excepciones Dichos mappers (o mapeadores) de excepcionespueden convertir una excepcioacuten en una respuesta HTTP Si las excepciones no estaacutengestionadas por un mapper eacutestas se propagan y se gestionan por el contenedor (deservlets) en el que se estaacute ejecutando JAX-RS JAX-RS proporciona tambieacuten la clasejavaxwsrsWebApplicationException Esta excepcioacuten puede lanzarse por elcoacutedigo de nuestra aplicacioacuten y seraacute procesado automaacuteticamente por JAX-RS sin necesidadde disponer de forma expliacutecita de ninguacuten mapper Vamos a ver coacutemo utilizar esta clase

Servicios Rest

99

La clase javaxwsrsWebApplicationException

JAX-RS incluye una excepcioacuten unchecked que podemos lanzar desde nuestraaplicacioacuten RESTful (ver la documentacioacuten del httpdocsoraclecomjavaee7apijavaxwsrsWebApplicationExceptionhtml [API]) Esta excepcioacuten se puede pre-inicializar con un objetoResponse o con un coacutedigo de estado particular

Clase javaxwsrsWebApplicationException

public class WebApplicationException extends RuntimeException Constructores public WebApplicationException()

public WebApplicationException(Response response) public WebApplicationException(int status)

public WebApplicationException(ResponseStatus status)

public WebApplicationException(String message) public WebApplicationException(Throwable cause) public WebApplicationException(Throwable cause Response response) public WebApplicationException(Throwable cause int status) public WebApplicationException(Throwable cause ResponseStatus status)

public Response getResponse() ]

Podemos crear una instancia a partir de un objeto ResponseCreacioacuten de una instancia a partir de un coacutedigo de estadoCreacioacuten de una instancia a partir de un String Por defecto se incluye el coacutedigo de estado500 El String que se pasa como paraacutemetro se almacena para su posterior recuperacioacutena traveacutes del mensaje `getMessage()

Cuando JAX-RS detecta que se ha lanzado la excepcioacuten WebApplicationException lacaptura y realiza una llamada al meacutetodo WebApplicationExceptiongetResponse()para obtener un objeto Response que enviaraacute al cliente Si la aplicacioacuten ha inicializado laexcepcioacuten WebApplicationException con un coacutedigo de estado o un objeto Response dicho coacutedigo o Response se utilizaraacuten para crear la respuesta HTTP real En otro caso laexcepcioacuten WebApplicationException devolveraacute el cliente el coacutedigo de respuesta 500Internal Server Error

Por ejemplo supongamos que tenemos un servicio web que permite a los usuarios solicitarinformacioacuten de nuestros clientes utilizando una representacioacuten XML

Path(clientes)public class ClienteResource GET Path(id) Produces(applicationxml) public Cliente getCliente(PathParam(id) int id) Cliente cli = recuperarCliente(id) if (cli == null) throw new WebApplicationException(ResponseStatusNOT_FOUND) return cli

Servicios Rest

100

En este ejemplo si no encontramos una instancia de Cliente con el id proporcionadolanzaremos una WebApplicationException que provocaraacute que se le enviacutee al clientecomo coacutedigo de respuesta 404 Not Found

Mapeado de excepciones

Normalmente las aplicaciones tienen que tratar con multitud de excepciones lanzadas desdenuestro coacutedigo de aplicacioacuten o por frameworks de terceros Dejar que el servlet JAX-RSque reside en el servidor maneje la excepcioacuten no nos proporciona demasiada flexibilidadSi capturamos y redirigimos todas estas excepciones a WebApplicationExceptionpodriacutea resultar bastante tedioso De forma alternativa podemos implementar y registrarinstancias de javaxwsrsextExceptionMapper Estos objetos saben coacutemo mapearuna excepcioacuten lanzada por la aplicacioacuten a un objeto Response

public interface ExceptionMapperltE extends Throwablegt Response toResponse(E exception)

Las clases que implementan la interfaz ExceptionMapperltTgt son proveedores demappings de excepciones (Exception Mapping Providers) y mapean una excepcioacuten runtimeo checked a una instancia de Response

Cuando un recurso JAX-RS lanza una excepcioacuten para la que existe un proveedor de mappingde excepciones eacuteste uacuteltimo se utiliza para devolver una instancia de Response Estarespuesta resultante se procesa como si hubiese sido generada por el recurso

Por ejemplo una excepcioacuten bastante utilizada por aplicaciones de bases de datos que utilizanJPA (Java Persistence Api) es javaxpersistenceEntityNotFoundException Estaexcepcioacuten se lanza cuando JPA no puede encontrar un objeto particular en la base de datosEn lugar de escribir coacutedigo que maneje dicha excepcioacuten de forma expliacutecita podemos escribirun ExceptionMapper para que gestione dicha excepcioacuten por nosotros Veaacutemos coacutemo

Provider public class EntityNotFoundMapper

implements ExceptionMapperltEntityNotFoundExceptiongt

public Response toResponse(EntityNotFoundException e)

return Responsestatus(ResponseStatusNOT_FOUND)build()

Nuestra implementacioacuten de ExceptionMapper debe anotarse con Provider Estole indica al runtime de JAX-RS que esta clase es un componente RESTLa clase que implementa ExceptionMapper debe proporcionar el tipo que se quiereparametrizar JAX-RS utiliza esta informacioacuten para emparejar las excepciones de tipoEntityNotFoundException con nuestra clase EntityNotFoundMapperEl meacutetodo toResponse() recibe la excepcioacuten lanzada ycrea un objeto Response que se utilizaraacute para construir la respuesta HTTP

Servicios Rest

101

Otro ejemplo es la excepcioacuten EJBException lanzada por aplicaciones que utilizan EJBs

Jerarquiacutea de excepciones

JAX-RS 20 proporciona una jerarquiacutea de excepciones para varias condiciones deerror para las peticiones HTTP La idea es que en lugar de crear una instancia deWebApplicationException e inicializarla con un coacutedigo de estado especiacutefico podemosuilizar en su lugar una de las excepciones de la jeraquiacutea (clases que heredan de la claseWebApplicationException) Por ejemplo podemos cambiar el coacutedigo anterior que utilizabaWebApplicationException y en su lugar usar javaxwsrsNotFoundException

Path(clientes)public class ClienteResource GET Path(id) Produces(applicationxml) public Cliente getCliente(PathParam(id) int id) Cliente cli = recuperarCliente(id) if (cli == null) throw new NotFoundException() return cli

Al igual que el resto de excepciones de la jerarquiacutea la excepcioacuten NotFoundExceptionhereda de WebApplicationException La siguiente tabla muestra algunas excepcionesque podemos utilizar todas ellas del paquete javaxwsrs Las que incluyen un coacutedigo deestado en el rango de 400 son subclases de ClientErrorException y como ya hemosindicado representan errores en las peticiones Las que presentan un coacutedigo de estado enel rango de 500 son subclases de ServerErrorException y representan errores delservidor

Table 5 Jerarquiacutea de excepciones JAX-RS

Excepcioacuten Coacutedigo de estado Descripcioacuten

BadRequestException 400 Mensaje mal formado

NotAuthorizedException 401 Fallo de autenticacioacuten

ForbiddenException 403 Acceso no permitido

NotFoundException 404 No se ha podido encontrar elrecurso

NotAllowedException 405 Meacutetodo HTTP no soportado

NotAcceptableException 406 Media type solicitado porel cliente no soportado(cabecera Accept de lapeticion)

NotSupportedException 415 El cliente ha incluido unMedia type no soportado(cabecera Content-Type dela peticion)

Servicios Rest

102

Excepcioacuten Coacutedigo de estado Descripcioacuten

InternalServerErrorException 500 Error general del servidor

ServiceUnavailableException 503 El servidor estaacute ocupadoo temporalmente fuera deservicio

bull La excepcioacuten BadRequestException se utiliza cuando el cliente enviacutea algo al servidorque eacuteste no puede interpretar Ejemplos de escenarios concretos que provocan que elruntime JAX-RS son cuando una peticioacuten PUT o POST contiene un cuerpo del mensaje conun documento XML o JSON mal formado de forma que falle el parsing del documento ocuando no puede convertir un valor especificado en la cabecera o cookie al tipo deseadoPor ejemplo

HeaderParam(Cabecera-Particular) int cabeceraCookieParam(miCookie) int cookie

Si el valor de la cabecera HTTP de la peticioacuten o el valor miCookie no puede convertirseen un entero se lanzaraacute la excepcioacuten BadRequestException

bull La excepcioacuten NotAuthorizedException (coacutedigo 401) se usa cuando queremosescribir nuestros propios protocolos de autorizacioacuten y queremos indicar al cliente que eacutestenecesita autenticarse con el servidor

bull La excepcioacuten ForbiddenException (coacutedigo 403)se usa generalmente cuando elcliente realiza una invocacioacuten para la que no tiene permisos de acceso Esto ocurrenormalmente debido a que el cliente no tiene el rol requerido

bull La excepcioacuten NotFoundException (coacutedigo 404) se usa cuando queremos comunicar alcliente que el recurso que estaacute solicitando no existe Esta excepcioacuten tambieacuten se generaraacute deforma automaacutetica por el runtime de JAX-RS cuando a eacuteste no le sea posible inyectar un valoren un PathParam QueryParam o MatrixParam Al igual que hemos comentado paraBadRequestException esto puede ocurrir si intentamos convertir el valor del paraacutemetroa un tipo que no admite esta conversioacuten

bull La excepcioacuten NotAllowedException (coacutedigo 405) se usa cuando el meacutetodo HTTP queel cliente estaacute intentando invocar no estaacute soportado por el recurso al que el cliente estaacuteaccediendo El runtime de JAX-RS lanza automaacuteticamente esta excepcioacuten si no encuentraninguacuten meacutetodo que pueda emparejar con el meacutetodo HTTP invocado

bull La excepcioacuten NotAcceptableException (coacutedigo 406) se usa cuando un cliente estaacutesolicitando un formato especiacutefico a traveacutes de la cabecera Accept El runtime de JAX-RSlanza automaacuteticamente esta excepcioacuten si no hay un meacutetodo con una anotacioacuten Producesque sea compatible con la cabecera Accept del cliente

bull La excepcioacuten NotSupportedException (coacutedigo 415)se usa cuando un cliente estaacuteenviando una representacioacuten que el servidor no comprende El runtime de JAX-RS lanzaautomaacuteticamente esta excepcioacuten si no hay un meacutetodo con una anotacioacuten Consumes quecoincida con el valor de la cabecera Content-Type de la peticioacuten

bull La excepcioacuten InternalServerErrorException (coacutedigo 500)es una excepcioacuten depropoacutesito general lanzada por el servidor Si en nuestra aplicacioacuten queremos lanzar estaexcepcioacuten deberiacuteamos hacerlo si se ha producido una condicioacuten de error que realmenteno encaja en ninguna de las situaciones que hemos visto El runtime de JAX-RS lanzaautomaacuteticamente esta excepcioacuten si falla un MessageBodyWriter o si se lanza algunaexcepcioacuten desde alguacuten ExceptionMapper

Servicios Rest

103

bull La excepcioacuten ServiceUnavailableException (coacutedigo 503)se usa cuando el servidorestaacute ocupado o temporalmente fuera de servicio En la mayoriacutea de los casos es suficientecon que el cliente vuelva a intentar realizar la llamada un tiempo maacutes tarde

Servicios Rest

104

37 Ejercicios

Servicio REST ejemplo

Para familiarizarnos con las el uso de diferentes manejadores de contenidos y manejo deexcepciones proporcionamos el moacutedulo el MOacuteDULO s3-ejemplo-rest con la implementacioacutende un servicio rest sencillo que podeacuteis probar con la herramienta postman

En el directorio srcmainresources de dicho moacutedulo teneacuteis un fichero de texto con lasinstrucciones (instruccionestxt) para construir desplegar y probar la aplicacioacuten de ejemplo

Plantillas que se proporcionan

Para esta sesioacuten proporcionamos un proyecto como plantilla con el nombre s3-filmotecaque tendraacutes utilizar como punto de partida para aplicar lo que hemos aprendido en esta sesioacuten

Se trata de una implementacioacuten parcial para gestionar una filmoteca con informacioacuten depeliacuteculas y actores

La estructura loacutegica del proyecto proporcionado es la siguiente

bull Paquete orgexpertojavadomain es la capa que contiene los objetos del dominiode la aplicacioacuten Por simplicidad no usamos una base de datos real sino que trabajamoscon datos en memoria

bull Paquete orgexpertojavaservice contiene la implementacioacuten de los servicios denuestra aplicacioacuten que seraacuten accedidos desde la capa rest

bull Paquete orgexpertojavarest constituye la capa rest de nuestra aplicacioacuten Estacapa es cliente de la capa de servicios

En la carpeta srcmainresources teneacuteis un fichero de texto (instruccionestxt) coninformacioacuten detallada sobre el API rest implementado

Uso de JAXB (05 puntos)

Utiliza las anotaciones JAXB oportunas para realizar el serializado de las entidades java a xmly json de forma que la lista de peliacuteculas de la filmoteca en formato xml sea

Peticioacuten rest GET peliculas

ltxml version=10 encoding=UTF-8 standalone=yesgtltpeliculasgt ltpelicula duracion=120 estreno=2015-12-08T000000+0100 id=1gt ltdirectorgtStanley Kubrickltdirectorgt lttitulogtEl resplandorlttitulogt ltpeliculagt ltpelicula duracion=155 estreno=2015-18-07T000000+0100 id=2gt ltdirectorgtDirector 2ltdirectorgt lttitulogtPelicula 2lttitulogt ltpeliculagtltpeliculasgt

Servicios Rest

105

Por defecto JAXB serializa los tipos Date con el formato ISO 80617

para los contenidos en el cuerpo de la petioacutenrespuesta Dicho formatoes YYYY-MM-DD (seguido de HHMMSS) Por otro lado si antildeadimosactores a las peliacuteculas eacutestos deberaacuten mostrarse bajo la etiquetaltactoresgt tal y como mostramos en el siguiente ejemplo

Los datos mostrados para una peliacutecula en formato xml tienen que presentar el siguienteaspecto

sourcejava] Peticioacuten rest GET peliculas1

ltxml version=10 encoding=UTF-8 standalone=yesgtltpelicula duracion=120 estreno=2015-12-08T000000+0100 id=1gt ltactoresgt ltactor nombre=Jack Nicholson personaje=Jack Torrancegt ltactoresgt ltdirectorgtStanley Kubrickltdirectorgt lttitulogtEl resplandorlttitulogtltpeliculagt

Puedes comprobar si has hecho bien el ejercicio utilizando Postman

Uso de manejadores de contenidos y clase Response (075 puntos)

Implementa una nueva peticioacuten POST que reciba los datos de una nueva peliacutecula desdeun formulario Recuerda que los datos de un formulario pueden ponerse en el cuerpo de lapeticioacuten HTTP como pares nombre=valor separados por amp Los espacios en blanco secodifican como 20 No es necesario poner comillas Por ejemplo

nombre1=valor20con20espaciosampnombre2=valor

Se proporciona el fichero indexhtml con un formulario para utilizarlo como alternativa apostman Para acceder al formulario usaremos httplocalhost8080s3-filmoteca

Modifica las peticiones POST sobre peliacuteculas y actores de forma que devuelvan enla cabecera Location la URI del nuevo recurso creado Por ejemplo

httplocalhost8080s3-filmotecapeliculas3

en el caso de una nueva peliacutecula y

httplocalhost8080s3-filmotecapeliculas3actoresSigourney20Weaver

si por ejemplo hemos antildeadido un nuevo actor a la peliacutecula con id=3

Modifica los meacutetodos GET para que devuelvan el estado 204 No Content en los casos enlos que la peliacutecula yo actor consultado no exista

7 httpseswikipediaorgwikiISO_8601

Servicios Rest

106

Modifica los meacutetodos GET para que devuelvan el estado 404 Not Found en los casos enlos que las listas de peliacuteculas yo actores esteacuten vaciacuteas

Implementa el coacutedigo para antildeadir un nuevo actor Tendraacutes que obtener la informacioacuten de lapeliacutecula y nombre del actor de la URI de la peticioacuten

Puedes comprobar si has hecho bien el ejercicio utilizando Postman

Manejo de excepciones (075 puntos)

Modifica el meacutetodo addPelicula de la capa de servicio (paquete orgexpertojavaservice)para que lance una excepcioacuten de tipo ServiceException con el mensaje El tiacutetulo de la peliacuteculano puede ser nulo ni vaciacuteo cuando se intente antildeadir una peliacutecula con un tiacutetulo con valor nullo vaciacuteo

El meacutetodo addPelicula debe lanzar tambieacuten una excepcioacuten de tipo ServiceException conel mensaje La peliacutecula ya existe cuando se intente antildeadir una peliacutecula con un tiacutetulo que yaexiste

Modifica el meacutetodo addActor de la capa de servicio para que lance las excepciones de tipoServiceException con los mensajes El tiacutetulo de la peliacutecula no puede ser nulo ni vaciacuteo cuandose intente antildeadir un actor a una peliacutecula cuyo tiacutetulo no existe o bien el mensaje EL actor yaexiste si intentamos antildeadir un actor a una peliacutecula que ya habiacuteamos antildeadido previamente

Implementa un mapper para capturar las excepciones de la capa de servicio de forma quese devuelva el estado 500 Internal Server error y como entidad del cuerpo de la respuestael mensaje asociado a las excepciones generadas por el servicio (El tiacutetulo de la peliacutecula nopuede ser nulo ni vaciacuteo EL actor ya existe hellip) La nueva clase pertenceraacute a la capa restPuedes ponerle el nombre ServiceExceptionMapper

Puedes comprobar si has hecho bien el ejercicio utilizando Postman

Servicios Rest

107

4 HATEOAS y Seguridad

En esta sesioacuten trataremos uno de los principios REST de obligado cumplimiento para poderhablar de un servicio RESTful Nos referimos a HATEOAS Hasta ahora hemos visto coacutemo losclientes pueden cambiar el estado de los recursos (el nombre del recurso se especifica en laURI de la peticioacuten) a traveacutes de los contenidos del cuerpo del mensaje o utilizando paraacutemetroso cabeceras de peticioacuten A su vez los servicios comunican el estado resultante de la peticioacuten alos clientes a traveacutes del contenido del cuerpo del mensaje coacutedigos de respuesta y cabecerasde respuesta Pues bien teniendo en cuenta lo anterior HATEOAS hace referencia a quecuando sea necesario tambieacuten deben incluirse los enlaces a los recursos (URI) en el cuerpode la respuesta (o en las cabeceras) para asiacute poder recuperar el recurso en cuestioacuten o losrecursos relacionados

En esta sesioacuten tambieacuten explicaremos algunos conceptos baacutesicos para poder dotar deseguridad a nuestros servicios REST

41 iquestQueacute es HATEOAS

Comuacutenmente se hace referencia a Internet como la Web (web significa red telarantildea) debidoa que la informacioacuten estaacute interconectada mediante una serie de hiperenlaces embebidosdentro de los documentos HTML Estos enlaces crean una especie de hilos o hebras entrelos sitios web relacionados en Internet Una consecuencia de ello es que los humanos puedennavegar por la Web buscando elementos de informacioacuten relacionados de su intereacutes haciendoclick en los diferentes enlaces desde sus navegadores Los motores de buacutesqueda puedentrepar o desplazarse por estos enlaces y crear iacutendices enormes de datos susceptibles deser buscados Sin ellos Internet no podriacutea tener la propiedad de ser escalable No habriacuteaforma de indexar faacutecilmente la informacioacuten y el registro de sitios web seriacutea un proceso manualbastante tedioso

Ademaacutes de los enlaces (links) otra caracteriacutestica fundamental de Internet es HTML Enocasiones un sitio web nos solicita que rellenemos alguna informacioacuten para compraralgo o registrarnos en alguacuten servicio El servidor nos indica a nosotros como clientes queacuteinformacioacuten necesitamos proporcionar para completar una accioacuten descrita en la paacutegina webque estamos viendo El navegador nos muestra la paacutegina web en un formado que podemosentender faacutecilmente Nosotros leemos la paacutegina web y rellenamos y enviamos el formulario Unformulario HTML es un formato de datos interesante debido a que auto-describe la interaccioacutenentre el cliente y el servidor

El principio arquitectoacutenico que describe el proceso de enlazado (linking) y el enviacuteo deformularios se denomina HATEOAS Las siglas del teacutermino HATEOAS significan HypermediaAs The Engine Of Application State (es decir el uso de Hipermedia como mecanismo demaacutequina de estados de la aplicacioacuten) La idea de HATEOAS es que el formato de los datosproporciona informacioacuten extra sobre coacutemo cambiar el estado de nuestra aplicacioacuten En laWeb los enlaces HTML nos permiten cambiar el estado de nuestro navegador Por ejemplocuando estamos leyendo una paacutegina web un enlace nos indica queacute posibles documentos(estados) podemos ver a continuacioacuten Cuando hacemos click sobre un enlace el estado delnavegador cambia al visitar y mostrar una nueva paacutegina web Los formularios HTML por otraparte nos proporcionan una forma de cambiar el estado de un recurso especiacutefico de nuestroservidor Por uacuteltimo cuando compramos algo en Internet por ejemplo estamos creando dosnuevos recursos en el servicio una transaccioacuten con tarjeta de creacutedito y una orden de compra

Servicios Rest

108

42 HATEOAS y Servicios Web

Cuando aplicamos HATEOAS a los servicios web la idea es incluir enlaces en nuestrosdocumentos XML o JSON La mayoriacutea de las aplicaciones RESTful basadas en XML utilizanel formato Atom Syndication Format8 para implementar HATEOAS

Enlaces Atom

Los enlaces Atom constituyen un mecanismo estaacutendar para incluir enlaces (links) en nuestrosdocumentos XML Veamos un ejemplo

ltclientesgt ltlink rel=next href=httpejemplocomclientesinicio=2amptotal=2 type=applicationxmlgt ltcliente id=123gt ltnombregtJuan Garcialtnombregt ltclientegt ltcliente id=332gt ltnombregtPablo Bozoltnombregt ltclientegtltclientesgt

El documento anterior representa una lista de clientes y el elemento ltlinkgt que contieneun enlace indica la forma de obtener los siguientes clientes de la lista

Un enlace Atom es simplemente un elemento XML (elemento ltlinkgt ) con unos atributosespeciacuteficos

bull El atributo rel Se utiliza para indicar la relacioacuten del enlace con el elemento XML en elque anidamos dicho enlace Es el nombre loacutegico utilizado para referenciar el enlace Esteatributo tiene el mismo significado para la URL que estamos enlazando que la etiquetaHTML ltagt tiene para la URL sobre la que estamos haciendo click con el ratoacuten en elnavegador Si el enlace hace referencia al propio elemento XML en el que incluimos elenlace entonces asignaremos el valor del atributo self

bull El atributo href es la URL a la que podemos acceder para obtener nueva informacioacuten ocambiar el estado de nuestra aplicacioacuten

bull El atributo type indica el media type asociado con el recurso al que apunta la URL

Cuando un cliente recibe un documento con enlaces Atom eacuteste busca la relacioacuten en la queestaacute interesado (atributo rel ) e invoca la URI indicada en el atributo href

Ventajas de utilizar HATEOAS con Servicios Web

Resulta bastante obvio por queacute los enlaces y los formularios tienen mucho que ver en laprevalencia de la Web Con un navegador tenemos una ventana a todo un mundo deinformacioacuten y servicios Las maacutequinas de buacutesqueda rastrean Internet e indexan sitios webpara que todos los datos esteacuten al alcance de nuestros dedos Esto es posible debido a quela Web es auto-descriptiva Cuando accedemos a un documento conocemos coacutemo recuperarinformacioacuten adicional siguiendo los enlaces situados en dicho documento Por ejemplo

8 httpwwww3org2005Atom

Servicios Rest

109

conocemos coacutemo realizar una compra en Amazon debido a que los formularios HTML nosindican coacutemo hacerlo

Cuando los clientes son maacutequinas en lugar de personas (los servicios Web tambieacuten seconocen como Web para maacutequinas frente a la Web para humanos proporcionada por elacceso a un servidor web a traveacutes de un navegador) el tema es algo diferente puesto que lasmaacutequinas no pueden tomar decisiones sobre la marcha cosa que los humanos siacute puedenhacer Las maacutequinas requieren que los programadores les digan coacutemo interpretar los datosrecibidos desde un servicio y coacutemo realizar transiciones entre estados como resultado de lasinteracciones entre clientes y servidores

En este sentido HATEOAS proporciona algunas ventajas importantes para contribuir a quelos clientes sepan coacutemo utilizar los servicios a la vez que acceden a los mismos Vamos acomentar algunas de ellas

Transparencia en la localizacioacuten

En un sistema RESTful gracias a HATEOAS soacutelo es necesario hacer puacuteblicas unas pocasURIs Los servicios y la informacioacuten son representados con enlaces que estaacuten embebidosen los formatos de los datos devueltos por las URIs puacuteblicas Los clientes necesitan conocerlos nombres loacutegicos de los enlaces para buscar a traveacutes de ellos pero no necesitan conocerlas ubicaciones reales en la red de los servicios a los que acceden

Los enlaces proporcionan un nivel de indireccioacuten de forma que los servicios subyacentespueden cambiar sus localizaciones en la red sin alterar la loacutegica ni el coacutedigo del cliente

Desacoplamiento de los detalles de la interaccioacuten

Consideremos una peticioacuten que nos devuelve una lista de clientes en una base de datosGET clientes Si nuestra base de datos tiene miles de datos probablemente noquerremos devolver todos ellos de una soacutela vez Lo que podemos hacer es definir una vistaen nuestra base de datos utilizando paraacutemetros de consulta por ejemplo

customersinicio=indiceInicioamptotal=numeroElementosDevueltos

El paraacutemetro inicio identifica el iacutendice inicial de nuestra lista de clientes El paraacutemetrototal especifica cuaacutentos clientes queremos que nos sean devueltos como respuesta

Lo que estamos haciendo en realidad es incrementar la cantidad de conocimiento que elcliente debe tener predefinido para interactuar con el servicio (es decir no soacutelo necesita saberla URI sino ademaacutes conocer la existencia de estos paraacutemetros) Supongamos que en el futuroel servidor decide que necesita cambiar la forma en la que se accede al nuacutemero de datossolicitados por el cliente Si el servidor cambia la interfaz los clientes antiguos dejaraacuten defuncionar a menos que cambien su coacutedigo

En lugar de publicar la interfaz REST anterior para obtener datos de los clientes podemosincluir dicha informacioacuten en el documento de respuesta por ejemplo

ltclientesgt ltlink rel=next href=httpejemplocomclientesinicio=2amptotal=2 type=applicationxmlgt ltcliente id=123gt

Servicios Rest

110

ltnombregtJuan Garcialtnombregt ltclientegt ltcliente id=332gt ltnombregtPablo Bozoltnombregt ltclientegtltclientesgt

Cuando incluimos un enlace Atom en un documento estamos asignando un nombre loacutegicoa una transicioacuten de estados En el ejemplo anterior la transicioacuten de estados es el siguienteconjunto de clientes a los que podemos acceder En lugar de tener que recordar cuaacuteles sonlos paraacutemetros de la URI que tenemos que utilizar en la siguiente invocacioacuten para obtener maacutesclientes lo uacutenico que tenemos que hacer es seguir el enlace proporcionado El cliente notiene que contabilizar en ninguacuten sitio la interaccioacuten ni tiene que recordar queacute seccioacuten de labase de datos estamos consultando actualmente

Ademaacutes el XML devuelto es auto-contenido iquestQueacute pasa si tenemos que pasar estedocumento a un tercero Tendriacuteamos que decirle que se trata de una vista parcial de la basede datos y especificar el iacutedice de inicio Al incluir el enlace en el documento ya no es necesarioproporcionar dicha informacioacuten adicional ya que forma parte del propio documento

Reduccioacuten de errores de transicioacuten de estados

Los enlaces no se utilizan solamente como un mecanismo para agregar informacioacuten denavegacioacuten Tambieacuten se utilizan para cambiar el estado de los recursos Pensemos en unaaplicacioacuten de comercio web a la que podemos acceder con la URI pedidos333

ltpedido id=333gt ltcliente id=123gtltclientegt ltimportegt9999ltimportegt ltlineas-pedidogt ltlineas-pedidogtltpedidogt

Supongamos que un cliente quiere cancelar su pedido Podriacutea simplemente invocar la peticioacutenHTTP DELETE pedidos333 Esta no es siempre la mejor opcioacuten ya que normalmenteel sistema necesitaraacute retener el pedido para propoacutesitos de almacenaje Por ello podriacuteamosconsiderar una nueva representacioacuten del pedido con un elemento cancelado a true

PUT pedidos333 HTTP11Content-Type applicationxmlltpedido id=333gt ltcliente id=123gtltclientegt ltimportegt9999ltimportegt ltcanceladogttrueltcanceladogt ltlineas-pedidogt ltlineas-pedidogtltpedidogt

Pero iquestqueacute ocurre si el pedido no puede cancelarse Podemos tener un cierto estado ennuestro proceso de pedidos en donde esta accioacuten no estaacute permitida Por ejemplo si el pedido

Servicios Rest

111

ya ha sido enviado entonces no puede cancelarse En este caso realmente no hay niguacutencoacutedigo de estado HTTP de respuesta que represente esta situacioacuten Una mejor aproximacioacutenes incluir un enlace para poder realizar la cancelacioacuten

ltpedido id=333gt ltcliente id=123gtltclientegt ltimportegt9999ltimportegt ltcanceladogtfalseltcanceladogt ltlink rel=cancelar href=httpejemplocompedidos333canceladogt ltlineas-pedidogt ltlineas-pedidogtltpedidogt

El cliente podriacutea invocar la orden GET pedidos333 y obtener el documento XMLque representa el pedido Si el documento contiene el enlace cancelar entonces el clientepuede cambiar el estado del pedido a cancelado enviando una orden PUT vaciacutea a la URIreferenciada en el enlace Si el documento no contiene el enlace el cliente sabe que estaoperacioacuten no es posible Esto permite que el servicio web controle en tiempo real la forma enla que el cliente interactua con el sistema

Enlaces en cabeceras frente a enlaces Atom

Una alternativa al uso de enlaces Atom en el cuerpo de la respuesta es utilizar enlaces enlas cabeceras de la respuesta (httptoolsietforghtmlrfc5988) Vamos a explicar eacutesto con unejemplo

Consideremos el ejemplo de cancelacioacuten de un pedido que acabamos de ver En lugar deutilizar un enlace Atom para especificar si se permite o no la cancelacioacuten del pedido podemosutilizar la cabecera Link (es uno de los posibles campos que podemos incluir como cabeceraen una respuesta HTTP)) De esta forma si un usuario enviacutea la peticioacuten GET pedidos333 recibiraacute la siguiente respuesta HTTP

HTTP11 200 OKContent-Type applicationxmlLink lthttpejemplocompedidos333canceladogt rel=cancel

ltpedido id=333gt ltpedidogt

La cabecera Link tiene las mismas caracteriacutesticas que un enlace Atom La URI estaacute entrelos signos lt y gt y estaacute seguida por uno o maacutes atributos delimitados por El atributo reles obligatorio y tiene el mismo significado que el correspondiente atributo Atom com el mismonombre En el ejemplo no se muestra pero podriacuteamos especificar el media type utilizando elatributo type

43 HATEOAS y JAX-RS

JAX-RS no proporciona mucho soporte para implementar HATEOAS HATEOAS se definepor la aplicacioacuten por lo que no hay mucho que pueda aportar ninguacuten framework Lo que siacute

Servicios Rest

112

proporciona JAX-RS son algunas clases que podemos utilizar para construir las URIs de losenlaces HATEOAS

Construccioacuten de URIs con UriBuilder

Una clase que podemos utilizar es javaxwsrscoreUriBuilder Esta clase nospermite construir URIs elemento a elemento y tambieacuten permite incluir plantillas de paraacutemetros(segmentos de ruta variables)

Clase UriBuilder meacutetodos para instanciar objetos de la clase

public abstract class UriBuilder public static UriBuilder fromUri(URI uri) throws IllegalArgumentException public static UriBuilder fromUri(String uri) throws IllegalArgumentException public static UriBuilder fromPath(String path) throws IllegalArgumentException public static UriBuilder fromResource(Classltgt resource) throws IllegalArgumentException public static UriBuilder fromLink(Link link) throws IllegalArgumentException

Las instancias de UriBuilder se obtienen a partir de meacutetodos estaacuteticos con la formafromXXX() Podemos inicializarlas a partir de una URI una cadena de caracteres o laanotacioacuten Path de una clase de recurso

Para extraer modificar yo componer una URI se pueden utilizar meacutetodos como

Clase UriBuilder meacutetodos para manipular las URIs

public abstract UriBuilder clone() crea una copia

crea una copia con la informacioacuten de un objeto URIpublic abstract UriBuilder uri(URI uri) throws IllegalArgumentException

meacutetodos para asignarmodificar valores de los atributos de los objetos UriBuilderpublic abstract UriBuilder scheme(String scheme) throws IllegalArgumentExceptionpublic abstract UriBuilder userInfo(String ui)public abstract UriBuilder host(String host) throws IllegalArgumentExceptionpublic abstract UriBuilder port(int port) throws IllegalArgumentExceptionpublic abstract UriBuilder replacePath(String path)

meacutetodos que antildeaden elementos a nuestra URIpublic abstract UriBuilder path(String path)public abstract UriBuilder segment(String segments)public abstract UriBuilder matrixParam(String name Object values)public abstract UriBuilder queryParam(String name Object values)

Servicios Rest

113

meacutetodo que instancia el valor de una plantilla de la URIpublic abstract UriBuilder resolveTemplate(String name Object value)

Los meacutetodos build() construyen la URI Eacutesta puede contener plantillas de paraacutemetros( segmentos de ruta variables) que deberemos inicializar utilizando pares nombrevalor o bienuna lista de valores que reemplazaraacuten a los paraacutemetros de la plantilla en el orden en el queaparezcan

Clase UriBuilder meacutetodos buildXXX() para construir las URIs

public abstract URI buildFromMap(MapltString extends Objectgt values) throws IllegalArgumentException UriBuilderException

public abstract URI build(Object values) throws IllegalArgumentException UriBuilderException

Veamos alguacuten ejemplo que muestra coacutemo crear inicializar componer y construir una URIutilizando un UriBuilder

UriBuilder builder = UriBuilderfromPath(clientesid)builderscheme(http) host(hostname) queryParam(param=param)

Con este coacutedigo estamos definiendo una URI como

httphostnameclientesidparam=param

Puesto que tenemos plantillas de paraacutemetros necesitamos inicializarlos con valores quepasaremos como argumentos para crear la URI final Si queremos reutilizar la URI quecontiene las plantillas deberiacuteamos realizar una llamada a clone() antes de llamar al meacutetodobuild() ya que eacuteste reemplazaraacute los paraacutemetros de las plantillas en la estructura internadel objeto

UriBuilder clone = builderclone()URI uri = clonebuild(ejemplocom 333 valor)

El coacutedigo anterior dariacutea lugar a la siguiente URI

httpejemplocomclientes333param=valor

Tambieacuten podemos definir un objeto de tipo Map que contenga los valores de las plantillas

MapltString Objectgt map = new HashMapltString Objectgt()

Servicios Rest

114

mapput(hostname ejemplocom)mapput(id 333)mapput(param valor)UriBuilder clone = builderclone()URI uri = clonebuildFromMap(map)

Otro ejemplo interesante es el de crear una URI a partir de las expresiones Path definidasen las clases JAX-RS anotadas A continuacioacuten mostramos el coacutedigo

Path(clientes)public class ServicioClientes

Path(id) public Cliente getCliente(PathParam(id) int id)

Podemos referenciar esta clase y el meacutetodo getCliente() a traveacutes de la claseUriBuilder de la siguiente forma

UriBuilder builder = UriBuilderfromResource(ServicioClientesclass)builderhost(hostname)builderpath(ServicioClientesclass getCliente)

El coacutedigo anterior define la siguiente plantilla para la URI

httphostnameclientesid

A partir de esta plantilla podremos construir la URI utilizando alguno de los meacutetodos`buildXXX()

Tambieacuten podemos querer utilizar UriBuilder para crear URIS a partir de plantillas Para ellodisponemos de meacutetodos resolveTemplateXXX() que nos facilitan el trabajo

Clase UriBuilder meacutetodos resolveTemplateXXX() para crear URIs a partir de plantillas

public abstract UriBuilder resolveTemplate(String name Object value)public abstract UriBuilder resolveTemplate(String name Object value boolean encodeSlashInPath)public abstract UriBuilder resolveTemplateFromEncoded(String nameObject value)public abstract UriBuilder resolveTemplates(MapltString Objectgt templateValues)public abstract UriBuilder resolveTemplates( MapltStringObjectgt templateValues boolean encodeSlashInPath) throws IllegalArgumentExceptionpublic abstract UriBuilder resolveTemplatesFromEncoded( MapltString Objectgt templateValues) Devuelve la URI de la plantilla como una cadena de caracterespublic abstract String toTemplate()

Servicios Rest

115

Funcionan de forma similar a los meacutetodos build() y se utilizan para resolver parcialmentelas plantillas contenidas en la URI Cada uno de los meacutetodos devuelve una nueva instanciade UriBuilder de forma que podemos encadenar varias llamadas para resolver todas lasplantillas de la URI Finalmente usaremos el meacutetodo toTemplate() para obtener la nuevaplantilla en forma de String

String original = httphostidString nuevaPlantilla = UriBuilderfromUri(original) resolveTemplate(host localhost) toTemplate()

El valor de nuevaPlantilla para el coacutedigo anterior seriacutea httplocalhostid

URIs relativas mediante el uso de UriInfo

Cuando estamos escribiendo servicios que distribuyen enlaces hay cierta informacioacuten queprobablemente no conozcamos cuando estamos escribiendo el coacutedigo Por ejemplo podemosno conocer todaviacutea los hostnames de los enlaces o incluso los base paths de las URIs en elcaso de que estemos enlazando con otros servicios REST

JAX-RS proporciona una forma sencilla de solucionar estos problemas utilizando la interfazjavaxwsrscoreUriInfo Ya hemos introducido algunas caracteriacutesticas de estainterfaz en sesiones anteriores Ademaacutes de poder consultar informacioacuten baacutesica de la rutatambieacuten podemos obtener instancias de UriBuilder preinicializadas con la URI base utilizadapara definir los servicios JAX-RS o la URI utilizada para invocar la peticioacuten HTTP actual

public interface UriInfo public URI getRequestUri() public UriBuilder getRequestUriBuilder() public URI getAbsolutePath() public UriBuilder getAbsolutePathBuilder() public URI getBaseUri() public UriBuilder getBaseUriBuilder()

Por ejemplo supongamos que tenemos un servicio que permite acceder a Clientes desdeuna base de datos En lugar de tener una URI base que devuelva todos los clientes en unuacutenico documento podemos incluir los enlaces previo y sigiente de forma que podamosnavegar por los datos Vamos a mostrar coacutemo crear estos enlaces utilizando la URI parainvocar la peticioacuten

Path(clientes)public class ServicioClientes GET Produces(applicationxml)

public String getCustomers(Context UriInfo uriInfo)

UriBuilder nextLinkBuilder = uriInfogetAbsolutePathBuilder() nextLinkBuilderqueryParam(inicio 5) nextLinkBuilderqueryParam(total 10) URI next = nextLinkBuilderbuild() rellenar el resto del documento

Servicios Rest

116

Para acceder a la instancia UriInfo que representa al peticioacuten usamos la anotacioacutenjavaxwsrscoreContext para inyectarla como un paraacutemetro del meacutetodo delrecurso RESTObtenemos un UriBuilder preininicializado con la URI utilizada para acceder alservicio

Para el coacutedigo anterior y dependiendo de coacutemo se despliegue el servicio la URI creada podriacuteaser

httporgexpertojavajaxrsclientesinicio=5amptotal=10

Construccioacuten de enlaces (Links) en documentos XML y en cabeceras HTTP

JAX-RS proporciona cierto soporte para construir los enlaces y devolverlos en las cabecerasde respuesta o bien incluirlos en los documentos XML Para ello podemos utilizar las clasesjavawsrscoreLink y javawsrscoreLinkBuilder

Clase abstracta javaxwsrscoreLink

public abstract class Link public abstract URI getUri() public abstract UriBuilder getUriBuilder() public abstract String getRel() public abstract ListltStringgt getRels() public abstract String getTitle() public abstract String getType() public abstract MapltString Stringgt getParams() public abstract String toString()

Link es una clase abstracta que representa todos los metadatos contenidos en una cabecerao en un enlace Atom El meacutetodo getUri() representa el atributo href del enlace Atom Elmeacutetodo getRel() representa el atributo rel y asiacute sucesivamente Podemos referenciar atodos los atributos a traveacutes del meacutetodo getParams() Finalmente el meacutetodo toString()convertiraacute la instancia Link en una cadena de caracteres con el formato de una cabeceraLink

Para crear instancias de Link utilizaremos un LinkBuilder que crearemos con algunode estos meacutetodos

public abstract class Link public static Builder fromUri(URI uri) public static Builder fromUri(String uri) public static Builder fromUriBuilder(UriBuilder uriBuilder) public static Builder fromLink(Link link) public static Builder fromPath(String path) public static Builder fromResource(Classltgt resource) public static Builder fromMethod(Classltgt resource String method)

Servicios Rest

117

Los meacutetodos fromXXX() funcionan de forma similar a UriBuilderfromXXX() Todosinicializan el UriBuilder subyacente que utilizaremos para construir el atributo href delenlace

Los meacutetodos link() uri() y uriBuilder() nos permiten sobreescribir la URIsubyacente del enlace que estamos creando

public abstract class Link interface Builder public Builder link(Link link) public Builder link(String link) public Builder uri(URI uri) public Builder uri(String uri) public Builder uriBuilder(UriBuilder uriBuilder)

Los siguientes meacutetodos nos permiten asignar valores a varios atributos del enlace que estamosconstruyendo

public Builder rel(String rel) public Builder title(String title) public Builder type(String type) public Builder param(String name String value)

Finalmente eacutel meacutetodo build() nos permitiraacute construir el enlace

public Link build(Object values)

El objeto LinkBuilder tiene asociado una UriBuilder subyacente Los valorespasados como paraacutemetros del meacutetodo build() son utilizados por el UriBuilder paracrear una URI para el enlace Veamos un ejemplo

Link link = LinkfromUri(httphostraizclientesid) rel(update)type(textplain) build(localhost 1234)

Si realizamos una llamada a toString() sobre la instancia del enlace ( link )obtendremos lo siguiente

httplocalhostraizclientes1234gt rel=update type=textplain

A continuacioacuten mostramos dos ejemplos que muestran coacutemo crear instancias Link en lascabeceras y en el cuerpo de la respuesta como un enlace Atom

Escritura de enlaces en cabeceras HTTP

Servicios Rest

118

PathGETResponse get() Link link = LinkfromUri(abc)build() Response response = ResponsenoContent() links(link) build() return response

Inclusioacuten de un enlace Atom en el documento XMl de respuesta

import javaxwsrscoreLink

XmlRootElementpublic class Cliente private String nombre private ListltLinkgt enlaces = new ArrayListltLinkgt()

XmlElement public String getNombre() return nombre

public void setNombre(String nom) thisnombre = nom

XmlElement(name = enlace)

XmlJavaTypeAdapter(LinkJaxbAdapterclass) public ListltLinkgt getEnlaces() return enlaces

La clase Link contiene tambieacuten un JaxbAdapter con una implementacioacuten de la claseJAXB XmlAdapter que mapea los objetos JAX-RS de tipo Link a un valor quepuede ser serializado y deserializado por JAXB

El coacutedigo de este ejemplo permite construir cualquier enlace y antildeadirlo a la clase Clientede nuestro dominio Los enlaces seraacuten convertidos a elementos XML que se incluiraacuten en eldocumento XML de respuesta

44 Seguridad

Es importante que los servicios rest permitan un acceso seguro a los datos y funcionalidadesque proporcionan Especialmente para servicios que permiten la realizacioacuten de actualizacionesen los datos Tambieacuten es interesante asegurarnos de que terceros no lean nuestros mensajese incluso permitir que ciertos usuarios accedan a determinadas funcionalidades pero a otrasno

Ademaacutes de la especificacioacuten JAX-RS podemos aprovechar los servicios de seguridad quenos ofrece la web y Java EE y utilizarla en nuestros servicios REST Estos incluyen

AutentificacioacutenHace referencia a la validacioacuten de la identidad del cliente que accede a los serviciosNormalmente implica la comprobacioacuten de si el cliente ha proporcionado unos credenciales

Servicios Rest

119

vaacutelidos tales como el password En este sentido podemos utilizar los mecanismos quenos proporciona la web y las facilidades del contenedor de servlets de Java EE paraconfigurar los protocolos de autentificacioacuten

AutorizacioacutenUna vea que el cliente se ha autenticado (ha validado su identidad) querraacute interactuarcon nuestro servicio REST La autorizacioacuten hace referencia a decidir si un cierto usuariopuede acceder e invocar un determinado meacutetodo sobre una determinada URI Por ejemplopodemos habilitar el acceso a operaciones PUTPOSTDELETE para ciertos usuarios peropara otros no En este caso utilizaremos las facilidades que nos propociona el contenedorde servlets de Java EE para realizar autorizaciones

EncriptadoCuando un cliente estaacute interaccionando con un servicio REST es posible que alguienintercepte los mensajes y los lea si la conexioacuten HTTP no es segura Los datos sensiblesdeberiacutean protegerse con servicios criptograacuteficos tales como SSL

Autentificacioacuten en JAX-RS

Hay varios protocolos de autentificacioacuten En este caso vamos a ver coacutemo realizar unaautenticacioacuten baacutesica sobre HTTP (y que ya habeacuteis utilizado para servlets) Este tipo deautentificacioacuten requiere enviar un nombre de usuario y password codificados como Base-64en una cabecera de la peticioacuten al servidor El servidor comprueba si existe dicho usuario en elsistema y verifica el password enviado Veaacutemoslo con un ejemplo

Supongamos que un cliente no autorizado quiere acceder a nuestros servicios REST

GET clientes333 HTTP11

Ya que la peticioacuten no contiene informacioacuten de autentificacioacuten el servidor deberiacutea responderla siguiente respuesta

HTTP11 401 UnauthorizedWWW-Autenticate Basic realm=Cliente Realm

La respuesta 401 nos indica que el cliente no estaacute autorizado a acceder a dicha URI Lacabecera WWW-Autenticate especifica queacute protocolo de autentificacioacuten se deberiacutea usarEn este caso Basic significa que se deberiacutea utilizar una autentificacioacuten de tipo Basic Elatributo realm identifica una coleccioacuten de recursos seguros en un sitio web En este ejemploindica que solamente estaacuten autorizados a acceder al meacutetodo GET a traveacutes de la URI anteriortodos aquellos uarios que pertenezcan al realm Cliente Realm y seraacuten autentificados porel servidor mediante una autentificacioacuten baacutesica

Para poder realizar la autentificacioacuten el cliente debe enviar una peticioacuten que incluya lacabecera Authorization cuyo valor sea Basic seguido de la siguiente cadena decaracteres loginpassword codificada en Base64 (el valor de login y password representael login y password del usuario) Por ejemplo supongamos que el nombre del usuario esfelipe y el password es locking la cadena felipelocking codificada como Base64es ZmVsaXBlOmxvY2tpbmc= Por lo tanto nuestra peticioacuten deberiacutea ser la siguiente

GET clientes333 HTTP11

Servicios Rest

120

Authorization Basic ZmVsaXBlOmxvY2tpbmc=

El cliente deberiacutea enviar esta cabecera con todas y cada una de las peticiones que haga alservidor

El inconveniente de esta aproximacioacuten es que si la peticioacuten es interceptada por alguna entidadhostil en la red el hacker puede obtner faacutecilmente el usuario y el passwork y utilizarlos parahacer sus propias peticiones Utilizando una conexioacuten HTTP encriptada (HTTPS) se solucionaeste problema

Creacioacuten de usuarios y roles

Para poder utilizar la autentificacioacuten baacutesica necesitamos tener creados previamente los realmsen el servidor de aplicaciones Wildfly y registrar los usuarios que pertenecen a dichos realmsLa forma de hacerlo es ideacutentica a lo que ya habeacuteis visto en la asignatura de ComponentesWeb (a traveacutes del comando add-usersh )

Utilizaremos el realm por defecto ApplicationRealm de Wildfly que nos permitiraacute ademaacutescontrolar la autorizacioacuten mediante la asignacioacuten de roles a usuarios

Lo uacutenico que tendremos que hacer es antildeadir los usuarios a dicho realm a traveacutes de laherramienta $WILDFLY_HOMEbinadd-usersh

Al ejecutarla desde liacutenea de comandos deberemos elegir el ream ApplicationRealm eintroducir los datos para cada nuevo usuario que queramos antildeadir indicando su loginpassword y el grupo (rol) al que queremos que pertenezca dicho usuario

Los datos sobre los nuevos usuarios creados se almacenan en los ficheros application-usersproperties y application-rolesproperties tanto en el directorio$WILDFLY_HOMEstandaloneconfiguration como en $WILDFLY_HOMEdomainconfiguration

Una vez creados los usuarios tendremos que incluir en el fichero de configuracioacuten webxml la siguiente informacioacuten

ltweb-appgt

ltlogin-configgt ltauth-methodgtBASICltauth-methodgt

ltrealm-namegtApplicationRealmltrealm-namegt ltlogin-configgt

ltsecurity-constraintgt ltweb-resource-collectiongt ltweb-resource-namegtcustomer creationltweb-resource-namegt

lturl-patterngtrestresourceslturl-patterngt

lthttp-methodgtPOSTlthttp-methodgt ltweb-resource-collectiongt ltsecurity-constraintgt ltweb-appgt

Servicios Rest

121

El elemento ltlogin-configgt define coacutemo queremos autentificar nuestro despliegueEl subelemento ltauth-methodgt puede tomar los valores BASIC DIGEST orCLIENT_CERT correspondieacutendose con la autentificacioacuten Basic Digest y ClientCertificate respectivamenteEl valor de la etiqueta ltrealm-namegt es el que se mostraraacute como valor del atributorealm de la cabecera WWW-Autenticate si intentamos acceder al recurso sin incluirnuestras credenciales en la peticioacutenEl elemento ltlogin-configgt realmente NO activa la autentificacioacuten Por defectocualquier cliente puede acceder a cualquier URL proporcionada por nuestra aplicacioacutenweb sin restricciones Para forzar la autentificacioacuten debemos especificar el patroacuten URLque queremos asegurar (elemento lturl-patterngt )El elemento lthttp-methodgt nos indica que solamente queremos asegurar laspeticiones POST sobre esta URL Si no incluimos el elemento lthttp-methodgt todoslos meacutetodos HTTP seraacuten seguros En este ejemplo solamente queremos asegurar losmeacutetodos POST dirigidos a la URL restresources

Autorizacioacuten en JAX-RS

Mientras que la autentificacioacuten hacer referencia a establecer y verificar la identidad del usuariola autorizacioacuten tiene que ver con los permisos iquestEl usuario X estaacute autorizado para acceder aun determinado recurso REST

JAX-RS se basa en las especificaciones Java EE y de servlets para definir la forma de autorizara los usuarios En Java EE la autorizacioacuten se realiza asociando uno o maacutes roles con un usuariodado y a continuacioacuten asignando permisos basados en dicho rol Ejemplos de roles puedenser administrador empleado Cada rol tiene asignando unos permisos de acceso adeterminados recursos por lo que asignaremos los permisos utilizando cada uno de los roles

Para poder realizar la autorizacioacuten tendremos que incluir determinadas etiquetas en el ficherode configuracioacuten webxml (tal y como ya habeacuteis visto en la asignatura de ComponentesWeb) Veaacutemoslo con un ejemplo (en el que tambieacuten incluiremos autentificacioacuten)

Volvamos a nuestra aplicacioacuten de venta de productos por internet En esta aplicacioacutenes posible crear nuevos clientes enviando la informacioacuten en formato XML a un recursoJAX-RS localizado por la anotacioacuten Path(clientes) El servicio REST esdesplegado y escaneado por la clase Application anotada con ApplicationPath(servicios) de forma que la URI completa es serviciosclientes Queremosproporcionar seguridad a nuestro servicio de clientes de forma que solamente losadministradores puedan crear nuevos clientes Veamos cuaacutel seriacutea el contenido del ficherowebxml

ltxml version=10gtltweb-appgt ltsecurity-constraintgt ltweb-resource-collectiongt ltweb-resource-namegtcreacion de clientesltweb-resource-namegt lturl-patterngtserviciosclienteslturl-patterngt lthttp-methodgtPOSTlthttp-methodgt ltweb-resource-collectiongt

ltauth-constraintgt ltrole-namegtadminltrole-namegt ltauth-constraintgt ltsecurity-constraintgt

Servicios Rest

122

ltlogin-configgt ltauth-methodgtBASICltauth-methodgt ltrealm-namegtApplicationRealmltrealm-namegt ltlogin-configgt

ltsecurity-rolegt ltrole-namegtadminltrole-namegt ltsecurity-rolegtltweb-appgt

Especificamos queacute roles tienen permiso para acceder mediante POST a la URLservicescustomers Para ello utilizamos el elemento ltauth-constraintgtdentro de ltsecurity-constraintgt Este elemento tiene uno o maacutes subelementosltrole-namegt que definen queacute roles tienen permisos de acceso definidos porltsecurity-constraintgt En nuestro ejemplo estamos dando al rol adminpermisos para acceder a la URL servicescustomers con el meacutetodo POST Si ensu lugar indicamos un ltrole-namegt con el valor cualquier usuario podriacutea accedera dicha URL En otras palabras un ltrole-namegt con el valor significa que cualquierusuario que sea capaz de autentificarse puede acceder al recursoPara cada ltrole-namegt que usemos en nuestras declaraciones ltauth-constraintsgt debemos definir el correspondiente ltsecurity-rolegt en eldescriptor de despliegue

Una limitacioacuten cuando estamos declarando las ltsecurity-contraintsgt para los recursosJAX-RS es que el elemento lturl-patterngt solamente soporta el uso de en el patroacutenurl especificado Por ejemplo rest txt

Encriptacioacuten

Por defecto la especificacioacuten de servlets no requiere un acceso a traveacutes de HTTPSSi queremos forzar un acceso HTTPS podemos especificar un elemento ltuser-data-constraintgt como parte de nuestra definicioacuten de restricciones de seguridad ( ltsecurity-constraintgt ) Vamos a modificar nuestro ejemplo anterior para forzar un acceso a traveacutesde HTTPS

ltweb-appgt ltsecurity-constraintgt

ltweb-resource-collectiongt ltweb-resource-namegtcreacion de clientesltweb-resource-namegt lturl-patterngtserviciosclienteslturl-patterngt lthttp-methodgtPOSTlthttp-methodgt ltweb-resource-collectiongt

ltauth-constraintgt ltrole-namegtadminltrole-namegt ltauth-constraintgt

ltuser-data-constraintgt

lttransport-guaranteegtCONFIDENTIALlttransport-guaranteegt ltuser-data-constraintgt ltsecurity-constraintgt

Servicios Rest

123

ltweb-appgt

Todo lo que tenemos que hacer es declarar un elemento lttransport-guaranteegtdentro de ltuser-data-constraintgt con el valor CONFIDENTIAL Si un usuariointenta acceder a una URL con el patroacuten especificado a traveacutes de HTTP seraacute redirigidoa una URL basada en HTTPS

Anotaciones JAX-RS para autorizacioacuten

Java EE define un conjunto de anotaciones para definir metadatos de autorizacioacuten Laespecificacioacuten JAX-RS sugiere aunque no es obligatorio que las implementaciones pordiferentes vendedores den soporte a dichas anotaciones Eacutestas se encuentran en elpaquete javaxannotationsecurity y son RolesAllowed DenyAll PermitAll yRunAs

La anotacioacuten RolesAllowed define los roles permitidos para ejecutar una determinadaoperacioacuten Si anotamos una clase JAX-RS define el acceso para todas las operacionesHTTP definidas en la clase JAX-RS Si anotamos un meacutetodo JAX-RS la restriccioacuten se aplicasolamente al meacutetodo que se estaacute anotando

La anotacioacuten PermitAll especifica que cualquier usuario autentificado puede invocar anuestras operaciones Al igual que RolesAllowed esta anotacioacuten puede usarse en la clasepara definir el comportamiento por defecto de toda la clase o podemos usarla en cada unode los meacutetodos Veamos un ejemplo

Path(clientes)

RolesAllowed(ADMIN CLIENTE) public class ClienteResource

GET Path(id) Produces(applicationxml) public Cliente getClienter(PathParam(id) int id)

RolesAllowed(ADMIN) POST Consumes(applicationxml) public void crearCliente(Customer cust)

PermitAll GET Produces(applicationxml) public Customer[] getClientes()

Por defecto solamente los usuarios con rol ADMIN y CLIENTE pueden ejecutar losmeacutetodos HTTP definidos en la clase ClienteResourceSobreescribimos el comportamiento por defecto Para el meacutetodo crearCliente()solamente permitimos peticiones de usuarios con rol ADMINSobreescribimos el comportamiento por defecto Para el meacutetodo getClientes() deforma que cualquier usuario autentificado puede acceder a esta operacioacuten a traveacutes de laURI correspondiente con el meacutetodo GET

Servicios Rest

124

La ventaja de utilizar anotaciones es que nos permite una mayor flexibilidad que el usodel fichero de configuracioacuten webxml pudiendo definir diferentes autorizaciones a nivel demeacutetodo

Seguridad programada

Hemos visto como utilizar una seguridad declarativa es decir basaacutendonos en meta-datosdefinidos estaacuteticamente antes de que la aplicacioacuten se ejecute JAX-RS proporciona unaforma de obtener informacioacuten de seguridad que nos permite implementar seguridad de formaprogramada en nuestras aplicaciones

Podemos utilizar la interfaz javaxwsrscoreSecurityContext para determinar laidentidad del usuario que realiza la invocacioacuten al meacutetodo proporcionando sus credencialesTambieacuten podemos comprobar si el usuario pertenece o no a un determinado rol Esto nospermite implementar seguridad de forma programada en nuestras aplicaciones

public interface SecurityContext public Principal getUserPrincipal() public boolean isUserInRole(String role) public boolean isSecure() public String getAuthenticationScheme()

El meacutetodo getUserPrincipal() devuelve un objeto de tipojavaxsecurityPrincipal que representa al usuario que actualmente estaacute realizandola peticioacuten HTTP

El meacutetodo isUserInRole() nos permite determinar si el usuario que realiza la llamadaactual pertenece a un determinado rol

El meacutetodo isSecure() devuelve cierto si la peticioacuten actual es una conexioacuten segura

El meacutetodo getAuthenticationScheme() nos indica queacute mecanismo de autentificacioacuten seha utilizado para asegurar la peticioacuten (valores tiacutepicos devueltos por el meacutetodo son BASIC DIGEST CLIENT_CERT y FORM )

Podemos acceder a una instancia de SecurityContext inyectaacutendola en un campo meacutetodosetter o un paraacutemetro de un recurso utilizando la anotacioacuten Context Veamos un ejemploSupongamos que queremos obtener un fichero de log con todos los accesos a nuestra basede datos de clientes hechas por usuarios que no son administradores

Path(clientes)public class CustomerService GET Produces(applicationxml) public Cliente[] getClientes(Context SecurityContext sec) if (secisSecure() ampamp secisUserInRole(ADMIN)) loggerlog(secgetUserPrincipal() + ha accedido a la base de datos de clientes)

Servicios Rest

125

En este ejemplo inyectamos una instancia de SecurityContext como un paraacutemetro delmeacutetodo getClientes() Utilizamos el meacutetodo SecurityContextisSecure() paradeterminar si se trata de una peticioacuten realizada a traveacutes de un canal seguro (como HTTPS) Acontinuacioacuten utilizamos el meacutetodo SecurityContextisUserInRole() para determinarsi el usuario que realiza la llamada tiene el rol ADMIN o no Finalmente imprimimos el resultadoen nuestro fichero de logs

Con la introduccioacuten del API de filtros en JAX-RS 20 podemos implementar la interfazSecurityContext y sobreescribir la peticioacuten actual sobre SecurityContext utilizandoel meacutetodo ContainerRequestContextsetSecurityContext() Lo interesante deesto es que podemos implementar nuestros propios protocolos de seguridad Por ejemplo

import javaxwsrscontainerContainerRequestContextimport javaxwsrscontainerContainerRequestFilterimport javaxwsrscontainerPreMatchingimport javaxwsrscoreSecurityContextimport javaxwsrscoreHttpHeaders

PreMatchingpublic class CustomAuth implements ContainerRequestFilter protected MyCustomerProtocolHandler customProtocol =

public void filter(ContainerRequestContext requestContext) throws IOException String authHeader = requestgetHeaderString(HttpHeadersAUTHORIZATION) SecurityContext newSecurityContext = customProtocolvalidate(authHeader) requestContextsetSecurityContext(authHeader)

Este filtro no muestra todos los detalles pero siacute la idea Extrae la cabecera Authorizationde la peticioacuten y la pasa a nuestro propio servicio customerProtocol Eacuteste devuelve unaimplementacioacuten de SecurityContext Finalmente sobreescribimos el SecurityContext pordefecto utilizando la nueva implementacioacuten

No vamos a explicar el API de filtros de JAS-RS 20 Como ya habeacuteis visto en la asignaturade Componentes Web los filtros son objetos que se interponen entre el procesamiento delas peticiones tanto del servidor como del cliente

El filtro mostrado en el ejemplo es un filtro de peticioacuten en la parte del servidor Este tipo defiltros se ejecuta antes de que se invoque a un meacutetodo JAX-RS

Servicios Rest

126

45 Ejercicios

Para los ejercicios de esta sesioacuten proporcionamos el MOacuteDULO s4-foroAvanzado quetendreacuteis que usar como plantilla para realizar las tareas planteadas

El proyecto estaacute estructurado loacutegicamente en los siguientes paquetes

bull orgexpertojavanegocio

bull orgexpertojavarest

A su vez cada uno de ellos contiene los subpaquetes api y modelo con las clasesrelacionadas con los servicios proporcionados y los datos utilizados por los serviciosrespectivamente

El API rest implementado es el siguiente

bull Recurso UsuariosResourcejava

GET usuarios proporciona un listado con los usuarios del foro

bull Subrecurso UsuarioResourcejava

GET usuarioslogin proporciona informacioacuten sobre el usuario cuyo login es login

PUT usuarioslogin actualiza los datos de un usuario

DELETE usuarioslogin borra los datos de un usuario

GET usuariosloginmensajes obtiene un listado de los mensajes de un usuario

bull Recurso MensajesResourcejava

GET mensajes proporciona un listado con los mensajes del foro

POST mensajes antildeade un mensaje nuevo en el foro

GET mensajesid proporciona informacioacuten sobre el mensaje cuyo id es id

PUT mensajesid modifica un mensaje

DELETE mensajesid borra un mensaje

Una vez desplegada la aplicacioacuten podeacuteis antildeadir datos a la base de datos del foro utilizandolos datos del fichero srcmainresourcesforosql Para ello simplemente tendreacuteis que invocarla goal Maven correspondiente desde la ventana Maven Projects gt s4-foroAvanzado gt Pluginsgt sql gt sqlexecute

En el directorio srcmainresources teneacuteis un fichero de texto ( instruccionestxt )con informacioacuten adicional sobre la implementacioacuten proporcionada

A partir de las plantillas se pide

Uso de Hateoas (1 puntos)

Vamos a antildeadir a los servicios enlaces a las operaciones que podemos realizar con cadarecurso siguiendo el estilo Hateoas

a Para los usuarios

Servicios Rest

127

bull En el listado de usuarios antildeadir a cada usuario un enlace con relacioacuten self que apuntea la direccioacuten a la que estaacute mapeado el usuario individual

bull En la operacioacuten de obtencioacuten de un usuario individual incluir los enlaces para ver elpropio usuario (self) modificarlo (usuariomodificar) borrarlo (usuarioborrar) o ver losmensajes que envioacute el usuario (usuariomensajes)

b Para los mensajes

bull En el listado de mensajes antildeadir a cada mensaje un enlace con relacioacuten self queapunte a la direccioacuten a la que estaacute mapeado el mensaje individual

bull En la operacioacuten de obtencioacuten de un mensaje individual incluir los enlaces para ver elpropio mensaje (self) modificarlo (mensajemodificar) borrarlo (mensajeborrar) o verlos datos del usuario que envioacute el mensaje (mensajeusuario)

Utiliza postman para comprobar las modificaciones realizadas

Ejercicio seguridad (1 punto)

Vamos ahora a restringir el acceso al servicio para que soacutelo usuarios registrados puedanrealizar modificaciones Se pide

a Antildeadir al usuario pepe en el ApplicationRealm de wildfly con la contrasentildea pepe yperteneciente al grupo (rol) registrado

b Configurar mediante seguridad declarativa para que las operaciones de modificacioacuten(POST PUT y DELETE) soacutelo la puedan realizar los usuarios con rol registrado Utilizarautentificacioacuten de tipo BASIC

c Ahora vamos a hacer que la modificacioacuten o borrado de usuarios soacutelo puedarealizarlas el mismo usuario que va a modificarse o borrarse Para ello utilizaremosseguridad programada En el caso de que el usuario que va a realizar lamodificacioacuten o borrado quiera borrarmodificar otros usuarios lanzaremos la excepcioacutenWebApplicationException(StatusFORBIDDEN)

d Vamos a hacer lo mismo con los mensajes Soacutelo podraacute modificar y borrar mensajes elmismo usuario que los creoacute y al publicar un nuevo mensaje forzaremos que el login delmensaje sea el del usuario que hay autentificado en el sistema

Utiliza postman para comprobar las modificaciones realizadas

Servicios Rest

128

5 Api cliente Procesamiento JSON y Pruebas

Hasta ahora hemos hablado sobre la creacioacuten de servicios web RESTful y hemos probadonuestros servicios utilizando el cliente que nos proporciona IntelliJ curl o Postman pararealizar peticiones y observar las respuestas JAX-RS 20 proporciona un API cliente parafacilitar la programacioacuten de clientes REST que presentaremos en esta sesioacuten

En sesiones anteriores hemos trabajado con representaciones de texto y xmlfundamentalmente Aquiacute hablaremos con maacutes detalle de JSON que constituye otra forma derepresentar los datos de las peticiones y respuestas de servicios REST muy extendida

Finalmente veremos coacutemo implementar pruebas sobre nuestro servicio utilizando el APIcliente y el framework junit

51 API cliente Visioacuten general

La especificacioacuten JAX-RS 20 incorpora un API cliente HTTP que facilita enormemente laimplementacioacuten de nuestros clientes RESTful y constituye una clara alternativa al uso declases Java como javanetURL libreriacuteas externas (como la de Apache) u otras solucionespropietarias

El API cliente estaacute contenido en el paquete javaxwsrsclient y estaacute disentildeado paraque se pueda utilizar de forma fluida (fluent) Esto significa como ya hemos visto que loutilizaremos encadenando una sucesioacuten de llamadas a meacutetodos del API permitieacutendonos asiacuteescribir menos liacuteneas de coacutedigo Baacutesicamente estaacute formado por tres clases principales ClientWebTarget y Response (ya hemos hablado de esta uacuteltima en sesiones anteriores)

Para acceder a un recurso REST mediante el API cliente es necesario seguir los siguientespasos

1 Obtener una instancia de la interfaz Client

2 Configurar la instancia Client a traveacutes de un target (instancia de WebTarget )

3 Crear una peticioacuten basada en el target anterior

4 Invocar la peticioacuten

Vamos a mostrar un coacutedigo ejemplo para ilustrar los pasos anteriores En este caso vamos ainvocar peticiones POST y GET sobre una URL (target) para crear y posteriormente consultarun objeto Cliente que representaremos en formato XML

Client client = ClientBuildernewClient()

WebTarget target =

clienttarget(httpexpertojavaorgclientes)

Response response = target

request()

post(Entityxml(new Cliente(Alvaro Gomez)))

responseclose()

Servicios Rest

129

Cliente cliente = targetqueryParam(nombre Alvaro Gomez) request()

get(Clienteclass) clientclose()

Obtenemos una instancia javaxwsrsclientClientCreamos un WebTargetCreamos la peticioacutenRealizamos una invocacioacuten POSTCerramos (liberamos) el input stream para esta respuesta (en el caso de que esteacutedisponible y abierto) Es una operacioacuten idempotente es decir podemos invocarlamuacuteltiples veces con el mismo efectoA partir de un WebTarget establecemos los valores de los queryParams de la URIde la peticioacuten creamos la peticioacuten y realizamos una invocacioacuten GET

A continuacioacuten explicaremos con detalle los pasos a seguir para implementar un clienteutilizando el API de JAX-RS

Obtenemos una instancia Client

La interfaz javaxwsrsclientClient es el principal punto de entrada del API ClienteDicha interfaz define las acciones e infraestructura necesarias requeridas por un cliente RESTpara consumir un servicio web RESTful Los objetos Client se crean a partir de la claseClientBuilder

Clase ClientBuilder utilizada para crear objetos Client

public abstract class ClientBuilder implements ConfigurableltClientBuildergt public static Client newClient() public static Client newClient(final Configuration configuration)

public static ClientBuilder newBuilder()

public abstract ClientBuilder sslContext(final SSLContext sslContext) public abstract ClientBuilder keyStore(final KeyStore keyStore final char[] password) public ClientBuilder keyStore(final KeyStore keyStore final String password) public abstract ClientBuilder trustStore(final KeyStore trustStore) public abstract ClientBuilder hostnameVerifier(final HostnameVerifier verifier)

public abstract Client build()

La forma maacutes sencilla de crear un objeto Client es medianteClientBuildernewClient() Este meacutetodo proporciona una instancia pre-inicializada detipo Client lista para ser usada La clase ClientBuilder nos proporciona meacutetodos adicionalescon los que podremos configurar diferentes propiedades del objeto

Veamos un ejemplo de uso de ClientBuildernewBuilder() utilizando ademaacutes algunode los meacutetodos proporcionados para configurar nuestra instancia de tipo Client que vamos

Servicios Rest

130

a crear Los meacutetodos register() y property() son meacutetodos de la interfaz Configurable(y que son implementados por ClientBuilder)

Ejemplo de uso de ClientBuilder

Client cliente = ClientBuildernewBuilder()

property(connectiontimeout 100)

sslContext(sslContext)

register(JacksonJsonProviderclass)

build()

Creamos un ClientBuilder invocando al meacutetodo estaacuteticoClientBuildernewBuilder()Asignamos una propiedad especiacutefica de la implementacioacuten concreta de JAX-RS queestemos utilizando que controla el timeout de las conexiones de los socketsEspecificamos el sslContext que queremos utilizar para gestionar las conexiones HTTPRegistramos a traveacutes del meacutetodo register() (de la interfaz Configurable) una claseanotada con Provider Dicha clase conoce coacutemo serializar objetos Java a JSONy viceversaFinalmente realizamos una llamada a build() para crear la instancia Client

Las instancias de Client gestionan conexiones con el cliente utilizando sockets y sonobjetos bastante pesados Se deberiacutean reutilizar las instancias de esta interfaz en la medida delo posible ya que la inicializacioacuten y destruccioacuten de dichas instancias consume mucho tiempoPor lo tanto por razones de rendimiento debemos limitar el nuacutemero de instancias Clienten nuestra aplicacioacuten

Client client = ClientBuildernewClient()

clientclose()

Obtenemos una instancia de tipo Client invocando al meacutetodoClientBuildernewClient()Utilizamos el meacutetodo close() para cerrar la instancia Client despueacutes de realizartodas las invocaciones sobre el target del recurso De esta forma cerramos la conexioacutende forma que se liberan sus recursos y ya no podremos seguir usaacutendola

Recuerda siempre invocar el meacutetodo close() sobre nuestros objetosClient despueacutes de que hayamos realizado todas las invocacionessobre el target dellos recursos REST A menudo los objetos Clientreutilizan conexiones por razones de rendimiento Si no los cerramosdespueacutes de utilizarlos estaremos desaprovechando recursos del sistemamuy valiosos Cerrar la conexioacuten implica cerrar el socket

Igualmente si el resultado de una invocacioacuten a un servicio rest es unainstancia de Response debemos invocar el meacutetodo close() sobredichos objetos Response para liberar la conexioacuten Liberar una conexionsignifica permitir que eacutesta esteacute disponible para otro uso por una instanciaClient Liberar la conexioacuten no implica cerrar el socket

La interfaz Client es una sub-interfaz de Configurable Esto nos permitiraacute utililizarlos meacutetodos property() y register() para cambiar la configuracioacuten y registrarcomponentes en la parte del cliente en tiempo de ejecucioacuten

Servicios Rest

131

Interfaz Client (es una subinterfaz de Configurable)

public interface Client extends ConfigurableltClientgt

public void close()

public WebTarget target(String uri) public WebTarget target(URI uri) public WebTarget target(UriBuilder uriBuilder) public WebTarget target(Link link)

Sin embargo el principal propoacutesito de Client es crear instancias de WebTarget comoveremos a continuacioacuten

Configuramos el target del cliente (URI)

La interfaz javaxwsrsclientWebTarget representa la URI especiacutefica quequeremos invocar para acceder a un recurso REST particular

Interfaz WebTarget (es una subinterfaz de Configurable)

public interface WebTarget extends ConfigurableltWebTargetgt

public URI getUri() public UriBuilder getUriBuilder()

public WebTarget path(String path) public WebTarget resolveTemplate(String name Object value) public WebTarget resolveTemplates(MapltString Objectgt templateValues) public WebTarget matrixParam(String name Object values) public WebTarget queryParam(String name Object values)

La interfaz WebTarget tiene meacutetodos para extender la URI inicial que hayamosconstruido Podemos antildeadir por ejemplo segmentos de path o paraacutemetros deconsulta invocando a los meacutetodos WebTargetpath() o WebTargetqueryParam() respectivamente Si la instancia de WebTarget contiene plantillas de paraacutemetros losmeacutetodos WebTargetresolveTemplate() pueden asignar valores a las variablescorrespondientes Por ejemplo

Ejemplo para crear la URI httpejemplocomclientes123verboso=true

WebTarget target = client

target(httpejemplocomclientesid)

resolveTemplate(id 123)

queryParam(verboso true)

Servicios Rest

132

Inicializamos un WebTarget con una URI que contiene una plantilla con un paraacutemetroid El objeto client es una instancia de la clase `ClientEl meacutetodo resolveTemplate() rellena la expresioacuten id con el valor 123Finalmente antildeadimos a la URI un paraacutemetro de consulta verboso=true

Las instancias de WebTarget son inmutables con respecto a la URI que contienen Estosignifica que los meacutetodos para especificar segmentos de path adicionales y paraacutemetrosdevuelven una nueva instancia de WebTarget Sin embargo las instancias de WebTargetson mutables respecto a su configuracioacuten Por lo tanto la configuracioacuten de objetosWebTarget no crea nuevas instancias

Veamos otro ejemplo

WebTarget base = clientetarget(httpexpertojavaorg)

WebTarget clienteURI = basepath(cliente)

clienteURIregister(MyProviderclass)

base es una instancia de WebTarget con el valor de URI httpexertojavaorgclienteURI es una instancia de WebTarget con el valor de URI httpexertojavaorgclienteConfiguramos clienteURI registrando la clase MyProvider

En este ejemplo creamos dos instancias de WebTarget La instancia clienteURI heredala configuracioacuten de base y posteriormente modificamos la configuramos registrando unaclase Provider Los cambios sobre la configuracioacuten de clienteURI no afectan a laconfiguracioacuten de base ni tampoco se crea una nueva instancia de WebTarget

Los beneficios del uso de WebTarget se hacen evidentes cuando construimos URIscomplejas por ejemplo cuando extendemos nuestra URI base con segmentos de pathadicionales o plantillas El siguiente ejemplo ilustra estas situaciones

WebTarget base = clientetarget(httpexpertojavaorg)

WebTarget saludo = basepath(hola)path(quien) Response res = saludoresolveTemplate(quien mundo)request()get()

base representa la URI httpexpertojavaorgsaludo representa la URI httpexpertojavaholaquien

En el siguiente ejemplo utilizamos una URI base y a partir de ella construimos otras URIs querepresentan servicios diferentes proporcionados por nuestro recurso REST

Client cli = ClientBuildernewClient()WebTarget base = clienttarget(httpejemplowebapi)

WebTarget lectura = basepath(leer)

WebTarget escritura = basepath(escribir)

lectura representa la uri httpejemplowebapileerescritura representa la uri httpejemplowebapiescribir

El meacutetodo WebTargetpath() crea una nueva instancia de WebTarget antildeadiendo a laURI actual el segmento de ruta que se pasa como paraacutemetro

Servicios Rest

133

Construimos y Realizamos la peticioacuten

Una vez que hemos creado y configurado convenientemente el WebTarget que representala URI que queremos invocar tenemos que construir la peticioacuten y finalmente realizarla

Para construir la peticioacuten podemos Utilizar uno de los meacutetodos WebTargetrequest() que mostramos a continuacioacuten

Interfaz WebTarget meacutetodos para comenzar a construir la peticioacuten

public interface WebTarget extends ConfigurableltWebTargetgt public InvocationBuilder request() public InvocationBuilder request(String acceptedResponseTypes) public InvocationBuilder request(MediaType acceptedResponseTypes)

Normalmente invocaremos WebTargetrequest() pasando como paraacutemetro el mediatype aceptado como respuesta en forma de String o utilizando una de las constantes dejavaxwsrscoreMediaType Los meacutetodos WebTargetrequest() devuelven unainstancia de InvocationBuilder una interfaz que proporciona meacutetodos para prepararla peticioacuten del cliente y tambieacuten para invocarla

La interface InvocationBuilder Contiene un conjunto de meacutetodos que nos permitenconstruir diferentes tipos de cabeceras de peticiones Asiacute por ejemplo proporciona variosmeacutetodos acceptXXX() para indicar diferentes tipos MIME lenguajes o encodingaceptados Tambieacuten proporciona meacutetodos cookie() para especificar cookies para enviaral servidor Finalmente proporciona meacutetodos header() para especificar diferentes valoresde cabeceras

Ejemplos de uso de esta interfaz para construir la peticioacuten

Client cli = ClientBuildernewClient()cliinvocation(LinkvalueOf(httpejemplorest))accept(applicationjson)get()si no utilizamos el meacutetodo invocation podemos hacerlo asiacuteclitarget(httpejemplorest)request(applicationjson)get()

Client cliente = ClientBuildernewClient()WebTarget miRecurso = clienttarget(httpejemplowebapimensaje) request(MediaTypeTEXT_PLAIN)

El uso de una constante MediaType es equivalente a utilizar el String que define el tipoMIME

InvocationBuilder builder = miRecursorequest(textplain)

Hemos visto que WebTarget implementa meacutetodos request() cuyosparaacutemetros especifican el tipo MIME de la cabecera Accept de la peticioacutenEl coacutedigo puede resultar maacutes legible si usamos en su lugar el meacutetodo

Servicios Rest

134

InvocationBuilderaccept() En cualquier caso es una cuestioacuten de gustospersonales

Despueacutes de determinar el media type de la respuesta invocamos la peticioacuten realizando unallamada a uno de los meacutetodos de la instancia de InvocationBuilder que se correspondecon el tipo de peticioacuten HTTP esperado por el recurso REST al que va dirigido dicha peticioacutenEstos meacutetodos son

bull get()

bull post()

bull delete()

bull put()

bull head()

bull options()

La interfaz InvocationBuilder es una subinterfaz de la interfaz SyncInvoker y es la queespecifica los meacutetodos anteriores (get post hellip) para realizar peticiones siacutencronas es decirque hasta que no nos conteste el servidor no podremos continuar procesando el coacutedigo enla parte del cliente

Las peticiones GET tienen los siguientes prototipos

Interface SyncInvoker peticiones GET siacutencronas

public interface SyncInvoker ltTgt T get(ClassltTgt responseType) ltTgt T get(GenericTypeltTgt responseType) Response get()

Los primeros dos meacutetodos geneacutericos convertiraacuten una respuesta HTTP con eacutexito a tipos Javaespeciacuteficos indicados como paraacutemetros del meacutetodo El tercero devuelve una instancia de tipoResponse Por ejemplo

Ejemplos de peticiones GET utilizando el API cliente jasx-rx 20

Client cli = ClientBuildernewClient()

peticioacuten get que devuelve una instancia de ClienteCliente cliRespuesta = clitarget(httpejemploclientes123) request(applicationjson)

get(Clienteclass)

peticioacuten get que devuelve una lista de objetos ClienteListltClientegt cliRespuesta2 = clitarget(httpejemploclientes) request(applicationxml)

get(new GenericTypeltListltClientegtgt() )

peticioacuten get que devuelve un objeto de tipo ResponseResponse respuesta = clitarget(httpejemploclientes245)

Servicios Rest

135

request(applicationjson)

get() try if (respuestagetStatus() == 200) Cliente cliRespuesta =

respuestareadEntity(Clienteclass) finally respuestaclose()

En la primera peticioacuten queremos que el servidor nos devuelva la respuesta en formatoJSON y posteriormente la convertiremos en el tipo Cliente utilizando un de loscomponentes MessageBodyReader registradosEn la segunda peticioacuten utilizamos la clase javaxwsrscoreGenericType parainformar al correspondiente MessageBodyReader del tipo de objetos de nuestra ListaPara ello creamos una clase anoacutenima a la que le pasamos como paraacutemetro el tipogeneacuterico que queremos obtenerEn la tercera peticioacuten obtenemos una instancia de Response a partir de la cual podemosobtener el cuerpo del mensaje de respuesta del servidorEl meacutetodo readEntity() asocia el tipo Java solicitado (en este caso el tipo java Cliente) yel contenido de la respuesta recibida con el correspondiente proveedor de entidades (detipo MessageBodyReader) para obtener dicho tipo Java a partir de la respuesta HTTPrecibida En sesiones anteriores hemos utilizado la clase Response desde el servicioREST para construir la respuesta que se enviacutea al cliente

Recordemos algunos de los meacutetodos que podemos utilizar desde el cliente para analizar larespuesta que hemos obtenido

Meacutetodos de la clase Response que utilizaremos desde el cliente

public abstract class Response

public abstract Object getEntity()

public abstract int getStatus()

public abstract ResponseStatusType getStatusInfo()

public abstract MultivaluedMapltString Objectgt getMetadata()

public abstract URI getLocation()

public abstract MediaType getMediaType()

public MultivaluedMapltStringObjectgt getHeaders()

public abstract ltTgt T readEntity(ClassltTgt entityType)

public abstract ltTgt T readEntity(GenericTypeltTgt entityType)

public abstract void close()

El meacutetodo getEntity() devuelve el objeto Java correspondiente al cuerpo delmensaje HTTPEl meacutetodo getStatus() devuelve el coacutedigo de respuesta HTTPEl meacutetodo getStatusInfo() devuelve la informacioacuten de estado asociada con larespuestaEl meacutetodo getMetadata() devuelve una instancia de tipo MultivaluedMap con lascabeceras de la respuesta

Servicios Rest

136

El meacutetodo getLocation() devuelve la URI de la cabecera Location de la respuestaEl meacutetodo getMediaType() devuelve el mediaType del cuerpo de la respuestaEl meacutetodo getHeaders() devuelve las cabeceras de respuesta con sus valorescorrespondientesEl meacutetodo readEntity() devuelve la entidad del cuerpo del mensaje utilizando unMessageBodyReader que soporte el mapeado del inputStream de la entidad a la claseJava especificada como paraacutemetroEl meacutetodo readEntity() tambieacuten puede devolver una clase geneacuterica si se disponedel MessageBodyReader correspondienteEl meacutetodo close() cierra el input stream correspondiente a la entidad asociada delcuerpo del mensaje (en el caso de que esteacute disponible y abierto) Tambieacuten liberacualquier otro recurso asociado con la respuesta (como por ejemplo datos posiblementealmacenados en un buffer)

Veamos otro ejemmplo Si el recurso REST espera una peticioacuten HTTP GET invocaremosel meacutetodo InvocationBuilderget() El tipo de retorno del meacutetodo deberiacuteacorresponderse con la entidad devuelta por el recurso REST que atenderaacute la peticioacuten

Client cliente = ClientBuildernewClient()WebTarget miRecurso = clientetarget(httpejemplowebapilectura)String respuesta = miRecursorequest(MediaTypeTEXT_PLAIN) get(Stringclass)

O tambieacuten podriacuteamos codificarlo como

Client cliente = ClientBuildernewClient()String respuesta = cliente target(httpejemplowebapilectura) request(MediaTypeTEXT_PLAIN) get(Stringclass)

Si el tipo de retorno de la peticioacuten GET es una coleccioacuten usaremosjavaxwsrscoreGenericTypeltTgt como paraacutemetro del meacutetodo en donde T es eltipo de la coleccioacuten

ListltPedidoAlmacengt pedidos = client target(httpejemplowebapilectura) path(pedidos) request(MediaTypeAPPLICATION_XML) get(new GenericTypeltListltPedidoAlmacengtgt() )

Si el recurso REST destinatario de la peticioacuten espera una peticioacuten de tipo HTTP POSTinvocaremos el meacutetodo InvocationBuilderpost()

Las peticiones POST tienen los siguientes prototipos

Interface SyncInvoker peticiones POST siacutencronas

public interface SyncInvoker ltTgt T post(Entityltgt entity ClassltTgt responseType)

Servicios Rest

137

ltTgt T post(Entityltgt entity GenericTypeltTgt responseType) Response post(Entityltgt entity)

Los primeros dos meacutetodos geneacutericos enviacutean una entidad (clase java + tipo MIME asociado)indicada como primer paraacutemetro del meacutetodo y como segundo paraacutemetro se indica el tipo javaal que se convertiraacute la respuesta recibida El tercero enviacutea una entidad y devuelve una instanciade tipo Response Por ejemplo

Veamos un ejemplo de invocacioacuten de peticiones POST

Ejemplo de peticioacuten POST utilizando el API cliente jasx-rx 20

Client cli = ClientBuildernewClient()Pedido pe = new PedidoAlmacen()Pedido peRespuesta = cli target() request() post(Entityentity(new Pedido() applicationjson) Pedidoclass)

En este caso estamos realizando una peticioacuten POST Como payload del mensaje enviamosun objeto Pedido representado en formato json La entidad esperada como respuesta debeser de tipo Pedido

Esto implica que en el lado del servidor el meacutetodo que atiende la peticioacuten Post tendraacute unparaacutemetro de tipo Pedido y se deberaacuten serializar los objetos de tipo Pedido a json ya que esel tipo MIME asociado a esta entidad ( especificado en la cabera Content-Type de la peticioacutenHTTP)

La clase Entity encapsula los objetos Java que queremos enviar con las peticiones GET oPOST No tiene un constructor puacuteblico En su lugar tenemos que invocar uno de sus meacutetodosestaacuteticos

Clase javaxwsrsclientEntity

public final class EntityltTgt

public static ltTgt EntityltTgt entity(T entity String mediaType)

public static ltTgt EntityltTgt entity(T entity MediaType mediaType)

public static ltTgt EntityltTgt xml(final T entity)

public static ltTgt EntityltTgt json(final T entity)

public static ltTgt EntityltTgt text(T entity)

public static EntityltFormgt form(final Form form)

El meacutetodo estaacutetico entity() crea una entidad (clase Java) con un tipo MIME asociado dadopor la cadena de caracteres mediaTypeEl meacutetodo estaacutetico entity() crea una entidad (clase Java) con un tipo MIME indicado enmediaTypeEl meacutetodo xml crea una entidad (clase Java) con el tipo MIME applicationxml

Servicios Rest

138

El meacutetodo json crea una entidad (clase Java) con el tipo MIME applicationjsomEl meacutetodo text crea una entidad (clase Java) con el tipo MIME textplainEl meacutetodo form crea una entidad (clase Java) con el tipo MIME applicationx-www-form-urlencoded

Veamos otro ejemplo de invocacioacuten POST que utiliza la clase Entity

Ejemplo de peticioacuten POST y uso de clase Entity

NumSeguimiento numSeg = client target(httpejemplowebapiescritura)

request(MediaTypeAPPLICATION_XML)

post(Entityxml(pedido) NumeroSeguimientoclass)

Especificamos como paraacutemetro de la peticioacuten request() el tipo MIME que aceptamos enla respuesta (cabecera HTTP Accept)Realizamos una peticioacuten POST El cuerpo del mensaje se crea con la llamadaEntityxml(pedido) El tipo Entity encapsula la entidad del mensaje (tipo JavaPedido) y el tipo MIME asociado (tipo MIME applicationxml)

Veamos un ejemplo en el que enviamos paraacutemetros de un formulario en una peticioacuten POST

Ejemplo de enviacuteo de datos de un formulario en una peticioacuten POST

Form form = new Form()param(nombre Pedro) param(apellido Garcia)Response response = clienttarget(httpejemploclientes) request() post(Entityform(form))responseclose()

La peticioacuten POST del coacutedigo anterior enviacutea los datos del formulario y espera recibir comorespuesta una entidad de tipo Response

El coacutedigo en el lado del servidor seraacute similar a eacuteste

Servicio rest que sirve una peticioacuten POST a partir de datos de un formulario

POSTPath(clientes)Produces(texthtml)public Response crearCliente(FormParam(nombre)String nom FormParam(apellido)String ape) creamos el nuevo cliente return Responseok(RESPONSE_OK)build()

Manejo de excepciones

Veamos queacute ocurre si se produce una excepcioacuten cuando utilizamos una forma de invocacioacutenque automaacuteticamente convierte la respuesta en el tipo especificado Supongamos el siguienteejemplo

Servicios Rest

139

Cliente cli = clienttarget(httptiendacomclientes123) request(applicationjson) get(Clienteclass)

En este escenario el framework del cliente convierte cualquier coacutedigo de error HTTP en unade las excepciones que antildeade JAX-RS 20 (BadRequesException ForbiddenExceptionhellip) yque ya hemos visto Podemos capturar dichas excepciones en nuestro coacutedigo para tratarlasadecuadamente

try Cliente cli = clienttarget(httptiendacomclientes123) request(applicationjson) get(Clienteclass) catch (NotAcceptableException notAcceptable) catch (NotFoundException notFound)

Si el servidor responde con un error HTTP no cubierto por alguna excepcioacutenespeciacutefica JAX-RS entonces se lanza una excepcioacuten de propoacutesito general La claseClientErrorException cubre cualquier coacutedigo de error en la franja del 400 La claseServerErrorException cubre cualquier coacutedigo de error en la franja del 500

Si el servidor enviacutea alguna de los coacutedigos de respuesta HTTP 3xx (clasificados como coacutedigosde la categoriacutea redireccioacuten) el API cliente lanza una RedirectionException a partir de lacual podemos obtener la URL para poder tratar la redireccioacuten nosotros mismos Por ejemplo

WebTarget target = clienttarget(httptiendacomclientes123)boolean redirected = false

Cliente cli = nulldo try cli = targetrequest(applicationjson) get(Clienteclass) catch (RedirectionException redirect) if (redirected) throw redirect redirected = true target = clienttarget(redirectgetLocation()) while (cli == null)

En este ejemplo volvemos a iterar si recibimos un coacutedigo de respuesta 3xx El coacutedigo seasegura de que soacutelo permitimos un coacutedigo de este tipo cambiando el valor de la variableredirect en el bloque en el que capturamos la exceptioacuten A continuacioacuten cambiamos elWebTarget (en el bloque catch ) al valor de la cabecera Location de la respuesta delservidor

Los coacutedigos de estado HTTP 3xx indican que es neceario realizar algunaaccioacuten adicional para que el servidor pueda completar la peticioacuten Laaccioacuten requerida puede llevarse a cabo sin necesidad de interactuar con el

Servicios Rest

140

cliente soacutelo si el meacutetodo utilizado en la segunda peticioacuten es GET o HEAD(ver httpwwww3orgProtocolsrfc2616rfc2616-sec10html)

52 Procesamiento JSON

JSON (JavaScript Object Notation) es un formato para el intercambio de datos basado en textoderivado de Javascript (Javascript disponde de una funcioacuten nativa eval() para convertirstreams JSON en objetos con propiedades que son accesibles sin necesidad de manipularninguna cadena de caracteres)

La especificacioacuten JSR 3539 proporciona un API para el procesamiento de datos JSON(parsing transformacioacuten y consultas)

La gramaacutetica de los objetos JSON es bastante simple Soacutelo se requieren dos estructurasobjetos y arrays Un objeto es un conjunto de pares nombre-valor y un array es una lista devalores JSON define siete tipos de valores string number object array true false y null

El siguiente ejemplo muestra datos JSON para un objeto que contiene pares nombre-valor

Ejemplo formato JSON

nombre John apellidos Smith edad 25 direccion calle 21 2nd Street ciudad New York codPostal 10021 telefonos [ tipo fijo numero 212 555-1234 tipo movil numero 646 555-4567 ]

El objeto anterior tiene cinco pares nombre-valor

bull Los dos primeros son nombre y apellidos con el valor de tipo String

bull El tercero es edad con el valor de tipo number

bull El cuarto es direccion con el valor de tipo object

bull El quinto es telefonos cuyo valor es de tipo array con dos objetos

JSON tiene la siguiente sintaxis

bull Los objetos estaacuten rodeados por llaves sus pares de elementos nombre-valor estaacutenseparados por una coma y el nombre y el valor de cada par estaacuten separados por dospuntos Los nombres en un objeto son de tipo String mientras que sus valores

9 httpsjcporgaboutJavacommunityprocessfinaljsr353indexhtml

Servicios Rest

141

pueden ser cualquiera de los siete tipos que ya hemos indicado incluyendo a otro objetou otro array

bull Los arrays estaacuten rodeados por corchetes [] y sus valores estaacuten separados por una coma Cada valor en un array puede ser de un tipo diferente incluyendo a otro objeto o array

bull Cuando los objetos y arrays contienen otros objetos yo arrays los datos adquieren unaestructura de aacuterbol

Los servicios web RESTful utilizan JSON habitualmente tanto en las peticiones como en lasrespuestas La cabecera HTTP utilizada para indicar que el contenido de una peticioacuten o unarespuesta es JSON es la siguiente

Content-Type applicationjson

La representacioacuten JSON es normalmente maacutes compacta que las representaciones XML debidoa que JSON no tiene etiquetas de cierre A diferencia de XML JSON no tiene un esquemade definicioacuten y validacioacuten de datos ampliamente aceptado

Actualmente las aplicaciones Java utilizan diferentes libreriacuteas para producirconsumir JSONque tienen que incluirse junto con el coacutedigo de la aplicacioacuten incrementando asiacute el tamantildeo delarchivo desplegado El API de Java para procesamiento JSON proporciona un API estaacutendarpara analizar y generar JSON de forma que las aplicaciones que utilicen dicho API sean maacutesligeras y portables

Para generar y parsear datos JSON hay dos modelos de programacioacuten que son similares alos usados para documentos XML

bull El modelo de objetos crea un aacuterbol en memoria que representa los datos JSON

bull El modelo basado en streaming utiliza un parser que lee los datos JSON elemento aelemento (uno cada vez)

Java EE incluye soporte para JSR 353 de forma que el API de java para procesamiento JSONse encuentra en los siguientes paquetes

bull El paquete javaxjson contiene interfaces para leer escribir y construir datos JSONseguacuten el modelo de objetos asiacute como otras utilidades

bull El paquete javaxjsonstream contiene una interfaz para parsear y generar datosJSON para el modelo streaming

Vamos a ver coacutemo producir y consumir datos JSON utilizando cada uno de los modelos

53 Modelo de procesamiento basado en el modelo de objetos

En este caso se crea un aacuterbol en memoria que representa los datos JSON (todos losdatos) Una vez construido el aacuterbol se puede navegar por eacutel analizarlo o modificarlo Estaaproximacioacuten es muy flexible y permite un procesamiento que requiera acceder al contenidocompleto del aacuterbol En contrapartida normalmente es maacutes lento que el modelo de streamingy requiere utilizar maacutes memoria El modelo de objetos genera una salida JSON navegandopor el aacuterbol entero de una vez

El siguiente coacutedigo muestra coacutemo crear un modelo de objetos a partir de datos JSON desdeun fichero de texto

Creacioacuten de un modelos de objetos a partir de datos JSON

Servicios Rest

142

import javaioFileReaderimport javaxjsonJsonimport javaxjsonJsonReaderimport javaxjsonJsonStructureJsonReader reader = JsoncreateReader(new FileReader(datosjsontxt))JsonStructure jsonst = readerread()

El objeto jsonst puede ser de tipo JsonObject o de tipo JsonArray dependiendo delos contenidos del fichero JsonObject y JsonArray son subtipos de JsonStructure Este objeto representa la raiacutez del aacuterbol y puede utilizarse para navegar por el aacuterbol o escribirloen un stream como datos JSON

Vamos a mostrar alguacuten ejemplo en el que utilicemos un StringReader

Objeto JSON con dos pares nombre-valor

jsonReader = JsoncreateReader(new StringReader( + manzanaroja + plaacutetanoamarillo + ))JsonObject json = jsonReaderreadObject()

jsongetString(manzana) jsongetString(plaacutetano)

El meacutetodo getString() devuelve el valor del string para la clave especificadacomo paraacutemetro Pueden utilizarse otros meacutetodos getXXX() para acceder al valorcorrespondiente de la clave en funcioacuten del tipo de dicho objeto

Un array con dos objetos cada uno de ellos con un par nombre-valor puede leerse como

Array con dos objetos

jsonReader = JsoncreateReader(new StringReader([ + manzanarojo + plaacutetanoamarillo + ]))

JsonArray jsonArray = jsonReaderreadArray()

La interfaz JsonArray tambieacuten tiene meacutetodos get para valores de tipo boolean integer y String en el iacutendice especificado (esta interfaz hereda de javautilList)

Creacioacuten de un modelos de objetos desde el coacutedigo de la aplicacioacuten

A continuacioacuten mostramos un ejemplo de coacutedigo para crear un modelo de objetos medianteprogramacioacuten

Ejemplo de creacioacuten de un modelo de objetos JSON desde programacioacuten

import javaxjsonJsonimport javaxjsonJsonObjectJsonObject modelo =

JsoncreateObjectBuilder() add(nombre Duke)

Servicios Rest

143

add(apellidos Java) add(edad 18) add(calle 100 Internet Dr) add(ciudad JavaTown) add(codPostal 12345) add(telefonos

JsoncreateArrayBuilder() add(JsoncreateObjectBuilder() add(tipo casa) add(numero 111-111-1111)) add(JsoncreateObjectBuilder() add(tipo movil) add(numero 222-222-2222))) build()

El tipo JsonObject representa un objeto JSON El meacutetodoJsoncreateObjectBuilder() crea un modelo de objetos en memoria antildeadiendoelementos desde el coacutedigo de nuestra aplicacioacutenEl meacutetodo JsoncreateArrayBuilder() crea un modelo de arrays en memoriaantildeadiendo elementos desde el coacutedigo de nuestra aplicacioacuten

El objeto modelo de tipo JsonObject representa la raiacutez del aacuterbol que es creado anidandollamadas a meacutetodos acuteadd()acute y construyendo el aacuterbol a traveacutes del meacutetodo build()

La estructura JSON generada es la siguiente

Ejemplo formato JSON generado mediante programacioacuten

nombre Duke apellidos Java edad 18 calle 100 Internet Dr ciudad JavaTown codPostal 12345 telefonos [ tipo casa numero 111-111-1111 tipo movil numero 222-222-2222 ]

Navegando por el modelo de objetos

A continuacioacuten mostramos un coacutedigo de ejemplo para navegar por el modelo de objetos

import javaxjsonJsonValueimport javaxjsonJsonObjectimport javaxjsonJsonArrayimport javaxjsonJsonNumberimport javaxjsonJsonStringpublic static void navegarPorElArbol(JsonValue arbol String clave)

Servicios Rest

144

if (clave = null) Systemoutprint(Clave + clave + ) switch(arbolgetValueType()) case OBJECT Systemoutprintln(OBJETO) JsonObject objeto = (JsonObject) arbol for (String nombre objectkeySet()) navegarPorElArbol(objectget(nombre) name) break case ARRAY Systemoutprintln(ARRAY) JsonArray array = (JsonArray) arbol for (JsonValue val array) navegarPorElArbol(val null) break case STRING JsonString st = (JsonString) arbol Systemoutprintln(STRING + stgetString()) break case NUMBER JsonNumber num = (JsonNumber) arbol Systemoutprintln(NUMBER + numtoString()) break case TRUE case FALSE case NULL Systemoutprintln(arbolgetValueType()toString()) break

El meacutetodo navegarPorElArbol() podemos usarlo con el ejemplo anterior de la siguienteforma

navegarPorElArbol(modelo OBJECT)

El meacutetodo navegarPorElArbol() tiene dos argumentos un elemento JSON y una claveLa clave se utiliza para imprimir los pares clave-valor dentro de los objetos Los elementos enel aacuterbol se representan por el tipo JsonValue Si el elemento es un objeto o un array serealiza una nueva llamada a este meacutetodo es invocada para cada elemento contenido en elobjeto o el array Si el elemento es un valor eacuteste se imprime en la salida estaacutendar

El meacutetodo JsonValuegetValueType() identifica el elemento como un objeto un arrayo un valor Para los objetos el meacutetodo JsonObjectkeySet() devuelve un conjunto deStrings que contienene las claves de los objetos y el meacutetodo JsonObjectget(Stringnombre) devuelve el valor del elemento cuya clave es nombre Para los arraysJsonArray implementa la interfaz ListltJsonValuegt Podemos utilizar bucles formejorados con el valor de SetltStringgt devuelto por JsonObjectkeySet() y coninstancias de JsonArray tal y como hemos mostrado en el ejemplo

Escritura de un modelo de objetos en un stream

Los modelos de objetos creados en los ejemplos anteriores pueden escribirse en un streamutilizando la clase JsonWriter de la siguiente forma

Servicios Rest

145

import javaioStringWriterimport javaxjsonJsonWriterStringWriter stWriter = new StringWriter()

JsonWriter jsonWriter = JsoncreateWriter(stWriter)

jsonWriterwriteObject(modelo)

jsonWriterclose()

String datosJson = stWritertoString()Systemoutprintln(datosJson)

El meacutetodo JsoncreateWriter() toma como paraacutemetro un OutputStreamEl meacutetodo JsonWriterwriteObject() escribe el objeto JsonObject en elstreamEl meacutetodo JsonWriterclose() cierra el stream de salida

Modelo de procesamiento basado en streaming

El modelo de streaming utiliza un parser basado en eventos que va leyendo los datos JSONde uno en uno El parser genera eventos y detiene el procesamiento cuando un objeto o arraycomienza o termina cuando encuentra una clave o encuentra un valor Cada elemento puedeser procesado o rechazado por el coacutedigo de la aplicacioacuten y a continuacioacuten el parser continuacuteacon el siguiente evento Esta aproximacioacuten es adecuada para un procesamiento local en elcual el procesamiento de un elemento no requiere informacioacuten del resto de los datos El modelode streaming genera una salida JSON para un determinado stream realizando una llamada auna funcioacuten con un elemento cada vez

A continuacioacuten veamos con ejemplos coacutemo utilizar el API para el modelo de streaming

bull Para leer datos JSON utilizando un parser (JsonParser)

bull Para escribir datos JSON utilizando un generador (JsonGenerator)

Lectura de datos JSON

El API para el modelo streaming es la aproximacioacuten maacutes eficiente para parsear datos JSONutilizando eventos

import javaxjsonJsonimport javaxjsonstreamJsonParserJsonParser parser = JsoncreateParser(new StringReader(datosJson))while (parserhasNext()) JsonParserEvent evento = parsernext() switch(evento) case START_ARRAY case END_ARRAY case START_OBJECT case END_OBJECT case VALUE_FALSE case VALUE_NULL case VALUE_TRUE Systemoutprintln(eventotoString())

Servicios Rest

146

break case KEY_NAME Systemoutprint(eventotoString() + + parsergetString() + - ) break case VALUE_STRING case VALUE_NUMBER Systemoutprintln(eventotoString() + + parsergetString()) break

El ejemplo consta de tres pasos

1 Obtener una instancia de un parser invocando el meacutetodo estaacuteticoJsoncreateParser()

2 Iterar sobre los eventos del parser utilizando los meacutetodos JsonParserhasNext() yJsonParsernext()

3 Realizar un procesamiento local para cada elemento

El ejemplo muestra los diez posibles tipos de eventos del parser El meacutetodoJsonParsernext() avanza al siguiente evento Para los tipos de eventos KEY_NAME VALUE_STRING y VALUE_NUMBER podemos obtener el contenido del elemento invocandoal meacutetodo JsonParsergetString() Para los eventos VALUE_NUMBER podemostambieacuten usar los siguientes meacutetodos

bull JsonParserisIntegralNumber

bull JsonParsergetInt

bull JsonParsergetLong

bull JsonParsergetBigDecimal

El parser genera los eventos START_OBJECT y END_OBJECT para un objeto JSON vaciacuteo

Para un objeto con dos pares nombre-valor

manzajaroja plaacutetanoamarillo

Mostramos los eventos generados

START_OBJECT manzajaKEY_NAMErojaVALUE_STRING plaacutetanoKEY_NAMEamarilloVALUE_STRINGEND_OBJECT

Los eventos generados para un array con dos objetos JSON seriacutean los siguientes

[START_ARRAY START_OBJECT manzajaKEY_NAMErojaVALUE_STRING END_OBJECT

Servicios Rest

147

START_OBJECT plaacutetanoKEY_NAMEamarilloVALUE_STRING END_OBJECT]END_ARRAY

Escritura de datos JSON

El siguiente coacutedigo muestra coacutemo escribir datos JSON en un fichero utilizando el API para elmodelo de streaming

Ejemplo de escritura de datos JSON con el modelo de streaming

FileWriter writer = new FileWriter(testtxt)JsonGenerator gen = JsoncreateGenerator(writer)genwriteStartObject() write(nombre Duke) write(apellidos Java) write(edad 18) write(calle 100 Internet Dr) write(ciudad JavaTown) write(codPostal 12345) writeStartArray(telefonos) writeStartObject() write(tipo casa) write(numero 111-111-1111) writeEnd() writeStartObject() write(tipo movil) write(numero 222-222-2222) writeEnd() writeEnd() writeEnd()genclose()

Este ejemplo obtiene un generador JSON invocando al meacutetodo estaacuteticoJsoncreateGenerator() que toma como paraacutemetro un output stream o un writerstream El ejemplo escribe los datos JSON en el fichero texttxt anidando llamadas a losmeacutetodos write() writeStartArray() writeStartObject() and writeEnd() El meacutetodo JsonGeneratorclose() cierra el output stream o writer stream subyacente

54 Pruebas de servicios REST

Hasta ahora hemos visto varias formas de probar nuestros servicios REST desde liacutenea decomandos con Curl desde IntelliJ con la herramienta Test RESTFul Web Service y desde elnavegador Chrome con Postman (siendo esta uacuteltima la que maacutes hemos utilizado)

Vamos a ver coacutemo implementar tests para nuestros servicios REST utilizando Maven y JUnitPara ello repasaremos algunas cuestiones baacutesicas sobre los ciclos de vida de Maven10

Ciclo de vida de Maven y tests JUnit

Un ciclo de vida en Maven es una secuencia de acciones determinada que defineel proceso de construccioacuten de un proyecto en concreto Como resultado del proceso deconstruccioacuten de un proyecto obtendremos un artefacto (fichero) de un cierto tipo (porejemplo jar war earhellip) Por lo tanto podriacuteamos decir que un ciclo de vida estaacute formado por

10 httpsmavenapacheorgref333maven-corelifecycleshtml

Servicios Rest

148

las acciones necesarias para convertir nuestros archivos fuente que constituyen el proyectoen por ejemplo un jar un warhellip

Maven propone 3 ciclos de vida es decir tres posibles secuencias de acciones que podemosutilizar (y modificar a nuestra conveniencia) para construir nuestro proyecto Dichos ciclos devida son clean site y el denominado default-lifecycle

Cada ciclo de vida estaacute formado por fases Una fase es un concepto abstracto y define el tipode acciones que se deberiacutean llevar a cabo Por ejemplo una fase del ciclo de vida por defectoes compile para referirse a las acciones que nos permiten convertir los ficheros java en losficheros class correspondientes

Cada fase estaacute formada por un conjunto de goals que son las acciones que se llevaraacuten a caboen cada una de las fases Las goals no viven de forma independiente sino que cualquiergoal siempre forma parte de un plugin Maven Podriacuteamos decir que un plugin por lo tantoes una agrupacioacuten loacutegica de una serie de goals relacionadas Por ejemplo el plugin wildflycontiene una serie de goals para desplegar re-desplegar deshacer-el-despliegue arrancarel servidor etc es decir agrupa las acciones que podemos realizar sobre el servidor wildflyUna goal se especifica siempre anteponiendo el nombre del plugin al que pertenece seguidode dos puntos por ejemplo wildflydeploy indica que se trata de la goal deploy que perteneceal plugin wildfly de maven

Pues bien por defecto Maven asocia ciertas goals a las fases de los tres ciclos de vidaCuando se ejecuta una fase de un ciclo de vida por ejemplo mvn package se ejecutan todaslas goals asociadas a todas las fases anteriores a la fase package en orden y finalmentelas goals asociadas a la fase package Por supuesto podemos alterar en cualquier momentoeste comportamiento por defecto incluyendo los plugins y goals correspondientes dentro dela etiqueta ltbuildgt en nuestro fichero de configuracioacuten pomxml

Vamos a implementar tests JUnit Los tests como ya habeacuteis visto en sesiones anterioresen el directorio srctest Algunas normas importantes son que los tests pertenezcan almismo paquete loacutegico al que pertencen las clases Java que estamos probando Por ejemplosi estamos haciendo pruebas sobre las clases del paquete orgexpertojavarest los testsdeberiacutean pertenecer al mismo paquete aunque fiacutesicamente el coacutedigo fuente y sus pruebasestaraacuten separados (el coacutedigo fuente estaraacute en srcmain y los tests en srctest)

Para realizar pruebas sobre nuestros servicios REST necesitamos que el servidor Wilfly esteacuteen marcha Tambieacuten necesitamos empaquetar el coacutedigo en un fichero war y desplegarlo enel servidor todo esto ANTES de ejecutar los tests

Las acciones para arrancar el servidor Wilfly y desplegar nuestra aplicacioacuten en eacutel NO formanparte de las acciones (o goals) incluidas por defecto en el ciclo de vida por defecto de Mavencuando nuestro proyecto tiene que empaquetarse como un war (etiqueta ltpackaginggt denuestro pomxml) Podeacuteis consultar aquiacute11 la lista de goals asociadas a las fases del ciclo devida por defecto de Maven

Por otro lado en el ciclo de vida por defecto se incluye una goal para ejecutar los testsasociada a la fase test Dicha goal es surefiretest El problema es que por defecto la fasetest se ejecuta ANTES de la fase package y por lo tanto antes de empaquetar y desplegarnuestra aplicacioacuten en Wildfly

Por lo tanto tendremos que alterar convenientemente este comportamiento por defectopara que se ejecuten las acciones de nuestro proceso de construccioacuten que necesitemos

11 httpsmavenapacheorgref333maven-coredefault-bindingshtmlPlugin_bindings_for_war_packaging

Servicios Rest

149

y en el orden en el que lo necesitemos Como ya hemos indicado antes esto lo haremosincluyendo dichas acciones en la etiqueta ltbuildgt de nuestro pomxml y configurandolasconvenientemente para asegurarnos que el orden en el que se ejecutan es el que queremos

La siguiente figura muestra parte de la secuencia de fases llevadas a cabo por Maven ensu ciclo de vida por defecto Para conseguir nuestros propoacutesitos simplemente antildeadiremosla goals wildflydeploy y la asociaremos a la fase pre-integration-test y cambiaremos lafase a la que estaacute asociada la goal surefiretest para que los tests se ejecuten DESPUEacuteS dehaber desplegado el war en Wildfly

A continuacioacuten mostramos los cambios que tenemos que realizar en el fichero de configuracioacutenpomxml

Adicioacuten de las goals wildflydeploy y surefiretest a las fases pre-integration-test ysurefiretest respectivamente

lt-- forzamos el despliegue del war generado durante la fase pre-integration-test justo despueacutes de obtener dicho war--gtltplugingt ltgroupIdgtorgwildflypluginsltgroupIdgt ltartifactIdgtwildfly-maven-pluginltartifactIdgt ltversiongt102Finalltversiongt ltconfigurationgt lthostnamegtlocalhostlthostnamegt ltportgt9990ltportgt ltconfigurationgt

Servicios Rest

150

ltexecutionsgt ltexecutiongt ltidgtwildfly-deployltidgt ltphasegtpre-integration-testltphasegt ltgoalsgt ltgoalgtdeployltgoalgt ltgoalsgt ltexecutiongt ltexecutionsgtltplugingt

lt--ejecutaremos los test JUnit en la fase integration-test inmediatamente despueacutes de la fase pre-integration-test y antes de la fase verify--gtltplugingt ltgroupIdgtorgapachemavenpluginsltgroupIdgt ltartifactIdgtmaven-surefire-pluginltartifactIdgt ltversiongt218ltversiongt ltconfigurationgt ltskipgttrueltskipgt ltconfigurationgt ltexecutionsgt ltexecutiongt ltidgtsurefire-itltidgt ltphasegtintegration-testltphasegt ltgoalsgt ltgoalgttestltgoalgt ltgoalsgt ltconfigurationgt ltskipgtfalseltskipgt ltconfigurationgt ltexecutiongt ltexecutionsgtltplugingt

Tambieacuten necesitamos incluir en el pomxml las libreriacuteas de las que depende el coacutedigo depruebas de nuestro proyecto (clases XXXXTest situadas en srctest) libreriacutea JUnit JAXB yel API cliente de JAX-RS Por lo que antildeadimos en el las dependencias correspondientes

Dependencias del coacutedigo srctest con JUnit y API cliente de JAX-RS

ltdependencygt ltgroupIdgtjunitltgroupIdgt ltartifactIdgtjunitltartifactIdgt ltversiongt412ltversiongt ltscopegttestltscopegtltdependencygtltdependencygt ltgroupIdgtorgjbossresteasyltgroupIdgt ltartifactIdgtresteasy-clientltartifactIdgt ltversiongt3013Finalltversiongt ltscopegttestltscopegtltdependencygtltdependencygt ltgroupIdgtorgjbossresteasyltgroupIdgt

Servicios Rest

151

ltartifactIdgtresteasy-jaxb-providerltartifactIdgt ltversiongt3013Finalltversiongtltdependencygt

Dado que vamos a trabajar con el API Json y dado que ejecutaremos los tests desde lamaacutequina virtual de Java y no dentro del servidor WildFly necesitamos antildeadir tambieacuten lassiguientes libreriacuteas

Dependencias del coacutedigo srctest con el API Json de jaxrs

lt--Libreriacuteas para serializardeserializar json --gtltdependencygt ltgroupIdgtorgjbossresteasyltgroupIdgt ltartifactIdgtresteasy-jackson-providerltartifactIdgt ltversiongt3013Finalltversiongtltdependencygt

lt--Jaxrs API json --gtltdependencygt ltgroupIdgtorgjbossresteasyltgroupIdgt ltartifactIdgtresteasy-json-p-providerltartifactIdgt ltversiongt3013Finalltversiongtltdependencygt

No hemos incluido en el pomxml la orden para arrancar Wildfly Vamosa hacer esto desde IntelliJ en un perfil de ejecucioacuten como ya habeacuteishecho en sesiones anteriores De esta forma podremos ver desde IntelliJla consola de logs del servidor En este caso podemos crear un perfilsolamente para arrancar el servidor Wildfly (no es necesario que se incluyael despliegue del war generado puesto que lo haremos desde la ventanaMaven Projects) Antes de iniciar el proceso de construccioacuten por lo tantotendremos que asegurarnos de que hemos arrancado Wildlfly

Con estos cambios en el pomxml y ejecutando el comando mvn verify se llevaraacuten a cabolas siguientes acciones en este orden

bull Despueacutes de compilar el proyecto obtenemos el war (fase package )

bull El war generado se despliega en el servidor de aplicaciones Wilfly (fase pre-integration-test )

bull Se ejecutan los test JUnit sobre la aplicacioacuten desplegada en el servidor (faseintegration-test )

Anotaciones JUnit y aserciones AssertThat

JUnit 4 proporciona anotaciones para forzar a que los meacutetodos anotados con Test seejecuten en el orden que nos interese (por defecto no estaacute garantizado que se ejecuten enel orden en el que se escriben)

En principio debemos programar los tests para que sean totalmente independientes unos deotros y por lo tanto el orden de ejecucioacuten no influya para nada en el resultado de su ejecucioacutentanto si se ejecuta el primero como a mitad o el uacuteltimo El no hacer los tests independientes

Servicios Rest

152

hace que el proceso de testing se alargue y complique innecesariamente ya que puede serque unos tests enmascaren en resultado de otros o que no podamos saber si ciertas partesdel coacutedigo estaacuten bien o mal implementadas hasta que los tests de los que dependemos sehayan superado con eacutexito

Auacuten asiacute y dado que muchas veces se obtienen errores por hacer asunciones en el ordende la ejecucioacuten de los tests JUnit nos permite fijar dicho orden Para ello utilizaremosla anotacioacuten FixMethodOrder indicando el tipo de ordenacioacuten como por ejemploMethodSortersNAME_ASCENDING de forma que se ejecutaraacuten los tests por ordenlexicograacutefico

Por ejemplo

Ejemplo para forzar el orden de ejecucioacuten de los test (orden lexicograacutefico)

FixMethodOrder(MethodSortersNAME_ASCENDING)public class TestMethodOrder

Test public void testB() Systemoutprintln(second)

Test public void testA() Systemoutprintln(first)

Test public void testC() Systemoutprintln(third)

En ese caso el orden de ejecucioacuten seraacute testA() a continuacioacuten testB() y finalmentetestC()

Otra aportacioacuten de JUnit 4 es la incorporacioacuten de aserciones de tipoassertThat En sesiones anteriores habeacuteis utilizado aserciones con meacutetodosAssertassertEquals(resultado_esperado resultado_real) Los nuevosmeacutetodos AssertassertThat() permiten una mayor flexibilidad a la hora de expresar lasaserciones realizadas en nuestros tests asiacute como una mayor legibilidad de los mismos Elprototipo general de las aserciones de este tipo es

assertThat([value] [matcher statement])

en donde [value] es el resultado real (valor sobre el que se quiere afirmar algo)y [matcher statement] es un Matcher u objeto que realiza operaciones deemparejamiento sobre una secuencia de caracteres seguacuten un determinado patroacuten

Por ejemplo

Ejemplos de sentencias assertThat

assertThat(x is(not(4)))

Servicios Rest

153

assertThat(responseStringJson

either(containsString(nombre))and(containsString(apellido)))

assertThat(myList hasItem(3))

Aquiacute utilizamos un matcher con el patroacuten 4 esta sentencia devuelve false si x = 4Podemos combinar varios matchers de forma que se tengan que satisfacer maacutes de unoEn este caso aplicamos el matcher sobre un conjunto de elementos

Hay varias libreriacuteas que implementan Matchers JUnit incluye parte de los matchers deHamcrest (Hamcrest es un framework para escribir objetos matcher permitiendo definir reglasde matching de forma declarativa) Otra libreriacutea interesante para realizar testing de serviciosrest que utilizan representaciones Json es la libreriacutea hamcrest-json que podemos utilizarpara realizar aserciones sobre dos objetos Json

Por ejemplo supongamos que nuestro objeto Json contiene una lista de enlaces Hateoas detipo Link Los objetos Link seraacuten serializadosdeserializados (Wildfly utiliza Jackson pararealizar estas tareas) convenientemente Cuando serializamos un objeto Link (obtenemossu representacioacuten Json) veremos ademaacutes de los objetos uri valor type valor y relvalor que son los que baacutesicamente utilizamos al crear los enlaces Hateoas otros comouriBuilder hellip paramshellip que puede que no nos interese consultar o incluso que no leshayamos asignado ninguacuten valor

Si en nuestro test queremos comprobar que el objeto Json que nos devuelve el servicio(resultado real) se corresponde con el valor esperado tendremos que comparar ambasrepresentaciones Ahora bien puede que solamente nos interese comparar ciertos valorescontenidos en el objeto Json no el objeto completo

Hacer esta comprobacioacuten elemento a elemento es bastante tediosoLa libreriacutea hamcrest-json nos proporciona lo que estamos buscandocon los meacutetodos sameJSONAs() allowingExtraUnexpectedFields() yallowingAnyArrayOrdering() de la siguiente forma

Meacutetodo para comparar dos representaciones Json ClaseukcodatumedgehamcrestjsonSameJSONAs

AssertassertThat(age43 friend_ids[16 52 23] sameJSONAs(friend_ids[52 23 16]) allowingExtraUnexpectedFields() allowingAnyArrayOrdering())

En este coacutedigo tenemos una representacioacuten formada por dos objetos uno de los cuales tienecomo valor un array de enteros Si el servicio rest devuelve un objeto Json con maacutes elementoso en otro orden en este caso el resultado de la sentencia assertThat es true Volviendo alejemplo anterior de un objeto Json que contiene enlaces Hatehoas podriacuteamos realizar lasiguiente comparacioacuten

Comparamos dos objetos Json que contienen hiperenlaces Hateoas (objetos Link)

JsonObject json_object = clienttarget(httplocalhost8080forousuarios) request(MediaTypeAPPLICATION_JSON)

get(JsonObjectclass)

String json_string = json_objecttoString()

Servicios Rest

154

JsonObject usuarios = JsoncreateObjectBuilder() add(usuarios JsoncreateArrayBuilder() add(JsoncreateObjectBuilder() add(nombre Pepe Lopez) add(links JsoncreateArrayBuilder() add(JsoncreateObjectBuilder() add(uri httplocalhost8080forousuariospepe) add(type applicationxmlapplicationjson) add(rel self)))) add(JsoncreateObjectBuilder() add(nombre Ana Garcia) add(links JsoncreateArrayBuilder() add(JsoncreateObjectBuilder() add(uri httplocalhost8080forousuariosana) add(type applicationxmlapplicationjson) add(rel self)))))

build()

AssertassertThat(json_string sameJSONAs(usuariostoString()) allowingExtraUnexpectedFields()

allowingAnyArrayOrdering())

Realizamos la llamada al servicio REST y recibimos como respuesta un objeto Json Eneste caso nuestro objeto Json estaacute formado por una lista de objetosObtenemos la representacioacuten de nuestro objeto Json (resultado real) en forma de cadenade caracteresCreamos un nuevo objeto Json con el resultado esperadoComparamos ambos objetos Si el resultado real incluye maacutes elementos que loscontenidos en json_string o en otro orden consideraremos que hemos obtenido larespuesta correcta

Para utilizar esta libreriacutea en nuestro proyecto simplemente tendremos que antildeadirla comodependencia en la configuracioacuten de nuestro pomxml

Libreriacutea para comparar objetos Json en los tests

lt--Hamcrest Json --gtltdependencygt ltgroupIdgtukcodatumedgeltgroupIdgt ltartifactIdgthamcrest-jsonltartifactIdgt ltversiongt02ltversiongtltdependencygt

Observaciones sobre los tests y algunos ejemplos de tests

Recuerda que para utilizar el API cliente necesitas utilizar instanciasjavaxwsrsclientClient que debemos cerrar siempre despueacutes de su uso paracerrar el socket asociado a la conexioacuten

Servicios Rest

155

Para ello podemos optar por Crear una uacutenica instancia Client antes de ejecutar cualquiertest (meacutetodo BeforeClass) y cerrar el socket despueacutes de ejecutar todos los tests (meacutetodoAfterClass) Crear una uacutenica instancia Client antes de ejecutar CADA test (meacutetodoBefore) y cerrar el socket despueacutes de ejecutar CADA tests (meacutetodo After)

Si el resultado de una invocacioacuten sobre la instancia Client es de tipojavaxwsrscoreResponse debemos liberar de forma expliacutecita la conexioacuten para quepueda ser usada de nuevo por dicha instancia Client

Por ejemplo supongamos que queremos realizar un test en el que realizamos una operacioacutenPOST y a continuacioacuten una operacioacuten GET para verificar que el nuevo recurso se ha antildeadididocorrectamente

Ejemplo de Test que utiliza una instancia Client para todos los tests

public class TestRESTServices private static final String BASE_URL = httplocalhost8080rest private static URI uri = UriBuilderfromUri(BASE_URL)build() private static Client client

BeforeClass public static void initClient()

client = ClientBuildernewClient()

AfterClass public static void closeClient()

clientclose()

Test public void createAndRetrieveACustomer()

Customer customer = Creamos un nuevo cliente Response response = clienttarget(uri) request() post(Entityentity(customer MediaTypeAPPLICATION_JSON)) assertEquals(ResponseStatusCREATED responsegetStatusInfo()) URI referenceURI = responsegetLocation()

responseclose()

Obtenemos el recurso que hemos antildeadido response = clienttarget(referenceURI)request()get()

Customer retrieved_customer = responsereadEntity(Customerclass) assertEquals(ResponseStatusOK responsegetStatusInfo()) assertEquals(retreivedRefgetName() rgetName())

responseclose()

Creamos una instancia Client ANTES de ejecutar cualquier testCerramos el socket asociado a la conexioacuten DESPUEacuteS de ejecutar TODOS los testsLiberamos la conexioacuten para poder reutilizarla

Servicios Rest

156

Liberamos la conexioacuten para poder reutilizarlaAhora veamos otro ejemplo en el que utilizamos una instancia Client para cada test

Ejemplo de Test que utiliza una instancia Client para CADA test

public class TestRESTServices

private Client client

Before public void setUp()

thisclient = ClientBuildernewClient()

After public void tearDown()

thisclientclose()

Test public void getAllCustomersAsJson() String uriString = httplocalhost8080restcustomers JsonArray json_array = client target(uriString) request(MediaTypeAPPLICATION_JSON) accept(MediaTypeAPPLICATION_JSON) get(JsonArrayclass)

AssertassertEquals(2 json_arraysize())

Test public void getAllCustomers() String uriString = httplocalhost8080restcustomers Consultamos los datos de todos los customers ListltCustomergt lista_usuarios = clienttarget(uriString) request(applicationjson) get(new GenericTypeltListltCustomergtgt() ) AssertassertEquals(2 lista_usuariossize())

Creamos una instancia Client ANTES de ejecutar CADA testCerramos el socket asociado a la conexioacuten DESPUEacuteS de ejecutar CADA los tests

Podemos ver en este uacuteltimo ejemplo que no es necesario liberar la conexioacuten entre usossucesivos de la instancia Client si no utilizamos la clase Response En este caso el procesose realiza de forma automaacutetica por el sistema

Finalmente comentaremos que debido a un bug en la especificacioacuten JAX-RS eldeserializado del de los objetos Link no se realiza por lo que obendremos una listade Links vaciacutea (ver httpkingsfleetblogspotcomes201405reading-and-writing-jax-rs-link-objectshtml) Podemos comprobar que si obtenemos la representacioacuten en formato texto dela entidad del mensaje dicha lista de objetos tendraacute el valor correcto

Si no utilizamos la solucioacuten propuesta en el enlace anterior deberemos usar la anotacioacutenJsonIgnoreProperties(ignoreUnknown = true) De esta forma ignoraremos el

Servicios Rest

157

deserializado de los objetos Link pero tendremos que utilizar la representacioacuten en formato decadena de caracteres del recurso json en lugar del objeto java Link asociado

Asiacute por ejemplo si nuestro recurso Customer tiene asociado una lista de objetos Link parapoder utilizar el API Cliente y acceder a la lista de enlaces usaremos la anotacioacuten anterior enla implementacioacuten de la clase Customer

JsonIgnoreProperties(ignoreUnknown = true)XmlRootElement(name=customer)public class Customer int id String name ListltLinkgt links

Servicios Rest

158

55 Ejercicios

Tests utilizando el API cliente y un mapeador de excepciones (1 punto)

Se proporciona como plantilla el MOacuteDULO IntelliJ s5-tienda con una implementacioacuten parcialde una tienda de clientes on-line Este proyecto ya contiene varios tests implementados amodo de ejemplo

Los recursos rest implementados lanzan excepciones de tipo RestException si porejemplo se intenta realizar una consulta sobre un producto yo usuario que no existe

Se ha implementado un mapeador de excepciones RestExceptionMapper quecaptura excepciones de tipo RuntimeException y devuelve una respuesta de tipoErrorMensajeBean que seraacute serializada a formato json yo formato xml (dependiendo delvalor de la cabecera Accept de la peticioacuten) con informacioacuten sobre el error producido

Implementa los siguientes dos tests test7recuperarTodosLosUsuarios() en el querealizamos una invocacioacuten GET sobre httplocalhost8080s5-tiendarestclientes Esta URIpodriacutea corresponderse con un meacutetodo anotado con GET y que devolviese una lista de todoslos clientes de la tienda Sin embargo no existe tal meacutetodo en nuestro recursos rest Verificaque dicha invocacioacuten devuelve el coacutedigo de estado 500 (Internal Server Error) y que en elcuerpo del mensaje se recibe Servicio no disponible

bull test8recuperarClienteQueNoExiste() en el que intentamos recuperar lainformacioacuten de un cliente que no exista en nuestra base de datos En este caso debemosverificar que el mensaje obtenido en formato json es el siguiente

status Not Found code 404 message El producto no se encuentra en la base de datos developerMessage error

Tests utilizando el API Json y JUnit (1 punto)

Vamos a seguir usando el proyecto s4-foroAvanzado con el que hemos trabajado en la sesioacutenanterior

Vamos a implementar algunos tests con JUnit en los que utilizaremos ademaacutes del API clienteel API Json que nos proporciona jaxrs

Para ejecutar los tests necesitamos modificar el pomxml antildeadiendo las dependenciascorrespondientes que hemos visto a lo largo de la sesioacuten y antildeadiendo las goals para que seejecuten los tests despueacutes de desplegar la aplicacioacuten en Wildfly

Proporcionamos el contenido del pomxml con las libreriacuteas y plugins que necesitaraacutes(aunque como ejercicio deberiacuteas intentar modificar la configuracioacuten tuacute mismo y luego puedescomprobar el resultado con el pomxml que se proporciona) El contenido del nuevo pomxmllo tienes en srctestresourcesnuevo-pommxl

Inicializacioacuten de los datos para los tests

Vamos a utilizar DBUnit para inicializar la BD para realizar los tests Para ello tendraacutes queantildeadir en el pomxml las dependencias necesarias (ya estaacuten antildeadidas en el fichero de

Servicios Rest

159

configuracioacuten proporcionado) En el fichero srctestresourcesforo-inicialxml encontrareacuteis elconjunto de datos con el que inicializaremos la base de datos para ejecutar nuestros tests

No es necesario (aunque es una muy buena praacutectica) que inicialicemos la BD para cada test

Implementacioacuten de los tests

Vamos a implementar los siguientes tests (que se ejecutaraacuten en en este mismo orden)

bull test1ConsultaTodosUsuarios() recuperamos los datos de todos los usuarios delforo Recuerda que previamente tienes que haber inicializado la BD con los datos del ficheroforo-inicialxml Recupera los datos en forma de JsonObject y comprueba que el nuacutemerode usuarios es el correcto Tambieacuten debes comprobar que tanto el login como los enlaceshatehoas para cada usuario estaacuten bien creados En concreto para cada usuario debesverificar que la uri (uri) el tipo mime (type) y el tipo de enlace (rel) son los correctos

bull test2CreamosMensajeDePepe() crearemos un nuevo mensaje del usuario con loginpepe Recuerda que este usuario tiene el rol registrado El mensaje tendraacute el asuntocena y el texto seraacute Mejor me voy al cine En este caso deberaacutes comprobar el valorde estado (debe ser 201) y debes recuperar (consultar con una peticioacuten REST) el mensajepara comprobar que la operacioacuten de insertar el mensaje ha tenido eacutexito

bull test3CreamosMensajeDeUsuarioNoAutorizado() creamos un nuevo mensaje deun usuario que no estaacute autorizado (por ejemplo de un usuario con login juan) En estecaso el mensaje tendraacute el asunto cena y el mensaje puede ser Pues yo tampoco voyEl resultado debe ser el coacutedigo de estado 401 ( Unauthorized)

bull test4ConsultaUsuario() Consultamos los datos del usuario pepe Recuperaremoslos datos como un JsonObject y comprobaremos que el valor de la uri para el tipo derelacioacuten self del enlace Link asociado es el correcto

  • Servicios Rest
  • Table of Contents
  • 1 Introduccioacuten a REST Disentildeo y creacioacuten de servicios RESTful
    • 11 iquestQueacute es un servicio Web
      • Servicios Web RESTful
        • 12 Fundamentos de REST
          • Recursos
          • Representacioacuten de los recursos
          • Direccionabilidad de los recursos URI
          • Uniformidad y restricciones de las interfaces
            • 13 Disentildeo de servicios Web RESTful
            • 14 Un primer servicio JAX-RS
              • Modelo de objetos
              • Modelado de URIs
              • Definicioacuten del formato de datos
                • Formato de datos para operaciones de lectura y modificacioacuten de los recursos
                • Formato de datos para operaciones de creacioacuten de los recursos
                  • Asignacioacuten de meacutetodos HTTP
                    • Visualizacioacuten de todos los Pedidos Clientes o Productos
                    • Obtencioacuten de Pedidos Clientes o Productos individuales
                    • Creacioacuten de un Pedido Cliente o Producto
                    • Actualizacioacuten de un Pedido Cliente o Producto
                    • Borrado de un Pedido Cliente o Producto
                    • Cancelacioacuten de un Pedido
                      • Implementacioacuten del servicio Creacioacuten del proyecto Maven
                      • Implementacioacuten del servicio Recursos JAX-RS
                        • Clases de nuestro dominio (entidades) Clientejava
                        • Clases de nuestro servicio RESTful ClienteResourcejava
                        • Creacioacuten de clientes
                        • Consulta de clientes
                        • Modificacioacuten de clientes
                          • Construccioacuten y despliegue del servicio
                          • Probando nuestro servicio
                            • 15 Ejercicios
                              • Servicio REST ejemplo (0 puntos)
                              • Servicio REST saludo (1 punto)
                              • Servicio REST foro (1 punto)
                                  • 2 Anotaciones baacutesicas JAX-RS El modelo de despliegue
                                    • 21 iquestCoacutemo funciona el enlazado de meacutetodos HTTP
                                    • 22 La anotacioacuten Path
                                      • Expresiones Path
                                        • Expresiones regulares
                                        • Reglas de precedencia
                                          • Paraacutemetros matrix (Matrix parameters)
                                          • Subrecursos (Subresource Locators)
                                            • Caraacutecter dinaacutemico del dispatching de peticiones
                                                • 23 Usos de las anotaciones Produces y Consumes
                                                  • Anotacioacuten Consumes
                                                  • Anotacioacuten Produces
                                                    • 24 Inyeccioacuten de paraacutemetros JAX-RS
                                                      • javaxwsrsPathParam
                                                      • Interfaz UriInfo
                                                      • javaxwsrsMatrixParam
                                                      • javaxwsrsQueryParam
                                                      • javaxwsrsFormParam
                                                      • javaxwsrsHeaderParam
                                                      • javaxwsrscoreContext
                                                      • javaxwsrsBeanParam
                                                      • Conversioacuten automaacutetica de tipos
                                                      • Valores por defecto (DefaultValue)
                                                        • 25 Configuracioacuten y despliegue de aplicaciones JAX-RS
                                                          • Configuracioacuten mediante la clase Application
                                                          • Configuracioacuten mediante un fichero webxml
                                                          • Configuracioacuten en un contenedor que no disponga de una implementacioacuten JAX-RS
                                                            • 26 Ejercicios
                                                              • Creacioacuten de un recurso creacioacuten y consulta de temas en el foro (05 puntos)
                                                              • Despliegue y pruebas del recurso (05 puntos)
                                                              • Muacuteltiples consultas de los temas del foro (05 puntos)
                                                              • Creacioacuten de subrecursos (05 puntos)
                                                                  • 3 Manejadores de contenidos Respuestas del servidor y manejo de excepciones
                                                                    • 31 Proveedores de entidades
                                                                      • Interfaz javaxwsrsextMessageBodyReader
                                                                      • Interfaz javaxwsrsextMessageBodyWriter
                                                                        • 32 Proveedores de entidad estaacutendar incluidos en JAX-RS
                                                                          • javaxwsrscoreStreamingOutput
                                                                          • javaioInputStream javaioReader
                                                                          • javaioFile
                                                                          • byte[]
                                                                          • String char[]
                                                                          • MultivaluedMapltString Stringgt y formularios de entrada
                                                                            • 33 Muacuteltiples representaciones de recursos
                                                                            • 34 Introduccioacuten a JAXB
                                                                              • Clase JAXBContext
                                                                              • Manejadores JAX-RS para JAXB
                                                                              • JAXB y JSON
                                                                                • 35 Respuestas del servidor
                                                                                  • Coacutedigos de respuesta por defecto
                                                                                    • Respuestas que indican eacutexito
                                                                                    • Respuestas que indican una situacioacuten de fallo
                                                                                      • Elaboracioacuten de respuestas con la clase Response
                                                                                        • Inclusioacuten de cookies en la respuesta
                                                                                        • El tipo enumerado de coacutedigos de estado
                                                                                        • La clase javaxwsrscoreGenericEntity
                                                                                            • 36 Manejadores de excepciones
                                                                                              • La clase javaxwsrsWebApplicationException
                                                                                              • Mapeado de excepciones
                                                                                              • Jerarquiacutea de excepciones
                                                                                                • 37 Ejercicios
                                                                                                  • Servicio REST ejemplo
                                                                                                  • Plantillas que se proporcionan
                                                                                                  • Uso de JAXB (05 puntos)
                                                                                                  • Uso de manejadores de contenidos y clase Response (075 puntos)
                                                                                                  • Manejo de excepciones (075 puntos)
                                                                                                      • 4 HATEOAS y Seguridad
                                                                                                        • 41 iquestQueacute es HATEOAS
                                                                                                        • 42 HATEOAS y Servicios Web
                                                                                                          • Enlaces Atom
                                                                                                          • Ventajas de utilizar HATEOAS con Servicios Web
                                                                                                            • Transparencia en la localizacioacuten
                                                                                                            • Desacoplamiento de los detalles de la interaccioacuten
                                                                                                            • Reduccioacuten de errores de transicioacuten de estados
                                                                                                              • Enlaces en cabeceras frente a enlaces Atom
                                                                                                                • 43 HATEOAS y JAX-RS
                                                                                                                  • Construccioacuten de URIs con UriBuilder
                                                                                                                  • URIs relativas mediante el uso de UriInfo
                                                                                                                  • Construccioacuten de enlaces (Links) en documentos XML y en cabeceras HTTP
                                                                                                                    • 44 Seguridad
                                                                                                                      • Autentificacioacuten en JAX-RS
                                                                                                                        • Creacioacuten de usuarios y roles
                                                                                                                          • Autorizacioacuten en JAX-RS
                                                                                                                          • Encriptacioacuten
                                                                                                                            • Anotaciones JAX-RS para autorizacioacuten
                                                                                                                              • Seguridad programada
                                                                                                                                • 45 Ejercicios
                                                                                                                                  • Uso de Hateoas (1 puntos)
                                                                                                                                  • Ejercicio seguridad (1 punto)
                                                                                                                                      • 5 Api cliente Procesamiento JSON y Pruebas
                                                                                                                                        • 51 API cliente Visioacuten general
                                                                                                                                          • Obtenemos una instancia Client
                                                                                                                                          • Configuramos el target del cliente (URI)
                                                                                                                                          • Construimos y Realizamos la peticioacuten
                                                                                                                                          • Manejo de excepciones
                                                                                                                                            • 52 Procesamiento JSON
                                                                                                                                            • 53 Modelo de procesamiento basado en el modelo de objetos
                                                                                                                                              • Creacioacuten de un modelos de objetos desde el coacutedigo de la aplicacioacuten
                                                                                                                                              • Navegando por el modelo de objetos
                                                                                                                                              • Escritura de un modelo de objetos en un stream
                                                                                                                                              • Modelo de procesamiento basado en streaming
                                                                                                                                                • Lectura de datos JSON
                                                                                                                                                • Escritura de datos JSON
                                                                                                                                                    • 54 Pruebas de servicios REST
                                                                                                                                                      • Ciclo de vida de Maven y tests JUnit
                                                                                                                                                      • Anotaciones JUnit y aserciones AssertThat
                                                                                                                                                      • Observaciones sobre los tests y algunos ejemplos de tests
                                                                                                                                                        • 55 Ejercicios
                                                                                                                                                          • Tests utilizando el API cliente y un mapeador de excepciones (1 punto)
                                                                                                                                                          • Tests utilizando el API Json y JUnit (1 punto)
                                                                                                                                                            • Inicializacioacuten de los datos para los tests
                                                                                                                                                              • Implementacioacuten de los tests
Page 5: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones

Servicios Rest

5

iquestQueacute es un endpointLos servicios pueden interconectarse a traveacutes de la red En unaarquitectura orientada a servicios cualquier interaccioacuten punto a puntoimplica dos endpoints uno que proporciona un servicio y otro de loconsume Es decir que un endpoint es cada uno de los elementosen nuestro caso nos referimos a servicios que se situacutean en ambosextremos de la red que sirve de canal de comunicacioacuten entre ellosCuando hablamos de servicios Web un endpoint se especifica medianteuna URI

A nivel teacutecnico los servicios pueden implementarse de varias formas En estesentido podemos distinguir dos tipos de servicios Web los denominados servicios Webgrandes (big Web Services) los llamaremos servicios Web SOAP y servicios ligeros oservicios Web RESTful

Los servicios Web SOAP se caracterizan por utilizar mensajes XML que siguen el estaacutendarSOAP (Simple Object Access Protocol) Ademaacutes contienen una descripcioacuten de las operacionesproporcionadas por el servicio escritas en WSDL (Web Services Description Language) unlenguaje basado en XML

Los servicios Web RESTful por el contrario pueden intercambiar mensajes escritos endiferentes formatos y no requieren el publicar una descripcioacuten de las operaciones queproporcionan por lo que necesitan una menor infraestructura para su implementacioacutenNosotros vamos a centrarnos en el uso de estos servicios

Servicios Web RESTful

Son un tipo de Servicios Web que se adhieren a una serie de restricciones arquitectoacutenicasenglobadas bajo las siglas de REST y que utilizan estaacutendares Web tales como URIs HTTPXML y JSON

El API Java para servicios Web RESTful (JAX-RS) permite desarrollar servicios Web RESTfulde forma sencilla La versioacuten maacutes reciente del API es la 20 cuya especificacioacuten estaacute publicadaen el documento JSR-339 y que podemos descargar desde httpsjcporgenjsrdetailid=339 A lo largo de estas sesiones veremos coacutemo utilizar JAX-RS para desarrollar serviciosWeb RESTful Dicho API utiliza anotaciones Java para reducir los esfuerzos de programacioacutende los servicios

12 Fundamentos de REST

El teacutermino REST proviene de la tesis doctoral de Roy Fielding publicada en el antildeo 2000y significa REpresentational State Transfer (podemos acceder a la tesis original en httpwwwicsuciedu~fieldingpubsdissertationtophtm) REST es un conjunto de restriccionesque cuando son aplicadas al disentildeo de un sistema crean un estilo arquitectoacutenico de softwareDicho estilo arquitectoacutenico se caracteriza por seguir los siguientes principios

bull Debe ser un sistema cliente-servidor

bull Tiene que ser sin estado es decir no hay necesidad de que los servicios guarden lassesiones de los usuarios (cada peticioacuten al servicio tiene que ser independiente de lasdemaacutes)

bull Debe soportar un sistema de cacheacutes la infraestructura de la red deberiacutea soportar cacheacuteen diferentes niveles

Servicios Rest

6

bull Debe ser un sistema uniformemente accesible (con una interfaz uniforme) Estarestriccioacuten define coacutemo debe ser la interfaz entre clientes y servidores La idea es simplificary desacoplar la arquitectura permitiendo que cada una de sus partes puede evolucionarde forma independiente Una interfaz uniforme se debe caracterizar por

Estar basada en recursos La abstraccioacuten utilizada para representar la informacioacuten ylos datos en REST es el recurso y cada recurso debe poder ser accedido medianteuna URI (Uniform Resource Identifier)

Orientada a representaciones La interaccioacuten con los servicios tiene lugar atraveacutes de las representaciones de los recursos que conforman dicho servicio Unrecurso referenciado por una URI puede tener diferentes formatos (representaciones)Diferentes plataformas requieren formatos diferentes Por ejemplo los navegadoresnecesitan HTML JavaScript requiere JSON (JavaScript Object Notation) y unaaplicacioacuten Java puede necesitar XML

Interfaz restringida Se utiliza un pequentildeo conjunto de meacutetodos bien definidos paramanipular los recursos

Uso de mensajes auto-descriptivos cada mensaje debe incluir la suficienteinformacioacuten como para describir coacutemo procesar el mensaje Por ejemplo se puedeindicar coacutemo parsear el mensaje indicando el tipo de contenido del mismo (xml htmltextohellip)

Uso de Hipermedia como maacutequina de estados de la aplicacion (HATEOAS) Lospropios formatos de los datos son los que dirigen las transiciones entre estados dela aplicacioacuten Como veremos maacutes adelante con maacutes detalle el uso de HATEOAS(Hypermedia As The Engine Of Application State) va a permitir transferir de formaexpliacutecita el estado de la aplicacion en los mensajes intercambiados y por lo tantorealizar interacciones con estado

bull Tiene que ser un sistema por capas un cliente no puede discernir si estaacute accediendodirectamente al servidor o a alguacuten intermediario Las capas intermedias van a permitirsoportar la escalabilidad asiacute como reforzar las poliacuteticas de seguridad

A continuacioacuten analizaremos algunas de las abstracciones que constituyen un sistemaRESTful recursos representaciones URIs y los tipos de peticiones HTTP que constituyen lainterfaz uniforme utilizada en las transferencias clienteservidor

Recursos

Un recurso REST es cualquier cosa que sea direccionable (y por lo tanto accesible) a traveacutesde la Web Por direccionable nos referimos a recursos que puedan ser accedidos y transferidosentre clientes y servidores Por lo tanto un recurso es una correspondencia loacutegica ytemporal con un concepto en el dominio del problema para el cual estamos implementandouna solucioacuten

Algunos ejemplos de recursos REST son

bull Una noticia de un perioacutedico

bull La temperatura de Alicante a las 400pm

bull Un valor de IVA almacenado en una base de datos

bull Una lista con el historial de las revisiones de coacutedigo en un sistema CVS

bull Un estudiante en alguna aula de alguna universidad

Servicios Rest

7

bull El resultado de una buacutesqueda de un iacutetem particular en Google

Aun cuando el mapeado de un recurso es uacutenico diferentes peticiones a un recursopueden devolver la misma representacioacuten binaria almacenada en el servidor Por ejemploconsideremos un recurso en el contexto de un sistema de publicaciones En este casouna peticioacuten de la uacuteltima revisioacuten publicada y la peticioacuten de la revisioacuten nuacutemero 12 enalguacuten momento de tiempo pueden devolver la misma representacioacuten del recurso cuandola uacuteltima revisioacuten sea efectivamente la 12 Por lo tanto cuando la uacuteltima revisioacuten publicadase incremente a la versioacuten 13 una peticioacuten a la uacuteltima revisioacuten devolveraacute la versioacuten 13 yuna peticioacuten de la revisioacuten 12 continuaraacute devolviendo la versioacuten 12 En definitiva cada unode los recursos puede ser accedido directamente y de forma independiente pero diferentespeticiones podriacutean apuntar al mismo dato

Debido a que estamos utilizando HTTP para comunicarnos podemos transferir cualquier tipode informacioacuten que pueda transportarse entre clientes y servidores Por ejemplo si realizamosuna peticioacuten de un fichero de texto de la CNN nuestro navegador mostraraacute un fichero de textoSi solicitamos una peliacutecula flash a YouTube nuestro navegador recibiraacute una peliacutecula flash Enambos casos los datos son transferidos sobre TCPIP y el navegador conoce coacutemo interpretarlos streams binarios debido a la cabecera de respuesta del protocolo HTTP Content-Type Porlo tanto en un sistema RESTful la representacioacuten de un recurso depende del tipo deseado porel cliente (tipo MIME) el cual estaacute especificado en la peticioacuten del protocolo de comunicaciones

Representacioacuten de los recursos

La representacioacuten de los recursos es lo que se enviacutea entre los servidores y clientes Unarepresentacioacuten muestra el estado temporal del dato real almacenado en alguacuten dispositivo dealmacenamiento en el momento de la peticioacuten En teacuterminos generales es un stream binariojuntamente con los metadatos que describen coacutemo dicho stream debe ser consumido por elcliente yo servidor (los metadatos tambieacuten puden contener informacioacuten extra sobre el recursocomo por ejemplo informacioacuten de validacioacuten y encriptacioacuten o coacutedigo extra para ser ejecutadodinaacutemicamente)

A traveacutes del ciclo de vida de un servicio web pueden haber varios clientes solicitando recursosClientes diferentes son capaces de consumir diferentes representaciones del mismo recursoPor lo tanto una representacioacuten puede tener varias formas como por ejemplo una imagen untexto un fichero XML o un fichero JSON pero tienen que estar disponibles en la misma URL

Para respuestas generadas para humanos a traveacutes de un navegador una representacioacutentiacutepica tiene la forma de paacutegina HTML Para respuestas automaacuteticas de otros servicios web lalegibilidad no es importante y puede utilizarse una representacioacuten mucho maacutes eficiente comopor ejemplo XML

El lenguaje para el intercambio de informacioacuten con el servicio queda a eleccioacuten deldesarrollador A continuacioacuten mostramos algunos formatos comunes que podemos utilizarpara intercambiar esta informacioacuten

Table 1 Ejemplos de formatos utilizados por los servicios REST

Formato Tipo MIME

Texto plano textplain

HTML texthtml

XML applicationxml

JSON applicationjson

Servicios Rest

8

De especial intereacutes es el formato JSON Se trata de un lenguaje ligero de intercambio deinformacioacuten que puede utilizarse en lugar de XML (que resulta considerablemente maacutespesado) para aplicaciones AJAX De hecho en Javascript puede leerse este tipo de formatosimplemente utilizando el meacutetodo eval()

Direccionabilidad de los recursos URI

Una URI o Uniform Resource Identifier en un servicio web RESTful es un hiper-enlace aun recurso y es la uacutenica forma de intercambiar representaciones entre clientes y servidoresUn servicio web RESTful expone un conjunto de recursos que identifican los objetivos de lainteraccioacuten con sus clientes

El conjunto de restricciones REST no impone que las URIs deban ser hiper-enlacesSimplemente hablamos de hiper-enlaces porque estamos utilizando la Web para crearservicios web Si estuvieacutesemos utilizando un conjunto diferente de tecnologiacuteas soportadasuna URI RESTful podriacutea ser algo completamente diferente Sin embargo la idea dedireccionabilidad debe permanecer

En un sistema REST la URI no cambia a lo largo del tiempo ya que la implementacioacutende la arquitectura es la que gestiona los servicios localiza los recursos negocia lasrepresentaciones y enviacutea respuestas con los recursos solicitados Y lo que es maacutes importantesi hubiese un cambio en la estructura del dispositivo de almacenamiento en el lado del servidor(por ejemplo un cambio de servidores de bases de datos) nuestras URIs seguiraacuten siendo lasmismas y seraacuten vaacutelidas mientras el servicio web siga estando en marcha o el contexto delrecurso no cambie

Sin las restricciones REST los recursos se acceden por su localizacioacutenlas direcciones web tiacutepicas son URIs fijas Si por ejemplo renombramosun fichero en el servidor la URI seraacute diferente si movemos el fichero a undirectorio diferente la URI tambieacuten seraacute diferente

El formato de una URI se estandariza como sigue

schemehostportpathqueryStringfragment

En donde

bull scheme es el protocolo que estamos utilizando para comunicarnos con el servidor Paraservicios REST normalmente el protocolo seraacute http o https

bull El teacutermino host es un nombre DNS o una direccioacuten IP

bull A continuacioacuten se puede indicar de forma opcional un puerto (mediante port ) que es unvalor numeacuterico El host y el port representan la localizacioacuten de nuestro recurso en la red

bull Seguidamente aparece una expresioacuten path que es un conjunto de segmentos de textodelimitados por el caraacutecter (pensemos en la expresioacuten path como en una lista dedirectorios de un fichero en nuestra maacutequina)

bull Esta expresioacuten puede ir seguida opcionalmente por una queryString El caraacutecter )separa el path de la queryString Esta uacuteltima es una lista de paraacutemetros representadoscomo pares nombrevalor Cada par estaacute delimitado por el caraacutecter amp

La uacuteltima parte de la URI es el fragment delimitado por el caraacutecter Normalmente seutiliza para apuntar a cierto lugar del documento al que estamos accediendo

Servicios Rest

9

En una URI no todos los caracteres estaacuten permitidos de forma que algunos caracteres secodificaraacuten de acuerdo a las siguientes reglas

bull Los caracteres a-z A-Z 0-9 - y _ permanecen igual

bull El caracter espacio se convierte en el caraacutecter +

bull El resto de caracteres se codifican como una secuencia de bits siguiendo un esquema decodificacioacuten hexadecimal de forma que cada dos diacutegitos hexadecimales van precedidospor el caraacutecter

Un ejemplo de URI podriacutea ser eacuteste

httpexpertojavauaesrecursosclientesapellido=MartinezampcodPostal=02115

En el ejemplo anterior el host viene dado por expertojavauaes el path o ruta de acceso alrecurso es recursosclientes y hemos especificado los paraacutemetros apellido y codPostal conlos valores Martinez y 02115 respectivamente

Si por ejemplo en nuestra aplicacioacuten tenemos informacioacuten de clientes podriacuteamos acceder ala lista correspondiente mediante una URL como la siguiente

httpexpertojavauaesrecursosclientes

Esto nos devolveraacute la lista de clientes en el formato que el desarrollador del servicio hayadecidido Hay que destacar por lo tanto que en este caso debe haber un entendimiento entreel consumidor y el productor del servicio de forma que el primero comprenda el lenguajeutilizado por el segundo

La URL anterior nos podriacutea devolver un documento como el siguiente

ltxml version=10gtltclientesgt ltclientegthttpexpertojavauaesrecursosclientes1ltclientegt ltclientegthttpexpertojavauaesrecursosclientes2ltclientegt ltclientegthttpexpertojavauaesrecursosclientes4ltclientegt ltclientegthttpexpertojavauaesrecursosclientes6ltclientegtltclientesgt

En este documento se muestra la lista de clientes registrados en la aplicacioacuten cada uno deellos representado tambieacuten por una URL Accediendo a estas URLs a su vez podremosobtener informacioacuten sobre cada curso concreto o bien modificarlo

Uniformidad y restricciones de las interfaces

Ya hemos introducido los conceptos de recursos y sus representaciones Hemos dichoque los recursos son correspondencias (mappings) de los estados reales de las entidadesque son intercambiados entre los clientes y servidores Tambieacuten hemos dicho que lasrepresentaciones son negociadas entre los clientes y servidores a traveacutes del protocolo decomunicacioacuten en tiempo de ejecucioacuten (a traveacutes de HTTP) A continuacioacuten veremos con detalle

Servicios Rest

10

lo que significa el intercambio de estas representaciones y lo que implica para los clientes yservidores el realizar acciones sobre dichos recursos

El desarrollo de servicios web REST es similar al desarrollo de aplicaciones web Sin embargola diferencia fundamental entre el desarrollo de aplicaciones web tradicionales y las maacutesmodernas es coacutemo pensamos sobre las acciones a realizar sobre nuestras abstraccionesde datos De forma maacutes concreta el desarrollo moderno estaacute centrado en el concepto denombres (intercambio de recursos) el desarrollo tradicional estaacute centrado en el conceptode verbos (acciones remotas a realizar sobre los datos) Con la primera forma estamosimplementando un servicio web RESTful con la segunda un servicio similar a una llamada aprocedimiento remoto- RPC) Y lo que es maacutes un servicio RESTful modifica el estado delos datos a traveacutes de la representacioacuten de los recursos (por el contrario una llamada aun servicio RPC oculta la representacioacuten de los datos y en su lugar enviacutea comandos paramodificar el estado de los datos en el lado del servidor) Finalmente en el desarrollo modernode aplicaciones web limitamos la ambiguumledad en el disentildeo y la implementacioacuten debido aque tenemos cuatro acciones especiacuteficas que podemos realizar sobre los recursos CreateRetrieve Update y Delete (CRUD) Por otro lado en el desarrollo tradicional de aplicacionesweb podemos tener otras acciones con nombres o implementaciones no estaacutendar

A continuacioacuten indicamos la correspondencia entre las acciones CRUD sobre los datos y losmeacutetodos HTTP asociados

Table 2 Operaciones REST sobre los recursos

Accioacuten sobre los datos Protocolo HTTP equivalente

CREATE POST

RETRIEVE GET

UPDATE PUT

DELETE DELETE

El principio de uniformidad de la interfaz de acceso a recursos es fundamental y quizaacute el maacutesdifiacutecil de seguir por los programadores acostumbrados al modelo RPC (Remote ProcedureCall) La idea subyacente es utilizar uacutenicamente un conjunto finito y claramente establecidode operaciones para la interaccioacuten con los servicios Esto significa que no tendremos unparaacutemetro accioacuten en nuestra URI y que soacutelo utilizaremos los meacutetodos HTTP para accedera nuestros servicios Cada uno de los meacutetodos tiene un propoacutesito y significado especiacuteficosque mostramos a continuacioacuten

GETGET es una operacioacuten soacutelo de lectura Se utiliza para recuperar informacioacuten especiacuteficadel servidor Tambieacuten se trata de una operacioacuten idempotente y segura Idempotentesignifica que no importa cuaacutentas veces invoquemos esta operacioacuten el resultado (queobservaremos como usuarios) debe ser siempre el mismo Segura significa que unaoperacioacuten GET no cambia el estado del servidor en modo alguno es decir no debe exhibirninguacuten efecto lateral en el servidor Por ejemplo el hecho de leer un documento HTMLno deberiacutea cambiar el estado de dicho documento

PUTLa operacioacuten PUT solicita al servidor el almacenar el cuerpo del mensaje enviado condicha operacioacuten en la direccioacuten proporcionada en el mensaje HTTP Normalmente semodela como una insercioacuten o actualizacioacuten (nosotros la utilizaremos solamente comoactualizacioacuten) Es una propiedad idempotente Cuando se utiliza PUT el cliente conoce

Servicios Rest

11

la identidad del recurso que estaacute creando o actualizando Es idempotente porque enviar elmismo mensaje PUT maacutes de una vez no tiene ninguacuten efecto sobre el servicio subyacenteUna analogiacutea podriacutea ser un documento de texto que estemos editando No importa cuaacutentasveces pulsemos el botoacuten de grabar el fichero que contiene el documento loacutegicamenteseraacute el mismo documento

DELETEEsta operacioacuten se utiliza para eliminar recursos Tambieacuten es idempotente

POSTPost es la uacutenica operacioacuten HTTP que no es idempotente ni segura Cada peticioacuten POSTpuede modificar el servicio de forma exclusiva Se puede enviar o no informacioacutencon la peticioacuten Tambieacuten podemos recibir o no informacioacuten con la respuesta Paraimplementar servicios REST es deseable enviar informacioacuten con la peticioacuten y tambieacutenrecibir informacioacuten con la respuesta

Adicionalmente podemos utilizar otras dos operaciones HTTP

HEADEs una operacioacuten exactamente igual que GET excepto que en lugar de devolver un cuerpode mensaje solamente devuelve un coacutedigo de respuesta y alguna cabecera asociada conla peticioacuten

OPTIONSSe utiliza para solicitar informacioacuten sobre las opciones disponibles sobre un recurso en elque estamos interesados Esto permite al cliente determinar las capacidades del servidory del recurso sin tener que realizar ninguna peticioacuten que provoque una accioacuten sobre elrecurso o la recuperacioacuten del mismo

PATCHSe utiliza para para realiza reemplazos (actualizaciones) parciales de un documento yaque la operacioacuten PUT soacutelo permite una actualizacioacuten completa del recurso (y requiereindicar una representacioacuten completa del mismo) Es uacutetil cuando el recurso a modificares complejo y solamente queremos actualizar parte de su contenido En este caso solonecesitamos indicar la parte que queremos cambiar

13 Disentildeo de servicios Web RESTful

El disentildeo de servicios RESTful no es muy diferente del disentildeo de aplicaciones webtradicionales tenemos requerimientos de negocio tenemos usuarios que quieren realizaroperaciones sobre los datos y tenemos restricciones hardware que van a condicionar nuestraarquitectura software La principal diferencia reside en el hecho de que tenemos que buscara partir de los requerimientos cuaacuteles van a ser los recursos que van a ser accedidos a traveacutesde los servicios sin preocuparnos de queacute operaciones o acciones especiacuteficas van a poderserealizar sobre dichos recursos (el proceso de disentildeo depende de los nombres no de losverbos)

Podemos resumir los principios de disentildeo de servicios web RESTful en los siguientes cuatropasos

1 Elicitacioacuten de requerimientos y creacioacuten del modelo de objetos Este paso es similar aldisentildeo orientado a objetos El resultado del proceso puede ser un modelo de clases UML

2 Identificacioacuten de recursos Este paso consiste en identificar los objetos de nuestromodelo sin preocuparnos de las operaciones concretas a realizar sobre dichos objetos

Servicios Rest

12

3 Definicioacuten de las URIs Para satisfacer el principio de direccionabilidad de los recursostendremos que definir las URIs que representaraacuten los endpoints de nuestros servicios yque constituiraacuten los puntos de entrada de los mismos

4 Definicioacuten de la representacioacuten de los recursos Puesto que los sistemas REST estaacutenorientados a la representacioacuten tendremos que definir el formato de los datos queutilizaremos para intercambiar informacioacuten entre nuestros servicios y clientes

5 Definicioacuten de los meacutetodos de acceso a los recursos Finalmente tendremos que decidirqueacute meacutetodos HTTP nos permitiraacuten acceder a las URIs que queremos exponer asiacutecomo queacute haraacute cada meacutetodo Es muy importante que en este paso nos cintildeamos alas restricciones que definen los principios RESTful que hemos indicado en apartadosanteriores

14 Un primer servicio JAX-RS

Vamos a ilustrar los pasos anteriores con un ejemplo concretamente definiremos una interfazRESTful para un sistema sencillo de gestioacuten de pedidos de un hipoteacutetico comercio por internetLos potenciales clientes de nuestro sistema podraacuten realizar compras modificar pedidosexistentes en nuestro sistema asiacute como visualizar sus datos personales o la informacioacuten sobrelos productos que son ofertados por el comercio

Modelo de objetos

A partir de los requerimientos del sistema obtenemos el modelo de objetos El modelode objetos de nuestro sistema de ventas por internet es bastante sencillo Cada pedidoen el sistema representa una uacutenica transaccioacuten de compra y estaacute asociada con un clienteparticular Los pedidos estaraacuten formados por una o maacutes liacuteneas de pedido Las liacuteneas de pedidorepresentan el tipo y el nuacutemero de unidades del producto adquirido

Basaacutendonos en esta descripcioacuten de nuestro sistema podemos extraer que los objetos denuestro modelo son Pedido Cliente LineaPedido y Producto Cada objeto de nuestromodelo tiene un identificador uacutenico representado por la propiedad id dada por un valor detipo entero La siguiente figura muestra un diagrama UML de nuestro modelo

Estamos interesados en consultar todos los pedidos realizados asiacute como cada pedido deforma individual Tambieacuten queremos poder realizar nuevos pedidos asiacute como actualizar

Servicios Rest

13

pedidos existentes El objeto ServicioPedidos representa las operaciones que queremosrealizar sobre nuestos objetos Pedido Cliente LineaPedido y Producto

Modelado de URIs

Lo primero que haremos para crear nuestra interfaz distribuida es definir y poner nombre acada uno de los endpoints de nuestro sistema En un sistemam RESTful los endpoints seraacutenlos recursos del sistema que identificaremos mediante URIs

En nuestro modelo de objetos queremos poder interactuar con Pedidos Clientes y ProductosEacutestos seraacuten por lo tanto nuestros recursos de nivel maacutes alto Por otro lado estamosinteresados en obtener una lista de cada uno de estos elementos de alto nivel asiacute comointeractuar con los elementos indiduales de cada tipo El objeto LineaPedido es un objetoagregado del objeto Pedido por lo que no lo consideraremos com un recurso de nivel superiorMaacutes adelante veremos que podremos exponerlo como un subrecurso de un Pedido particularpero por ahora asumiremos que estaacute oculto por el formato de nuestros datos Seguacuten estouna posible lista de URIs que expondraacute nuestro sistema podriacutea ser

bull pedidos

bull pedidosid

bull productos

bull productosid

bull clientes

bull clientesid

Fiacutejate que hemos representado como URIs los nombres en nuestromodelo de objetos Recuerda que las URIS no deberiacutean utilizarse comomini-mecanismos de RPC ni deberiacutean identificar operaciones En vez deeso tenemos que utilizar una combinacioacuten de meacutetodos HTTP y de datos(recursos) para modelar las operaciones de nuestro sistema RESTful

Definicioacuten del formato de datos

Una de las cosas maacutes importantes que tenemos que hacer cuando definimos la interfazRESTful es determinar coacutemo se representaraacuten los recursos que seraacuten accedidos por losusuarios de nuestro API REST Quizaacute XML sea el formato maacutes popular de la web y puede serprocesado por la mayor parte de los lenguajes de programacioacuten Como veremos maacutes adelanteJSON es otro formato popular menos verboso que XML y que puede ser interpretadodirectamente por JavaScript (lo cual es perfecto para aplicaciones Ajax por ejemplo) Porahora utilizaremos el formato XML en nuestro ejemplo

Generalmente tendriacuteamos que definir un esquema XML para cada representacioacuten quequeramos transimitir a traves de la red Un esquema XML define la gramaacutetica del formato dedatos Por simplicidad vamos a omitir la creacioacuten de esquemas asumiendo que los ejemplosque proporcionamos se adhieren a sus correspondientes esquemas

A continuacioacuten distinguiremos entre dos formatos de datos uno para las operaciones delectura y actualizacioacuten y otro para la operacioacuten de creacioacuten de recursos

Formato de datos para operaciones de lectura y modificacioacuten de los recursos

Las representaciones de los recursos Pedido Cliente y Producto tendraacuten un elemento XMLen comuacuten al que denominaremos link

Servicios Rest

14

ltlink rel=self href=httporgexpertojavagt

El elemento (o etiqueta) link indica a los clientes que obtengan un documento XML comorepresentacioacuten del recurso doacutende pueden interaccionar en la red con dicho recurso enparticular El atributo self le indica al cliente queacute relacioacuten tiene dicho enlace con la URIdel recurso al que apunta (informacioacuten contenida en el atributo href ) El valor self indicaque estaacute apuntando a siacute mismo Maacutes adelante veremos la utilidad del elemento link cuandoagreguemos informacioacuten en documentos XML maacutes grandes

El formato de representacioacuten del recurso Cliente podriacutea ser

ltcliente id=8gt ltlink rel=self href=httporgexpertojavaclientes8gt ltnombregtPedroltnombregt ltapellidosgtGarcia Perezltapellidosgt ltdirecciongtCalle del Pino 5ltdirecciongt ltcodPostalgt08888ltcodPostalgt ltciudadgtMadridltciudadgtltclientegt

El formato de representacioacuten del recurso Producto podriacutea ser

ltproducto id=34gt ltlink rel=self href=httporgexpertojavaproductos34gt ltnombregtiPhone 6ltnombregt ltpreciogt800ltpreciogt ltcantidadgt1ltcantidadgtltproductogt

Finalmente el formato de la representacioacuten del recurso Pedido podriacutea ser

ltpedido id=233gt ltlink rel=self href=httporgexpertojavapedidos233gt lttotalgt800lttotalgt ltfechagtDecember 22 2014 0656ltfechagt ltcliente id=8gt ltlink rel=self href=httporgexpertojavaclientes8gt ltnombregtPedroltnombregt ltapellidosgtGarcia Perezltapellidosgt ltdirecciongtCalle del Pino 5ltdirecciongt ltcodPostalgt08888ltcodPostalgt ltciudadgtMadridltciudadgt ltclientegt ltlineasPedidogt ltlineaPedido id=1gt ltproducto id=34gt ltlink rel=self href=httporgexpertojavaproductos34gt ltnombregtiPhone 6ltnombregt

Servicios Rest

15

ltpreciogt800ltpreciogt ltcantidadgt1ltcantidadgt ltproductogt ltlineaPedidogt ltlineasPedidogtltpedidogt

El formato de datos de un Pedido tiene en un primer nivel la informacioacuten del total conel importe total del pedido asiacute como la fecha en la que se hizo dicho pedido Pedido es unbuen ejemplo de composicioacuten de datos ya que un pedido incluye informacioacuten sobre el Clientey el Productos Aquiacute es donde el elemento ltlinkgt puede resultar particularmente uacutetil Si elusuario estaacute interesado en interaccionar con el Cliente que ha realizado el pedido o en uno delos productos del mismo se proporciona la URI necesaria para interactuar con cada uno dedichos recursos De esta forma cuando el usuario del API consulte un pedido podraacute ademaacutesacceder a informacioacuten adicional relacionada con la consulta realizada

Formato de datos para operaciones de creacioacuten de los recursos

Cuando estamos creando nuevos Pedidos Clientes o Productos no tiene mucho sentidoincluir un atributo id y un elemento link en nuestro documento XML El servidor seraacute elencargado de crear los ids cuando inserte nuestro nuevo objeto en la base de datos Tampococonocemos la URI del nuevo objeto creado ya que seraacute el servidor el encargado de generarloPor lo tanto para crear un nuevo Producto el formato de la informacioacuten podriacutea ser el siguiente

ltproductogt ltlink rel=self href=httporgexpertojavaclientes8gt ltnombregtiPhoneltnombregt ltpreciogt800ltpreciogtltproductogt

Asignacioacuten de meacutetodos HTTP

Finalmente tendremos que decidir queacute meacutetodos HTTP expondremos en nuestro servicio paracada uno de los recursos asiacute como definir queacute haraacuten dichos meacutetodos Es muy importanteno asignar funcionaldad a un meacutetodo HTTP que sobrepase los liacutemites impuestos por laespecificacioacuten de dicho meacutetodo Por ejemplo una operacioacuten GET sobre un recurso concretodeberiacutea ser de soacutelo lectura No deberiacutea cambiar el estado del recurso cuando invoquemos laoperacioacuten GET sobre eacutel Si no seguimos de forma estricta la semaacutentica de los meacutetodos HTTPlos clientes asiacute como cualquier otra herramienta administrativa no pueden hacer asuncionessobre nuestros servicios de forma que nuestro sistema se vuelve maacutes complejo

Veamos para cada uno de los meacutetodos de nuestro modelo de objetos cuales seraacuten las URIsy meacutetodos HTTP que usaremos para representarlos

Visualizacioacuten de todos los Pedidos Clientes o Productos

Los tres objetos de nuestro modelo Pedidos Clientes y Productos son accedidos ymanipulados de forma similar Los usuarios pueden estar interesados en ver todos losPedidos Clientes o Productos en el sistema Las siguientes URIs representan dichos objetoscomo un grupo

bull pedidos

Servicios Rest

16

bull productos

bull clientes

Para obtener una lista de Pedidos Clientes o Productos el cliente remoto realizara unallamada al meacutetodo HTTP GET sobre la URI que representa el grupo de objetos Un ejemplode peticioacuten podriacutea ser la siguiente

GET productos HTTP11

Nuestro servicio responderaacute con los datos que representan todos los Pedidos de nuestrosistema Una respuesta podriacutea ser eacutesta

HTTP11 200 OKContent-Type applicationxml

ltproductosgt ltproducto id=111gt ltlink rel=self href=httporgexpertojavaproductos111gt ltnombregtiPhoneltnombregt ltpreciogt64899ltpreciogt ltproductogt ltproducto id=222gt ltlink rel=self href=httporgexpertojavaproductos222gt ltnombregtMacbookltnombregt ltpreciogt159999ltpreciogt ltproductogt ltproductosgt

Un problema que puede darse con esta peticioacuten es que tengamos miles de Pedidos Clienteso Productos en nuestro sistema por lo que podemos sobrecargar a nuestro cliente y afectarnegativamente a los tiempos de respuesta Para mitigar esta problema permitiremos que elusuario especifique unos paraacutemetros en la URI para limitar el tamantildeo del conjunto de datosque se va a devolver

GET pedidosstartIndex=0ampsize=5 HTTP11GET productosstartIndex=0ampsize=5 HTTP11GET clientesstartIndex=0ampsize=5 HTTP11

En las oacuterdenes anteriores hemos definido dos paraacutemetros de peticioacuten startIndex ysize El primero de ellos es un iacutendice numeacuterico que representa a partir de queacute posicioacuten enla lista de Pedidos Clientes o Productos comenzaremos a enviar la informacioacuten al clienteEl paraacutemetro size especifica cuaacutentos de estos objetos de la lista queremos que nos seandevueltos

Estos paraacutemetros seraacuten opcionales de forma que el cliente no tiene que especificarlos en suURI

Obtencioacuten de Pedidos Clientes o Productos individuales

Ya hemos comentado previamente que podriacuteamos utilizar las siguientes URIs para obtenerPedidos Clientes o Productos

Servicios Rest

17

bull pedidosid

bull productosid

bull clientesid

En este caso usaremos el meacutetodo HTTP GET para recuperar objetos individuales en elsistema Cada invocacioacuten GET devolveraacute la informacioacuten del correspondiente objeto Porejemplo

GET pedidos233 HTTP11

Para esta peticioacuten el cliente estaacute interesado en obtener una representacioacuten del Pedido conidentificador 233 Las peticiones GET para Productos y Clientes podriacutean funcionar de formasimilar El mensaje de respuesta podriacutea parecerse a algo como esto

HTTP11 200 OKContent-Type applicationxml

ltpedido id=233gtltpedidogt

El coacutedigo de respuesta es 200 OK indicando que la peticioacuten ha tenido eacutexito La cabeceraContent-Type especifica el formato del cuerpo de nuestro mensaje como XML y finalmenteobtenemos la representacioacuten del Pedido solicitado

Creacioacuten de un Pedido Cliente o Producto

Para crear un Pedido Cliente o Producto utilizaremos el meacutetodo POST En este caso el clienteenviacutea una representacioacuten del nuevo objeto que se prentende crear a la URI padre de surepresentacioacuten y por lo tanto podremos omitir el identificador del recurso Por ejemplo

Peticioacuten POST para crear un pedidio

POST pedidos HTTP11Content-Type applicationxml

ltpedidogt lttotalgt19902lttotalgt ltfechagtDecember 22 2008 0656ltfechagt ltpedidogt

El servicio recibe el mensaje POST procesa la XML y crea un nuevo pedido en la basede datos utilizando un identificador generado de forma uacutenica Si bien esta aproximacioacutenfunciona perfectamente se le pueden plantear varias cuestiones al usuario iquestQueacute ocurre siel usuario quiere visualizar modificar o eliminar el pedido que acaba de crear iquestCuaacutel es elidentificador del nuevo recurso iquestCuaacutel es la URI que podemos utilizar para interactuar con elnuevo recurso Para resolver estas cuestiones antildeadiremos alguna informacioacuten al mensajede respuesta HTTP El cliente podriacutea recibir un mensaje similar a eacuteste

Respuesta de una peticioacuten POST para crear un pedido

HTTP11 201 Created

Servicios Rest

18

Content-Type applicationxmlLocation httporgexpertojavapedidos233

ltpedido id=233gt ltlink rel=self href=httporgexpertojavapedidos233gt lttotalgt19902lttotalgt ltfechagtDecember 22 2008 0656ltfechagt ltpedidogt

HTTP requiere que si POST crea un nuevo recurso se debe responder con un coacutedigo 201Created Tambieacuten se requieer que la cabecera Location en el mensaje de respuestaproporcione una URI al usuario que ha hecho la peticioacuten para que eacuteste pueda interactuar conla Peticioacuten que acaba de crear (por ejemplo para modificar dicho Pedido) Es opcional porparte del servidor devolver en la respuesta la representacioacuten del nuevo recurso creado Ennuestro ejemplo optamos por devolver una representacioacuten XML de la Peticion creada con elidentificador del atributo asiacute como el elemento link

Actualizacioacuten de un Pedido Cliente o Producto

Para realizar modificaciones sobre los recursos que ya hemos creado utilizaremos el meacutetodoPUT En este caso un ejemplo de peticioacuten podriacutea ser eacutesta

Peticioacuten PUT para modificar un pedidio

PUT pedidos233 HTTP11Content-Type applicationxml

ltproducto id=111gt ltnombregtiPhoneltnombregt ltpreciogt64999ltpreciogtltproductogt

Tal y como he hemos indicado anteriormente la operacioacuten PUT es idempotente Lo quesignifica que no importa cuaacutentas veces solicitemos la peticioacuten PUT el producto subyacentesigue permaneciendo con el mismo estado final

Cuando un recurso se modifica mediante PUT la especificacioacuten HTTP requiere que el servidorenviacutee un coacutedigo de respuesta 200 OK y un cuerpo de mensaje de respuesta o bien el coacutedigo204 No Content sin ninguacuten cuerpo de mensaje en la respuesta

En nuestro caso devolveremos un coacutedigo de estado 204 y un mensaje sin cuerpo derespuesta

RECUERDA Es importante NO confundir POST con PUTMuchas veces se confunden los meacutetodos PUT y POST El significado deestos meacutetodos es el siguiente

bull POST Publica datos en un determinado recurso El recurso debe existirpreviamente y los datos enviados son antildeadidos a eacutel Por ejemplo paraantildeadir nuevos pedidos con POST hemos visto que debiacuteamos hacerlocon el recurso lista de pedidos (pedidos) ya que la URI del nuevopedido todaviacutea no existe La operacioacuten NO es idempotente es decir si

Servicios Rest

19

antildeadimos varias veces el mismo alumno apareceraacute repetido en nuestralista de pedidos con URIs distintas

bull PUT Hace que el recurso indicado tome como contenido los datosenviados El recurso podriacutea no existir previamente y en caso de queexistiese seriacutea sobrescrito con la nueva informacioacuten A diferencia dePOST PUT es idempotente Muacuteltiples llamadas ideacutenticas a la mismaaccioacuten PUT siempre dejaraacuten el recurso en el mismo estado Laaccioacuten se realiza sobre la URI concreta que queremos establecer (porejemplo pedidos215) de forma que varias llamadas consecutivas conlos mismos datos tendraacuten el mismo efecto que realizar soacutelo una deellas

Borrado de un Pedido Cliente o Producto

Modelaremos el borrado de los recursos utilizando el meacutetodo HTTP DELETE El usuariosimplemente invocaraacute el meacutetodo DELETE sobre la URI que representa el objeto que queremoseliminar Este meacutetodo haraacute que dicho recurso ya no exista en nuestro sistema

Cuando eliminamos un recurso con DELETE la especificacioacuten requiere que se enviacutee un coacutedigode respuesta 200 OK y un cuerpo de mensaje de respuesta o bien un coacutedigo de respuesta204 No Content sin un cuerpo de mensaje de respuesta

En nuestro caso devolveremos un coacutedigo de estado 204 y un mensaje sin cuerpo derespuesta

Cancelacioacuten de un Pedido

Hasta ahora las operaciones de nuestro modelo de objetos encajan bastante bien enla especificacioacuten de los correspondientes meacutetodos HTTP Hemos utilzado GET para leerdatos PUT para realizar modificaciones POST para crear nuevos recursos y DELETE paraeliminarlos En nuestro sistema los Pedidos pueden eliminarse o tambieacuten cancelarse Yahemos comentado que el borrado de un recurso lo elimina completamente de nuestra basede datos La operacioacuten de cancelacioacuten solamente cambia el estado del Pedido y lo siguemanteniendo en el sistema iquestCoacutemo podriacuteamos modelar esta operacioacuten

Cuando modelamos una interfaz RESTful para las operaciones de nuestro modelo de objetosdeberiacuteamos plantearnos la siguiente pregunta iquestla operacioacuten es un estado del recurso Sila respuesta es siacute entonces deberiacuteamos modelar esta operacioacuten dentro del formato de losdatos

La cancelacioacuten de un pedido es un ejemplo perfecto de esto que acabamos de decir La claveestaacute en que esta operacioacuten en realidad es un estado especiacutefico del Pedido eacuteste puede estarcancelado o no Cuando un usuario accede a un Pedido puede desear conocer si el Pedidoha sido o no cancelado Por lo tanto la informacioacuten sobre la cancelacioacuten deberiacutea formar partedel formato de datos de un Pedido Asiacute antildeadiremos un nuevo elemento a la informacioacuten delPedido

ltpedido id=233gt ltlink rel=self href=httporgexpertojavapedidos233gt lttotalgt19902lttotalgt ltfechagtDecember 22 2008 0656ltfechagt ltcanceladogtfalseltcanceladogt

Servicios Rest

20

ltpedidogt

Ya que el estado cancelado se modela en el propio formato de datos modelaremos la accioacutende cancelacioacuten con una operacioacuten HTTP PUT que ya conocemos

PUT pedidos233 HTTP11Content-Type applicationxml

ltpedido id=233gt ltlink rel=self href=httporgexpertojavapedidos233gt lttotalgt19902lttotalgt ltfechagtDecember 22 2008 0656ltfechagt ltcanceladogttrueltcanceladogt ltpedidogt

En este ejemplo modificamos la representacioacuten del Pedido con el elemento ltcanceladogtcon valor true

Este patroacuten de modelado no siempre sirve en todos los casos Por ejemplo imaginemosque queremos ampliar el sistema de forma que borremos del sistema todos los pedidoscancelados No podemos modelar esta operacioacuten de la misma manera que la de cancelacioacutenya que esta operacioacuten no cambia el estado de nuestra aplicacioacuten (no es en siacute misma un estadode la aplicacioacuten)

Para resolver este problema podemos modelar esta nueva operacioacuten como un subrecursode pedidos y realizar un borrado de los pedidos cancelados mediante el meacutetodo HTTP POSTde dicho subrecurso de la siguiente forma

POST pedidoseliminacion HTTP11

Un efecto interesante de lo que acabamos de hacer es que puesto que ahora eliminaciones una URI podemos hacer que la interfaz de nuestro servicios RESTful evolucionen conel tiempo Por ejemplo la orden GET pedidoseliminacion podriacutea devolver la uacuteltima fechaen la que se procedioacute a eliminar todos los pedidos cancelados asiacute como queacute pedidosfueron cancelados iquestY si queremos antildeadir alguacuten criterio a la hora de realizar el borradode pedidos cancelados Podriacuteamos introducir paraacutemetros para indicar que soacutelo queremoseliminar aquellos pedidos que esteacuten cancelados en una fecha anterior a una dada Comovemos podemos mantener una interfaz uniforme y centildeirnos a las operaciones HTTP tal ycomo estaacuten especificadas y a la vez dotar de una gran flexiblidad a la interfaz de nuestrosistema RESTful Hablaremos con maacutes detalle de los subrecursos en la siguiente sesioacuten

Implementacioacuten del servicio Creacioacuten del proyecto Maven

Vamos a utilizar Maven para crear la estructura del proyecto que contendraacute la implementacioacutende nuestro servicio Rest Inicialmente podemos utilizar el mismo arquetipo con el que habeacuteistrabajado en sesiones anteriores Y a continuacioacuten modificaremos la configuracioacuten del ficheropomxml para implementar nuestros servicios

Una opcioacuten es generar la estructura del proyecto directamente desde liacutenea de comandos Elcomando es el siguiente (recuerda que debes escribirlo en una misma liacutenea Los caracteres

Servicios Rest

21

que aparecen en el comando no forman parte del mismo simplemente indican que no sedebe pulsar el retorno de carro)

mvn --batch-mode archetypegenerate -DarchetypeGroupId=orgcodehausmojoarchetypes -DarchetypeArtifactId=webapp-javaee7 -DgroupId=orgexpertojava -DartifactId=ejemplo-rest

En donde

bull archetypeGroupId y archetypeArtifactId son los nombres del groupId yartifactId del arquetipo Maven que nos va a generar la plantilla para nuestro proyecto

bull groupId y artifactId son los nombres que asignamos como groupId y artifactId denuestro proyecto En este caso hemos elegido los valores orgexpertojava y ejemplo-restrespectivamente

Si utilizamos IntelliJ para crear el proyecto tenemos que

1 Crear un nuevo proyecto (New Project)

2 Elegir el tipo de proyecto Maven

3 Crear el proyecto Maven a partir de un arquetipo con las siguientes coordenadas

bull GroupId orgcodehausmojoarchetypes

bull ArtifactId webapp-javaee7

bull Version 11

4 Indicar las coordenadas de nuestro proyecto

bull GroupId orgexpertojava

bull ArtifactId ejemplo-rest

bull Version 10-SNAPSHOT

5 Confirmamos los datos introducidos

6 Para finalizar especificamos el nombre de nuestro proyecto en IntelliJ

bull Project Name ejemplo-rest (este valor tambieacuten identificaraacute el nombre del moacutedulo enIntelliJ)

7 Por comodidad marcaremos Enable autoimport para importar automaacuteticamente cualquiercambio en el proyecto

Una vez que hemos creado el proyecto con IntelliJ el paso siguiente es cambiar laconfiguracioacuten del pommxl que nos ha generado el arquetipo para incluir las propiedadesdependencias pluginshellip que necesitaremos para implementar nuestros recursos REST

Como ya sabemos el fichero pomxml contiene la configuracioacuten que utiliza Maven paraconstruir el proyecto A continuacioacuten indicamos las modificaciones en el fichero pomxmlgenerado inicialmente para adecuarlo a nuestras necesidades particulares

bull Cambiamos las propiedades del proyecto (etiqueta ltpropertiesgt ) por

Servicios Rest

22

Propiedades del proyecto

ltpropertiesgt ltprojectbuildsourceEncodinggtUTF-8ltprojectbuildsourceEncodinggtltpropertiesgt

bull Indicamos las dependencias del proyecto (etiqueta ltdependenciesgt en donde seincluyen las libreriacuteas necesarias para la construccioacuten del proyecto) En nuestro casonecesitamos incluir la libreriacutea javaxjavaee-web-api70 que contiene el apiestaacutendar de javaee 7 Marcamos el aacutembito de la libreriacutea (etiqueta ltscopegt ) comoprovided Con esto estamos indicando que soacutelo necesitaremos el jar correspondientepara compilar el proyecto y por lo tanto no incluiremos dicho jar en el fichero wargenerado para nuestra aplicacioacuten ya que dicha libreriacutea ya estaraacute disponible desde elservidor de aplicaciones en el que residiraacute nuestra aplicacioacuten

Libreriacuteas utilizadas para construir el proyecto (dependencias)

ltdependenciesgt ltdependencygt ltgroupIdgtjavaxltgroupIdgt ltartifactIdgtjavaee-web-apiltartifactIdgt ltversiongt70ltversiongt ltscopegtprovidedltscopegt ltdependencygtltdependenciesgt

bull A continuacioacuten configuramos la construccioacuten del proyecto (etiqueta ltbuildgt ) de lasiguiente forma (cambiamos la configuracioacuten original por la que mostramos a continuacioacuten)

Configuracioacuten de la construccioacuten del proyecto

ltbuildgt lt-- Especificamos el nombre del war que seraacute usado como context root cuando despleguemos la aplicacioacuten --gt ltfinalNamegt$projectartifactIdltfinalNamegt

ltpluginsgt lt-- Compilador de java Utilizaremos la versioacuten 17 --gt ltplugingt ltgroupIdgtorgapachemavenpluginsltgroupIdgt ltartifactIdgtmaven-compiler-pluginltartifactIdgt ltversiongt31ltversiongt ltconfigurationgt ltsourcegt17ltsourcegt lttargetgt17lttargetgt ltconfigurationgt ltplugingt

lt-- Servidor de aplicaciones wildfly --gt ltplugingt ltgroupIdgtorgwildflypluginsltgroupIdgt ltartifactIdgtwildfly-maven-pluginltartifactIdgt ltversiongt102Finalltversiongt ltconfigurationgt lthostnamegtlocalhostlthostnamegt

Servicios Rest

23

ltportgt9990ltportgt ltconfigurationgt ltplugingt

lt-- Cuando generamos el war no es necesario que el fichero webxml esteacute presente --gt ltplugingt ltgroupIdgtorgapachemavenpluginsltgroupIdgt ltartifactIdgtmaven-war-pluginltartifactIdgt ltversiongt23ltversiongt ltconfigurationgt ltfailOnMissingWebXmlgtfalseltfailOnMissingWebXmlgt ltconfigurationgt ltplugingt ltpluginsgtltbuildgt

Implementacioacuten del servicio Recursos JAX-RS

Una vez que tenemos la estructura del proyecto implementaremos los recursos de nuestraaplicacioacuten que seraacuten clases Java que utilizaraacuten anotaciones JAX-RS para enlazar y mapearpeticiones HTTP especiacuteficas a meacutetodos java los cuales serviraacuten dichas peticiones Eneste caso vamos a ilustrar con un ejemplo una posible implementacioacuten para el recursoCliente Tenemos que diferenciar entre las clases java que representaraacuten entidades denuestro dominio (objetos java que representan elementos de nuestro negocio y que seraacutenalmacenados tiacutepicamente en una base de datos) de nuestros recursos JAX-RS que tambieacutenseraacuten clases java anotadas y que utilizaraacuten objetos de nuestro dominio para llevar a cabo lasoperaciones expuestas en el API RESTful que hemos disentildeado

Asiacute por ejemplo implementaremos las clases

bull Clientejava representa una entidad del dominio Contiene atributos y suscorrespondientes getters y setters

bull ClienteResourcejava representa las operaciones RESTful sobre nuestro recurso Clienteque hemos definido en esta sesioacuten Es una clase java con anotaciones JAX-RS que nospermitiraacute insertar modificar borrar consultar un cliente asiacute como consultar la lista declientes de nuestro sistema

Clases de nuestro dominio (entidades) Clientejava

La clase que representa nuestra entidad del dominio Cliente es una clase java plana con suscorrespondientes atributos y getters y setters

Implementacioacuten del dominio clientejava

package orgexpertojavadomain

XmlRootElement(name=cliente)XmlAccessorType(XmlAccessTypeFIELD)public class Cliente private int id private String nombre private String apellidos private String direccion

Servicios Rest

24

private String codPostal private String ciudad

public int getId() return id public void setId(int id) thisid = id

public String getNombre() return nombre public void setNombre(String nom) thisnombre = nom

public String getApellidos() return apellidos public void setApellidos(String apellidos) thisapellidos = apellidos

public String getDireccion() return direccion public void setDireccion(String dir) thisdireccion = dir

public String getCodPostal() return codPostal public void setCodPostal(String cp) thiscodPostal = cp

public String getCiudad() return ciudad public void setCiudad(String ciudad) thisciudad = ciudad

Hemos anotado la clase Cliente con XmlRootElement y XmlAccessorType Hablaremos de estas anotaciones en sesiones posteriores las cuales se encargan delserializadodeserializado del cuerpo del mensaje (en formato xml o json) a nuestra clase javaCliente

Clases de nuestro servicio RESTful ClienteResourcejava

Una vez definido el objeto de nuestro dominio que representaraacute un objeto Cliente vamos a vercoacutemo implementar nuestros servicio JAX-RS para que diferentes usuarios de forma remotapuedan interactuar con nuestra base de datos de clientes

La implementacioacuten del servicio es lo que se denomina una resource class que no es maacutesque una clase java que utiliza anotaciones JAX-RS

Por defecto una nueva instancia de nuestra clase de recursos se crea para cada peticioacuten aese recurso Es lo que se conoce como un objeto per-request Esto implica que se crea unobjeto Java para procesar cada peticioacuten de entrada y se destruye automaacuteticamente cuandola peticioacuten se ha servido Per-request tambieacuten implica sin estado ya que no se guarda elestado del servicio entre peticiones

Comencemos con la implementacioacuten del servicio

package orgexpertojavaservices

import

Path(clientes)public class ClienteResource

private static MapltInteger Clientegt clienteDB = new ConcurrentHashMapltInteger Clientegt()

Servicios Rest

25

private static AtomicInteger idContador = new AtomicInteger()

Podemos observar que ClientResource es una clase java planay que no implementaninguna interfaz JAX-RS particular La anotacioacuten javaxwsrsPath indica que laclase ClienteResource es un servicio JAX-RS Todas las clases que queramos que seanreconocidas como servicios JAX-RS tienen que tener esta anotacioacuten Fiacutejate que estaanotacioacuten tiene el valor clientes Este valor representa la raiacutez relativa de la URI de nuestroservicio RESTful Si la URI absoluta de nuestro servidor es por ejemplo httpexpertojavaorglos meacutetodos expuestos por nuestra clase ClienteResource estariacutean disponibles bajo la URIhttpexpertojavaorgclientes

En nuestra clase definimos un Mapa para el campo ClienteDB quealmacenaraacute en memoria a los objetos Cliente de nuestro sistema Utilizamos unjavautilconcurrentConcurrentHashMap como tipo de clienteDB ya que nuestro recursoseraacute accedido concurrentemente por los usuarios de nuestro servicio rest El campoidContador lo utilizaremos para generar nuevos identificadores de nuestros objetos Clientecreados El tipo de este campo es javautilconcurrentatomicAtomicInteger para garantizarque siempre generaremos un identificador uacutenico aunque tengamos peticiones concurrentes

Justificacioacuten del caracter static de los atributosComo nuestros objetos seraacuten de tipo per-request el runtime de JAX-RScrearaacute una instancia de ClienteResource para cada pecioacuten que se realicesobre nuestro servicio La maacutequina virtual de java ejecutaraacute cada peticioacutena nuestro servicio en un hilo (thread) diferente permitiendo asiacute el accesoconcurrente a nuestro recurso Puesto que hemos decidido almacenaren memoria la informacioacuten de los clientes necesitamos que los atributosclienteDB y idContador sean static para que todas las instancias deClienteResource tengan acceso a la lista de clientes en memoria y nohaya problemas de concurrencia En realidad lo que estamos haciendocon eacutesto es permitir que el servicio guarde el estado entre peticiones Enun sistema real ClienteResource probablemente interactuacutee con una basede datos para almacenar y recuperar la informacioacuten de los clientes y porlo tanto no necesitaremos guardar el estado entre peticiones

Una mejor solucioacuten seriacutea no utilizar variables estaacuteticas y definir nuestroservicio como singleton Si hacemos eacutesto solamente se creariacutea unainstancia de clienteResource y estariacuteamos manteniendo el estado delas peticiones En la siguiente sesioacuten explicaremos coacutemo configurar unservicio como singleton Por simplicidad de momento optaremos por laopcioacuten de que los objetos RESTful sean per-request

Creacioacuten de clientes

Para implementar la creacioacuten de un nuevo cliente utilizamos el mismo modelo que hemosdisentildeado previamente Una peticioacuten HTTP POST enviacutea un documento XML que representaal cliente que queremos crear

El coacutedigo para crear nuevos clientes en nuestro sistema podriacutea ser eacuteste

POST

Consumes(applicationxml)

public Response crearCliente(Cliente cli)

Servicios Rest

26

el paraacutemetro cli se instancia con los datos del cliente del body del mensaje HTTP idContador++ clisetId(idContadorincrementAndGet())

clienteDBput(cligetId() cli)

Systemoutprintln(Cliente creado + cligetId()) return Responsecreated(URIcreate(clientes

+ cligetId()))build()

se recibe una peticioacuten POSTel cuerpo de la peticioacuten debe tener formato xmlcontiene la informacioacuten del documento xml del cuerpo de la peticioacuten de entradase antildeade el nuevo objeto Cliente a nuestro mapa de clientes (clienteDB)este meacutetodo se ejectua en el servidor por lo que el mensaje soacutelo seraacute visible por ejemplosi consultamos los mensajes generados por el servidor durante la ejecucioacuten el meacutetododevuelve un coacutedigo de respuesta 201 Created junto con una cabecera Locationapuntando a la URI absoluta del cliente que acabamos de crear

Vamos a explicar la implementacioacuten con maacutes detalle

Para enlazar peticiones HTTP POST con el meacutetodo crearCliente() lo anotamos conla anotacioacuten javaxwsrsPOST La anotacioacuten Path combinada con la anotacioacutenPOST enlaza todas las peticiones POST dirigidas a la URI relativa clientes al meacutetodo JavacrearCliente()

La anotacioacuten javaxwsrsConsumes aplicada a crearCliente() especifica queacute media typeespera el meacutetodo en el cuerpo del mensaje HTTP de entrada Si el cliente incluye en su peticioacutenPOST un media type diferente de XML se enviacutea un coacutedigo de error al cliente

El meacutetodo crearCliente() tiene un paraacutemetro de tipo Cliente En JAX-RS cualquier paraacutemetrono anotado con anotaciones JAX-RS se considera que es una representacioacuten del cuerpodel mensaje de la peticioacuten de entrada HTTP Las anotaciones que hemos introducido en laclase Cliente de nuestro dominio realizan el trabajo de convertir el documento xml contenidoen el cuerpo de la peticioacuten htpp de entrada en una instancia de nuestra clase Cliente

Solamente UNO de los paraacutemetros del meacutetodo Java puede representar elcuerpo del mensaje de la peticioacuten HTTP Esto significa que el resto deparaacutemetros deben anotarse con alguna anotacioacuten JAX-RS que veremosmaacutes adelante

El meacutetodo crearCliente() devuelve una respuesta de tipo javaxwsrscoreResponse El meacutetodo estaacutetico Responsecreated() crea un objeto Response que contiene un coacutedigode estado 201 Created Tambieacuten antildeade una cabecera Location con un valor similar ahttpexpertojavaorgclientes123 dependiendo del valor del valor de base de la raiacutez dela URI del servidor y el identificador generado para el objeto Cliente (en este caso se habriacuteagenerado el identificador 123) Maacutes adelante explicaremos con detalle el uso de la claseResponse

Consulta de clientes

A continuacioacuten mostramos un posible coacutedigo para consultar la informacioacuten de un cliente

GET

Servicios Rest

27

Path(id)Produces(applicationxml)public Cliente recuperarClienteId(PathParam(id) int id) final Cliente cli = clienteDBget(id) if (cli == null) throw new WebApplicationException(ResponseStatusNOT_FOUND) return new Cliente(cligetId() cligetNombre() cligetApellidos() cligetDireccion() cligetCodPostal() cligetCiudad())

En este caso anotamos el meacutetodo recuperarClienteId() con la anotacioacutenjavaxwsrsGET para enlazar las operaciones HTTP GET con este meacutetodo Java

Tambieacuten anotamos recuperarClienteId() con la anotacioacuten javaxwsrsPRODUCES Estaanotacioacuten indica a JAX-RS que valor tiene la cabecera HTTP Content-Type en la respuestaproporcionada por la operacioacuten GET En este caso estamos indicando que seraacute de tipoapplicationxml

En la implementacioacuten del meacutetodo utilizamos el paraacutemetro id para consultar si existe unobjeto Cliente en nuestro mapa clienteDB Si dicho cliente no existe lanzaremos la excepcioacutenjavaxwsrsWebApplictionException Esta excepcioacuten provocaraacute que el coacutedigo derespuesta HTTP sea 404 Not Found y significa que el recurso cliente requerido no existeDiscutiremos maacutes adelante el tema del manejo de excepciones

Modificacioacuten de clientes

Vamos a mostrar coacutemo seriacutea el coacutedigo para modificar un cliente

PUTPath(id)Consumes(applicationxml)public void modificarCliente(PathParam(id) int id Cliente nuevoCli)

Cliente actual = clienteDBget(id) if (actual == null) throw new WebApplicationException(ResponseStatusNOT_FOUND)

actualsetNombre(nuevoCligetNombre()) actualsetApellidos(nuevoCligetApellidos()) actualsetDireccion(nuevoCligetDireccion()) actualsetCodPostal(nuevoCligetCodPostal()) actualsetCiudad(nuevoCligetCiudad())

Anotamos el meacutetodo modificarCliente() con javaxwsrsPUT para enlazar laspeticiones HTTP PUT a este meacutetodo Al igual que hemos hecho con recuperarClienteId() elmeacutetodo modificarCliente() estaacute anotado adicionalmente con Path de forma que podamosatender peticiones a traveacutes de las URIs clientesid

El meacutetodo modificarCliente() tiene dos paraacutemetros El primero es un paraacutemetro id querepresenta el objeto Cliente que estamos modificando Al igual que ocurriacutea con el meacutetodorecuperarClienteId() utilizamos la anotacioacuten PathParam para extraer el identificador a

Servicios Rest

28

partir de la URI de la peticioacuten de entrada El segundo paraacutemetro es un objeto Cliente querepresenta el cuerpo del mensaje de entrada ya que no tiene ninguna anotacioacuten JAX-RS

El meacutetodo intenta encontrar un objeto Cliente en nuestro mapa clienteDB Si no existeprovocamos una WebApplicationException que enviaraacute una respuesta al usuario con elcoacutedigo 404 Not Found Si el objeto Cliente existe modificamos nuestro objeto Clienteexistente con los nuevos valores que obtenemos de la peticioacuten de entrada

Construccioacuten y despliegue del servicio

Una vez implementado nuestro servicio RESTful necesitamos poner en marcha el procesode construccioacuten El proceso de construccioacuten compilaraacutehellip empaquetaraacute hellip y finalmente nospermitiraacute desplegar nuestro servicio en el servidor de aplicaciones

Para poder empaquetar nuestro servicio RESTful como un war que se desplegaraacute en elservidor de aplicaciones vamos a incluir un proveedor de servicios JAX-RS en el descriptorde despliegue de nuestra aplicacioacuten (fichero webxml) En la siguiente sesioacuten justificaremos laexistencia de dicho proveedor (que seraacute un servlet) y explicaremos el modelo de desplieguede los servicios JAX-RS Los pasos a seguir desde IntelliJ para configurar el despliegue denuestro servicio son

bull Antildeadimos el directorio WEB-INF como subdirectorio de webapp

bull Nos vamos a FileProject StructurehellipFacetsWeb y antildeadimos el fichero webxml (enel panel Deployment descriptors pulsamos sobre + y seleccionamos webxml) Editamoseste fichero para antildeadir el servlet que serviraacute las peticiones de nuestros servicios RESTindicando cuaacutel seraacute la ruta en la que estaraacuten disponibles dichos servicios (en nuestroejemplo indicaremos la ruta rest) Dicha ruta es relativa a la ruta del contexto de nuestraaplicacioacuten y que por defecto es el nombre del artefacto war desplegado que hemosindicado en la etiqueta ltfinalNamegt dentro del ltbuildgt del fichero de configuracioacuten deMaven (pomxml)

Contenido del fichero webxml

ltweb-app version=30 xmlns=httpjavasuncomxmlnsjavaee xmlnsxsi=httpwwww3org2001XMLSchema-instance xsischemaLocation=httpjavasuncomxmlnsjavaee httpjavasuncomxmlnsjavaeeweb-app_3_0xsdgt lt-- One of the way of activating REST Services is adding these lines the server is responsible for adding the corresponding servlet automatically if the src folder has the Annotations to receive REST invocation--gt ltservlet-mappinggt ltservlet-namegtjavaxwsrscoreApplicationltservlet-namegt lturl-patterngtrestlturl-patterngt ltservlet-mappinggtltweb-appgt

A continuacioacuten ya estamos en disposicioacuten de iniciar la construccioacuten del proyecto con Mavenpara compilar empaquetar y desplegar nuestro servicio en Wildfly

Si utilizamos el terminal la secuencia de pasos para empaquetar y desplegar nuestroproyecto seriacutean

cd ejemplo-rest

Servicios Rest

29

mvn package

usrlocalwildfly-821Finalbinstandalonesh

mvn wildflydeploy

Nos situamos en el directorio que contiene el pomxml de nuestro proyectoEmpaquetamos el proyecto (obtendremos el war)Arrancamos el servidor wildflyDesplegamos el war generado en el servidor wildfly

Secuencia correcta de acciones

En ejecuciones posteriores despueacutes de realizar modificaciones en nuestro coacutedigo esrecomendable ejecutar mvn clean previamente al empaquetado del proyecto

Por lo tanto y suponiendo que el servidor de aplicaciones ya estaacute en marcha lasecuencia de acciones (comandos maven) que deberiacuteamos realizar para asegurarnosde que vamos a ejecutar exactamente la aplicacioacuten con los uacuteltimos cambios quehayamos introducido son

bull mvn wildflyundeploy

bull mvn clean

bull mvn package

bull mvn wildflydeploy

Tambieacuten podriacuteamos realizar todas estas acciones con un uacutenico comando maven

bull mvn wildflyundeploy clean package wildflydeploy

Si utilizamos IntelliJ antildeadiremos un nuevo elemento de configuracioacuten de ejecucioacuten desdeRunEdit Configurations Pulsamos el icono + y antildeadimos la configuracioacuten de tipo JBosssServerLocal Podemos ponerle por ejemplo como nombre Wilfdly start A continuacioacutenconfiguramos la ruta del servidor wildfly como usrlocalwildfly-821Final

Cuando lancemos este elemento de ejecucioacuten desde IntelliJ automaacuteticamente seconstruiraacute el proyecto (obtendremos el war) y arrancaremos wildfly Para desplegarel war utlizaremos la ventana Maven Projects y haremos doble click sobre ejemplo-restPluginswildflywildflydeploy

Probando nuestro servicio

Podemos probar nuestro servicio de varias formas Vamos a mostrar como hacerlodirectamente desde liacutenea de comandos utilizando IntelliJ o bien utilizando la herramientaPostman (que teneacuteis disponible desde el navegador Chrome)

Invocacioacuten del servicio desde liacutenea de comandosUtilizaremos la herramienta curl Por ejemplo para realizar una insercioacuten de un cliente elcomando seriacutea

Servicios Rest

30

curl -i -H Accept applicationxml -H Content-Type applicationxml -X POST -d clientexml httplocalhost8080ejemplo-restrestclientes

En donde

-iTambieacuten se puede utilizar la opcioacuten equivalente --include Indica que se debe incluirlas cabeceras HTTP en la respuesta recibida Recuerda que la peticioacuten POST devuelveen la cabecera Location el enlace del nuevo recurso creado (puede hacerlo en unacabedera Location o como un campo ltlinkgt del elemento creado en el cuerpo delmensaje lo veremos maacutes adelante) Esta informacioacuten seraacute necesaria para poder consultarla informacioacuten del nuevo cliente creado

-HIndica un par cabecera_valor_ En nuestro caso lo utilizamos para especificar los valoresde las cabeceras HTTP Accept y Content-Type

-XIndica el meacutetodo a invocar (GET POST PUThellip)

-dTambieacuten se puede utilizar --data Indica cuaacuteles son los datos enviados en el mensajede entrada en una peticioacuten POST Si los datos especificados van precedidos por estamos indicando que dichos datos estaacuten en un fichero Por ejemplo en la orden anteriorescribimos en el fichero clientexml los datos del cliente que queremos antildeadir en nuestrosistema

El contenido del fichero clientexml podriacutea ser eacuteste

ltxml version=10 encoding=UTF-8gtltclientesgt ltclientegt ltnombregtPepe ltnombregt ltapellidosgtGarcia Lopezltapellido1gt ltdirecciongtCalle del pino 3ltapellido2gt ltcodPostalgt0001ltcodPostalgt ltciudadgtAlicanteltciudadgt ltclientegtltclientesgt

Finalmente en la orden indicamos la URI a la que queremos acceder en este caso

httplocalhost8080ejemplo-restrestclientes

Una vez insertado el cliente podemos recuperar el cliente utilizando el enlace que se incluyeen la cabecera de respuesta Location

curl -i -H Accept applicationxml -H Content-Type applicationxml -X GET httplocalhost8080ejemplo-restrestclientes1

Servicios Rest

31

Invocacioacuten del servicio desde IntelliJIntelliJ nos proporciona una herramienta para probar servicios REST desde ToolsTestRESTful Web Service Desde esta nueva ventana podremos invocar al servicio RESTindicando el tipo de peticioacuten HTTP asiacute como las cabeceras y cuerpo de la peticioacuten

La siguiente figura muestra la elaboracioacuten de una peticioacuten POST a nuestro servicio REST

A continuacioacuten mostramos la ejecucioacuten de una peticioacuten GET

Cuando realizamos una peticioacuten POST debemos indicar el contenido del cuerpo del mensajeEn la siguiente figura observamos que tenemos varias opciones disponibles como por ejemploteclear directamente dicho contenido (opcioacuten Text) o bien subir dicha informacioacuten desdeun fichero en nuestro disco duro (opcioacuten File Contents) Podemos ver que hemos elegido estauacuteltima opcioacuten para probar nuestro servicio

Invocacioacuten del servicio desde PostmanOtra alternativa sencilla para probar nuestro servicio REST es la herramienta postmanque podemos lanzar desde el navegador en nuestro caso Chrome

Accederemos a la aplicacioacuten desde la barra de marcadores seleccionando Aplicaciones yy a continuacioacuten pulsaremos sobre el icono Postman

El aspecto de la herramienta es el que mostramos a continuacioacuten

Servicios Rest

32

Postman a diferencia de las alternativas anteriores nos permitiraacute guardar un historial depeticiones de forma que podamos repetir la ejecucioacuten de nuestros tests exactamente de lamisma forma aunque no de forma automaacutetica sino que tenemos que lanzar manualmentecada test que queramos volver a ejecutar

Tambieacuten podemos crear colecciones que no son maacutes que carpetas que contienen unconjunto de peticiones de nuestro historial Por ejemplo podemos crear la coleccioacuten s1-rest-ejercicio1 en donde guardaremos todas las peticiones que hayamos hecho sobre el ejercicio1 de la primera sesioacuten de rest

Podeacuteis crearos una cuenta gratuita para almacener y gestionar vuestras peticiones rest Unavez que tengaacuteis creadas varias colecciones Postman nos permite guardarlas en nuestrodisco duro en formato json

Servicios Rest

33

15 Ejercicios

Antes de empezar a crear los proyectos debes descargarte el repositorio git java_uaejercicios-rest-expertojava en el que vas a implementar los ejercicios relativos a laasignatura de Servicios REST El proceso es el mismo que el seguido en sesiones anteriores

1 Accedemos al repositorio y realizamos un Fork en nuestra cuenta personal (asiacute podremostener una copia con permisos de escritura)

2 Realizamos un Clone en nuestra maacutequina

$ git clone httpsbitbucketorgltalumnogtejercicios-rest-expertojava

De esta forma se crea en nuestro ordenador el directorio ejercicios-rest-expertojavay se descarga en eacutel un proyecto IntelliJ en el que iremos antildeadiendo MOacuteDULOS para cada unode los ejercicios Contiene tambieacuten el fichero gitignore asiacute como diferentes moacutedulos conlas plantillas que vayamos a necesitar para realizar los ejercicios

A partir de este momento se puede trabajar con dicho proyecto y realizar Commit y Pushcuando sea oportuno

$ cd ejercicios-rest-expertojava$ git add $ git commit -a -m Mensaje de commit$ git push origin master

Los MOacuteDULOS IntelliJ que iremos antildeadiendo tendraacuten todos el prefijo sx- siendo x elnuacutemero de la sesioacuten correspondiente (por ejemplo s1-ejercicio s2-otroEjerciciohellip)

Servicio REST ejemplo (0 puntos)

Para familiarizarnos con las peticiones http POST PUT GET DELETE se proporciona elMOacuteDULO s1-ejemplo-rest con la implementacioacuten de un servicio rest que podeacuteis probar biendesde liacutenea de comandos con la utilidad curl desde el navegador con la herramienta postman o bien desde IntelliJ con el cliente REST incluido en el IDE

En el directorio srcmainresources de dicho moacutedulo teneacuteis un fichero de texto(instruccionestxt) con las instrucciones para construir desplegar y probar la aplicacioacuten deejemplo

Servicio REST saludo (1 punto)

Vamos a implementar un primer servicio RESTful muy sencillo Para ello seguiremos lassiguientes indicaciones

bull Creamos un MOacuteDULO Maven con IntelliJ (desde el directorio ejercicios-rest-expertojava ) con el arquetipo webapp-javaee7 tal y como hemos visto en losapuntes de la sesioacuten Las coordenadas del artefacto Maven seraacuten

GroupId orgexpertojava

ArtifactId s1-saludo-rest

Servicios Rest

34

version 10-SNAPSHOT

bull Configuramos el pommxl del proyecto para poder compilar empaquetar y desplegarnuestro servicio Consulta los apuntes para ver cuaacutel debe ser el contenido de las etiquetasltpropertiesgt ltdependenciesgt y ltbuildgt

bull Creamos la carpeta WEB-INF y antildeadimos el fichero de configuracioacuten webxml tal y comohemos visto en los apuntes (esto seraacute necesario para configurar el despliegue) En estecaso queremos mapear los servicios REST contenidos en el paquete orgexpertojavaal directorio recursos dentro de nuestro contexto (recuerda que el contexto de nuestraaplicacioacuten web vendraacute dado por el valor de la etiqueta ltfinalNamegt anidada dentro deltbuildgt)

bull Creamos un recurso de nombre HolaMundoResource que se mapee a la direccioacuten holamundo Implementar un meacutetodo de forma que al acceder a eacutel por GET nos devuelvaen texto plano (textplain) el mensaje Hola mundo Una vez desplegada la aplicacioacutenen el servidor WildFly prueba el servicio mediante la utilidad Postman desde ChromeComprobar que la invocacioacuten

GET httplocalhost8080saludo-restholamundo

Devuelve como cuerpo del mensaje Hola mundo

bull Vamos a antildeadir un segmento variable a la ruta Implementa un meacutetodo GET nuevode forma que si accedemos a holamundonombre antildeade el nombre indicado al saludo(separado por un espacio en blanco y seguido por )

Una vez desplegada la aplicacioacuten en el servidor WildFly prueba el servicio mediante la utilidadTest RESTFul Web Service de IntelliJ o con Postman Comprobar que la invocacioacuten

GET httplocalhost8080saludo-restholamundopepe

Devuelve como cuerpo del mensaje Hola mundo pepe

bull Hacer que se pueda cambiar el saludo mediante un meacutetodo PUT El nuevo saludo llegaraacutetambieacuten como texto plano en el cuerpo de la peticioacuten y posteriores invocaciones a losmeacutetodos GET utilizaraacuten el nuevo saludo Almacenaremos el nuevo saludo en una variableestaacutetica de nuestro recurso iquestQueacute pasa si no lo es (lo hemos explicado en los apuntespuedes hacer la prueba para ver queacute ocurre si la variable no es estaacutetica)

Una vez desplegada la aplicacioacuten en el servidor WildFly prueba el servicio con Postmano bien mediante la utilidad Test RESTFul Web Service de IntelliJ Realizar las siguientesinvocaciones (en este orden)

PUT httplocalhost8080saludo-restholamundoy en el cuerpo del mensaje Buenos diacuteas

GET httplocalhost8080saludo-restholamundoGET httplocalhost8080saludo-restholamundopepe

Servicios Rest

35

La segunda invocacioacuten debe devolver como cuerpo del mensaje Buenos dias Al ejecutarla tercera invocacioacuten el cuerpo del mensaje de respuesta deberiacutea ser Buenos diasPepe

Servicio REST foro (1 punto)

Vamos a implementar un servicio RESTful que contemple las cuatro operaciones baacutesicas(GET PUT POST y DELETE) Se trata de un foro con en el que los usuarios pueden intervenirde forma anoacutenima en diferentes conversaciones

Primero debes crear un nuevo moacutedulo Maven configurar el pomxml asiacute como el ficherowebxml de la misma forma que hemos hecho en el ejercicio anterior pero para esteejercicio

bull Las coordenadas del moacutedulo Maven seraacuten

GroupId orgexpertojava

ArtifactId s1-foro-rest

version 10-SNAPSHOT

bull Nuestros servicios REST estaraacuten disponibles en la URI httplocalhost8080s1-foro-rest

El foro estaraacute formado por diferentes mensajes Por lo tanto el modelo del dominio de nuestraaplicacioacuten estaraacute formado por la clase Mensaje que contendraacute un identificador y una cadenade caracteres que representaraacute el contenido del mensaje (recuerda que debes implementarlos correspondientes getters y setters)

Por simplicidad vamos a almacenar los mensajes de nuestro foro en memoria Estos estaraacutendisponibles desde la clase DatosEnMemoria que contendraacute la variable estaacutetica

static MapltInteger Mensajegt datos = new HashMapltInteger Mensajegt()

Los servicios que proporcionaraacute el foro estaraacuten implementados en la claseMensajeResource Se accederaacute a ellos traveacutes de la ruta relativa a la raiacutez de nuestrosservicios mensajes Concretamente podremos realizar las siguientes operaciones

bull Antildeadir un nuevo mensaje al foro con la URI relativa a la raiacutez de nuestros servicios mensajes El texto del mensaje estaraacute en el cuerpo de la peticioacuten y el tipo MIME asociadoseraacute textplain (contenido de la cabecera Content-type de la peticioacuten HTTP)Nuestra respuesta debe incluir en la cabecera Location de la respuesta HTTP la URIdel nuevo recurso creado Utiliza para ello la clase Response tal y como hemos mostradoen el coacutedigo de ejemplo proporcionado para el ejercicio anterior Hablaremos con detallesobre esta clase en sesiones posteriores

Recuerda que para acceder al cuerpo de la peticioacuten basta con definir unparaacutemetro de tipo String JAX-RS automaacuteticamente lo instanciaraacute a partirdel cuerpo de la peticioacuten y lo convertiraacute en un objeto de tipo String

bull Modificar un mensaje determinado con un identificador con valor id a traveacutes de la URIrelativa a la raiacutez de nuestros servicios mensajesid ( id debe ser por tanto unsegmento de ruta variable) Si no existe ninguacuten mensaje con el identificador id se lanzaraacutela excepcioacuten WebApplicationException(ResponseStatusNOT_FOUND)

Servicios Rest

36

bull Borrar un mensaje determinado con un identificador con valor id atraveacutes de la URI relativa a la raiacutez de nuestros servicios mensajesid Igual que en el caso anterior si el identificador proporcionado no secorresponde con el de ninguacuten mensaje del foro se lanzaraacute la excepcioacutenWebApplicationException(ResponseStatusNOT_FOUND)

bull Consultar todos los mensajes del foro (la URI relativa seraacute mensajes ) El resultado semostraraacute en tantas liacuteneas como mensajes Cada mensaje iraacute precedido de su identificadorTambieacuten se informaraacute del nuacutemero total de mensajes en el foro (La respuesta seraacute unacadena de caracteres Al final del ejercicio mostramos un ejemplo de mensaje de respuestapara esta operacioacuten)

bull Consultar un mensaje determinado con un identificador con valor id a traveacutes dela URI relativa a la raiacutez de nuestros servicios mensajesid Si el identificadorproporcionado no se corresponde con el de ninguacuten mensaje del foro se lanzaraacute laexcepcioacuten WebApplicationException(ResponseStatusNOT_FOUND)

Prueba el servicio utilizando Postman o el cliente de IntelliJ para servicios REST con lassiguientes entradas

bull Crea los mensajes Mensaje numero 1 Mensaje numero 2 Mensaje numero 3 en esteorden

bull Consulta los mensajes del foro El resultado debe ser

1 Mensaje numero 1

2 Mensaje numero 2

3 Mensaje numero 3

Numero total de mensajes = 3

bull Cambia el mensaje con identificador 2 por Nuevo mensaje numero 2

bull Consulta los mensajes del foro El resultado debe ser

1 Mensaje numero 1

2 Nuevo Mensaje numero 2

3 Mensaje numero 3

Numero total de mensajes = 3

bull Borra el mensaje con identificador 3

bull Consulta el mensaje con el identificador 3 Se debe obtener una respuesta 404 NotFound

bull Consulta los mensajes del foro El resultado debe ser

1 Mensaje numero 1

2 Nuevo Mensaje numero 2

Numero total de mensajes = 2

bull Antildeade el mensaje Mensaje final Vuelve a consultar los mensajes el resultado debe ser

1 Mensaje numero 1

Servicios Rest

37

2 Nuevo Mensaje numero 2

4 Mensaje final

Numero total de mensajes = 3

Para evitar problemas con el id generado si hemos borrado mensajeslo maacutes sencillo es que el identificador vaya incrementaacutendose siemprecon cada nuevo mensaje Esto puede hacer que queden huecos en lanumeracioacuten como en el ejemplo anterior

Servicios Rest

38

2 Anotaciones baacutesicas JAX-RS El modelo de despliegue

Ya hemos visto como crear un servicio REST baacutesico Ahora se trata de analizar con maacutesdetalle aspectos fundamentales sobre la implementacioacuten de los servicios Comenzaremos pordetallar los usos de la anotacioacuten Path que es la que nos permite etiquetar una clase Javacomo un recurso REST sobre el que podremos realizar las operaciones que hemos identificadoen la sesioacuten anterior Tambieacuten hablaremos algo maacutes sobre las anotaciones Produces yConsumes que ya hemos utilizado para implementar nuestro primer servicio

En segundo lugar hablaremos sobre la extraccioacuten de informacioacuten de las peticiones HTTP ycoacutemo podemos inyectar esa informacioacuten en nuestro coacutedigo java Esto nos permitiraacute servir laspeticiones sin tener que escribir demasiado coacutedigo adicional

Finalmente explicaremos maacutes detenidamente coacutemo configurar el despliegue de nuestraaplicacioacuten REST de forma que sea portable

21 iquestCoacutemo funciona el enlazado de meacutetodos HTTP

JAX-RS define cinco anotaciones que se corresponden con operaciones HTTP especiacuteficas

bull javaxwsrsGET

bull javaxwsrsPUT

bull javaxwsrsPOST

bull javaxwsrsDELETE

bull javaxwsrsHEAD

En la sesioacuten anterior ya hemos utilizado estas anotaciones para hacer corresponder (enlazar)peticiones HTTP GET con un meacutetodo Java concreto

Por ejemplo

Path(clientes)public class ServicioCliente

GET Produces(applicationxml) public String getTodosLosClientes()

En este coacutedigo la anotacioacuten GET indica al runtime JAX-RS que el meacutetodo javagetTodosLosClientes() atiende peticiones HTTP GET dirigidas a la URI clientes

Soacutelamente se puede utilizar una de las anotaciones anteriores para unmismo meacutetodo Si se aplica maacutes de uno se produce un error durante eldespliegue de la aplicacioacuten

Es interesante conocer que cada una de estas anotaciones a su vez estaacute anotada con otrasanotaciones (podriacuteamos llamarlas meta anotaciones) Por ejemplo la implementacioacuten de laanotacioacuten GET tiene este aspecto

package javaxwsrsimport

Servicios Rest

39

Target(ElementTypeMETHOD)Retention(RetentionPolicyRUNTIME)HttpMethod(HttpMethodGET)public interface GET

GET en siacute mismo no tiene ninguacuten significado especial para el proveedor JAX-RS (runtimede JAX-RS) Lo que hace que la anotacioacuten GET sea significativo para el runtime de JAX-RSes el valor de la meta anotacioacuten javaxwsrsHttpMethod (en este caso HttpMethodGET )Este valor es el que realmente decide que un determinado meacutetodo Java se enlace con undeterminado meacutetodo HTTP

iquestCuaacuteles son las implicaciones de eacutesto Pues que podemos crear nuevas anotaciones quepodemos enlazar a otros meacutetodos HTTP que no sean GET POST PUT DELETE o HEAD Deesta forma podriacuteamos permitir que diferentes tipos de clientes que hacen uso de la operacioacutenHTTP LOCK puedan ser atendidos por nuestro servicio REST (como por ejemplo un clienteWebDAV )

22 La anotacioacuten Path

La anotacioacuten Path identifica la plantilla de path para la URI del recurso al que se accede yse puede especificar a nivel de clase o a nivel de meacutetodo de dicho recurso

El valor de una anotacioacuten Path es una expresioacuten que denota una URI relativa a la URIbase del servidor en el que se despliega el recurso a la raiz del contexto de la aplicacioacuten yal patroacuten URL al que responde el runtime de JAX-RS

Un segmento de la URI es cada una de las subcadenas delimitadas por que aparecenen dicha URI Por ejemplo la URI httpejemploclientescomclientesviprecientes contiene 4segmentos de ruta ejemploclientescom clientes vip y recientes

La anotacioacuten Path no es necesario que contenga una ruta que empieceo termine con el caraacutecter El runtime de JAX-RS analiza igualmente laexpresioacuten indicada como valor de Path

Para que una clase Java sea identificada como una clase que puede atender peticiones HTTPeacutesta tiene que estar anotada con al menos la expresioacuten Path() Este tipo de clasesse denominan recursos JAX-RS raiacutez

Para recibir una peticioacuten un meacutetodo Java debe tener al menos una anotacioacuten de meacutetodoHTTP como por ejemplo javaxwsrsGET Este meacutetodo no requiere tener ninguna anotacioacutenPath adicional Por ejemplo

Path(pedidos)public class PedidoResource GET public String getTodosLosPedidos()

Una peticioacuten HTTP GET pedidos se delegaraacute en el meacutetodo getTodosLosPedidos()

Servicios Rest

40

Podemos aplicar tambieacuten Path a un meacutetodo Java Si hacemos esto la expresioacuten de laanotacioacuten Path de la clase se concatenaraacute con la expresioacuten de la anotacioacuten Path delmeacutetodo Por ejemplo

Path(pedidos)public class PedidoResource

GET Path(noPagados) public String getPedidosNoPagados()

De esta forma una peticioacuten GET pedidosnoPagados se delegaraacute en el meacutetodogetPedidosNoPagados()

Podemos tener anotaciones Path para cada meacutetodo que seraacuten relativos a la ruta indicadaen la anotacioacuten Path de la definicioacuten de la clase Por ejemplo la siguiente clase de recursosirve peticiones a la URI pedidos

Path(pedidos)public class PedidoResource

GET public String getPedidos()

Si quisieacuteramos proporcionar el servicio en la URI pedidosincidencias por ejemplono necesitamos una nueva definicioacuten de clase y podriacuteamos anotar un nuevo meacutetodogetIncidenciasPedidos() de la siguiente forma

Path(pedidos)public class PedidoResource

GET public String getPedidos()

GET Path(incidencias) public String getIncidenciasPedidos()

Ahora tenemos una clase de recurso que gestiona peticiones para pedidos y para pedidosincidencias

Expresiones Path

El valor de una anotacioacuten Path puede ser una cadena de caracteres o tambieacutenpuede contener expresiones maacutes complejas si es necesario nos referiremos a ellas comoexpresiones Path

Servicios Rest

41

Una expresioacuten Path puede incluir variables que se indican entre llaves que seraacuten sustituidasen tiempo de ejecucioacuten dependiendo del valor que se indique en la llamada al recurso Asiacutepor ejemplo si tenemos la siguiente anotacioacuten

GETPath(clientesid)

y el usuario realiza la llamada

GET httporgexpertojavacontextorestclientesPedro

la peticioacuten se delegaraacute en el meacutetodo que esteacute anotado con las anotaciones anteriores y el valorde id seraacute instanciado en tiempo de ejecucioacuten a Pedro

Para obtener el valor del nombre del cliente utilizaremos la anotacioacuten PathParam en losparaacutemetros del meacutetodo de la siguiente forma

GETPath(clientesnombre)public String getClientePorNombre(PathParam(nombre) String nombre)

Una expresioacuten Path puede tener maacutes de una variable cada una figuraraacute entre llaves Porejemplo si utilizamos la siguiente expresioacuten Path

Path(nombre1nombre2)public class MiResource

podremos atender peticiones dirigidas a URIs que respondan a la plantilla

httporgexpertojavacontextorecursosnombre1nombre2

como por ejemplo

httporgexpertojavacontextorecursosPedroLopez

Las expresiones Path pueden incluir maacutes de una variable para referenciar un segmento deruta Por ejemplo

Path()public class ClienteResource GET Path(clientesapellido1-apellido2) public String getCliente(PathParam(apellido1) String ape1 PathParam(apellido2) String ape2)

Servicios Rest

42

Una peticioacuten del tipo

GET httporgexpertojavacontextoclientesPedro-Lopez

seraacute procesada por el meacutetodo getCliente()

Expresiones regulares

Las anotaciones Path pueden contener expresiones regulares (asociadas a las variables)Por ejemplo si nuestro meacutetodo getClienteId() tiene un paraacutemetro de tipo entero podemosrestringir las peticiones para tratar solamente aquellas URIs que contengan diacutegitos en elsegmento de ruta que nos interese

Path(clientes)public class ClienteResource GET Path(id d+) solo soporta diacutegitos public String getClienteId(PathParam(id) int id)

Si la URI de la peticioacuten de entrada no satisface ninguna expresioacuten regular de ninguno de losmetodos del recurso entonces se devolveraacute el coacutedigo de error 404 Not Found

El formato para especificar expresiones regulares para las variables del path es

nombre-variable [ expresion-regular ]

El uso de expresiones regulares es opcional Si no se proporciona una expresioacuten regularpor defecto se admite cualquier caraacutecter En teacuterminos de una expresioacuten regular la expresioacutenregular por defecto seriacutea

[^]+

Por ejemplo si queremos aceptar solamente nombres que comiencen por una letra y acontinuacioacuten puedan contener una letra o un diacutegito lo expresariacuteamos como

Path(clientes)public class ClienteResource GET Path(nombre [a-zA-Z][a-zA-Z_0-9]) public String getClienteNombre(PathParam(nombre) string nom)

Servicios Rest

43

De esta forma la URI clientesaaa no seriacutea vaacutelida la URI clientesa9 activariacutea elmeacutetodo getClienteNombre() y la URI clientes89 activariacutea el meacutetodo getClienteId()

Las expresiones regulares no se limitan a un soacutelo segmento de la URI Por ejemplo

Path(clientes)public class ClienteResource GET Path(id +) public String getCliente(PathParam(id) String id)

GET Path(id +direccion) public String getDireccion(PathParam(id) String id)

La expresioacuten regular + indica que estaacuten permitidos cualquier nuacutemero de caracteres Asiacutepor ejemplo la peticioacuten GET clientespedrolopez podriacutea delegarse en el meacutetodogetClientes()

El meacutetodo getDireccion() tiene asociada una expresioacuten maacutes especiacutefica la cual puedemapearse con cualquier cadena de caracteres que termine con direccion Seguacuten eacutestola peticioacuten GET clientespedrolopezdireccion podriacutea delegarse en el meacutetodogetDireccion()

Reglas de precedencia

En el ejemplo anterior acabamos de ver que las expresiones Path para getCliente() ygetDireccion() son ambiguas Una peticioacuten GET clientespedrolopezdireccionpodriacutea mapearse con cualquiera de los dos meacutetodos La especificacioacuten JAX-RS define lassiguientes reglas para priorizar el mapeado de expresiones regulares

bull El primer criterio para ordenar las acciones de mapeado es el nuacutemero de caracteres literalesque contiene la expresioacuten Path teniendo prioridad aquellas con un mayor nuacutemero decaracteres literales El patroacuten de la URI para el meacutetodo getCliente() tiene 10 caraacutecteresliterales clientes El patroacuten para el meacutetodo getDireccion() tiene 19 clientesdireccion Por lo tanto se elegiriacutea primero el meacutetodo getDireccion()

bull El segundo criterio es el nuacutemero de variables en expresiones Path (por ejemplo id oid +) Teniendo precedencia las patrones con un mayor nuacutemero de variables

bull El tercer criterio es el nuacutemero de variables que tienen asociadas expresiones regulares(tambieacuten en orden descendente)

A continuacioacuten mostramos una lista de expresiones Path ordenadas en orden descendentede prioridad

1 clientesidnombredireccion

2 clientesid +direccion

3 clientesiddireccion

4 clientesid +

Servicios Rest

44

Las expresiones 13 se analizariacutean primero ya que tienen maacutes caracteres literales que laexpresioacuten nuacutemero 4 Si bien las expresiones 13 tienen el mismo nuacutemero de caracteresliterales La expresioacuten 1 se analizariacutea antes que las otras dos debido a la segunda regla (tienemaacutes variables) Las expresiones 2 y 3 tienen el mismo nuacutemero de caracteres literales y elmismo nuacutemero de variables pero la expresioacuten 2 tiene una variable con una expresioacuten regularasociada

Estas reglas de ordenacioacuten no son perfectas Es posible que siga habiendo ambiguumledadespero cubren el 90 de los casos Si el disentildeo de nuestra aplicacioacuten presenta ambiguumledadesaplicando estas reglas es bastante probable que hayamos complicado dicho disentildeo y seriacuteaconveniente revisarlo y refactorizar nuestro esquema de URIs

Paraacutemetros matrix (Matrix parameters)

Los paraacutemetros matrix con pares nombre-valor incluidos como parte de la URI Aparecen alfinal de un segmento de la URI (segmento de ruta) y estaacuten delimitados por el caraacutecter Por ejemplo

httpejemplocochescomseatibizacolor=black2006

En la ruta anterior el paraacutemetro matrix aparece despueacutes del segmento de ruta ibiza Su nombrees color y el valor asociado es black

Un paraacutemetro matrix es diferente de lo que denominamos paraacutemetro de consulta (queryparameter) ya que los paraacutemetros matrix representan atributos de ciertos segmentos de laURI y se utilizan para propoacutesitos de identificacioacuten Pensemos en ellos como adjetivos Losparaacutemetros de consulta por otro lado siempre aparecen al final de la URI y siemprepertenecen al recurso completo que estemos referenciando

Los paraacutemetros matrix son ignorados cuando el runtime de JAX-RS realiza el matching de laspeticiones de entrada a meacutetodos de recursos REST De hecho es ilegal incluir paraacutemetrosmatrix en las expresiones Path Por ejemplo

Path(seat)public class SeatService

GET Path(ibizaanyo) Produces(imagejpeg) public Response getIbizaImagen(PathParam(anyo) String anyo)

Si la peticioacuten de entrada es GET seatibizacolor=black2009 el meacutetodo getIbizaImagen()seriacutea elegido por el proveedor de JAX-RS para servir la peticioacuten de entrada y seriacutea invocadoLos paraacutemetros matrix NO se consideran parte del proceso de matching debido a quenormalmente son atributos variables de la peticioacuten

Subrecursos (Subresource Locators)

Acabamos de ver la capacidad de JAX-RS para hacer corresponder de forma estaacuteticaa traveacutes de la anotacioacuten Path URIs especificadas en la entrada de la peticioacuten conmeacutetodos Java especiacuteficos JAX-RS tambieacuten nos permitiraacute de forma dinaacutemica servir nosotros

Servicios Rest

45

mismos las peticiones a traveacutes de los denominados subresource locators (localizadores desubrecursos)

Los subresource locators son meacutetodos Java anotados con Path pero sin anotacionesGET PUT hellip Este tipo de meacutetodos devuelven un objeto que es en siacute mismo un servicioJAX-RS que sabe coacutemo servir el resto de la peticioacuten Vamos a describir mejor este conceptocon un ejemplo

Supongamos que queremos extender nuestro servicio que proporciona informacioacuten sobre losclientes Disponemos de diferentes bases de datos de clientes seguacuten regiones geograacuteficasQueremos antildeadir esta informacioacuten en nuestro esquema de URIs pero desacoplando labuacutesqueda del servidor de base de datos de la consulta particular de un cliente en concretoAntildeadiremos la informacioacuten de la zona geograacutefica en la siguiente expresioacuten Path

clienteszona-dbclienteId

A continuacioacuten definimos la clase ZonasClienteResource que delegaraacute en la claseClienteResource que ya teniacuteamos definida

Path(clientes)public class ZonasClienteResource

Path(zona-db) public ClienteResource getBaseDeDatos(PathParam(zona) String db) devuelve una instancia dependiendo del paraacutemetro db ClienteResource resource = localizaClienteResource(db) return resource

protected ClienteResource localizaClienteResource(String db)

La clase ZonasClienteResource es nuestro recurso raiacutez Dicha clase no atiende ningunapeticioacuten HTTP directamente Nuestro recurso raiacutez procesa el segmento de URI que hacereferencia a la base de datos en donde buscar a nuestro cliente y devuelve una instancia dedicha base de datos (o maacutes propiamente dicho del objeto con en que accederemos a dichabase de datos) El proveedor de JAX-RS utiliza dicha instancia para servir el resto de lapeticioacuten

public class ClienteResource private MapltInteger Clientegt clienteDB = new ConcurrentHashMapltInteger Clientegt() private AtomicInteger idContador = new AtomicInteger()

public ClienteResource(MapltInteger Clientegt clienteDB) thisclienteDB = clienteDB

POST Consumes(applicationxml)

Servicios Rest

46

public Response crearCliente(InputStream is)

GET Path(id) Produces(applicationxml) public Cliente recuperarClienteId(PathParam(id) int id)

PUT Path(id) Consumes(applicationxml) public void modificarCliente(PathParam(id) int id Cliente cli)

Si un usuario enviacutea la peticioacuten GET clientesnorteamerica-db333 el proveedorJAX-RS primero realizaraacute un matching de la expresioacuten sobre el meacutetodoZonasClienteResourcegetBaseDeDatos() A continuacioacuten procesaraacute el resto de la peticioacuten(333) a traveacutes del meacutetodo ClienteResourcerecuperarClienteId()

Podemos observar que la nueva clase ClienteResource ademaacutes de tener un nuevoconstructor ya no estaacute anotada con Path Esto implica que ya no es un recurso de nuestrosistema es un subrecurso y no debe ser registrada en el runtime de JAX-RS a traveacutes de laclase Application (como veremos maacutes adelante)

Veamos otro ejemplo Supongamos que tenemos un conjunto de alumnos del que podemosobtener el listado completo de alumnos y antildeadir nuevos alumnos pero ademaacutes queremos quecada alumno individual pueda consultarse modificarse o borrarse Una forma sencilla de trataresto es dividir el coacutedigo en un recurso (lista de alumnos) y un subrecurso (alumno individual)de la siguiente forma

Path(alumnos)public class AlumnosResource

Context UriInfo uriInfo

GET Produces(MediaTypeAPPLICATION_XML MediaTypeAPPLICATION_JSON) public ListltAlumnoBeangt getAlumnos() return FactoriaDaosgetAlumnoDao()getAlumnos()

POST Consumes(MediaTypeAPPLICATION_JSON) public void addAlumno(AlumnoBean alumno) throws IOException String dni = FactoriaDaosgetAlumnoDao()addAlumno(alumno) URI uri = uriInfogetAbsolutePathBuilder()path(dni)build(dni) Responsecreated(uri)build()

Path(alumno) public AlumnoResource getAlumno( PathParam(alumno) String dni)

Servicios Rest

47

return new AlumnoResource(uriInfo dni)

Vemos que en este recurso inyectamos informacioacuten sobre la URI solicitada como variable deinstancia (utilizando la anotacioacuten Context de la que hablaremos maacutes adelante) Para elconjunto de alumnos ofrecemos dos operaciones obtener la lista de alumnos y antildeadir unnuevo alumno a la lista la cual devuelve como respuesta la URI que nos da acceso al recursoque acabamos de antildeadir

Sin embargo lo maacutes destacable es el uacuteltimo meacutetodo Eacuteste se ejecutaraacute cuando antildeadamos a laruta el identificador de un alumno (por ejemplo alumnos15 ) En este caso lo que hace esdevolver un subrecurso (AlumnoResource) para asiacute tratar un alumno individual (destacamosque el nombre estaacute en singular para distinguirlo del recurso anterior que representa elconjunto)

Cuando hacemos esto estamos delegando en el nuevo Recurso para tratar la peticioacuten

public class AlumnoResource

UriInfo uriInfo

String dni

public AlumnoResource(UriInfo uriInfo String dni) thisuriInfo = uriInfo thisdni = dni

GET Produces(MediaTypeAPPLICATION_XMLMediaTypeAPPLICATION_JSON) public AlumnoBean getAlumno() AlumnoBean alumno = FactoriaDaosgetAlumnoDao()getAlumno(dni) if(alumno==null) throw new WebApplicationException(StatusNOT_FOUND) return alumno

PUT Consumes(MediaTypeAPPLICATION_XML) public Response setAlumno(AlumnoBean alumno) El DNI del alumno debe coincidir con el de la URI alumnosetDni(dni)

if(FactoriaDaosgetAlumnoDao()getAlumno(dni) = null) FactoriaDaosgetAlumnoDao()updateAlumno(alumno) return ResponsenoContent()build() else FactoriaDaosgetAlumnoDao()addAlumno(alumno) return Responsecreated(uriInfogetAbsolutePath())build()

Servicios Rest

48

DELETE public void deleteAlumno() FactoriaDaosgetAlumnoDao()deleteAlumno(dni)

Este recurso ya no es un recurso raiacutez mapeado a una ruta determinada (podemos ver que laclase no lleva la anotacioacuten Path) sino que es creado desde otro recurso Es por lo tantoun subrecurso

Como ya hemos visto los subrecursos nos permiten simplificar la forma de trabajar conconjuntos de recursos definiendo en un uacutenico meacutetodo la ruta de acceso a un recurso individualen lugar de tenerlo que hacer de forma independiente para cada operacioacuten

Ademaacutes este disentildeo modular de los recursos nos va a permitir reutilizar determinadosrecursos dentro de otros Por ejemplo dentro del recurso de un alumno podriacuteamos ver la listade asignaturas en las que se ha matriculado y reutilizar el subrecurso encargado de acceder alas asignaturas para poder acceder a sus datos a partir del recurso del alumno No deberemosabusar de esta caracteriacutestica ya que si creamos relaciones ciacuteclicas perdemos la caracteriacutesticadeseable de los servicios REST de que cada recurso estaacute asignado a una uacutenica URI

En un subrecurso NO podemos inyectar objetos de contexto mediantela anotacioacuten Context ya que no estamos en un recurso raiacutez Porese motivo en el ejemplo hemos proporcionado el objeto UriInfo enel constructor del subrecurso De forma alternativa tambieacuten podriacuteamoshaber inyectado este objeto como paraacutemetro de sus meacutetodos en ese casosi que habriacutea sido posible la inyeccioacuten

Caraacutecter dinaacutemico del dispatching de peticiones

En los ejemplos anteriores hemos ilustrado el concepto de subresource locator aunque nohemos mostrado completamente su caraacutecter dinaacutemico Asiacute si volvemos al primero de ellosel meacutetodo ZonasClienteResourcegetBaseDeDatos() puede devolver cualquier instancia decualquier clase En tiempo de ejecucioacuten el proveedor JAX-RS buscaraacute el interior de estainstancia meacutetodos de recurso que puedan gestionar la peticioacuten

Supongamos que tenemos dos bases de datos de clientes con diferentes tipos deidentificadores Una de ellas utiliza una clave numeacuterica La otra utiliza una clave formada porel nombre y apellidos Necesitamos tener dos clases diferentes para extraer la informacioacutenadecuada de la URI de la peticioacuten Cambiaremos la implementacioacuten de la siguiente forma

Path(clientes)public class ZonasClienteResourceResource protected ClienteResource europa = new ClienteResource() protected OtraClaveClienteResource norteamerica = new OtraClaveClienteResource()

Path(zona-db) public Object getBaseDeDatos(PathParam(zona) String db) if (dbequals(europa)) return europa else if (dbequals(norteamerica)) return northamerica

Servicios Rest

49

else return null

En lugar de devolver una instancia de ClienteResource el meacutetodo getBaseDeDatos() devuelveuna instancia de javalangObject JAX-RS analizaraacute la instancia devuelta para ver coacutemoprocesar el resto de la peticioacuten

Ahora si un usuario enviacutea la peticioacuten GET clienteseuropa-db333 se utilizaraacute la claseClienteResource para servir el resto de la peticioacuten Si la peticioacuten es GET clientesnorteamerica-dbjohn-smith utilizaremos el nuevo subrecurso OtraClaveClienteResource

public class OtraClaveClienteResource private MapltString Clientegt clienteDB = new ConcurrentHashMapltString Clientegt() GET Path(nombre-apellidos) Produces(applicationxml) public Cliente getCliente(PathParam(nombre) String nombre PathParam(apellidos) String apelllidos)

PUT Path(nombre-apellidos) Consumes(applicationxml) public void actualizaCliente()PathParam(nombre) String nombre PathParam(apellidos) String apelllidos Cliente cli)

23 Usos de las anotaciones Produces y Consumes

La informacioacuten enviada a un recurso y posteriormente devuelta al cliente que realizoacute la peticioacutense especifica con la cabecera HTTP Media-Type tanto en la peticioacuten como en la respuestaComo ya hemos visto podemos especificar que representaciones de los recursos (valor deMedia_Type) son capaces de aceptar yo producir nuestros servicios mediante las siguientesanotaciones

bull javaxwsrsConsumes

bull javaxwsrsProduces

La ausencia de dichas anotaciones es equivalente a incluirlas con el valor de media type es decir su ausencia implica que se soporta (acepta) cualquier tipo de representacioacuten

Anotacioacuten Consumes

Esta anotacioacuten funciona conjuntamente con POST y PUT Le indica al framework (libreriacuteasJAX-RS) a queacute meacutetodo se debe delegar la peticioacuten de entrada Especiacuteficamente el clientefija la cabecera HTTP Content-Type y el framework delega la peticioacuten al correspondientemeacutetodo capaz de manejar dicho contenido Un ejemplo de anotacioacuten con PUT es lasiguiente

Servicios Rest

50

Path(pedidos)public class PedidoResource PUT Consumes(applicationxml) public void modificarPedido(Pedido representation)

Si Consumes se aplica a la clase por defecto los meacutetodos correspondientes aceptan lostipos especificados de tipo MIME Si se aplica a nivel de meacutetodo se ignora cualquier anotacioacutenConsumes a nivel de clase para dicho meacutetodo

En este ejemplo le estamos indicando al framework que el meacutetodo modificarPedido() aceptaun recurso cuya representacioacuten (tipo MIME) es applicationxml (y que se almacenaraacute en lavariable representation hablaremos de ello en la siguiente sesioacuten) Por lo tanto un clienteque se conecte al servicio web a traveacutes de la URI pedidos debe enviar una peticioacuten HTTPPUT conteniendo el valor de applicationxml como tipo MIME de la cabecera HTTPContent-Type y el cuerpo (body) del mensaje HTTP debe ser por tanto un documentoxml vaacutelido

Si no hay meacutetodos de recurso que puedan responder al tipo MIME solicitado (tipo MIMEespecificado en la anotacioacuten Consumes del servicio) se le devolveraacute al cliente un coacutedigoHTTP 415 (Unsupported Media Type) Si el meacutetodo que consume la representacioacutenindicada como tipo MIME no devuelve ninguna representacioacuten se enviaraacute un el coacutedigo HTTP204 (No content) A continuacioacuten mostramos un ejemplo en el que sucede eacutesto

POSTConsumes(applicationxml)public void creaPedido(Pedido pedido) Crea y almacena un nuevo _Pedido_

Podemos ver que el meacutetodo consume una representacioacuten en texto plano pero devuelvevoid es decir no devuelve ninguna representacioacuten En este caso se enviacutea el coacutedigo de estadoHTTP 204 No content en la respuesta

Un recurso puede aceptar diferentes tipos de entradas Asiacute podemos utilizar la anotacioacutenPUT con maacutes de un meacutetodo para gestionar las repuestas con tipos MIME diferentes Porejemplo podriacuteamos tener un meacutetodo para aceptar estructuras XML y otro para aceptarestructuras JSON

Path(pedidos)public class PedidoResource PUT Consumes(applicationxml) public void modificarPedidoXML(InputStream pedido) PUT Consumes(applicationjson) public void modificarPedidoJson(InputStream pedido)

Servicios Rest

51

Anotacioacuten Produces

Esta anotacioacuten funciona conjuntamente con GET POST y PUT Indica al frameworkqueacute tipo de representacioacuten se enviacutea de vuelta al cliente

De forma maacutes especiacutefica el cliente enviacutea una peticioacuten HTTP junto con una cabecera HTTPAccept que se mapea directamente con el Content-Type que el meacutetodo produce Por lo tantosi el valor de la cabecera Accept HTTP es applicationxml el meacutetodo que gestiona la peticioacutendevuelve un stream de tipo MIME applicationxml Esta anotacioacuten tambieacuten puede utilizarse enmaacutes de un meacutetodo en la misma clase de recurso Un ejemplo que devuelve representacionesXML y JSON seriacutea el siguiente

Path(pedidos)public class PedidoResource

GET Produces(applicationxml) public String getPedidoXml()

GET Produces(applicationjson) public String getPedidoJson()

Si un cliente solicita una peticioacuten a una URI con un tipo MIME no soportadopor el recurso el framework JAX-RS lanza la excepcioacuten adecuadaconcretamente el runtime de JAX-RS enviacutea de vuelta un error HTTP 406Not acceptable

Se puede declarar maacutes de un tipo en la misma declaracioacuten Produces como por ejemplo

Produces(applicationxml applicationjson)public String getPedidosXmlOJson()

El meacutetodo getPedidosXmlOJson() seraacute invocado si cualquiera de los dos tipos MIMEespecificados en la anotacioacuten Produces son aceptables (la cabecera Accept de la peticioacutenHTTP indica queacute representacioacuten es aceptable) Si ambas representaciones son igualmenteaceptables se elegiraacute la primera

En lugar de especificar los tipos MIME como cadenas detexto en Consumes y Produces podemos utilizar lasconstantes definidas en la clase javaxwsrscoreMediaTypecomo por ejemplo MediaTypeAPPLICATION_XML oMediaTypeAPPLICATION_JSON en lugar de applicationxml yapplicationjson

24 Inyeccioacuten de paraacutemetros JAX-RS

Buena parte del trabajo de JAX-RS es el extraer informacioacuten de una peticioacuten HTTP einyectarla en un meacutetodo Java Podemos estar interesados en un fragmento de la URI de

Servicios Rest

52

entrada en los paraacutemetros de peticioacutenhellip El cliente tambieacuten podriacutea enviar informacioacuten en lascabeceras de la peticioacuten A continuacioacuten indicamos una lista con algunas de las anotacionesque podemos utilizar para inyectar informacioacuten de las peticiones HTTP

bull javaxwsrsPathParam

bull javaxwsrsMatrixParam

bull javaxwsrsQueryParam

bull javaxwsrsFormParam

bull javaxwsrsHeaderParam

bull javaxwsrsContext

bull javaxwsrsBeanParam

Habitualmente estas anotaciones se utilizan en los paraacutemetros de un meacutetodo de recurso JAX-RX Cuando el proveedor de JAX-RS recibe una peticioacuten HTTP busca un meacutetodo Java quepueda servir dicha peticioacuten Si el meacutetodo Java tiene paraacutemetros anotados con alguna de estasanotaciones extraeraacute la informacioacuten de la peticioacuten HTTP y la pasaraacute como un paraacutemetrocuando se invoque el meacutetodo

javaxwsrsPathParam

Ya la hemos utilizado en la sesioacuten anterior PathParam nos permite inyectar el valor de losparaacutemetros de la URI definidos en expresiones Path Recordemos el ejemplo

Path(clientes)public class ClienteResource

GET Path(id) Produces(applicationxml) public Cliente recuperarClienteId(PathParam(id) int id)

Podemos referenciar maacutes de un paraacutemetro en el path de la URI en nuestros meacutetodo javaPor ejemplo supongamos que estamos utilizando el nombre y apellidos para identificar a uncliente en nuestra clase de recurso

Path(clientes)public class ClienteResource

GET Path(nombre-apellidos) Produces(applicationxml) public Cliente recuperarClienteId(PathParam(nombre) String nom PathParam(apellidos) String ape)

Servicios Rest

53

En ocasiones un parameacutetro de path de la URI puede repetirse en diferentes expresionesPath que conforman el patroacuten de matching completo para un meacutetodo de un recurso (porejemplo puede repetirse en la expresioacuten Path de la clase y de un meacutetodo) En estos casos laanotacioacuten PathParam siempre referencia el paraacutemetro path final Asiacute en el siguiente coacutedigo

Path(clientesid)public class ClienteResource

GET Path(direccionid) Produces(textplain) public String getDireccion(PathParam(id) String direccionId)

Si nuestra peticioacuten HTTP es GET clientes123direccion456 el paraacutemetro direccionIddel meacutetodo getDireccion() tendriacutea el valor inyectado de 456

Interfaz UriInfo

Podemos disponer ademaacutes de un API maacutes general para consultar y extraer informacioacuten sobrelas peticiones URI de entrada Se trata de la interfaz javaxwsrscoreUriInfo

public interface UriInfo public javanetURI getAbsolutePath() public UriBuilder getAbsolutePathBuilder()

public javanetURI getBaseUri() public UriBuilder getBaseUriBuilder()

public String getPath() public ListltPathSegmentgt getPathSegments() public MultivaluedMapltString Stringgt getPathParameters()

Los meacutetodos getAbsolutePathBuilder() y getAbsolutePath() devuelven la ruta absoluta dela peticioacuten HTTP en forma de UriBuilder y URI respectivamente

Los meacutetodos getBaseUri() y getBaseUriBuilder() devuelven la ruta base de la aplicacioacuten(ruta raiz de nuestros servicios rest) en forma de UriBuilder y URI respectivamente

El meacutetodo UriInfogetPath() permite obtener la ruta relativa de nuestros servicios RESTutilizada para realizar el matching con nuestra peticioacuten de entrada (es la ruta de la peticioacutenactual relativa a la ruta base de la peticioacuten rest)

El meacutetodo UriInfogetPathSegments() divide la ruta relativa de nuestro servicio REST enuna serie de objetos PathSegment (segmentos de ruta delimitados por )

El meacutetodo UriInfogetPathParameters() devuelve un objeto de tipo MultivaluedMap con todoslos paraacutemetros del path definidos en todas las expresiones Path de nuestra peticioacuten rest

Servicios Rest

54

Por ejemplo si la ruta de nuestra petcioacuten http es httplocalhost8080contextorestclientes2(siendo contexto la ruta raiacutez del war desplegado y rest la ruta de servicio de jax-rs)

bull la ruta absoluta (meacutetodo getAbsolutePath()) seriacutea httplocalhost8080contextorestclientes2

bull la ruta base (meacutetodo getBaseUri) seriacutea httplocalhost8080contextorest

bull la ruta relativa a la ruta base (meacutetodo getPath()) seriacutea clientes2

bull el nuacutemero de segmentos de la peticioacuten rest (meacutetodo getPathSegments()) seriacutean 2 clientesy 2

Podemos inyectar una instancia de la interfaz UriInfo utilizando la anotacioacutenjavaxwsrscoreContext A continuacioacuten mostramos un ejemplo

Path(cochesmarca)public class CarResource

GET Path(modeloanyo) Produces(imagejpeg) public Response getImagen(Context UriInfo info) String fabricado = infogetPathParameters()getFirst(marca) PathSegment modelo = infogetPathSegments()get(2) String color = modelogetMatrixParameteres()getFirst(color)

En este ejemplo inyectamos una instancia de UriInfo como paraacutemetro del meacutetodo getImagen()A continuacioacuten hacemos uso de dicha instancia para extraer informacioacuten de la URI

Recuerda que tambieacuten podriacuteamos inyectar una instancia de UriInfo en unavariable de instancia de la clase raiacutez de nuestro recurso

El meacutetodo CarResourcegetImagen() utiliza la interfazjavaxwsrscorePathSegment que como ya hemos indicado representa unsegmento de ruta

package javaxwsrscorepublic interface PathSegment String getPath() MultivaluedMapltString Stringgt getMatrixParameters()

El meacutetodo PathSegmentgetPath() devuelve el valor de la cadena de caracteres del segmentode ruta actual sin considerar niguacuten paraacutemetro matrix que pudiese contener

El meacutetodo PathSegmentgetMatrixParameters() devuelve un mapa con todos losparaacutemetros matrix aplicados a un segmento de ruta

Supongamos que realizamos la siguiente peticioacuten http para el coacutedigo anterior (claseCarResource)

Servicios Rest

55

GET cochesseatleoncolor=rojo2015

Esta peticioacuten es delegada en el meacutetodo ClarResourcegetImagen() La ruta contiene 4segmentos coches seat leon y 2015 La variable _modelo tomaraacute el valor leon y la variablecolor se instanciaraacute con el valor rojo

javaxwsrsMatrixParam

La especificacioacuten JAX-RS nos permite inyectar una matriz de valores de paraacutemetros a traveacutesde la anotacioacuten javaxwsrsMatrixParam

Path(cochesmarca)public class CarResource

GET Path(modeloanyo) Produces(imagejpeg) public Response getImagen(PathParam(marca) String marca PathParam(modelo) String modelo MatrixParam(color) String color)

El uso de la anotacioacuten MatrixParam simplifica nuestro coacutedigo y lo hace algo maacutes legibleSi por ejemplo la peticioacuten de entrada es

GET cochesseatibizacolor=black2009

entonces el paraacutemetro color del meacutetodo CarResourcegetImagen() tomariacutea el valor black

javaxwsrsQueryParam

La anotacioacuten javaxwsrsQueryParam nos permite inyectar paraacutemetros de consulta(query parameters) de la URI en los valores de los paraacutemetros de los meacutetodos java denuestros recursos Por ejemplo supongamos que queremos consultar informacioacuten de nuestrosclientes y queremos recuperar un subconjunto de clientes de nuestra base de datos NuestraURI de peticioacuten podriacutea ser algo asiacute

GET clientesinicio=0amptotal=10

El paraacutemetro de consulta inicio representa el iacutendice (o posicioacuten) del primer cliente quequeremos consultar y el paraacutemetro total representa cuaacutentos clientes en total queremosobtener como respuesta Una implementacioacuten del servicio RESTful podriacutea contener elsiguiente coacutedigo

Path(clientes)public class ClienteResource GET Produces(applicationxml) public String getClientes(QueryParam(inicio) int inicio

Servicios Rest

56

QueryParam(total) int total)

En este ejemplo el paraacutemetro inicio tomariacutea el valor 0 y el paraacutemetro total tomariacutea elvalor 10 (JAX-RS convierte automaacuteticamente las cadenas de caracteres de los paraacutemetrosde consulta en enteros)

javaxwsrsFormParam

La anotacioacuten javaxwsrsFormParam se utiliza para acceder al cuerpo del mensajede la peticioacuten HTTP de entrada cuyo valor de Content-Type es applicationx-www-form-urlencoded Es decir se utiliza para acceder a entradas individuales de un formulario HTMLPor ejemplo supongamos que para registrar a nuevos clientes en el sistema tenemos querellenar el siguiente formulario

ltFORM action=httpejemplocomclientes method=postgt ltPgt Nombre ltINPUT type=text name=nombregtltBRgt Apellido ltINPUT type=text name=apellidogtltBRgt ltINPUT type=submit value=Sendgt ltPgtltFORMgt

La ejecucioacuten de este coacutedigo inyectaraacute los valores del formulario como paraacutemetros de nuestromeacutetodo Java que representa el servicio de la siguiente forma

Path(clientes)public class ClienteResource POST public void crearCliente(FormParam(nombre) String nom FormParam(apellido) String ape)

Aquiacute estamos inyectando los valores de nombre y apellidos del formulario HTML en losparaacutemetors nom y ape del meacutetodo java crearCliente() Los datos del formulario viajan atraveacutes de la red codificados como URL-encoded Cuando se utiliza la anotacioacuten FormParamJAX-RS decodifica de forma automaacutetica las entradas del fomulario antes de inyectar susvalores

Asiacute por ejemplo si tecleamos los valores Maria Luisa y_Perlado_ como valores en loscampos de texto nombre y apellido del formulario el cuerpo de nuestro mensaje HTTP seraacutenombre=Maria20Luisaapellido=Perlado Este mensaje seraacute recibido por nuestro meacutetodoque extraeraacute los valores correspondientes y los instanciaraacute en los paraacutemetros nom y ape delmeacutetodo _ClienteResourcecrearCliente()

javaxwsrsHeaderParam

La anotacioacuten javaxwsrsHeaderParam se utiliza para inyectar valores de lascabeceras de las peticiones HTTP Por ejemplo si estamos interesados en la paacutegina web quenos ha referenciado o enlazado con nuestro servicio web podriacuteamos acceder a la cabeceraHTTP Referer utilizando la anotacioacuten HeaderParam de la siguiente forma

Servicios Rest

57

Path(miservicio)public class MiServicio GET Produces(texthtml) public String get(HeaderParam(Referer) String referer)

De forma alternativa podemos acceder de forma programativa a todas las cabeceras de lapeticioacuten de entrada utilizando la interfaz javaxwsrscoreHttpHeaders

public interface HttpHeaders public ListltStringgt getRequestHeader(String name) public MultivaluedMapltString Stringgt getRequestHeaders()

El meacutetodo getRequestHeader() permite acceder a una cabecera en concreto y el meacutetodogetRequestHeaders() nos proporciona un objeto de tipo Map que representa todas lascabeceras A continuacioacuten mostramos un ejemplo que accede a todas las cabeceras de lapeticioacuten HTTP de entrada

Path(miservicio)public class MiServicio GET Produces(texthtml) public String get(Context HttpHeaders cabeceras) String referer = headersgetRequestHeader(Referer)get(0) for (String header headersgetRequestHeaders()keySet()) Systemoutprintln(Se ha utilizado esta cabecera + header)

javaxwsrscoreContext

Dentro de nuestros recursos JAX-RS podemos inyectar determinados objetos coninformacioacuten sobre el contexto de JAX-RS sobre el contexto de servlets o sobreelementos de la peticioacuten recibida desde el cliente Para ello utilizaremos la anotacioacutenjavaxwsrscoreContext

En los ejemplos de esta sesioacuten ya hemos visto como utilizarla para inyectar objetos de tipoUriInfo y HttpHeaders

A continuacioacuten mostramos un ejemplo en el que podos obtener detalles sobre el contextodel despliegue de la aplicacion asi como del contexto de peticiones individuales utilizando laanotacion Context

Implementacioacuten de un servicio que muestra informacioacuten sobre el contexto de la peticioacuten

Path(orders)

Servicios Rest

58

public class PedidoResource

Context Application app

Context UriInfo uri

Context HttpHeaders headers

Context Request request

Context SecurityContext security

Context Providers providers

GET Produces(applicationxml) public ListltOrdergt getAll(QueryParam(start)int from QueryParam(end)int to) (appgetClasses()) (urigetPath()) (headersgetRequestHeader(HttpHeadersACCEPT)) (headersgetCookies()) (requestgetMethod()) (securityisSecure())

Application proporciona acceso a la informacioacuten de la configuracioacuten de la aplicacioacuten(clase Application)UriInfo proporciona acceso a la URI de la peticioacutenHttpHeaders proporciona acceso a las cabeceras de la peticioacuten HTTP La anotacioacutenHeaderParam puede tambieacuten utilizarse para enlazar una cabecera HTTP a unparaacutemetro de un meacutetodo de nuestro recurso a un campo del mismo o a una propiedadde un beanRequest se utiliza para procesar la respuestas tiacutepicamente se usa juntamente con laclase Response para construir la respuesta de forma dinaacutemicaSecurityContext proporciona acceso a la informacioacuten de la peticioacuten actual relacionadacon la seguridadProviders proporciona informacioacuten sobre la buacutesqueda del runtime de las instancias deproveedores utilizando un conjunto de criterios de buacutesqueda

Con respecto a contexto de servlets podremos inyectar informacioacuten de ServletContextServletConfig HttpServletRequest y HttpServletResponse Debemos recordar que losrecursos JAX-RS son invocados por un servlet dentro de una aplicacioacuten web por lo quepodemos necesitar tener acceso a la informacioacuten del contexto de servlets Por ejemplo sinecesitamos acceder a la ruta en disco donde tenemos los datos de nuestra aplicacioacuten webtendremos que inyectar el objeto ServletContext

GETProduces(imagejpeg)public InputStream getImagen(Context ServletContext sc) return scgetResourceAsStream(fotos + nif + jpg)

javaxwsrsBeanParam

La anotacioacuten javaxwsrsBeanParam nos permite inyectar una clase especiacutefica cuyosmeacutetodos o atributos esteacuten anotados con alguna de las anotaciones de inyeccioacuten de paraacutemetrosxxxParam que hemos visto en esta sesioacuten Por ejemplo supongamos esta clase

Servicios Rest

59

public class ClienteInput FormParam(nombre) String nombre

FormParam(apellido) String apellido

HeaderParam(Content-Type) String contentType

public String getFirstName()

La clase ClienteInput es un simple POJO (Plain Old Java Object) que contiene el nombrey apellidos de un cliente asiacute como el tipo de contenido del mismo Podemos dejar que JAX-RScree inicialice e inyecte esta clase usando la anotacioacuten BeanParam de la siguiente forma

Path(clientes)public class ClienteResource POST public void crearCliente(BeanParam ClienteInput newCust)

El runtime de JAX-RS analizaraacute los paraacutemetros anotados con BeanParam para inyectarlas anotaciones correspondientes y asignar el valor que corresponda En este ejemplo la claseClienteInput contendraacute dos valores de un formulario de entrada y uno de los valores de lacabecera de la peticioacuten De esta forma nos podemos evitar una larga lista de paraacutemetros enel meacutetodo crearCliente() (en este caso son soacutelo tres pero podriacutean ser muchos maacutes)

Conversioacuten automaacutetica de tipos

Todas las anotaciones que hemos visto referencian varias partes de la peticioacuten HTTP Todasellas se representan como una cadena de caracteres en dicha peticioacuten HTTP JAX-RS puedeconvertir esta cadena de caracteres en cualquier tipo Java siempre y cuando se cumpla almenos uno de estos casos

1 Se trata de un tipo primitivo Los tipos int short float double byte char y booleanpertenecen a esta categoriacutea

2 Se trata de una clase Java que tiene un constructor con un uacutenico paraacutemetro de tipo String

3 Se trata de una clase Java que tiene un meacutetodo estaacutetico denominado valueOf() que tomaun uacutenico String como argumento y devuelve una instancia de la clase

4 Es una clase de tipo javautilListltTgt javautilSetltTgt o javautilSortedSetltTgt en dondeT es un tipo que satisface los criterios 2 oacute 3 o es un String Por ejemplo ListltDoublegtSetltStringgt o SortedSetltIntegergt

Si el runtime JAX-RS falla al convertir una cadena de caracteres en el tipo Java especificadose considera un error del cliente Si se produce este fallo durante el procesamiento de unainyeccioacuten de tipo MatrixParam QueryParam o PathParam se devuelve al clienteun error 404 Not found Si el fallo tiene lugar con el procesamiento de las inyecciones

Servicios Rest

60

HeaderParam o CookieParam (esta uacuteltima no la hemos visto) entonces se enviacutea al clienteel eror 400 Bad Request

Valores por defecto (DefaultValue)

Suele ser habitual que algunos de los paraacutemetros proporcionados en las peticiones a serviciosRESTful sean opcionales Cuando un cliente no proporciona esta informacioacuten opcional en lapeticioacuten JAX-RS inyectaraacute por defecto un valor null si se trata de un objeto o un valor ceroen el caso de tipos primitivos

Estos valores por defecto no siempre son los que necesitamos para nuestro servicioPara solucionar este problema podemos definir nuestro propio valor por defecto para losparaacutemetros que sean opcionales utilizando la anotacioacuten javaxwsrsDefaultValue

Consideremos el ejemplo anterior relativo a la recuperacioacuten de la informacioacuten de unsubconjunto de clientes de nuestra base de datos Para ello utilizaacutebamos dos paraacutemetros deconsulta para indicar el iacutendice del primer elemento asiacute como el nuacutemero total de elementosque estamos interesados en recuperar En este caso no queremos que el cliente tengaque especificar siempre estos paraacutemetros al realizar la peticion Usaremos la anotacioacutenDefaultValue para indicar los valores por defecto que nos interese

Path(clientes)public class ClienteResource GET Produces(applicationxml) public String getClientes( DefaultValue(0) QueryParam(inicio) int inicio DefaultValue(10) QueryParam(total) int total)

Hemos usado DefaultValue para especificar un iacutendice de comienzo con valor cero y untamantildeo del subconjunto de los datos de la respuesta JAX-RS utilizaraacute las reglas de conversioacutende cadenas de caracteres que acabamos de indicar para convertir el valor del paraacutemetro enel tipo Java que especifiquemos

25 Configuracioacuten y despliegue de aplicaciones JAX-RS

Como ya hemos visto en la sesioacuten anterior implementamos nuestros servicios REST utilizandoel API de Java JAX-RS (especificacioacuten JSR-339) Una aplicacioacuten JAX-RS consiste en unoo maacutes recursos y cero o maacutes proveedores En este apartado vamos a describir ciertosaspectos aplicados a las aplicaciones JAX-RS como un todo concretamente a la configuracioacuteny tambieacuten a la publicacioacuten de las mismas cuando utilizamos un servidor de aplicacionesJavaEE 7 o bien un contenedor de servlets 30 que incluyan una implementacioacuten del APIJAX-RS Tambieacuten indicaremos coacutemo configurar el despliegue en el caso de no disponer comomiacutenimo de un contenedor de servlets 30

Configuracioacuten mediante la clase Application

Tanto los recursos (clases anotadas con Path) como los proveedores que conforman nuestraaplicacioacuten JAX-RS pueden configurarse utilizando una subclase de Application Cuandohablamos de configuracioacuten nos estamos refiriendo en este caso a definir los mecanismospara localizar las clases que representan los recursos asiacute como a los proveedores

Servicios Rest

61

Un proveedor es una clase que implementa una o algunade las siguientes interfaces JAX-RS MesssageBodyReaderMessageBodyWriter ContextResolverltTgt y ExceptionMapperltTgt Lasdos primeras permiten crear proveedores de entidades (entity providers)la tercera es un proveedor de contexto (context provider) y la uacuteltima unproveedor de mapeado de excepciones (exception mapping provider) Lasclases que actuacutean como proveedores estaacuten anotadas con Providerpara que puedan ser identificadas automaacuteticamente por el runtime JAX-RS

El uso de una subclase de Application para configurar nuestros servicios REST constituyela forma maacutes sencilla de desplegar los servicios JAX-RS en un servidor de aplicacionescertificado como Java EE (en este caso Wildfly cumple con este requisito) o un contenedorstandalone de Servlet 3 (como por ejemplo Tomcat)

Pasemos a conocer la clase javaxwsrscoreApplication El uso de la claseApplication es la uacutenica forma portable de decirle a JAX-RS queacute servicios web (clasesanotadas con Path) asiacute como queacute otros elementos como filtros interceptoreshellip queremospublicar (desplegar)

La clase Application se define como

package javaxwsrscore

import javautilCollectionsimport javautilSet

public abstract class Application private static final SetltObjectgt emptySet = CollectionsemptySet()

public abstract SetltClassltgtgt getClasses()

public SetltObjectgt getSingletons() return emptySet

La clase Application es muy simple Como ya hemos indicado su propoacutesito es proporcionaruna lista de clases y objetos que queremos desplegar

El meacutetodo getClasses() devuelve una lista de clases de servicios web y proveedores JAX-RS Cualquier servicio JAX-RS devuelto por este meacutetodo sigue el modelo per-request queya hemos introducido en la sesioacuten anterior Cuando la implementacioacuten de JAX-RS determinaque una peticioacuten HTTP necesita ser procesada por un meacutetodo de una de estas clases secrearaacute una instancia de dicha clase durante la peticioacuten y se destruiraacute al finalizar la mismaEn este caso estamos delegando en el runtime JAX-RS la creacioacuten de los objetos Lasclases proveedoras son instanciadas por el contenedor JAX-RS y registradas una uacutenica vezpor aplicacioacuten

El meacutetodo getSingletons() devuelve una lista de servicios y proveedores web JAX-RSya instanciados Nosotros como programadores de las aplicaciones somos responsablesde crear estos objetos El runtime JAX-RS iteraraacute a traveacutes de la lista de objetos y los registraraacuteinternamente

Servicios Rest

62

Un ejemplo de uso de una subclase de Application podriacutea ser eacuteste

package orgexpertojava

import javaxwsrscoreApplicationimport javaxwsrsApplicationPath

ApplicationPath(rest)public class ComercioApplication extends Application

public SetltClassltgtgt getClasses() HashSetltClassltgtgt set = new HashSetltClassltgtgt() setadd(ClienteResourceclass) setadd(PedidoResourceclass) return set

public SetltObjectgt getSingletons() JsonWriter json = new JsonWriter() TarjetaCreditoResource servicio = new TarjetaCreditoResource()

HashSetltObjectgt set = new HashSet() setadd(json) setadd(servicio) return set

La anotacioacuten ApplicationPath define la base URL de la ruta para todos nuestrosservicios JAX-RS desplegados Asiacute por ejemplo accederemos a todos nuestros serviciosJAX-RS seraacuten desde la ruta rest cuando los ejecutemos En el ejemplo anterior estamosindicando que ClienteResource y PedidoResource son servicios per-request El meacutetodogetSingletons() devuelve el servicio de tipo TarjetaCreditoResource asiacute como el proveedorJsonWriter (que implementa la interfaz MessageBodyWriter)

Si tenemos al menos una implementacioacuten de la clase Application anotada conApplicationPath esta seraacute detectada y desplegada automaacuteticamente por el servidor deaplicaciones

Podemos aprovechar completamente esta capacidad para escanear y detectarautomaacuteticamente nuestros servicios si tenemos implementada una subclase de Applicationpero dejamos que getSingletons() devuelva el conjunto vaciacuteo y no indicamos nada en elmeacutetodo getClasses() de esta forma

package orgexpertojava

import javaxwsrsApplicationPathimport javaxwsrscoreApplication

ApplicationPath(rest)public class ComercioApplication extends Application

Servicios Rest

63

En este caso el servidor de aplicaciones se encargaraacute de buscar en el directorio WEB-INFclasses y en cualquier fichero jar dentro del directorio WEB-INFlib A continuacioacuten antildeadiraacutecualquier clase anotada con Path o Provider a la lista de cosas que necesitan serdesplegadas y registradas en el runtime JAX-RS

Los servicios REST son atendidos por un servlet que es especiacutefico de la implementacioacutenJAX-RS utilizada por el servidor de aplicaciones El servidor wildfly utiliza la implementacioacutende JAX-RS 20 denomindada resteasy (otra implementacioacuten muy utilizada es jersey porejemplo con el servidor de aplicaciones Glassfish) El runtime de JAX-RS contiene un servletinicializado con un paraacutemetro de inicializacioacuten de tipo javaxwsrsApplication cuyo valor seraacuteinstanciado automaacuteticamente por el servidor de aplicaciones con el nombre de la subclasede Application que sea detectada en el war de nuestra aplicacioacuten

Configuracioacuten mediante un fichero webxml

En la sesioacuten anterior no hemos utilizado de forma expliacutecita la clase Application para configurarel despliegue En su lugar hemos indicado esta informacioacuten en el fichero webxml

ltweb-app version=30 xmlns=httpjavasuncomxmlnsjavaee xmlnsxsi=httpwwww3org2001XMLSchema-instance xsischemaLocation=httpjavasuncomxmlnsjavaee httpjavasuncomxmlnsjavaeeweb-app_3_0xsdgt lt-- Con estas liacuteneas el servidor es el responsable de antildeadir el servlet correspondiente de forma automaacutetica Si en nuestro war tenemos clases anotadas con anotaciones JAX-RS para recibir invocaciones REST eacutestas seraacuten detectadas y registradas--gt ltservlet-mappinggt ltservlet-namegtjavaxwsrscoreApplicationltservlet-namegt lturl-patterngtrestlturl-patterngt ltservlet-mappinggtltweb-appgt

Esta configuracioacuten es equivalente a incluir una subclase de Application sin sobreescribir losmeacutetodos correspondientes En este caso se antildeade de forma dinaacutemica el servlet que sirvelas peticiones REST con el nombre javaxwsrscoreApplication de forma que se detectenautomaacuteticamente todas las clases de recursos y clases proveedoras empaquetadas en el warde la aplicacioacuten

Configuracioacuten en un contenedor que no disponga de una implementacioacuten JAX-RS

Si queremos hacer el despliegue sobre servidores de aplicaciones o servidores web queden soporte a una especificacioacuten de servlets con una versioacuten inferior a la 30 tendremosque configurar MANUALMENTE el fichero webxml para que cargue el servlet de nuestraimplementacioacuten propietaria de JAX-RS (cuyos ficheros jar deberemos incluir en el directorioWEB-INFlib de nuestro war) Un ejemplo de configuracioacuten podriacutea ser eacuteste

Configuracioacuten del fichero webxml (directorio de fuentes webappWEB-INFwebxml)

ltxml version=10gtltweb-appgt ltservletgt

Servicios Rest

64

ltservlet-namegtJAXRSltservlet-namegt ltservlet-classgt orgjbossresteasypluginsserverservletHttpServletDispatcher ltservlet-classgt ltinit-paramgt ltparam-namegt javaxwsrsApplication ltparam-namegt ltparam-valuegt orgexpertoJavaComercioApplication ltparam-valuegt ltinit-paramgt ltservletgt

ltservlet-mappinggt ltservlet-namegtJAXRSltservlet-namegt lturl-patterngtrestlturl-patterngt ltservlet-mappinggtltweb-appgt

En la configuracioacuten anterior estamos indicando de forma expliacutecita el servlet JAX-RS que recibelas peticiones REST que a su vez utilizaraacute la clase Application para detectar queacute servicios yproveedores REST seraacuten desplegados en el servidor

Tambieacuten seraacute necesario incluir la libreriacutea con la implementacioacuten JAX-RS 20 de formaexpliacutecita en el war generado (recordemos que para ello tendremos que utilizar la etiquetaltscopegtcompileltscopegt para que se antildeadan los jar correspondientes)

Libreriacutea con la implementacioacuten de JAX-RS 20

ltdependencygt ltgroupIdgtjavaxltgroupIdgt ltartifactIdgtjavaee-web-apiltartifactIdgt ltversiongt70ltversiongt ltscopegtcompileltscopegtltdependencygt

Servicios Rest

65

26 Ejercicios

Para esta sesioacuten antildeadiremos un nuevo moacutedulo en el que implementaremos un servicio restincorporando los conceptos que hemos explicado durante la sesioacuten En concreto

bull Creamos un moacutedulo Maven con IntelliJ (desde el directorio ejercicios-rest-expertojava ) con el arquetipo webapp-javaee7 tal y como hemos visto en losapuntes de la sesioacuten Las coordenadas del artefacto Maven seraacuten

GroupId orgexpertojava

ArtifactId s2-foro-nuevo

version 10-SNAPSHOT

bull Configuramos el pommxl del proyecto para poder compilar empaquetar y desplegarnuestro servicio en el servidor de aplicaciones Wildfly Consulta los apuntes para ver cuaacuteldebe ser el contenido de las etiquetas ltpropertiesgt ltdependenciesgt y ltbuildgt

bull Vamos a estructurar los fuentes (directorio srcmainjava) de nuestro proyecto en lossiguientes paquetes

orgexpertojavadatos contendraacute clases relacionadas con los datos a los que accedenuestra aplicacioacuten rest Por simplicidad almacenaremos en memoria los datos denuestra aplicacioacuten

orgexpertojavamodelo contiene las clases de nuestro modelo de objetos que seraacutenclases java con atributos y sus correspondientes getters y setters

orgexpertojavarest contiene los recursos JAX-RS que implementan nuestrosservicios rest asiacute como las clases necesarias para automatizar el despliegue de dichosrecursos

Creacioacuten de un recurso creacioacuten y consulta de temas en el foro (05 puntos)

Vamos a crear un recurso JAX-RS al que denominaremos TemasResource (en el paqueteorgexpertojavarest ) En el siguiente ejercicio al configurar la aplicacioacuten haremos que esterecurso sea un singleton Nuestro recurso gestionaraacute sus propios datos en memoria Porejemplo podemos utilizar un atributo private de tipo HashMap en el que almacenaremos lostemas cada uno con un identificador numeacuterico como clave Tambieacuten necesitaremos un atributopara generar las claves para cada uno de los temas Por ejemplo

private MapltInteger Temagt temasDB = new HashMapltInteger Temagt()private int contadorTemas = 0

Fiacutejate que si utilizamos los tipos HashMap e int podemos tener problemasde concurrencia si muacuteltiples usuarios estaacuten realizando peticiones paracrear yo consultar los temas del foro En una situacioacuten real deberiacuteamosutilizar en su lugar los tipos ConcurrentHasMap y AtomicInteger paraevitar el que dos usuarios intentaran crear un nuevo tema con la mismaclave perdieacutendose asiacute uno de los dos temas creados Al tratarse de unejercicio en el que solamente tendremos un cliente no nos plantearaacuteninguacuten problema el trabajar con HashMap e int por lo que podeacuteis elegircualquiera de las dos opciones para realizar el ejercicio

Servicios Rest

66

bull Nuestro recurso estaraacute accesible en el servidor en la ruta temas (relativa a la raiacutez delcontexto de nuestra aplicacioacuten y a la ruta de nuestro servlet JAX-RS que determinaremoscon la anotacioacuten ApplicationPath de nuestra clase Application)

bull En el paquete orgexpertojavamodelo crearemos la clase Tema con los atributosprivados

int idString nombre

y sus correspondientes getters y setters

setId() getId()setNombre() getNombre()

bull Implementamos un primer meacutetodo en el recurso TemasResource denominadocreaTema() para poder crear un nuevo tema en el foro Dicho meacutetodo atenderaacutepeticiones POST a nuestro servicio Los datos de entrada (cadena de caracteres querespresenta el nombre del tema) se pasan a traveacutes de un formulario html en el que tenemosuna uacutenica entrada denominada nombre

Puedes incluir el siguiente contenido en el fichero indexhtml para introducir los datosdesde el navegador

ltDOCTYPE htmlgtlthtmlgtltheadgt lttitlegtStart Pagelttitlegt ltmeta http-equiv=Content-Type content=texthtml charset=UTF-8gtltheadgtltbodygtlth1gtAlta de temas en el foro lth1gtltform action=s2-foro-nuevoresttemas method=postgt Nombre del tema ltinput type=text name=nombre gtltbr gt

ltinput type=submit value=Enviar gtltformgtltbodygtlthtmlgt

Cada nuevo Tema creado se antildeadiraacute a nuestra base de datos en memoria temasDB juntocon un identificador numeacuterico (que se iraacute incrementando para cada nueva instancia creada)

bull Implementamos un segundo meacutetodo para consultar los temas creados en el foro Elmeacutetodo se denominaraacute verTemasTodos() y devuelve (en formato texto) todos los temasactualmente creados Dado que puede haber un gran nuacutemero de ellos vamos a permitirque el usuario decida cuaacutentos elementos como maacuteximo quiere consultar a partir de unaposicioacuten determinada Por defecto si no se indica esta informacioacuten se mostraraacuten comomaacuteximo los primeros 8 temas registrados en el foro Si el identificador a partir del cualqueremos iniciar la consulta es mayor que el nuacutemero de temas almacenados entoncesdevolveremos la cadena No es posible atender la consulta Ejemplos de URIs que aceptadicho meacutetodo son

Servicios Rest

67

temas

en este caso y suponiendo que hayamos creado solamente los tres temas del apartadoanterior el resultado seriacutea

Listado de temas del 1 al 81 animales2 plantas3 ab

temasinicio=2amptotal=2

el resultado seriacutea

Listado de temas del 2 al 32 plantas3 ab

temasinicio=7amptotal=1

el resultado seriacutea

No es posible atender la consulta

Como ya hemos comentado las URIs indicadas en este ejercicio sonrelativas a la raiacutez del contexto de nuestra aplicacioacuten y a la ruta especificadapara nuestros servicios rest Recuerda que si has configurado el pomxmlcomo en la sesioacuten anterior la raiacutez del contexto de la aplicacioacuten vendraacutedada por el valor de la etiqueta ltfinalNamegt anidada en ltbuildgt Ennuestro caso deberiacutea ser s2-foro-nuevo Maacutes adelante fijaremos la rutade nuestros servicios rest como rest Por ejemplo la URI completa parael uacuteltimo apartado seriacutea httplocalhost8080s2-foro-nuevoresttemasinicio=7amptotal=1

Despliegue y pruebas del recurso (05 puntos)

Vamos a construir y desplegar nuestro servicio en el servidor de aplicaciones Para ello vamosa utilizar una subclase de Application que antildeadiremos en el paquete orgexpertojavarestLa ruta en la que se van a servir nuestras peticiones rest seraacute rest Fiacutejate que el recursoque hemos creado es el encargado de gestionar (crear modificarhellip) sus propios datosPor lo tanto necesitamos que nuestro recurso REST sea un singleton Implementa la claseForoApplication y realiza la construccioacuten y despliegue del proyecto A continuacioacutenprueba el servicio utilizando postman Puedes probar la insercioacuten de temas utilizando tambieacutenel formulario a traveacutes de la URI httplocalhost8080s2-foro-nuevo Podemos utilizar lasentradas del apartado anterior de forma que comprobemos que se crean correctamentelos temas animales plantas y ab y que obtenemos los listados correctos tanto si noindicamos el inicio y total de elementos como si decidimos mostrar los temas desde el 2 hastael 3

Servicios Rest

68

Cuando utilices el cliente IntelliJ para probar meacutetodos POST debesproporcionar un Request Body no vaciacuteo En este caso como en lapropia URI incluimos el contenido del mensaje que es el nombre del temaque queremos antildeadir al foro tendraacutes que seleccionar Text aunque norellenemos el campo correspondiente De no hacerlo asiacute obtendremoscomo respuesta un cuerpo de mensaje vaciacuteo y la cabecera de respuestaHTTP11 415 Unsupported Media Type

Muacuteltiples consultas de los temas del foro (05 puntos)

Implementa tres nuevas consultas de los temas del foro de forma que

bull Se pueda realizar una consulta de un tema concreto a partir de su identificador numeacuterico(el meacutetodo solamente debe admitir identificadores formados por uno o maacutes diacutegitos) Si eltema consultado no existe se debe devolver una excepcioacuten con la cabecera de respuestaHTTP11 404 Not Found Por ejemplo

temas2

Debe devolver lo siguiente

Ver el tema 2plantas

temas4

Obtenemos como respuesta un cuerpo de mensaje vaciacuteo y la cabecera de respuestaHTTP11 404 Not Found

bull Se pueda realizar una consulta de los temas que comiencen por uno de los siguientescaracteres a b c oacute d Por ejemplo teniendo en cuenta que hemos introducido los temasanteriores

temasa

Debe devolver lo siguiente

Listado de temas que comienzan por aanimales

temasd

Debe devolver Listado de temas que comienzan por d

bull Se pueda realizar una consulta de los temas que contengan una subcadena de caracteresPor ejemplo teniendo en cuenta que hemos introducido los temas anteriores

temasma + Debe devolver lo siguiente

Listado de temas que contienen la subcadena maanimales

Servicios Rest

69

Creacioacuten de subrecursos (05 puntos)

Vamos a crear el subrecurso MensajesResource (en el paquete orgexpertojavarest)de forma que este recurso gestione la creacioacuten y consulta de mensajes para cada unode los temas del foro Este subrecurso debe atender peticiones desde rutas del tipotemasidentificadorTemamensajes siendo identificadorTema la clave numeacutericaasociada a uno de los temas almacenados

bull En este caso nuestro subrecurso no seraacute un singleton por lo que necesitaremos almacenarlos mensajes en otra clase diferente (ya que crearemos una nueva instancia del recursopara cada peticioacuten) La clase DatosEnMemoria (en el paquete orgexpertojavadatos)seraacute la encargada de almacenar en memoria la informacioacuten de los mensajes publicadospara cada tema Por ejemplo puedes utilizar los siguientes campos estaacuteticos paragestionar los mensajes

public static MapltMensaje Stringgt mensajesDB = new HashMapltMensaje Stringgt()

La clave seraacute el propio mensaje (objeto Mensaje que se asociaraacute al tema correspondiente)

public static int contadorMen = 0

Como ya hemos comentado puedes usar ConcurrentHashMap yAtomicInteger en lugar de los tipos anteriores para evitar problemas deconcurrencia

bull En el paquete orgexpertojavadatos crearemos la clase Mensaje con los atributosprivados

int idString textoString autor=anonimo

y sus correspondientes getters y setters

setId() getId()setTexto() getTexto()setAutor() getAutor()

bull Vamos a crear un meacutetodo para poder realizar la publicacioacuten de un mensaje de texto en elforo en uno de los temas ya creados Independientemente del tipo de peticioacuten realizadasobre los mensajes si el tema indicado en la URI no existe lanzaremos la excepcioacutenWebApplicationException(ResponseStatusNOT_FOUND) Veamos alguacuten ejemplo

Deberemos poder realizar una peticioacuten POST a temas1mensajes con el cuerpo demensaje = Mensaje numero 1 El mensaje creado por defecto tendraacute asociado el autoranonimo

Servicios Rest

70

Si realizamos una peticioacuten para antildeadir un mensaje a la URI temas9mensajesdeberiacuteamos obtener como cabecera de respuesta HTTP11 404 Not Foundindependientemente del cuerpo del mensaje

bull Vamos a crear un meacutetodo para realizar una consulta de todos los mensajes publicados enun tema concreto Por ejemplo

Una peticioacuten GET a temas1mensajes deberiacutea dar como resultado

Lista de mensajes para el tema animales1 Mensaje anonimo

Si realizamos una peticioacuten GET a la URI temas9mensajes deberiacuteamos obtenercomo cabecera de respuesta HTTP11 404 Not Found independientemente delcuerpo del mensaje

bull Finalmente vamos a antildeadir dos nuevos meacutetodos para (a) antildeadir un nuevo mensajeen un tema concreto indicando el autor del mensaje Como restriccioacuten el nombre delautor deberaacute estar formado solamente por caracteres alfabeacuteticos utilizando mayuacutesculas ominuacutesculas y como miacutenimo tiene que tener un caracter y (b) consultar todos los mensajesque un determinado autor ha publicado en el foro en un tema determinado

Una peticioacuten POST a la URI temas1mensajespepe con el cuerpo de mensaje convalor mensaje de pepe deberiacutea crear un nuevo mensaje para el tema con identificador2 y devolver como resultado el nuevo id (yo la URI del nuevo recurso en la cabecerade respuesta Location si seguimos la ortodoxia REST) En caso de que devolvamos laURI del nuevo recurso podemos utilizar la orden

return Responsecreated(uriInfogetAbsolutePathBuilder()

segment(StringvalueOf(id))

build())

build()

Obtenemos el path absoluto de la uri que nos ha invocadoAntildeadimos el identificador id del nuevo recurso creadoConstruimos la nueva URIConstruimos el objeto Response

Veremos coacutemo manipular objetos de tipo Response en sesiones posteriores

Recuerda que para acceder al cuerpo de la peticioacuten basta con definir unparaacutemetro de tipo String JAX-RS automaacuteticamente lo instanciaraacute con elcuerpo de la peticioacuten como una cadena

bull Una peticioacuten GET a la URI temas1mensajesanonimo dariacutea como resultado

Lista de mensajes tema= animales y autor= anonimo

1 Mensaje anonimo

bull Una peticioacuten GET a la URI temas1mensajes dariacutea como resultado

Lista de mensajes para el tema animales

Servicios Rest

71

1 Mensaje anonimo2 mensaje de pepe

bull Una peticioacuten GET a la URI temas1mensajesroberto dariacutea como resultado

Lista de mensajes tema= animales y autor= roberto

Servicios Rest

72

3 Manejadores de contenidos Respuestas del servidor ymanejo de excepciones

En la sesioacuten anterior hemos hablado de coacutemo inyectar informacioacuten contenida en las cabecerasde las peticiones HTTP ahora nos detendremos en el cuerpo del mensaje tanto de la peticioacutencomo de la respuesta En el caso de las peticiones explicaremos el proceso de transformarlos datos de entrada en objetos Java para poder ser procesados por nuestros serviciosCon respecto a las respuestas proporcionadas por nuestros servicios analizaremos tanto loscoacutedigos de respuesta por defecto como la elaboracioacuten de respuestas complejas y manejo deexcepciones

31 Proveedores de entidades

JAX-RS define lo que se denominan proveedores de entidades que son clases queproporcionan servicios de mapeado entre las representaciones del cuerpo del mensaje HTTPy los correspondientes tipos java que utilizaremos en nuestros recursos (paraacutemetros en losmeacutetodos o bien como tipo de la respuesta de los mismos) Las entidades tambieacuten se conocencon el nombre de message payload o simplemente como payload y representan elcontenido del cuerpo del mensaje HTTP

ProvidersEl runtime de JAX-RS puede extenderse (ampliarse) utilizandoclases proveedoras (providers) suministradas por nuestra aplicacioacutenConcretamente JAX-RS nos proporciona un conjunto de interfaces quepodemos implementar en nuestra aplicacioacuten creando asiacute dichas clasesproveedoras de entidades (entity providers) La especificacioacuten de JAX-RS define un proveedor como una clase que implementa una o maacutesinterfaces JAX-RS (de entre un conjunto determinado) y que puedenanotarse con provider para ser descubiertas de forma automaacuteticapor el runtime de JAX-RS

Nuestra aplicacioacuten puede proporcionar su propio mapeado entre representaciones(tipos MIME) del mensaje de entrada y tipos Java implementando las interfacesMessageBodyWriter y MessageBodyReader convirtieacutendose asiacute en clases proveedorasde entidades (entity providers) Por ejemplo podemos tener nuestro propio proveedor deentidades para el formato XML o JSON de forma que utilizando las libreriacuteas de java paraprocesamiento XML o JSON (Java API for XML Processing JAXP1 y Java API for JSONProcessing JSON-P2) implementemos el serializadodeserializado del cuerpo del mensajeHTTP de entrada cuando eacuteste presente los tipos MIME applicationxml o application_jsonLas clases que realizan dichos mapeados son clases entity provider

Interfaz javaxwsrsextMessageBodyReader

La interfaz MessageBodyReader define el contrato entre el runtime de JAX-RS y loscomponentes que proporcionan servicios de mapeado desde diferentes representaciones(indicadas como tipos mime) al tipo Java correspondiente Cualquier clase que quieraproporcionar dicho servicio debe implementar la interfaz MessageBodyReader y debeanotarse con Provider para poder ser detectada de forma automaacutetica por el runtime deJAX-RS

1 httpswwwjcporgenjsrdetailsummaryid=2062 httpsjcporgenjsrdetailid=353

Servicios Rest

73

La secuencia loacutegica de pasos seguidos por una implementacioacuten de JAX-RS cuando se mapeael cuerpo de un mensaje HTTP de entrada a un paraacutemetro de un meacutetodo Java es la siguiente

1 Se obtiene el media type de la peticioacuten (valor de la cabecera HTTP Content-Type ) Sila peticioacuten no contiene una cabecera Content-Type se usaraacute applicationoctet-stream

2 Se identifica el tipo java del paraacutemetro cuyo valor seraacute mapeado desde el cuerpo delmensaje

3 Se localiza la clase MessageBodyReader que soporta el media type de la peticioacuten y seusa su meacutetodo readFrom() para mapear el contenido del cuerpo del mensaje HTTP enel tipo Java que corresponda

4 Si no es posible encontrar el MessageBodyReader adecuado se genera la excepcioacutenNotSupportedException con el coacutedigo 405

Interfaz javaxwsrsextMessageBodyWriter

La interfaz MessageBodyWriter define el contrato entre el runtime de JAX-RS ylos componentes que proporcionan servicios de mapeado desde un tipo Java a unarepresentacioacuten determinada Cualquier clase que quiera proporcionar dicho servicio debeimplementar la interfaz MessageBodyWriter y debe anotarse con Provider para poderser detectada de forma automaacutetica por el runtime de JAX-RS

La secuencia loacutegica de pasos seguidos por una implementacioacuten de JAX-RS cuando se mapeaun valor de retorno de un meacutetodo del recurso a una entidad del cuerpo de un mensaje HTTPes la siguiente

1 Se obtiene el objeto que seraacute mapeado a la entidad del cuerpo del mensaje

2 Se determina el media type de la respuesta

3 Se localiza la clase MessageBodyWriter que soporta el objeto que seraacute mapeado a laentidad del cuerpo del mensaje HTTP y se utiliza su meacutetodo writeTo() para realizardicho mapeado

4 Si no es posible encontrar el MessageBodyWriter adecuado se generala excepcioacuten InternalServerErrorException (que es una subclase deWebApplicationException ) con el coacutedigo 500

32 Proveedores de entidad estaacutendar incluidos en JAX-RS

Cualquier implementacioacuten de JAX-RS debe incluir un conjunto de implementaciones deMessageBodyReader y MessageBodyWriter de forma predeterminada para ciertascombinaciones de tipos Java y media types

Table 3 Proveedores de entidades estaacutendar de una implementacioacuten JAX-RS

Tipo Java Media Type

byte[] (Cualquier media type)

javalangString (Cualquier media type)

javaioInputStream (Cualquier media type)

Servicios Rest

74

Tipo Java Media Type

javaioReader (Cualquier media type)

javaioFile (Cualquier media type)

javaxactivationDataSource (Cualquier media type)

javaxxmltransformSource textxml applicationxml application+xml(tipos basados en xml)

javaxxmlbindJAXBElement andapplication-supplied JAXB classes

textxml applicationxml application+xml(tipos basados en xml)

MultivaluedMapltStringStringgt applicationx-www-form-urlencoded(Contenido de formularios)

StreamingOutput (Cualquier media type) (SoacuteloMessageBodyWriter )

javalangBoolean javalangCharacterjavalangNumber

textplain

A continuacioacuten comentaremos algunos de estos proveedores de entidades estaacutendar oconversores por defecto que permiten convertir el cuerpo del mensaje HTTP a objetos Javade diferentes tipos y viceversa

javaxwsrscoreStreamingOutput

StreamingOutput es una interfaz callback que implementamos cuando queremos tratar comoun flujo continuo (streaming) el cuerpo de la respuesta Constituye una alternativa ligera aluso de MessageBodyWriter

public interface StreamingOutput void write(OutputStream output) throws IOException WebApplicationException

Implementamos una instancia de esta interfaz y la utilizamos como tipo de retorno denuestros meacutetodos de recursos Cuando el runtime de JAX-RS estaacute listo para escribir elcuerpo de respuesta del mensaje se invoca al meacutetodo write() de la instancia deStreamingOutput Veamos un ejemplo

Path(miservicio) public class MiServicio GET Produces(textplain) StreamingOutput get() return new StreamingOutput() public void write(OutputStream output) throws IOException WebApplicationException outputwrite(hello worldgetBytes())

Hemos utilizado una clase interna anoacutenima que implementa la interfaz StreamingOutputen lugar de crear una clase puacuteblica separada La razoacuten de utilizar una clase interna es porque

Servicios Rest

75

en este caso al contener tan pocas liacuteneas de coacutedigo resulta beneficioso mantener dicha loacutegicadentro del meacutetodo del recurso JAX-RS de forma que el coacutedigo sea maacutes faacutecil de seguirNormalmente no tendremos necesidad de reutilizar la loacutegica implementada en otros meacutetodospor lo que no tiene demasiado sentido crear otra clase especiacutefica

iquestY por queacute no inyectamos un OutputStream directamente iquestPor queacute necesitamos un objetocallback La razoacuten es que asiacute dejamos que el runtime de JAX-RS maneje la salida de lamanera que quiera Por ejemplo por razones de rendimiento puede ser conveniente que JAX-RS utilice un thread para responder diferente del thread de peticioacuten

javaioInputStream javaioReader

Para leer el cuerpo de un mensaje de entrada podemos utilizar las clases InputStream oReader Por ejemplo

Path()public class MiServicio PUT Path(dato) public void modificaDato(InputStream is) byte[] bytes = readFromStream(is) String input = new String(bytes) Systemoutprintln(input)

private byte[] readFromStream(InputStream stream) throws IOException ByteArrayOutputStream baos = new ByteArrayOutputStream() byte[] buffer = new byte[1000] int wasRead = 0 do wasRead = streamread(buffer) if (wasRead gt 0) baoswrite(buffer 0 wasRead) while (wasRead gt -1) return baostoByteArray()

En este caso estamos leyendo bytes a partir de un javaioInputStream para convertirloen una cadena de caracteres que mostramos por pantalla

En el siguiente ejemplo creamos un javaioLineNumberReader a partir de un objetoReader e imprimimos cada liacutenea del cuerpo del mensaje de entrada

PUTPath(maslineas)public void putMasLineas(Reader reader) LineNumberReader lineReader = new LineNumberReader(reader) do String line = lineReaderreadLine() if (line = null) Systemoutprintln(line)

Servicios Rest

76

while (line = null)

No estamos limitados solamente a utilizar instancias de InputStream yo Reader paraleer el cuerpo de los mensajes de entrada Tambieacuten podemos devolver dichos objetos comorespuesta Por ejemplo

Path(fichero)public class FicheroServicio private static final String basePath =

GET Path(rutafichero ) Produces(textplain) public InputStream getFichero(PathParam(rutafichero) String path) FileInputStream is = new FileInputStream(basePath + path) return is

Aquiacute estamos inyectando un valor PathParam para crear una referencia a un fichero realde nuestro disco duro Creamos una instancia de javaioFileInputStream a partir delvalor de la ruta inyectada como paraacutemetro y la devolvemos como cuerpo de nuestro mensajede respuesta La implementacioacuten de JAX-RS leeraacute la respuesta de este stream de entrada y laalmacenaraacute en un buffer para posteriormente escribirla de forma incremental en el stream desalida de la respuesta En este caso debemos especificar la anotacioacuten Produces para quela implementacioacuten de JAX-RS conozca el valor que debe asignar a la cabecera Content-Type en la respuesta

javaioFile

Se pueden utilizar instancias de la clase javaioFile para entrada y salida decualquier MIME-TYPE (especificado en Content-Type yo Accept y en las anotacionesProduces yo Consumes ) El siguiente coacutedigo por ejemplo devuelve una referencia aun fichero en nuestro disco

Path(fichero)public class FicheroServicio private static final String baseRuta =

GET Path(rutafichero ) Produces(textplain) public File getFichero(PathParam(rutafichero) String ruta) return new File(baseRuta + ruta)

En este caso inyectamos el valor de la ruta del fichero con la anotacioacuten PathParam A partirde dicha ruta creamos un objeto javaioFile y lo devolvemos como cuerpo del mensaje

Servicios Rest

77

de respuesta La implementacioacuten JAX-RS leeraacute la informacioacuten abriendo un InputStreambasado en esta referencia al fichero y la escribiraacute en un buffer Posteriormente y de formaincremental volveraacute a escribir el contenido del buffer en el stream de salida de la respuesta Aligual que en el ejemplo anterior debemos especificar la anotacioacuten Produces para que JAX-RS sepa coacutemo rellenar la cabecera Content-Type de la respuesta

Tambieacuten podemos inyectar instancias de javaioFile a partir del cuerpo del mensaje dela peticioacuten Por ejemplo

POSTPath(masdatos)public void post(File fichero) Reader reader = new Reader(new FileInputStream(fichero)) LineNumberReader lineReader = new LineNumberReader(reader) do String line = lineReaderreadLine() if (line = null) Systemoutprintln(line) while (line = null)

En este caso la implementacioacuten de JAX-RS crea un fichero temporal en el disco para laentrada Lee la informacioacuten desde el buffer de la red y guarda los bytes leiacutedos en este ficherotemporal En el ejemplo los datos leiacutedos desde la red estaacuten representados por el el objetoFile inyectado por el runtime de JAX-RS (recuerda que soacutelo puede haber un paraacutemetro sinanotaciones en los meacutetodos del recurso y que eacuteste representa el cuerpo del mensaje de lapeticioacuten HTTP) A continuacioacuten el meacutetodo post() crea un javaioFileInputStreama partir del objeto File inyectado Finalmente utilizamos eacuteste stream de entrada para crearun objeto LineNumberReader y mostrar los datos por la consola

byte[]

Podemos utilizar un array de bytes como entrada y salida para cualquier tipo especificadocomo media-type A continuacioacuten mostramos un ejemplo

Path()public class MiServicio GET Produces(textplain) public byte[] get() return hello worldgetBytes()

POST Consumes(textplain) public void post(byte[] bytes) Systemoutprintln(new String(bytes))

Para cualquier meacutetodo de recurso JAX-RS que devuelva un array de bytes debemosespecificar la anotacioacuten Produces para que JAX-RS sepa queacute valor asignar a la cabeceraContent-Type

Servicios Rest

78

String char[]

La mayor parte de formatos en internet estaacuten basados en texto JAX-RS puede convertircualquier formato basado en texto a un String o a cualquier array de caracteres Porejemplo

Path()public class MiServicio GET Produces(applicationxml) public String get() return ltcustomergtltnamegtSergio Garcialtnamegtltcustomergt

POST Consumes(textplain) public void post(String str) Systemoutprintln(str)

Para cualquier meacutetodo de recurso JAX-RS que devuelva un Sring o un array de caracteresdebemos especificar la anotacioacuten Produces para que JAX-RS sepa que valor asignar a lacabecera Content-Type

MultivaluedMapltString Stringgt y formularios de entrada

Los formularios HTML son usados habitualmente para enviar datos a servidores web Losdatos del formulario estaacuten codificados con el media type applicationx-www-form-urlencoded Ya hemos visto como utilizar la anotacioacuten FormParam para inyectarparaacutemetros individuales de un formulario de las peticiones de entrada Tambieacuten podremosinyectar una instancia de MultivaluedMapltString Stringgt que representa todos losdatos del formulario enviado en la peticioacuten Por ejemplo

Path() public class MiServicio POST Consumes(applicationx-www-form-urlencoded) Produces(applicationx-www-form-urlencoded) public MultivaluedMapltStringStringgt post( MultivaluedMapltString Stringgt form) el formulario tiene los campos fieldName1 y fieldName2 Systemoutprintln(formgetFirst(fieldName1)) Systemoutprintln(formgetFirst(fieldName2)) return form

En este coacutedigo nuestro meacutetodo post() acepta peticiones POST y recibe unMultivaluedMapltStringStringgt que contiene todos los datos de nuestro formularioEn este caso tambieacuten devolvemos una instancia de un formulario como respuesta

Los datos del formulario pueden representarse en el cuerpo de la petcioacuten como paresnombre=valor separados por amp Por ejemplo

Servicios Rest

79

fieldName1=valor20con20espaciosampfielName2=otroValor

Los espacios en blanco se codifican como 20 No es necesario poner comillas

33 Muacuteltiples representaciones de recursos

Por defecto un recurso RESTful se produce o consume con el tipo MIME Un recursoRESTful puede restringir los media types que soporta tanto en la peticioacuten como en larespuesta utilizando las anotaciones Consumes y Produces respectivamente Estasanotaciones pueden especificarse como ya hemos visto a nivel de clase o de meacutetodo derecurso Las anotaciones especificadas sobre el meacutetodo prevalecen sobre las de la clase Laausencia de estas anotaciones es equivalente a su inclusioacuten con el tipo MIME () es decirsu ausencia implica que se soporta cualquier tipo

A continuacioacuten mostramos un ejemplo en el que un Pedido puede producirse tanto en formatoxml como en formato json

GETPath(id)Produces(applicationxml applicationjson)public Pedido getPedido(PathParam(id)int id)

El meacutetodo getPedido() puede generar ambas representaciones para el pedido El tipo exactode la respuesta viene determinado por la cabecera HTTP Accept de la peticioacuten

Otro ejemplo en el que pueden consumirse varios tipos MIME puede ser el siguiente

POSTPath(id)Consumes(applicationxml applicationjson)public Pedido addPedido(PathParam(id)int id)

En este caso el formato consumido vendraacute dado por el valor de la cabecera HTTP Content-Type de la peticioacuten

JAX-RS 20 nos permite indicar la preferencia por un media type en el lado del servidorutilizando el paraacutemetro qs (quality on service) qs toma valores entre 0000 y 1000 eindica la calidad relativa de una representacioacuten comparado con el resto de representacionesdisponibles Una representacioacuten con un valor de qs de 0000 nunca seraacute elegido Unarepresentacioacuten sin valor para el paraacutemetro qs se asume que dicho valor es 1000

Ejemplo

POSTPath(id)Consumes(applicationxml qs=075 applicationjson qs=1)public Pedido addPedido(PathParam(id)int id)

Si un cliente realiza una peticioacuten y no manifiesta ninguna preferencia por ningunarepresentacioacuten en particular o con una cabecera Accept con valor application entonces el

Servicios Rest

80

servidor seleccionaraacute la representacioacuten con el valor de qs maacutes alto (en este caso applicationjson) Los valores de qs son relativos y como tales solamente son comparables con otrosvalores qs dentro de la misma instancia de la anotacioacuten Consumes (o Produces)

Los clientes pueden indicar tambieacuten sus preferencias utilizando otro factor relativo de calidaden forma de paraacutemetro denominado q El valor del paraacutemetro q se utiliza para ordenar elconjunto de tipos aceptados q toma valores entre 0000 y 1000 (maacutexima preferencia) Al igualque antes Los valores de q son relativos y como tales solamente son comparables con otrosvalores q dentro de la misma cabecera Accept o Content-type

Las preferencias del servidor (valores de los paraacutemetros qs) soacutelo se tienenen cuenta si el cliente acepta muacuteltiples media types con el mismo valorde q

Veamos un ejemplo

GETPath(id)Produces(applicationxml qs=1 applicationjson qs=075)public Pedido getPedido(PathParam(id)int id)

Supongamos que un cliente lanza una petcioacuten GET con una valor para la cabecera Acceptde application q=05 texthtml En este caso el servidor determina que los tipos MIMEapplicationxml y applicationjson tienen la misma preferencia por parte del cliente (con valorde 05) por lo tanto el servidor elegiraacute la representacioacuten applicationjson ya que tiene un valorde qs mayor

34 Introduccioacuten a JAXB

JAXB (Java Architecture for XML Binding) es una especificacioacuten Java antigua (JSR 2223) yno estaacute definida por JAX-RS JAXB es un framework de anotaciones que mapea clases Java aXML y esquemas XML Es extremadamente uacutetil debido a que en lugar de interactuar con unarepresentacioacuten abstracta de un documento XML podemos trabajar con objetos Java realesque estaacuten maacutes cercanos al dominio que estamos modelando JAX-RS proporciona soportepara JAXB pero antes de revisar los manejadores de contenidos JAXB incuidos con JAX-RSveamos una pequentildea introduccioacuten al framework JAXB

Como ya hemos dicho si queremos mapear una clase Java existente a XML podemos utilizarJAXB a traveacutes de un conjunto de anotaciones Veaacutemoslo mejor con un ejemplo

XmlRootElement(name=cliente)XmlAccessorType(XmlAccessTypeFIELD)public class Cliente XmlAttribute protected int id

XmlElement protected String nombre

public Customer()

3 httpsjcporgaboutJavacommunityprocessmreljsr222index2html

Servicios Rest

81

public int getId() return thisid public void setId(int id) thisid = id

public String getNombre() return thisnombre public void setNombre(String nombre thisnombre = nombre

La anotacioacuten javaxxmlbindannotationXmlRootElement se utiliza en clasesjava para denotar que representan elementos XML (etiqueta XML raiacutez) En este caso estamosdiciendo que la clase Java representa un documento XML que tiene como etiqueta raiacutezltclientegt Las clases java anotadas con XmlRootElement se denomina beansJAXB

La anotacioacuten javaxxmlbindannotationXmlAttribute la hemos asociado alcampo id de nuestra clase Cliente Esta anotacioacuten indica que el campo id de la clasedebe mapearse como el atributo id del elemento raiacutez ltclientegt del documento XML Laanotacioacuten XmlAttribute tiene un atributo name de forma que podemos especificar elnombre exacto del atributo XML dentro del documento Por defecto tiene el mismo nombreque el campo anotado

Hemos utilizado la anotacioacuten javaxxmlbindannotationXmlElement en el camponombre de la clase Cliente Esta anotacioacuten indica a JAXB que debe mapearse el camponombre como el elemento ltnombregt anidado en la etiqueta raiacutez ltclientegt Igual queantes podemos especificar el nombre concreto del elememto XML Por defecto toma el mismonombre que el correspondiente campo anotado

La anotacioacuten javaxxmlbindannotationXmlAccessorType permite controlar laserializacioacuten por defecto de los atributos de la clase Esta anotacioacuten soacutelo puede ser usadaconjuntamente con XmlRootElement (y alguna otra anotacioacuten que no mostramos aquiacute)Hemos usado como valor XmlAccessTypeFIELD lo que significa que por defecto se debenserializar todos los campos (fields) de la clase (esteacuten anotados o no) y las propiedades(properties) de la clase que tengan anotaciones JAXB (a menos que la anotacioacuten seaXMLTransient)

Si alguno de los campos de la clase no tiene anotaciones JAXB asociadas por defecto seserializaraacuten como elementos (etiquetas) en el documento XML correspondiente Seguacuten ladocumentacioacuten4 de JAXB un campo es una variable de instancia no estaacutetica (normalmenteprivada)

Las propiedades de la clase vienen dadas por las combinaciones gettersetter de los atributosde la clase El coacutedigo anterior tiene dos propiedades nombre (dado por el par getNombresetNombre_) e id (par getIdsetId) Normalmente se anotan los meacutetodos getter Dichaspropiedades no estaacuten anotadas por lo que JAXB no las serializaraacute

Al proceso de serializar (convertir) un objeto Java en un documento XMLse le denomina marshalling El proceso inverso la conversioacuten de XML aobjetos Java se denomina unmarshalling

Con las anotaciones anteriores un ejemplo de una instancia de nuestra clase Cliente conun id de 42 y el valor de nombre Pablo Martinez tendriacutea el siguiente aspecto

ltcliente id=42gt

4 httpsjaxbjavanetnonav226docsapi

Servicios Rest

82

ltnombreCompletogtPablo MartinezltnombreCompletogtltclientegt

Observamos que se han serializado los campos (variables de instancia de la clase)

Si no especificamos la anotacioacuten XmlAccessorType por defecto se utilizaraacute

XmlAccessorType(XmlAccessTypePUBLIC_MEMBER)

El valor XmlAccessTypePUBLIC_MEMBER indica a JAXB que se deben serializar todoslos campos puacuteblicos de la clase todos los campos anotados y todas las propiedades (paresgettersetter) a menos que esteacuten anotadas con XMLTransient

Tambieacuten podriacuteamos utilizar

XmlAccessorType(XmlAccessTypeNONE)

En este caso la anotacioacuten XmlAccessTypeNONE indica soacutelo se deben serializar aquellaspropiedades yo campos de la clase que esteacuten anotados

A continuacioacuten indicamos en forma de tabla el uso de los diferentes XmlAccessType

Table 4 Valores utilizados para XmlAccessorType() conjuntamentecon XmlRootElement()

Valor Significado

XmlAccessTypePUBLIC_MEMBER Serializacioacuten por defecto si no se especificaXmlAccessorType() Serializa laspropiedades (pares gettersetter) y campospuacuteblicos a menos que esteacuten anotados conXMLTransient Si alguacuten campo nopuacuteblico estaacute anotado tambieacuten se serializa

XmlAccessTypeFIELD Serializa todos los campos (puacuteblicos oprivados) a menos que esteacuten anotados conXMLTransient

XmlAccessTypeNONE Solamente serializa aquellos camposy propiedades que esteacuten anotadas conanotaciones JAXB

XmlAccessTypePROPERTY Serializa cada par gettersetter a menosque esteacuten anotados con XMLTransient Si alguacuten campo (no puacuteblico) estaacute anotadotambieacuten se serializa

Podemos utilizar la anotacioacuten XmlElement para anidar otras clases anotadas con JAXBPor ejemplo supongamos que queremos antildeadir una clase Direccion a nuestra claseCliente

XmlRootElement(name=direccion)

Servicios Rest

83

XmlAccessorType(XmlAccessTypeFIELD)public class Direccion XmlElement protected String calle

XmlElement protected String cludad

XmlElement protected String codPostal

getters y setters

Simplemente tendriacuteamos que antildeadir el campo de tipo Direccion a nuestra clase Clientede la siguiente forma

XmlRootElement(name=cliente)XmlAccessorType(XmlAccessTypeFIELD)public class Cliente XmlAttribute protected int id

XmlElement protected String nombreCompleto

XmlElement protected Direccion direccion

public Customer()

getters y setters

En este caso una instancia de un Cliente con valores id =56 nombre =RicardoLopez calle =calle del oso 35 ciudad =Alicante y coacutedigo_postal =01010 seriacuteaserializado como

ltcliente id=56gt ltnombregtRicardo Lopezltnombregt ltdirecciongt ltcallegtcalle del oso 35ltcallegt ltciudadgtAlicanteltciudadgt ltcodPostalgt01010ltcodPostalgt ltdirecciongt ltclientegt

Veamos otro ejemplo Supongamos que tenemos el recurso EstadoResource con meacutetodosque responden a peticiones http GET y PUT

Path(estado)

Servicios Rest

84

public class EstadoResource

private static EstadoBean estadoBean = new EstadoBean()

GET Produces(applicationxml) public EstadoBean getEstado() return estadoBean

PUT Consumes(applicationxml) public void setEstado(EstadoBean estado) thisestadoBean = estado

En este caso la clase EstadoBean debe utilizar anotaciones JAXB para poder ser convertidaautomaacuteticamente por el runtime de JAX-RS en un documento XML y viceversa

XmlRootElement(name = estadoImpresora)public class EstadoBean

public String estado = Idle public int tonerRestante = 25 public ListltTareaBeangt tareas = new ArrayListltTareaBeangt()

Por defecto la anotacioacuten XmlRootElement realiza la serializacioacuten de la claseEstadoBean en formato xml utilizando los campos puacuteblicos y propiedades definidas en laclase ( estado tonerRestante y tareas ) Vemos que el campo tareas a su vez esuna coleccioacuten de elementos de tipo TareaBean que tambieacuten necesitan ser serializados Acontinuacioacuten mostramos la implementacioacuten de la clase TareaBeanjava

XmlRootElement(name = tarea)public class TareaBean public String nombre public String estado public int paginas

public TareaBean()

public TareaBean(String nombre String estado int paginas) thisnombre = nombre thisestado = estado thispaginas = paginas

Para serializar la clase es necesario que la clase tenga un constructor sin paraacutemetros

Servicios Rest

85

Si accedemos al servicio anterior nos devolveraacute la informacioacuten sobre el estado de la siguienteforma (resultado devuelto por el meacutetodo getEstado() anotado con GET)

ltxml version=10 encoding=UTF-8 standalone=yesgtltestadoImpresoragt ltestadogtIdleltestadogt lttonerRestantegt25lttonerRestantegt lttareasgt ltnombregttextodocltnombregt ltestadogtimprimiendoltestadogt ltpaginasgt13ltpaginasgt lttareasgt lttareasgt ltnombregttexto2docltnombregt ltestadogten esperaltestadogt ltpaginasgt5ltpaginasgt lttareasgtltestadoImpresoragt

Podemos observar que se utiliza el elemento xml lttareasgt para representar cada una de lasinstancias de TareaBean las cuales forman parte de de la lista del campo tareas de nuestraclase EstadoBean

Vamos a usar las anotaciones XmlAttribute y XmlElement de la siguiente forma

XmlRootElement(name=estadoImpresora)public class EstadoBean XmlAttribute(name=valor) public String estado = Idle XmlAttribute(name=toner) public int tonerRestante = 25 XmlElement(name=tarea) public ListltTareaBeangt tareas = new ArrayListltTareaBeangt()

En este caso el XML resultante quedariacutea de la siguiente forma

ltestadoImpresora valor=Idle toner=25gt lttareagt ltnombregttextodocltnombregt ltestadogtimprimiendoltestadogt ltpaginasgt13ltpaginasgt lttareagt lttareagt ltnombregttexto2docltnombregt ltestadogten esperaltestadogt ltpaginasgt5ltpaginasgt lttareagtltestadoImpresoragt

Servicios Rest

86

Si no se indica lo contrario por defecto se convierten los campos a elementos del XML

En caso de que los campos no sean puacuteblicos etiquetaremos los getterscorrespondiente (propiedades de la clase)

Hemos visto que para las listas el nombre que especificamos en XmlElement se utilizapara nombrar cada elemento de la lista Si queremos que ademaacutes se incluya un elemento queenvuelva a toda la lista podemos utilizar la etiqueta XmlElementWrapper

XmlRootElement(name=estadoImpresora)public class EstadoBean

XmlAttribute(name=valor) public String estado = Idle

XmlAttribute(name=toner) public int tonerRestante = 25

XmlElementWrapper(name=tareas) XmlElement(name=tarea) public ListltTareaBeangt tareas = new ArrayListltTareaBeangt()

En este caso tendremos un XML como el que se muestra a continuacioacuten

ltestadoImpresora valor=Idle toner=25gt lttareasgt lttareagt ltnombregttextodocltnombregt ltestadogtimprimiendoltestadogt ltpaginasgt13ltpaginasgt lttareagt lttareagt ltnombregttexto2docltnombregt ltestadogten esperaltestadogt ltpaginasgt5ltpaginasgt lttareagt lttareasgtltestadoImpresoragt

Para etiquetar una lista tambieacuten podemos especificar distintos tipos de elemento seguacutenel tipo de objeto contenido en la lista Por ejemplo supongamos que en el ejemploanterior la clase TareaBean fuese una clase abstracta que tiene dos posible subclasesTareaSistemaBean y TareaUsuarioBean Podriacuteamos especificar una etiqueta distintapara cada elemento de la lista seguacuten el tipo de objeto del que se trate con la etiquetaXmlElements de la siguiente forma

XmlElementWrapper(name=tareas) XmlElements( XmlElement(name=usuariotype=TareaUsuarioBeanclass

Servicios Rest

87

XmlElement(name=sistematype=TareaSistemaBeanclass) public ListltTareaBeangt tareas = new ArrayListltTareaBeangt()

De esta forma podriacuteamos tener un XML como el siguiente

ltestadoImpresora valor=Idle toner=25gt lttareasgt ltusuariogt ltnombregttextodocltnombregt ltestadogtimprimiendoltestadogt ltpaginasgt13ltpaginasgt ltusuariogt ltsistemagt ltnombregttexto2docltnombregt ltestadogten esperaltestadogt ltpaginasgt5ltpaginasgt ltsistemagt lttareasgtltestadoImpresoragt

Hemos visto que por defecto se serializan todos los campos Si queremos excluir algunode ellos de la serializacioacuten podemos hacerlo anotaacutendolo con XmlTransient Comoalternativa podemos cambiar el comportamiento por defecto de la serializacioacuten de la claseetiquetaacutendola con XmlAccessorType Por ejemplo

XmlAccessorType(NONE)XmlRootElement(name=estadoImpresora)public class EstadoBean

En este uacuteltimo caso especificando como tipo NONE no se serializaraacute por defectoninguacuten campo soacutelo aquellos que hayamos anotado expliacutecitamente con XmlElement oXmlAttribute Los campos y propiedades (getters) anotados con estas etiquetas seserializaraacuten siempre Ademaacutes de ellos tambieacuten podriacuteamos especificar que se serialicenpor defecto todos los campos puacuteblicos y los getters ( PUBLIC_MEMBER ) todos los getters( PROPERTY ) o todos los campos ya sean puacuteblicos o privados ( FIELD ) Todos los que seserialicen por defecto sin especificar ninguna etiqueta lo haraacuten como elemento

Por uacuteltimo si nos interesa que toda la representacioacuten del objeto venga dada uacutenicamente porel valor de uno de sus campos podemos etiquetar dicho campo con XmlValue

Clase JAXBContext

Para serializar clases Java a y desde XML es necesario interactuar con la clasejavaxxmlbindJAXBContext Esta clase nos permite inspeccionar las clasesJava para comprender la estructura de nuestras clases anotadas Dichas clasesse utilizan como factoriacuteas para las interfaces javaxxmlbindMarshaller yjavaxxmlbindUnmarshaller Las instancias de Marshaller se utilizan para creardocumentos XML a partir de objetos Java Las instancias de Unmarshaller se utilizan para crearobjetos Java a partir de documentos XML A continuacioacuten mostramos un ejemplo de uso de

Servicios Rest

88

JAXB para convertir una instancia de la clase Cliente que hemos definido anteriormentea formato XML para posteriormente volver a crear el objeto de tipo Cliente

Cliente cliente = new Cliente()clientesetId(42)

clientesetNombre(Lucia Arg)

JAXBContext ctx = JAXBContextnewInstance(Clienteclass) StringWriter writer = new StringWriter()

ctxcreateMarshaller()marshal(cliente writer)

String modString = writertoString()

cliente = (Cliente)ctxcreateUnmarshaller()

unmarshal(new StringReader(modString))

Creamos e inicializamos una instancia de tipo ClienteInicializamos JAXBContext para que pueda analizar la clase `ClienteUtilizamos una instancia de Marshaller para escribir el objeto Cliente como unString de Java ( StringWriter es un stream de caracteres que utilizaremos paraconstruir un String )Utilizamos una instancia de Unmarshaller para recrear el objeto Cliente a partir delString que hemos obtenido en el paso anterior

La clase JAXBContext constituye un punto en entrada al API JAXB paralos clientes de nuestros servicios RESTful Proporciona una abstraccioacutenpara gestionar el enlazado (binding) de informacioacuten XMLJava necesariapara implementar las operaciones de marshalling y unmarshalling

bull Unmarshalling La clase Unmarshaller proporciona a la aplicacioacutencliente la capacidad para convertir datos XML en un aacuterbol de objetosJava

bull Marshalling La clase Marshaller proporciona a la aplicacioacutencliente la capacidad para convertir un aacuterbol con contenidos Java denuevo en datos XML

Una vez que hemos proporcionado una visioacuten general sobre coacutemo funciona JAXB vamos aver coacutemo se integra con JAX-RS

Manejadores JAX-RS para JAXB

La especificacioacuten de JAX-RS indica que cualquier implementacioacuten debe soportar deforma automaacutetica el proceso de marshalling y unmarshalling de clases anotadas conXmlRootElement A continuacioacuten mostramos un ejemplo de la implementacioacuten de unservicio que hace uso de dichos manejadores Para ello utilizamos la clase Cliente que hemosanotado previamente con XmlRootElement y que hemos mostrado en apartados anteriores

Path(clientes)public class ClienteResource GET Path(id)

Servicios Rest

89

Produces(applicationxml) public Cliente getCliente(PathParam(id) int id) Cliente cli = findCliente(id) return cust

POST Consumes(applicationxml) public void crearCliente(Cliente cli)

Como podemos ver una vez que aplicamos las anotaciones JAXB a nuestras clases Java ( eneste caso a la clase Cliente ) es muy sencillo intercambiar documentos XML entre el clientey nuestro servicio web Los manejadores JAXB incluidos en la implementacioacuten de JAX-RSgestionaraacuten el marshallingunmarshalling de cualquier clase con anotaciones JAXB para losvalores de Content-Type applicationxml textxml o application+xml Por defecto tambieacuten se encargan de la creacioacuten e inicializacioacuten de instancias JAXBContext Debido a que la creacioacuten de las instancias JAXBContext puede ser cara la implementacioacutende JAX-RS normalmente las guarda despueacutes de la primera inicializacioacuten

JAXB y JSON

JAXB es lo suficientemente flexible como para soportar otros formatos ademaacutes de XMLAunque la especificacioacuten de JAX-RS no lo requiere muchas implementaciones de JAX-RSincluyen adaptadores de JAXB para soportar el formato JSON ademaacutes de XML JSON es unformato basado en texto que puede ser interpretado directamente por Javascript De hechoes el formato de intercambio preferido por las aplicaciones Ajax

JSON es un formato mucho maacutes simple que XML Los objetos estaacuten entre llaves ycontienen pares de clavevalor separados por comas Los valores pueden ser cadenas decaracteres booleanos ( true o false ) valores numeacutericos o arrays de los tipos anteriores

Supongamos que tenemos la siguiente descripcioacuten de un producto en formato XML

ltxml version=10 encoding=UTF-8gtltproductogt ltidgt1ltidgt ltnombregtiPadltnombregt ltdescripciongtDispositivo moacutevilltdescripciongt ltpreciogt500ltpreciogtltproductogt

La representacioacuten JSON equivalente seriacutea

id1 nombreiPad descripcionDispositivo moacutevil precio500

Servicios Rest

90

El formato JSON asociado por ejemplo al siguiente objeto Cliente

ltcliente id=56gt ltnombregtRicardo Lopezltnombregt ltdirecciongt ltcallegtcalle del oso 35ltcallegt ltciudadgtAlicanteltciudadgt ltcodPostalgt01010ltcodPostalgt ltdirecciongt ltclientegt

quedariacutea como sigue

nombre Ricardo Lopez direccion calle calle del oso 35 ciudad Alicante codPostal 01010

Como vemos en el ejemplo el formato JSON consiste baacutesicamente en objetos situados entrellaves los cuales estaacuten formados por pares clavevalor separadas por comas Cada clavey valor estaacute separado por Los valores pueden cadenas de caracteres booleanos (true ofalse) valores numeacutericos o vectores de los tipos anteriores (los vectores estaacuten delimitadospor corchetes)

Podemos antildeadir el formato applicationjson o bienMediaTypeAPPLICATION_JSON a la anotacioacuten Produces en nuestros meacutetodos derecurso para generar respuestas en formato JSON

GETPath(get)Produces(applicationxmlapplicationjson)public Producto getProducto()

En este ejemplo se elegiraacute el formato JSON en la respuesta si el cliente realiza una peticioacutenGET que incluye en la cabecera

Accept applicationjson

El tipo de respuesta es de tipo Producto En este caso Producto debe ser un bean JAXBes decir una clase anotada con XmlRootElement

Los meacutetodos de recurso pueden aceptar tambieacuten datos JSON para clases con anotacionesJAXB

POSTPath(producto)Consumes(applicationxmlapplicationjson)

Servicios Rest

91

public Response crearProducto(Producto prod)

En este caso el cliente deberiacutea incluir la siguiente cabecera cuando realice la peticioacuten POSTque incluya los datos JSON anteriores en el cuerpo del mensaje

Content-Type applicationjson

Hablaremos con maacutes detalle del formato JSON en una sesioacuten posterior

Finalmente y como resumen de lo anterior

JAXB

Para dar soporte al serializado y deserializado XML se utilizan beans JAXBPorejemplo las clases anteriores EstadoBean TareaBean Cliente y Productoson ejemplos de beans JAXB La clase que se serializaraacute como un recurso XMLo JSON se tiene que anotar con XmlRootElement Si en alguacuten elemento deun recurso se retorna un elemento de esa clase y se etiqueta con los tiposProduces(MediaTypeAPPLICATION_XMLMediatypeAPPLICATION_JSON )eacuteste se serializa automaacuteticamente utilizando el tipo de representacioacuten aceptada por elcliente Se puede consultar el artiacuteculo de Lars Vogel5 para maacutes informacioacuten

35 Respuestas del servidor

Vamos a explicar cuaacutel es el comportamiento por defecto de los meacutetodos de recursos JAX-RS en particular veremos cuaacuteles son los coacutedigos de respuesta HTTP por defecto teniendo encuenta situaciones de eacutexito asiacute como de fallo

Dado que en ocasiones vamos a tener que enviar cabeceras de respuesta especiacuteficas antecondiciones de error complejas tambieacuten vamos a explicar coacutemo podemos elaborar respuestascomplejas utilizando el API JAX-RS

Coacutedigos de respuesta por defecto

Los coacutedigos de respuesta por defecto se corresponden con el comportamiento indicado en laespecificacioacuten6 de la definicioacuten de los meacutetodos HTTP 11 Vamos a examinar dichos coacutedigosde respuesta con el siguiente ejemplo de recurso JAX-RS

Path(clientes)public class ClienteResource

Path(id) GET Produces(applicationxml) public Cliente getCliente(PathParam(id) int id)

5 httpwwwvogelladearticlesJAXBarticlehtml6 httpwwww3orgProtocolsrfc2616rfc2616-sec9html

Servicios Rest

92

POST Produces(applicationxml) Consumes(applicationxml) public Cliente crearCliente(Cliente nuevoCli)

PUT Path(id) Consumes(applicationxml) public void updateCliente(PathParam(id) int id Cliente cli)

Path(id) DELETE public void borrarCliente(PathParam(id) int id)

Respuestas que indican eacutexito

Los nuacutemeros de coacutedigo de respuestas HTTP con eacutexito se situacutean en el rango de 200 a 399

bull Para los meacutetodos crearCliente() y getCliente() se devolveraacute una respuestaHTTP con el coacutedigo 200 OK si el objeto Cliente que devuelven dichos meacutetodos noes null

bull Para los meacutetodos crearCliente() y getCliente() se devolveraacute una respuestaHTTP con el coacutedigo 204 No Content si el objeto Cliente que devuelven dichosmeacutetodos es null El coacutedigo de respuesta 204 no indica una condicioacuten de error Solamenteavisa al cliente de que todo ha ido bien pero que el mensaje de respuesta no contienenada en el cuerpo de la misma Seguacuten eacutesto si un meacutetodo de un recurso devuelve void por defecto se devuelve el coacutedigo de respuesta 204 No content Este es el caso para losmeacutetodos updateCliente() y borrarCliente() de nuestro ejemplo

La especificacioacuten HTTP es bastante consistente para los meacutetodos PUT POST GET yDELETE Si una respuesta exitosa HTTP contiene informacioacuten en el cuerpo del mensaje derespuesta entonces el coacutedigo de respuesta es 200 OK Si el cuerpo del mensaje estaacute vaciacuteoentonces se debe devolver 204 No Content

Respuestas que indican una situacioacuten de fallo

Es habitual que las respuestas fallidas se programen de forma que se lance una excepcioacutenLo veremos en un apartado posterior Aquiacute comentaremos algunas condiciones de error pordefecto

Los nuacutemeros de coacutedigo de error de respuesta estaacutendar en HTTP se situacutean en el rango entre400 y 599 En nuestro ejemplo si un cliente se equivoca tecleando la URI y eacutesta queda porejemplo como httphellipcliente entonces el servidor no encontraraacute ninguacuten meacutetodo delrecurso que pueda servir dicha peticioacuten (la URI correcta seriacutea httphellipclientes ) Eneste caso se enviaraacute como respuesta el coacutedigo 404 Not Found

Para los meacutetodos getCliente() y crearCliente() de nuestro ejemplo si el clientesolicita una respuesta con el tipo MIME texthtml entonces la implementacioacuten de JAX-RS devolveraacute automaacuteticamente 406 Not Acceptable con un mensaje de respuesta con elcuerpo de dicho mensaje vaciacuteo Esta respuesta indica que JAX-RS puede encontrar una rutade URI relativa que coincide con la peticioacuten pero no encuentra ninguacuten meacutetodo del recursoque devuelva la respuesta con ese tipo MIME

Servicios Rest

93

Si el cliente invoca una peticioacuten HTTP sobre una URI vaacutelida para la que no se puedeencontrar un meacutetodo de recurso asociado entonces el runtime de JAX-RS devolveraacute el coacutedigo405 Method Not Allowed Asiacute en nuestro ejemplo si el cliente solicita una operacioacuten PUTGET o DELETE sobre la URI clientes obtendraacute como respuesta 405 Method NotAllowed puesto que POST es el uacutenico meacutetodo HTTP que puede dar soporte a dicha URILa implementacioacuten de JAX-RS tambieacuten devolveraacute una cabecera de respuesta Allow con lalista de meacutetodos HTTP que pueden dar soporte a dicha URI Por lo tanto si nuestra aplicacioacutencliente realiza la siguiente peticioacuten de entrada

GET clientes

el servidor devolveraacute la siguiente respuesta

HTTP11 405 Method Not AllowedAllow POST

Elaboracioacuten de respuestas con la clase Response

Como ya hemos visto por ejemplo para peticiones GET si todo va bien se estaraacute devolviendoun coacutedigo de respuesta 200 (Ok) junto con el contenido especificado en el tipo de datosutilizado en cada caso Si devolvemos void el coacutedigo de respuesta seraacute 204 (No Content)

Sin embargo en ocasiones el servicio web que estamos disentildeando no puede implementarseutilizando el comportamiento por defecto de peticioacutenrespuesta inherente a JAX-RS En estoscasos necesitaremos controlar de forma expliacutecita la respuesta que se le enviacutea al cliente (cuerpodel mensaje de la respuesta HTTP) Por ejemplo cuando creamos un nuevo recurso con POSTdeberiacuteamos devolver 201 (Created) Para tener control sobre este coacutedigo nuestros recursosJAX-RS podraacuten devolver instancias de javaxwsrscoreResponse

GETProduces(MediaTypeAPPLICATION_XML)public Response getClientes() ClientesBean clientes = obtenerClientes()

return Responseok(clientes)build()

POSTConsumes(MediaTypeAPPLICATION_XML)public Response addCliente(ClienteBean cliente Context UriInfo uriInfo)

String id = insertarCliente(cliente) URI uri = uriInfo getAbsolutePathBuilder() path(id)

build(id)

return Responsecreated(uri)build()

Al crear una respuesta con Response podemos especificar una entidad que podraacute serun objeto de cualquiera de los tipos vistos anteriormente y que representa los datos a

Servicios Rest

94

devolver como contenido Por ejemplo cuando indicamos ok(clientes) estamos creandouna respuesta con coacutedigo 200 (Ok) y con el contenido generado por nuestro beanJAXB clientes Esto seraacute equivalente a haber devuelto directamente ClientesBean comorespuesta pero con la ventaja de que en este caso podemos controlar el coacutedigo de estadode la respuestaInsertamos una instancia de ClienteBean en la base de datos y obtenemos la claveasociada a dicho clienteEn la sesioacuten anterior hemos hablado de la interfaz uriInfo Es una interfaz inyectableque proporciona acceso a informacioacuten sobre la URI de la aplicacioacuten o la URI de laspeticiones recibidas En este caso estamos construyendo una nueva URI formada porla ruta absoluta de la peticioacuten de entrada http POST antildeadieacutendole la plantilla id yfinalmente sustituyendo el paraacutemetro de la plantilla por el valor id Supongamos quela peticioacuten POST contiene la uri httplocalhost8080recursosclientes Y que el valor de id es 8 El valor de la variable uri seraacute por tanto httplocalhost8080recursosclientes8Devolvemos la respuesta incluyendo la URI anterior en la cabecera HTTP `Location

Veamos con maacutes detalle la clase Response

public abstract class Response

public abstract Object getEntity()

public abstract int getStatus()

public abstract MultivaluedMapltString Objectgt getMetadata()

public abstract URI getLocation()

public abstract MediaType getMediaType()

public abstract void close()

El meacutetodo getEntity() devuelve el objeto Java correspondiente al cuerpo delmensaje HTTPEl meacutetodo getStatus() devuelve el coacutedigo de respuesta HTTPEl meacutetodo getMetadata() devuelve una instancia de tipo MultivaluedMap con lascabeceras de la respuestaEl meacutetodo getLocation() devuelve la URI de la cabecera Location de la respuestaEl meacutetodo getMediaType() devuelve el mediaType del cuerpo de la respuestaEl meacutetodo close() cierra el input stream correspondiente a la entidad asociada delcuerpo del mensaje (en el caso de que esteacute disponible y abierto) Tambieacuten liberacualquier otro recurso asociado con la respuesta (como por ejemplo datos posiblementealmacenados en un buffer)

Estos meacutetodos tiacutepicamente seraacuten invocados dese el cliente tal y como veremos maacutes adelantecuando expliquemos el API cliente

Los objetos Response no pueden crearse directamente Tienen que crearse a partir deinstancias de javaxwsrscoreResponseResponseBuilder devueltas por uno delos siguientes meacutetodos estaacuteticos de Response

public abstract class Response public abstract void close() public static ResponseBuilder status(Status status) public static ResponseBuilder status(int status) public static ResponseBuilder ok()

Servicios Rest

95

public static ResponseBuilder ok(Object entity) public static ResponseBuilder ok(Object entity MediaType type) public static ResponseBuilder ok(Object entity String type) public static ResponseBuilder ok(Object entity Variant var) public static ResponseBuilder serverError() public static ResponseBuilder created(URI location) public static ResponseBuilder noContent() public static ResponseBuilder notModified() public static ResponseBuilder notModified(EntityTag tag) public static ResponseBuilder notModified(String tag) public static ResponseBuilder seeOther(URI location) public static ResponseBuilder temporaryRedirect(URI location) public static ResponseBuilder notAcceptable(ListltVariantgt variants) public static ResponseBuilder fromResponse(Response response)

Veamos por ejemplo el meacutetodo ok()

ResponseBuilder ok(Object entity MediaType type)

Este meacutetodo recibe como paraacutemetros un objeto Java que queremos convertir en una respuestaHTTP y el Content-Type de dicha respuesta Como valor de retorno se obtiene unainstancia de tipo ResponseBuilder pre-inicializada con un coacutedigo de estado de 200 OK

El meacutetodo created() devuelve un ResponseBuilder para un recurso creado y asigna a lacabecera Location de la respuesta el valor de la URI proporionado como paraacutemetro

La clase ResponseBuilder es una factoriacutea utilizada para crear instancias individuales detipo Response

public static abstract class ResponseBuilder public abstract Response build() public abstract ResponseBuilder clone()

public abstract ResponseBuilder status(int status) public ResponseBuilder status(Status status)

public abstract ResponseBuilder entity(Object entity) public abstract ResponseBuilder type(MediaType type) public abstract ResponseBuilder type(String type)

public abstract ResponseBuilder variant(Variant variant) public abstract ResponseBuilder variants(ListltVariantgt variants)

public abstract ResponseBuilder language(String language) public abstract ResponseBuilder language(Locale language)

public abstract ResponseBuilder location(URI location) public abstract ResponseResponseBuilder header(String name Object value) public abstract ResponseResponseBuilder link(URI uri String rel)

Servicios Rest

96

public abstract ResponseResponseBuilder link(String uri String rel)

Vamos a mostrar un ejemplo sobre coacutemo crear respuestas utilizando un objeto Response En este caso el meacutetodo getLibro() devuelve un String que representa el libro en el queestaacute interesado nuestro cliente

Path(libro)public class LibroServicio GET Path(restfuljava) Produces(textplain) public Response getLibro()

String libro =

ResponseBuilder builder = Responseok(libro)

builderlanguage(fr)header(Some-Header some value)

return builderbuild()

Recuperamos los datos del libro solicitado En este caso vamos a devolver una cadenade caracteres que representa el libro en el que estamos interesadosInicializamos el cuerpo de la respuesta utilizando el meacutetodo Responseok() El coacutedigode estado de ResponseBuilder se inicializa de forma automaacutetica con 200Usamos el meacutetodo ResponseBuilderlanguage() para asignar el valorde la cabecera Content-Languaje a franceacutes Tambieacuten usamos el meacutetodoResponseBuilderheader() para asignar un valor concreto a otra cabeceraFinalmente creamos y devolvemos el objeto Response usando el meacutetodoResponseBuilderbuild()

Un detalle que es interesante destacar en este coacutedigo es que no indicamos ninguacuten valor parael Content-Type de la respuesta Debido a que ya hemos especificado esta informacioacutenen la anotacioacuten Produces del meacutetodo el runtime de JAX-RS devolveraacute el valor adecuadodel media type de la respuesta por nosotros

Inclusioacuten de cookies en la respuesta

JAX-RS proporciona la clase javaxwsrscoreNewCookie que utilizaremos para crearnuevos valores de cookies y enviarlos en las respuestas

Para poder incluir las cookies en nuestro objeto Response primero crearemoslas instancias correspondientes de tipo NewCookie las pasaremos al meacutetodoResponseBuildercookie() Por ejemplo

Path(myservice)public class MyService GET public Response get()

NewCookie cookie = new NewCookie(nombre pepe)

ResponseBuilder builder = Responseok(hola textplain) return buildercookie(cookie)build()

Servicios Rest

97

Creamos una nueva cookie con el nombre de clave nombre y le asignamos el valorpepeEn este caso al no indicar el tipo mime de la respuesta con la anotacioacuten Producesnecesitamos indicarlo (en este caso como un paraacutemetro del meacutetodo ok() )

El tipo enumerado de coacutedigos de estado

JAX-RS proporciona el tipo enumerado javaxwsrscoreStatus para representarcoacutedigos de respuesta especiacuteficos

public enum Status OK(200 OK) CREATED(201 Created) ACCEPTED(202 Accepted) NO_CONTENT(204 No Content) MOVED_PERMANENTLY(301 Moved Permanently) SEE_OTHER(303 See Other) NOT_MODIFIED(304 Not Modified) TEMPORARY_REDIRECT(307 Temporary Redirect) BAD_REQUEST(400 Bad Request) UNAUTHORIZED(401 Unauthorized) FORBIDDEN(403 Forbidden) NOT_FOUND(404 Not Found) NOT_ACCEPTABLE(406 Not Acceptable) CONFLICT(409 Conflict) GONE(410 Gone) PRECONDITION_FAILED(412 Precondition Failed) UNSUPPORTED_MEDIA_TYPE(415 Unsupported Media Type) INTERNAL_SERVER_ERROR(500 Internal Server Error) NOT_IMPLEMENTED(501 Not Implemented) SERVICE_UNAVAILABLE(503 Service Unavailable) public enum Family INFORMATIONAL SUCCESSFUL REDIRECTION CLIENT_ERROR SERVER_ERROR OTHER

public Family getFamily() public int getStatusCode() public static Status fromStatusCode(final int statusCode)

Cada valor del tipo Status se asocia con una familia especiacutefica de coacutedigos de respuestaHTTP Estas familias se identifican por el enumerado StatusFamily

bull Los coacutedigos en el rango del 100 se consideran informacionales

bull Los coacutedigos en el rango del 200 se consideran exitosos

bull Los coacutedigos en el rango del 300 son coacutedigos con eacutexito pero dentro de la categoriacutearedireccioacuten

bull Los coacutedigos de error pertenecen a los ragos 400 y 500 En el rango de 400 se consideranerrores del cliente y en el rango de 500 son errores del servidor

Servicios Rest

98

Tanto el meacutetodo Responsestatus() como ResponseBuilderstatus() puedenaceptar un valor enumerado de tipo Status Por ejemplo

DELETEResponse delete() return Responsestatus(StatusGONE)build()

En este caso estamos indicando al cliente que lo que queremos borrar ya no existe (410)

La clase javaxwsrscoreGenericEntity

Cuando estamos creando objetos de tipo Response se nos plantea un problema cuandoqueremos devolver tipos geneacutericos ya que el manejador JAXB necesita extraer la informacioacutendel tipo parametrizado de la respuesta en tiempo de ejecucioacuten Para estos casos JAX-RS proporciona la clase javaxwsrscoreGenericEntity Veamos su uso con unejemplo

GETProduces(applicationxml)public Response getListaClientes() ListltClientegt list = new ArrayListltClientegt() listadd(new Cliente())

GenericEntity entity =

new GenericEntityltListltClientegtgt(list)

return Responseok(entity)build()

La clase GenericEntity es tambieacuten una clase geneacuterica Lo que hacemos escrear una clase anoacutenima que extiende GenericEntity inicializando la plantilla deGenericEntity con el tipo geneacuterico que estemos utilizando

36 Manejadores de excepciones

Vamos a explicar coacutemo podemos tratar las excepciones en nuestros servicios RESTful

Los errores pueden enviarse al cliente bien creando y devolviendo el objeto Responseadecuado o lanzando una excepcioacuten Podemos lanzar cualquier tipo de excepcioacutentanto las denominadas checked (clases que heredan de javalangException ) comolas excepciones unchecked (clases que extienden javalangRuntimeException )Las excepciones generadas son manejadas por el runtime de JAX-RS si tenemosregistrado un mapper de excepciones Dichos mappers (o mapeadores) de excepcionespueden convertir una excepcioacuten en una respuesta HTTP Si las excepciones no estaacutengestionadas por un mapper eacutestas se propagan y se gestionan por el contenedor (deservlets) en el que se estaacute ejecutando JAX-RS JAX-RS proporciona tambieacuten la clasejavaxwsrsWebApplicationException Esta excepcioacuten puede lanzarse por elcoacutedigo de nuestra aplicacioacuten y seraacute procesado automaacuteticamente por JAX-RS sin necesidadde disponer de forma expliacutecita de ninguacuten mapper Vamos a ver coacutemo utilizar esta clase

Servicios Rest

99

La clase javaxwsrsWebApplicationException

JAX-RS incluye una excepcioacuten unchecked que podemos lanzar desde nuestraaplicacioacuten RESTful (ver la documentacioacuten del httpdocsoraclecomjavaee7apijavaxwsrsWebApplicationExceptionhtml [API]) Esta excepcioacuten se puede pre-inicializar con un objetoResponse o con un coacutedigo de estado particular

Clase javaxwsrsWebApplicationException

public class WebApplicationException extends RuntimeException Constructores public WebApplicationException()

public WebApplicationException(Response response) public WebApplicationException(int status)

public WebApplicationException(ResponseStatus status)

public WebApplicationException(String message) public WebApplicationException(Throwable cause) public WebApplicationException(Throwable cause Response response) public WebApplicationException(Throwable cause int status) public WebApplicationException(Throwable cause ResponseStatus status)

public Response getResponse() ]

Podemos crear una instancia a partir de un objeto ResponseCreacioacuten de una instancia a partir de un coacutedigo de estadoCreacioacuten de una instancia a partir de un String Por defecto se incluye el coacutedigo de estado500 El String que se pasa como paraacutemetro se almacena para su posterior recuperacioacutena traveacutes del mensaje `getMessage()

Cuando JAX-RS detecta que se ha lanzado la excepcioacuten WebApplicationException lacaptura y realiza una llamada al meacutetodo WebApplicationExceptiongetResponse()para obtener un objeto Response que enviaraacute al cliente Si la aplicacioacuten ha inicializado laexcepcioacuten WebApplicationException con un coacutedigo de estado o un objeto Response dicho coacutedigo o Response se utilizaraacuten para crear la respuesta HTTP real En otro caso laexcepcioacuten WebApplicationException devolveraacute el cliente el coacutedigo de respuesta 500Internal Server Error

Por ejemplo supongamos que tenemos un servicio web que permite a los usuarios solicitarinformacioacuten de nuestros clientes utilizando una representacioacuten XML

Path(clientes)public class ClienteResource GET Path(id) Produces(applicationxml) public Cliente getCliente(PathParam(id) int id) Cliente cli = recuperarCliente(id) if (cli == null) throw new WebApplicationException(ResponseStatusNOT_FOUND) return cli

Servicios Rest

100

En este ejemplo si no encontramos una instancia de Cliente con el id proporcionadolanzaremos una WebApplicationException que provocaraacute que se le enviacutee al clientecomo coacutedigo de respuesta 404 Not Found

Mapeado de excepciones

Normalmente las aplicaciones tienen que tratar con multitud de excepciones lanzadas desdenuestro coacutedigo de aplicacioacuten o por frameworks de terceros Dejar que el servlet JAX-RSque reside en el servidor maneje la excepcioacuten no nos proporciona demasiada flexibilidadSi capturamos y redirigimos todas estas excepciones a WebApplicationExceptionpodriacutea resultar bastante tedioso De forma alternativa podemos implementar y registrarinstancias de javaxwsrsextExceptionMapper Estos objetos saben coacutemo mapearuna excepcioacuten lanzada por la aplicacioacuten a un objeto Response

public interface ExceptionMapperltE extends Throwablegt Response toResponse(E exception)

Las clases que implementan la interfaz ExceptionMapperltTgt son proveedores demappings de excepciones (Exception Mapping Providers) y mapean una excepcioacuten runtimeo checked a una instancia de Response

Cuando un recurso JAX-RS lanza una excepcioacuten para la que existe un proveedor de mappingde excepciones eacuteste uacuteltimo se utiliza para devolver una instancia de Response Estarespuesta resultante se procesa como si hubiese sido generada por el recurso

Por ejemplo una excepcioacuten bastante utilizada por aplicaciones de bases de datos que utilizanJPA (Java Persistence Api) es javaxpersistenceEntityNotFoundException Estaexcepcioacuten se lanza cuando JPA no puede encontrar un objeto particular en la base de datosEn lugar de escribir coacutedigo que maneje dicha excepcioacuten de forma expliacutecita podemos escribirun ExceptionMapper para que gestione dicha excepcioacuten por nosotros Veaacutemos coacutemo

Provider public class EntityNotFoundMapper

implements ExceptionMapperltEntityNotFoundExceptiongt

public Response toResponse(EntityNotFoundException e)

return Responsestatus(ResponseStatusNOT_FOUND)build()

Nuestra implementacioacuten de ExceptionMapper debe anotarse con Provider Estole indica al runtime de JAX-RS que esta clase es un componente RESTLa clase que implementa ExceptionMapper debe proporcionar el tipo que se quiereparametrizar JAX-RS utiliza esta informacioacuten para emparejar las excepciones de tipoEntityNotFoundException con nuestra clase EntityNotFoundMapperEl meacutetodo toResponse() recibe la excepcioacuten lanzada ycrea un objeto Response que se utilizaraacute para construir la respuesta HTTP

Servicios Rest

101

Otro ejemplo es la excepcioacuten EJBException lanzada por aplicaciones que utilizan EJBs

Jerarquiacutea de excepciones

JAX-RS 20 proporciona una jerarquiacutea de excepciones para varias condiciones deerror para las peticiones HTTP La idea es que en lugar de crear una instancia deWebApplicationException e inicializarla con un coacutedigo de estado especiacutefico podemosuilizar en su lugar una de las excepciones de la jeraquiacutea (clases que heredan de la claseWebApplicationException) Por ejemplo podemos cambiar el coacutedigo anterior que utilizabaWebApplicationException y en su lugar usar javaxwsrsNotFoundException

Path(clientes)public class ClienteResource GET Path(id) Produces(applicationxml) public Cliente getCliente(PathParam(id) int id) Cliente cli = recuperarCliente(id) if (cli == null) throw new NotFoundException() return cli

Al igual que el resto de excepciones de la jerarquiacutea la excepcioacuten NotFoundExceptionhereda de WebApplicationException La siguiente tabla muestra algunas excepcionesque podemos utilizar todas ellas del paquete javaxwsrs Las que incluyen un coacutedigo deestado en el rango de 400 son subclases de ClientErrorException y como ya hemosindicado representan errores en las peticiones Las que presentan un coacutedigo de estado enel rango de 500 son subclases de ServerErrorException y representan errores delservidor

Table 5 Jerarquiacutea de excepciones JAX-RS

Excepcioacuten Coacutedigo de estado Descripcioacuten

BadRequestException 400 Mensaje mal formado

NotAuthorizedException 401 Fallo de autenticacioacuten

ForbiddenException 403 Acceso no permitido

NotFoundException 404 No se ha podido encontrar elrecurso

NotAllowedException 405 Meacutetodo HTTP no soportado

NotAcceptableException 406 Media type solicitado porel cliente no soportado(cabecera Accept de lapeticion)

NotSupportedException 415 El cliente ha incluido unMedia type no soportado(cabecera Content-Type dela peticion)

Servicios Rest

102

Excepcioacuten Coacutedigo de estado Descripcioacuten

InternalServerErrorException 500 Error general del servidor

ServiceUnavailableException 503 El servidor estaacute ocupadoo temporalmente fuera deservicio

bull La excepcioacuten BadRequestException se utiliza cuando el cliente enviacutea algo al servidorque eacuteste no puede interpretar Ejemplos de escenarios concretos que provocan que elruntime JAX-RS son cuando una peticioacuten PUT o POST contiene un cuerpo del mensaje conun documento XML o JSON mal formado de forma que falle el parsing del documento ocuando no puede convertir un valor especificado en la cabecera o cookie al tipo deseadoPor ejemplo

HeaderParam(Cabecera-Particular) int cabeceraCookieParam(miCookie) int cookie

Si el valor de la cabecera HTTP de la peticioacuten o el valor miCookie no puede convertirseen un entero se lanzaraacute la excepcioacuten BadRequestException

bull La excepcioacuten NotAuthorizedException (coacutedigo 401) se usa cuando queremosescribir nuestros propios protocolos de autorizacioacuten y queremos indicar al cliente que eacutestenecesita autenticarse con el servidor

bull La excepcioacuten ForbiddenException (coacutedigo 403)se usa generalmente cuando elcliente realiza una invocacioacuten para la que no tiene permisos de acceso Esto ocurrenormalmente debido a que el cliente no tiene el rol requerido

bull La excepcioacuten NotFoundException (coacutedigo 404) se usa cuando queremos comunicar alcliente que el recurso que estaacute solicitando no existe Esta excepcioacuten tambieacuten se generaraacute deforma automaacutetica por el runtime de JAX-RS cuando a eacuteste no le sea posible inyectar un valoren un PathParam QueryParam o MatrixParam Al igual que hemos comentado paraBadRequestException esto puede ocurrir si intentamos convertir el valor del paraacutemetroa un tipo que no admite esta conversioacuten

bull La excepcioacuten NotAllowedException (coacutedigo 405) se usa cuando el meacutetodo HTTP queel cliente estaacute intentando invocar no estaacute soportado por el recurso al que el cliente estaacuteaccediendo El runtime de JAX-RS lanza automaacuteticamente esta excepcioacuten si no encuentraninguacuten meacutetodo que pueda emparejar con el meacutetodo HTTP invocado

bull La excepcioacuten NotAcceptableException (coacutedigo 406) se usa cuando un cliente estaacutesolicitando un formato especiacutefico a traveacutes de la cabecera Accept El runtime de JAX-RSlanza automaacuteticamente esta excepcioacuten si no hay un meacutetodo con una anotacioacuten Producesque sea compatible con la cabecera Accept del cliente

bull La excepcioacuten NotSupportedException (coacutedigo 415)se usa cuando un cliente estaacuteenviando una representacioacuten que el servidor no comprende El runtime de JAX-RS lanzaautomaacuteticamente esta excepcioacuten si no hay un meacutetodo con una anotacioacuten Consumes quecoincida con el valor de la cabecera Content-Type de la peticioacuten

bull La excepcioacuten InternalServerErrorException (coacutedigo 500)es una excepcioacuten depropoacutesito general lanzada por el servidor Si en nuestra aplicacioacuten queremos lanzar estaexcepcioacuten deberiacuteamos hacerlo si se ha producido una condicioacuten de error que realmenteno encaja en ninguna de las situaciones que hemos visto El runtime de JAX-RS lanzaautomaacuteticamente esta excepcioacuten si falla un MessageBodyWriter o si se lanza algunaexcepcioacuten desde alguacuten ExceptionMapper

Servicios Rest

103

bull La excepcioacuten ServiceUnavailableException (coacutedigo 503)se usa cuando el servidorestaacute ocupado o temporalmente fuera de servicio En la mayoriacutea de los casos es suficientecon que el cliente vuelva a intentar realizar la llamada un tiempo maacutes tarde

Servicios Rest

104

37 Ejercicios

Servicio REST ejemplo

Para familiarizarnos con las el uso de diferentes manejadores de contenidos y manejo deexcepciones proporcionamos el moacutedulo el MOacuteDULO s3-ejemplo-rest con la implementacioacutende un servicio rest sencillo que podeacuteis probar con la herramienta postman

En el directorio srcmainresources de dicho moacutedulo teneacuteis un fichero de texto con lasinstrucciones (instruccionestxt) para construir desplegar y probar la aplicacioacuten de ejemplo

Plantillas que se proporcionan

Para esta sesioacuten proporcionamos un proyecto como plantilla con el nombre s3-filmotecaque tendraacutes utilizar como punto de partida para aplicar lo que hemos aprendido en esta sesioacuten

Se trata de una implementacioacuten parcial para gestionar una filmoteca con informacioacuten depeliacuteculas y actores

La estructura loacutegica del proyecto proporcionado es la siguiente

bull Paquete orgexpertojavadomain es la capa que contiene los objetos del dominiode la aplicacioacuten Por simplicidad no usamos una base de datos real sino que trabajamoscon datos en memoria

bull Paquete orgexpertojavaservice contiene la implementacioacuten de los servicios denuestra aplicacioacuten que seraacuten accedidos desde la capa rest

bull Paquete orgexpertojavarest constituye la capa rest de nuestra aplicacioacuten Estacapa es cliente de la capa de servicios

En la carpeta srcmainresources teneacuteis un fichero de texto (instruccionestxt) coninformacioacuten detallada sobre el API rest implementado

Uso de JAXB (05 puntos)

Utiliza las anotaciones JAXB oportunas para realizar el serializado de las entidades java a xmly json de forma que la lista de peliacuteculas de la filmoteca en formato xml sea

Peticioacuten rest GET peliculas

ltxml version=10 encoding=UTF-8 standalone=yesgtltpeliculasgt ltpelicula duracion=120 estreno=2015-12-08T000000+0100 id=1gt ltdirectorgtStanley Kubrickltdirectorgt lttitulogtEl resplandorlttitulogt ltpeliculagt ltpelicula duracion=155 estreno=2015-18-07T000000+0100 id=2gt ltdirectorgtDirector 2ltdirectorgt lttitulogtPelicula 2lttitulogt ltpeliculagtltpeliculasgt

Servicios Rest

105

Por defecto JAXB serializa los tipos Date con el formato ISO 80617

para los contenidos en el cuerpo de la petioacutenrespuesta Dicho formatoes YYYY-MM-DD (seguido de HHMMSS) Por otro lado si antildeadimosactores a las peliacuteculas eacutestos deberaacuten mostrarse bajo la etiquetaltactoresgt tal y como mostramos en el siguiente ejemplo

Los datos mostrados para una peliacutecula en formato xml tienen que presentar el siguienteaspecto

sourcejava] Peticioacuten rest GET peliculas1

ltxml version=10 encoding=UTF-8 standalone=yesgtltpelicula duracion=120 estreno=2015-12-08T000000+0100 id=1gt ltactoresgt ltactor nombre=Jack Nicholson personaje=Jack Torrancegt ltactoresgt ltdirectorgtStanley Kubrickltdirectorgt lttitulogtEl resplandorlttitulogtltpeliculagt

Puedes comprobar si has hecho bien el ejercicio utilizando Postman

Uso de manejadores de contenidos y clase Response (075 puntos)

Implementa una nueva peticioacuten POST que reciba los datos de una nueva peliacutecula desdeun formulario Recuerda que los datos de un formulario pueden ponerse en el cuerpo de lapeticioacuten HTTP como pares nombre=valor separados por amp Los espacios en blanco secodifican como 20 No es necesario poner comillas Por ejemplo

nombre1=valor20con20espaciosampnombre2=valor

Se proporciona el fichero indexhtml con un formulario para utilizarlo como alternativa apostman Para acceder al formulario usaremos httplocalhost8080s3-filmoteca

Modifica las peticiones POST sobre peliacuteculas y actores de forma que devuelvan enla cabecera Location la URI del nuevo recurso creado Por ejemplo

httplocalhost8080s3-filmotecapeliculas3

en el caso de una nueva peliacutecula y

httplocalhost8080s3-filmotecapeliculas3actoresSigourney20Weaver

si por ejemplo hemos antildeadido un nuevo actor a la peliacutecula con id=3

Modifica los meacutetodos GET para que devuelvan el estado 204 No Content en los casos enlos que la peliacutecula yo actor consultado no exista

7 httpseswikipediaorgwikiISO_8601

Servicios Rest

106

Modifica los meacutetodos GET para que devuelvan el estado 404 Not Found en los casos enlos que las listas de peliacuteculas yo actores esteacuten vaciacuteas

Implementa el coacutedigo para antildeadir un nuevo actor Tendraacutes que obtener la informacioacuten de lapeliacutecula y nombre del actor de la URI de la peticioacuten

Puedes comprobar si has hecho bien el ejercicio utilizando Postman

Manejo de excepciones (075 puntos)

Modifica el meacutetodo addPelicula de la capa de servicio (paquete orgexpertojavaservice)para que lance una excepcioacuten de tipo ServiceException con el mensaje El tiacutetulo de la peliacuteculano puede ser nulo ni vaciacuteo cuando se intente antildeadir una peliacutecula con un tiacutetulo con valor nullo vaciacuteo

El meacutetodo addPelicula debe lanzar tambieacuten una excepcioacuten de tipo ServiceException conel mensaje La peliacutecula ya existe cuando se intente antildeadir una peliacutecula con un tiacutetulo que yaexiste

Modifica el meacutetodo addActor de la capa de servicio para que lance las excepciones de tipoServiceException con los mensajes El tiacutetulo de la peliacutecula no puede ser nulo ni vaciacuteo cuandose intente antildeadir un actor a una peliacutecula cuyo tiacutetulo no existe o bien el mensaje EL actor yaexiste si intentamos antildeadir un actor a una peliacutecula que ya habiacuteamos antildeadido previamente

Implementa un mapper para capturar las excepciones de la capa de servicio de forma quese devuelva el estado 500 Internal Server error y como entidad del cuerpo de la respuestael mensaje asociado a las excepciones generadas por el servicio (El tiacutetulo de la peliacutecula nopuede ser nulo ni vaciacuteo EL actor ya existe hellip) La nueva clase pertenceraacute a la capa restPuedes ponerle el nombre ServiceExceptionMapper

Puedes comprobar si has hecho bien el ejercicio utilizando Postman

Servicios Rest

107

4 HATEOAS y Seguridad

En esta sesioacuten trataremos uno de los principios REST de obligado cumplimiento para poderhablar de un servicio RESTful Nos referimos a HATEOAS Hasta ahora hemos visto coacutemo losclientes pueden cambiar el estado de los recursos (el nombre del recurso se especifica en laURI de la peticioacuten) a traveacutes de los contenidos del cuerpo del mensaje o utilizando paraacutemetroso cabeceras de peticioacuten A su vez los servicios comunican el estado resultante de la peticioacuten alos clientes a traveacutes del contenido del cuerpo del mensaje coacutedigos de respuesta y cabecerasde respuesta Pues bien teniendo en cuenta lo anterior HATEOAS hace referencia a quecuando sea necesario tambieacuten deben incluirse los enlaces a los recursos (URI) en el cuerpode la respuesta (o en las cabeceras) para asiacute poder recuperar el recurso en cuestioacuten o losrecursos relacionados

En esta sesioacuten tambieacuten explicaremos algunos conceptos baacutesicos para poder dotar deseguridad a nuestros servicios REST

41 iquestQueacute es HATEOAS

Comuacutenmente se hace referencia a Internet como la Web (web significa red telarantildea) debidoa que la informacioacuten estaacute interconectada mediante una serie de hiperenlaces embebidosdentro de los documentos HTML Estos enlaces crean una especie de hilos o hebras entrelos sitios web relacionados en Internet Una consecuencia de ello es que los humanos puedennavegar por la Web buscando elementos de informacioacuten relacionados de su intereacutes haciendoclick en los diferentes enlaces desde sus navegadores Los motores de buacutesqueda puedentrepar o desplazarse por estos enlaces y crear iacutendices enormes de datos susceptibles deser buscados Sin ellos Internet no podriacutea tener la propiedad de ser escalable No habriacuteaforma de indexar faacutecilmente la informacioacuten y el registro de sitios web seriacutea un proceso manualbastante tedioso

Ademaacutes de los enlaces (links) otra caracteriacutestica fundamental de Internet es HTML Enocasiones un sitio web nos solicita que rellenemos alguna informacioacuten para compraralgo o registrarnos en alguacuten servicio El servidor nos indica a nosotros como clientes queacuteinformacioacuten necesitamos proporcionar para completar una accioacuten descrita en la paacutegina webque estamos viendo El navegador nos muestra la paacutegina web en un formado que podemosentender faacutecilmente Nosotros leemos la paacutegina web y rellenamos y enviamos el formulario Unformulario HTML es un formato de datos interesante debido a que auto-describe la interaccioacutenentre el cliente y el servidor

El principio arquitectoacutenico que describe el proceso de enlazado (linking) y el enviacuteo deformularios se denomina HATEOAS Las siglas del teacutermino HATEOAS significan HypermediaAs The Engine Of Application State (es decir el uso de Hipermedia como mecanismo demaacutequina de estados de la aplicacioacuten) La idea de HATEOAS es que el formato de los datosproporciona informacioacuten extra sobre coacutemo cambiar el estado de nuestra aplicacioacuten En laWeb los enlaces HTML nos permiten cambiar el estado de nuestro navegador Por ejemplocuando estamos leyendo una paacutegina web un enlace nos indica queacute posibles documentos(estados) podemos ver a continuacioacuten Cuando hacemos click sobre un enlace el estado delnavegador cambia al visitar y mostrar una nueva paacutegina web Los formularios HTML por otraparte nos proporcionan una forma de cambiar el estado de un recurso especiacutefico de nuestroservidor Por uacuteltimo cuando compramos algo en Internet por ejemplo estamos creando dosnuevos recursos en el servicio una transaccioacuten con tarjeta de creacutedito y una orden de compra

Servicios Rest

108

42 HATEOAS y Servicios Web

Cuando aplicamos HATEOAS a los servicios web la idea es incluir enlaces en nuestrosdocumentos XML o JSON La mayoriacutea de las aplicaciones RESTful basadas en XML utilizanel formato Atom Syndication Format8 para implementar HATEOAS

Enlaces Atom

Los enlaces Atom constituyen un mecanismo estaacutendar para incluir enlaces (links) en nuestrosdocumentos XML Veamos un ejemplo

ltclientesgt ltlink rel=next href=httpejemplocomclientesinicio=2amptotal=2 type=applicationxmlgt ltcliente id=123gt ltnombregtJuan Garcialtnombregt ltclientegt ltcliente id=332gt ltnombregtPablo Bozoltnombregt ltclientegtltclientesgt

El documento anterior representa una lista de clientes y el elemento ltlinkgt que contieneun enlace indica la forma de obtener los siguientes clientes de la lista

Un enlace Atom es simplemente un elemento XML (elemento ltlinkgt ) con unos atributosespeciacuteficos

bull El atributo rel Se utiliza para indicar la relacioacuten del enlace con el elemento XML en elque anidamos dicho enlace Es el nombre loacutegico utilizado para referenciar el enlace Esteatributo tiene el mismo significado para la URL que estamos enlazando que la etiquetaHTML ltagt tiene para la URL sobre la que estamos haciendo click con el ratoacuten en elnavegador Si el enlace hace referencia al propio elemento XML en el que incluimos elenlace entonces asignaremos el valor del atributo self

bull El atributo href es la URL a la que podemos acceder para obtener nueva informacioacuten ocambiar el estado de nuestra aplicacioacuten

bull El atributo type indica el media type asociado con el recurso al que apunta la URL

Cuando un cliente recibe un documento con enlaces Atom eacuteste busca la relacioacuten en la queestaacute interesado (atributo rel ) e invoca la URI indicada en el atributo href

Ventajas de utilizar HATEOAS con Servicios Web

Resulta bastante obvio por queacute los enlaces y los formularios tienen mucho que ver en laprevalencia de la Web Con un navegador tenemos una ventana a todo un mundo deinformacioacuten y servicios Las maacutequinas de buacutesqueda rastrean Internet e indexan sitios webpara que todos los datos esteacuten al alcance de nuestros dedos Esto es posible debido a quela Web es auto-descriptiva Cuando accedemos a un documento conocemos coacutemo recuperarinformacioacuten adicional siguiendo los enlaces situados en dicho documento Por ejemplo

8 httpwwww3org2005Atom

Servicios Rest

109

conocemos coacutemo realizar una compra en Amazon debido a que los formularios HTML nosindican coacutemo hacerlo

Cuando los clientes son maacutequinas en lugar de personas (los servicios Web tambieacuten seconocen como Web para maacutequinas frente a la Web para humanos proporcionada por elacceso a un servidor web a traveacutes de un navegador) el tema es algo diferente puesto que lasmaacutequinas no pueden tomar decisiones sobre la marcha cosa que los humanos siacute puedenhacer Las maacutequinas requieren que los programadores les digan coacutemo interpretar los datosrecibidos desde un servicio y coacutemo realizar transiciones entre estados como resultado de lasinteracciones entre clientes y servidores

En este sentido HATEOAS proporciona algunas ventajas importantes para contribuir a quelos clientes sepan coacutemo utilizar los servicios a la vez que acceden a los mismos Vamos acomentar algunas de ellas

Transparencia en la localizacioacuten

En un sistema RESTful gracias a HATEOAS soacutelo es necesario hacer puacuteblicas unas pocasURIs Los servicios y la informacioacuten son representados con enlaces que estaacuten embebidosen los formatos de los datos devueltos por las URIs puacuteblicas Los clientes necesitan conocerlos nombres loacutegicos de los enlaces para buscar a traveacutes de ellos pero no necesitan conocerlas ubicaciones reales en la red de los servicios a los que acceden

Los enlaces proporcionan un nivel de indireccioacuten de forma que los servicios subyacentespueden cambiar sus localizaciones en la red sin alterar la loacutegica ni el coacutedigo del cliente

Desacoplamiento de los detalles de la interaccioacuten

Consideremos una peticioacuten que nos devuelve una lista de clientes en una base de datosGET clientes Si nuestra base de datos tiene miles de datos probablemente noquerremos devolver todos ellos de una soacutela vez Lo que podemos hacer es definir una vistaen nuestra base de datos utilizando paraacutemetros de consulta por ejemplo

customersinicio=indiceInicioamptotal=numeroElementosDevueltos

El paraacutemetro inicio identifica el iacutendice inicial de nuestra lista de clientes El paraacutemetrototal especifica cuaacutentos clientes queremos que nos sean devueltos como respuesta

Lo que estamos haciendo en realidad es incrementar la cantidad de conocimiento que elcliente debe tener predefinido para interactuar con el servicio (es decir no soacutelo necesita saberla URI sino ademaacutes conocer la existencia de estos paraacutemetros) Supongamos que en el futuroel servidor decide que necesita cambiar la forma en la que se accede al nuacutemero de datossolicitados por el cliente Si el servidor cambia la interfaz los clientes antiguos dejaraacuten defuncionar a menos que cambien su coacutedigo

En lugar de publicar la interfaz REST anterior para obtener datos de los clientes podemosincluir dicha informacioacuten en el documento de respuesta por ejemplo

ltclientesgt ltlink rel=next href=httpejemplocomclientesinicio=2amptotal=2 type=applicationxmlgt ltcliente id=123gt

Servicios Rest

110

ltnombregtJuan Garcialtnombregt ltclientegt ltcliente id=332gt ltnombregtPablo Bozoltnombregt ltclientegtltclientesgt

Cuando incluimos un enlace Atom en un documento estamos asignando un nombre loacutegicoa una transicioacuten de estados En el ejemplo anterior la transicioacuten de estados es el siguienteconjunto de clientes a los que podemos acceder En lugar de tener que recordar cuaacuteles sonlos paraacutemetros de la URI que tenemos que utilizar en la siguiente invocacioacuten para obtener maacutesclientes lo uacutenico que tenemos que hacer es seguir el enlace proporcionado El cliente notiene que contabilizar en ninguacuten sitio la interaccioacuten ni tiene que recordar queacute seccioacuten de labase de datos estamos consultando actualmente

Ademaacutes el XML devuelto es auto-contenido iquestQueacute pasa si tenemos que pasar estedocumento a un tercero Tendriacuteamos que decirle que se trata de una vista parcial de la basede datos y especificar el iacutedice de inicio Al incluir el enlace en el documento ya no es necesarioproporcionar dicha informacioacuten adicional ya que forma parte del propio documento

Reduccioacuten de errores de transicioacuten de estados

Los enlaces no se utilizan solamente como un mecanismo para agregar informacioacuten denavegacioacuten Tambieacuten se utilizan para cambiar el estado de los recursos Pensemos en unaaplicacioacuten de comercio web a la que podemos acceder con la URI pedidos333

ltpedido id=333gt ltcliente id=123gtltclientegt ltimportegt9999ltimportegt ltlineas-pedidogt ltlineas-pedidogtltpedidogt

Supongamos que un cliente quiere cancelar su pedido Podriacutea simplemente invocar la peticioacutenHTTP DELETE pedidos333 Esta no es siempre la mejor opcioacuten ya que normalmenteel sistema necesitaraacute retener el pedido para propoacutesitos de almacenaje Por ello podriacuteamosconsiderar una nueva representacioacuten del pedido con un elemento cancelado a true

PUT pedidos333 HTTP11Content-Type applicationxmlltpedido id=333gt ltcliente id=123gtltclientegt ltimportegt9999ltimportegt ltcanceladogttrueltcanceladogt ltlineas-pedidogt ltlineas-pedidogtltpedidogt

Pero iquestqueacute ocurre si el pedido no puede cancelarse Podemos tener un cierto estado ennuestro proceso de pedidos en donde esta accioacuten no estaacute permitida Por ejemplo si el pedido

Servicios Rest

111

ya ha sido enviado entonces no puede cancelarse En este caso realmente no hay niguacutencoacutedigo de estado HTTP de respuesta que represente esta situacioacuten Una mejor aproximacioacutenes incluir un enlace para poder realizar la cancelacioacuten

ltpedido id=333gt ltcliente id=123gtltclientegt ltimportegt9999ltimportegt ltcanceladogtfalseltcanceladogt ltlink rel=cancelar href=httpejemplocompedidos333canceladogt ltlineas-pedidogt ltlineas-pedidogtltpedidogt

El cliente podriacutea invocar la orden GET pedidos333 y obtener el documento XMLque representa el pedido Si el documento contiene el enlace cancelar entonces el clientepuede cambiar el estado del pedido a cancelado enviando una orden PUT vaciacutea a la URIreferenciada en el enlace Si el documento no contiene el enlace el cliente sabe que estaoperacioacuten no es posible Esto permite que el servicio web controle en tiempo real la forma enla que el cliente interactua con el sistema

Enlaces en cabeceras frente a enlaces Atom

Una alternativa al uso de enlaces Atom en el cuerpo de la respuesta es utilizar enlaces enlas cabeceras de la respuesta (httptoolsietforghtmlrfc5988) Vamos a explicar eacutesto con unejemplo

Consideremos el ejemplo de cancelacioacuten de un pedido que acabamos de ver En lugar deutilizar un enlace Atom para especificar si se permite o no la cancelacioacuten del pedido podemosutilizar la cabecera Link (es uno de los posibles campos que podemos incluir como cabeceraen una respuesta HTTP)) De esta forma si un usuario enviacutea la peticioacuten GET pedidos333 recibiraacute la siguiente respuesta HTTP

HTTP11 200 OKContent-Type applicationxmlLink lthttpejemplocompedidos333canceladogt rel=cancel

ltpedido id=333gt ltpedidogt

La cabecera Link tiene las mismas caracteriacutesticas que un enlace Atom La URI estaacute entrelos signos lt y gt y estaacute seguida por uno o maacutes atributos delimitados por El atributo reles obligatorio y tiene el mismo significado que el correspondiente atributo Atom com el mismonombre En el ejemplo no se muestra pero podriacuteamos especificar el media type utilizando elatributo type

43 HATEOAS y JAX-RS

JAX-RS no proporciona mucho soporte para implementar HATEOAS HATEOAS se definepor la aplicacioacuten por lo que no hay mucho que pueda aportar ninguacuten framework Lo que siacute

Servicios Rest

112

proporciona JAX-RS son algunas clases que podemos utilizar para construir las URIs de losenlaces HATEOAS

Construccioacuten de URIs con UriBuilder

Una clase que podemos utilizar es javaxwsrscoreUriBuilder Esta clase nospermite construir URIs elemento a elemento y tambieacuten permite incluir plantillas de paraacutemetros(segmentos de ruta variables)

Clase UriBuilder meacutetodos para instanciar objetos de la clase

public abstract class UriBuilder public static UriBuilder fromUri(URI uri) throws IllegalArgumentException public static UriBuilder fromUri(String uri) throws IllegalArgumentException public static UriBuilder fromPath(String path) throws IllegalArgumentException public static UriBuilder fromResource(Classltgt resource) throws IllegalArgumentException public static UriBuilder fromLink(Link link) throws IllegalArgumentException

Las instancias de UriBuilder se obtienen a partir de meacutetodos estaacuteticos con la formafromXXX() Podemos inicializarlas a partir de una URI una cadena de caracteres o laanotacioacuten Path de una clase de recurso

Para extraer modificar yo componer una URI se pueden utilizar meacutetodos como

Clase UriBuilder meacutetodos para manipular las URIs

public abstract UriBuilder clone() crea una copia

crea una copia con la informacioacuten de un objeto URIpublic abstract UriBuilder uri(URI uri) throws IllegalArgumentException

meacutetodos para asignarmodificar valores de los atributos de los objetos UriBuilderpublic abstract UriBuilder scheme(String scheme) throws IllegalArgumentExceptionpublic abstract UriBuilder userInfo(String ui)public abstract UriBuilder host(String host) throws IllegalArgumentExceptionpublic abstract UriBuilder port(int port) throws IllegalArgumentExceptionpublic abstract UriBuilder replacePath(String path)

meacutetodos que antildeaden elementos a nuestra URIpublic abstract UriBuilder path(String path)public abstract UriBuilder segment(String segments)public abstract UriBuilder matrixParam(String name Object values)public abstract UriBuilder queryParam(String name Object values)

Servicios Rest

113

meacutetodo que instancia el valor de una plantilla de la URIpublic abstract UriBuilder resolveTemplate(String name Object value)

Los meacutetodos build() construyen la URI Eacutesta puede contener plantillas de paraacutemetros( segmentos de ruta variables) que deberemos inicializar utilizando pares nombrevalor o bienuna lista de valores que reemplazaraacuten a los paraacutemetros de la plantilla en el orden en el queaparezcan

Clase UriBuilder meacutetodos buildXXX() para construir las URIs

public abstract URI buildFromMap(MapltString extends Objectgt values) throws IllegalArgumentException UriBuilderException

public abstract URI build(Object values) throws IllegalArgumentException UriBuilderException

Veamos alguacuten ejemplo que muestra coacutemo crear inicializar componer y construir una URIutilizando un UriBuilder

UriBuilder builder = UriBuilderfromPath(clientesid)builderscheme(http) host(hostname) queryParam(param=param)

Con este coacutedigo estamos definiendo una URI como

httphostnameclientesidparam=param

Puesto que tenemos plantillas de paraacutemetros necesitamos inicializarlos con valores quepasaremos como argumentos para crear la URI final Si queremos reutilizar la URI quecontiene las plantillas deberiacuteamos realizar una llamada a clone() antes de llamar al meacutetodobuild() ya que eacuteste reemplazaraacute los paraacutemetros de las plantillas en la estructura internadel objeto

UriBuilder clone = builderclone()URI uri = clonebuild(ejemplocom 333 valor)

El coacutedigo anterior dariacutea lugar a la siguiente URI

httpejemplocomclientes333param=valor

Tambieacuten podemos definir un objeto de tipo Map que contenga los valores de las plantillas

MapltString Objectgt map = new HashMapltString Objectgt()

Servicios Rest

114

mapput(hostname ejemplocom)mapput(id 333)mapput(param valor)UriBuilder clone = builderclone()URI uri = clonebuildFromMap(map)

Otro ejemplo interesante es el de crear una URI a partir de las expresiones Path definidasen las clases JAX-RS anotadas A continuacioacuten mostramos el coacutedigo

Path(clientes)public class ServicioClientes

Path(id) public Cliente getCliente(PathParam(id) int id)

Podemos referenciar esta clase y el meacutetodo getCliente() a traveacutes de la claseUriBuilder de la siguiente forma

UriBuilder builder = UriBuilderfromResource(ServicioClientesclass)builderhost(hostname)builderpath(ServicioClientesclass getCliente)

El coacutedigo anterior define la siguiente plantilla para la URI

httphostnameclientesid

A partir de esta plantilla podremos construir la URI utilizando alguno de los meacutetodos`buildXXX()

Tambieacuten podemos querer utilizar UriBuilder para crear URIS a partir de plantillas Para ellodisponemos de meacutetodos resolveTemplateXXX() que nos facilitan el trabajo

Clase UriBuilder meacutetodos resolveTemplateXXX() para crear URIs a partir de plantillas

public abstract UriBuilder resolveTemplate(String name Object value)public abstract UriBuilder resolveTemplate(String name Object value boolean encodeSlashInPath)public abstract UriBuilder resolveTemplateFromEncoded(String nameObject value)public abstract UriBuilder resolveTemplates(MapltString Objectgt templateValues)public abstract UriBuilder resolveTemplates( MapltStringObjectgt templateValues boolean encodeSlashInPath) throws IllegalArgumentExceptionpublic abstract UriBuilder resolveTemplatesFromEncoded( MapltString Objectgt templateValues) Devuelve la URI de la plantilla como una cadena de caracterespublic abstract String toTemplate()

Servicios Rest

115

Funcionan de forma similar a los meacutetodos build() y se utilizan para resolver parcialmentelas plantillas contenidas en la URI Cada uno de los meacutetodos devuelve una nueva instanciade UriBuilder de forma que podemos encadenar varias llamadas para resolver todas lasplantillas de la URI Finalmente usaremos el meacutetodo toTemplate() para obtener la nuevaplantilla en forma de String

String original = httphostidString nuevaPlantilla = UriBuilderfromUri(original) resolveTemplate(host localhost) toTemplate()

El valor de nuevaPlantilla para el coacutedigo anterior seriacutea httplocalhostid

URIs relativas mediante el uso de UriInfo

Cuando estamos escribiendo servicios que distribuyen enlaces hay cierta informacioacuten queprobablemente no conozcamos cuando estamos escribiendo el coacutedigo Por ejemplo podemosno conocer todaviacutea los hostnames de los enlaces o incluso los base paths de las URIs en elcaso de que estemos enlazando con otros servicios REST

JAX-RS proporciona una forma sencilla de solucionar estos problemas utilizando la interfazjavaxwsrscoreUriInfo Ya hemos introducido algunas caracteriacutesticas de estainterfaz en sesiones anteriores Ademaacutes de poder consultar informacioacuten baacutesica de la rutatambieacuten podemos obtener instancias de UriBuilder preinicializadas con la URI base utilizadapara definir los servicios JAX-RS o la URI utilizada para invocar la peticioacuten HTTP actual

public interface UriInfo public URI getRequestUri() public UriBuilder getRequestUriBuilder() public URI getAbsolutePath() public UriBuilder getAbsolutePathBuilder() public URI getBaseUri() public UriBuilder getBaseUriBuilder()

Por ejemplo supongamos que tenemos un servicio que permite acceder a Clientes desdeuna base de datos En lugar de tener una URI base que devuelva todos los clientes en unuacutenico documento podemos incluir los enlaces previo y sigiente de forma que podamosnavegar por los datos Vamos a mostrar coacutemo crear estos enlaces utilizando la URI parainvocar la peticioacuten

Path(clientes)public class ServicioClientes GET Produces(applicationxml)

public String getCustomers(Context UriInfo uriInfo)

UriBuilder nextLinkBuilder = uriInfogetAbsolutePathBuilder() nextLinkBuilderqueryParam(inicio 5) nextLinkBuilderqueryParam(total 10) URI next = nextLinkBuilderbuild() rellenar el resto del documento

Servicios Rest

116

Para acceder a la instancia UriInfo que representa al peticioacuten usamos la anotacioacutenjavaxwsrscoreContext para inyectarla como un paraacutemetro del meacutetodo delrecurso RESTObtenemos un UriBuilder preininicializado con la URI utilizada para acceder alservicio

Para el coacutedigo anterior y dependiendo de coacutemo se despliegue el servicio la URI creada podriacuteaser

httporgexpertojavajaxrsclientesinicio=5amptotal=10

Construccioacuten de enlaces (Links) en documentos XML y en cabeceras HTTP

JAX-RS proporciona cierto soporte para construir los enlaces y devolverlos en las cabecerasde respuesta o bien incluirlos en los documentos XML Para ello podemos utilizar las clasesjavawsrscoreLink y javawsrscoreLinkBuilder

Clase abstracta javaxwsrscoreLink

public abstract class Link public abstract URI getUri() public abstract UriBuilder getUriBuilder() public abstract String getRel() public abstract ListltStringgt getRels() public abstract String getTitle() public abstract String getType() public abstract MapltString Stringgt getParams() public abstract String toString()

Link es una clase abstracta que representa todos los metadatos contenidos en una cabecerao en un enlace Atom El meacutetodo getUri() representa el atributo href del enlace Atom Elmeacutetodo getRel() representa el atributo rel y asiacute sucesivamente Podemos referenciar atodos los atributos a traveacutes del meacutetodo getParams() Finalmente el meacutetodo toString()convertiraacute la instancia Link en una cadena de caracteres con el formato de una cabeceraLink

Para crear instancias de Link utilizaremos un LinkBuilder que crearemos con algunode estos meacutetodos

public abstract class Link public static Builder fromUri(URI uri) public static Builder fromUri(String uri) public static Builder fromUriBuilder(UriBuilder uriBuilder) public static Builder fromLink(Link link) public static Builder fromPath(String path) public static Builder fromResource(Classltgt resource) public static Builder fromMethod(Classltgt resource String method)

Servicios Rest

117

Los meacutetodos fromXXX() funcionan de forma similar a UriBuilderfromXXX() Todosinicializan el UriBuilder subyacente que utilizaremos para construir el atributo href delenlace

Los meacutetodos link() uri() y uriBuilder() nos permiten sobreescribir la URIsubyacente del enlace que estamos creando

public abstract class Link interface Builder public Builder link(Link link) public Builder link(String link) public Builder uri(URI uri) public Builder uri(String uri) public Builder uriBuilder(UriBuilder uriBuilder)

Los siguientes meacutetodos nos permiten asignar valores a varios atributos del enlace que estamosconstruyendo

public Builder rel(String rel) public Builder title(String title) public Builder type(String type) public Builder param(String name String value)

Finalmente eacutel meacutetodo build() nos permitiraacute construir el enlace

public Link build(Object values)

El objeto LinkBuilder tiene asociado una UriBuilder subyacente Los valorespasados como paraacutemetros del meacutetodo build() son utilizados por el UriBuilder paracrear una URI para el enlace Veamos un ejemplo

Link link = LinkfromUri(httphostraizclientesid) rel(update)type(textplain) build(localhost 1234)

Si realizamos una llamada a toString() sobre la instancia del enlace ( link )obtendremos lo siguiente

httplocalhostraizclientes1234gt rel=update type=textplain

A continuacioacuten mostramos dos ejemplos que muestran coacutemo crear instancias Link en lascabeceras y en el cuerpo de la respuesta como un enlace Atom

Escritura de enlaces en cabeceras HTTP

Servicios Rest

118

PathGETResponse get() Link link = LinkfromUri(abc)build() Response response = ResponsenoContent() links(link) build() return response

Inclusioacuten de un enlace Atom en el documento XMl de respuesta

import javaxwsrscoreLink

XmlRootElementpublic class Cliente private String nombre private ListltLinkgt enlaces = new ArrayListltLinkgt()

XmlElement public String getNombre() return nombre

public void setNombre(String nom) thisnombre = nom

XmlElement(name = enlace)

XmlJavaTypeAdapter(LinkJaxbAdapterclass) public ListltLinkgt getEnlaces() return enlaces

La clase Link contiene tambieacuten un JaxbAdapter con una implementacioacuten de la claseJAXB XmlAdapter que mapea los objetos JAX-RS de tipo Link a un valor quepuede ser serializado y deserializado por JAXB

El coacutedigo de este ejemplo permite construir cualquier enlace y antildeadirlo a la clase Clientede nuestro dominio Los enlaces seraacuten convertidos a elementos XML que se incluiraacuten en eldocumento XML de respuesta

44 Seguridad

Es importante que los servicios rest permitan un acceso seguro a los datos y funcionalidadesque proporcionan Especialmente para servicios que permiten la realizacioacuten de actualizacionesen los datos Tambieacuten es interesante asegurarnos de que terceros no lean nuestros mensajese incluso permitir que ciertos usuarios accedan a determinadas funcionalidades pero a otrasno

Ademaacutes de la especificacioacuten JAX-RS podemos aprovechar los servicios de seguridad quenos ofrece la web y Java EE y utilizarla en nuestros servicios REST Estos incluyen

AutentificacioacutenHace referencia a la validacioacuten de la identidad del cliente que accede a los serviciosNormalmente implica la comprobacioacuten de si el cliente ha proporcionado unos credenciales

Servicios Rest

119

vaacutelidos tales como el password En este sentido podemos utilizar los mecanismos quenos proporciona la web y las facilidades del contenedor de servlets de Java EE paraconfigurar los protocolos de autentificacioacuten

AutorizacioacutenUna vea que el cliente se ha autenticado (ha validado su identidad) querraacute interactuarcon nuestro servicio REST La autorizacioacuten hace referencia a decidir si un cierto usuariopuede acceder e invocar un determinado meacutetodo sobre una determinada URI Por ejemplopodemos habilitar el acceso a operaciones PUTPOSTDELETE para ciertos usuarios peropara otros no En este caso utilizaremos las facilidades que nos propociona el contenedorde servlets de Java EE para realizar autorizaciones

EncriptadoCuando un cliente estaacute interaccionando con un servicio REST es posible que alguienintercepte los mensajes y los lea si la conexioacuten HTTP no es segura Los datos sensiblesdeberiacutean protegerse con servicios criptograacuteficos tales como SSL

Autentificacioacuten en JAX-RS

Hay varios protocolos de autentificacioacuten En este caso vamos a ver coacutemo realizar unaautenticacioacuten baacutesica sobre HTTP (y que ya habeacuteis utilizado para servlets) Este tipo deautentificacioacuten requiere enviar un nombre de usuario y password codificados como Base-64en una cabecera de la peticioacuten al servidor El servidor comprueba si existe dicho usuario en elsistema y verifica el password enviado Veaacutemoslo con un ejemplo

Supongamos que un cliente no autorizado quiere acceder a nuestros servicios REST

GET clientes333 HTTP11

Ya que la peticioacuten no contiene informacioacuten de autentificacioacuten el servidor deberiacutea responderla siguiente respuesta

HTTP11 401 UnauthorizedWWW-Autenticate Basic realm=Cliente Realm

La respuesta 401 nos indica que el cliente no estaacute autorizado a acceder a dicha URI Lacabecera WWW-Autenticate especifica queacute protocolo de autentificacioacuten se deberiacutea usarEn este caso Basic significa que se deberiacutea utilizar una autentificacioacuten de tipo Basic Elatributo realm identifica una coleccioacuten de recursos seguros en un sitio web En este ejemploindica que solamente estaacuten autorizados a acceder al meacutetodo GET a traveacutes de la URI anteriortodos aquellos uarios que pertenezcan al realm Cliente Realm y seraacuten autentificados porel servidor mediante una autentificacioacuten baacutesica

Para poder realizar la autentificacioacuten el cliente debe enviar una peticioacuten que incluya lacabecera Authorization cuyo valor sea Basic seguido de la siguiente cadena decaracteres loginpassword codificada en Base64 (el valor de login y password representael login y password del usuario) Por ejemplo supongamos que el nombre del usuario esfelipe y el password es locking la cadena felipelocking codificada como Base64es ZmVsaXBlOmxvY2tpbmc= Por lo tanto nuestra peticioacuten deberiacutea ser la siguiente

GET clientes333 HTTP11

Servicios Rest

120

Authorization Basic ZmVsaXBlOmxvY2tpbmc=

El cliente deberiacutea enviar esta cabecera con todas y cada una de las peticiones que haga alservidor

El inconveniente de esta aproximacioacuten es que si la peticioacuten es interceptada por alguna entidadhostil en la red el hacker puede obtner faacutecilmente el usuario y el passwork y utilizarlos parahacer sus propias peticiones Utilizando una conexioacuten HTTP encriptada (HTTPS) se solucionaeste problema

Creacioacuten de usuarios y roles

Para poder utilizar la autentificacioacuten baacutesica necesitamos tener creados previamente los realmsen el servidor de aplicaciones Wildfly y registrar los usuarios que pertenecen a dichos realmsLa forma de hacerlo es ideacutentica a lo que ya habeacuteis visto en la asignatura de ComponentesWeb (a traveacutes del comando add-usersh )

Utilizaremos el realm por defecto ApplicationRealm de Wildfly que nos permitiraacute ademaacutescontrolar la autorizacioacuten mediante la asignacioacuten de roles a usuarios

Lo uacutenico que tendremos que hacer es antildeadir los usuarios a dicho realm a traveacutes de laherramienta $WILDFLY_HOMEbinadd-usersh

Al ejecutarla desde liacutenea de comandos deberemos elegir el ream ApplicationRealm eintroducir los datos para cada nuevo usuario que queramos antildeadir indicando su loginpassword y el grupo (rol) al que queremos que pertenezca dicho usuario

Los datos sobre los nuevos usuarios creados se almacenan en los ficheros application-usersproperties y application-rolesproperties tanto en el directorio$WILDFLY_HOMEstandaloneconfiguration como en $WILDFLY_HOMEdomainconfiguration

Una vez creados los usuarios tendremos que incluir en el fichero de configuracioacuten webxml la siguiente informacioacuten

ltweb-appgt

ltlogin-configgt ltauth-methodgtBASICltauth-methodgt

ltrealm-namegtApplicationRealmltrealm-namegt ltlogin-configgt

ltsecurity-constraintgt ltweb-resource-collectiongt ltweb-resource-namegtcustomer creationltweb-resource-namegt

lturl-patterngtrestresourceslturl-patterngt

lthttp-methodgtPOSTlthttp-methodgt ltweb-resource-collectiongt ltsecurity-constraintgt ltweb-appgt

Servicios Rest

121

El elemento ltlogin-configgt define coacutemo queremos autentificar nuestro despliegueEl subelemento ltauth-methodgt puede tomar los valores BASIC DIGEST orCLIENT_CERT correspondieacutendose con la autentificacioacuten Basic Digest y ClientCertificate respectivamenteEl valor de la etiqueta ltrealm-namegt es el que se mostraraacute como valor del atributorealm de la cabecera WWW-Autenticate si intentamos acceder al recurso sin incluirnuestras credenciales en la peticioacutenEl elemento ltlogin-configgt realmente NO activa la autentificacioacuten Por defectocualquier cliente puede acceder a cualquier URL proporcionada por nuestra aplicacioacutenweb sin restricciones Para forzar la autentificacioacuten debemos especificar el patroacuten URLque queremos asegurar (elemento lturl-patterngt )El elemento lthttp-methodgt nos indica que solamente queremos asegurar laspeticiones POST sobre esta URL Si no incluimos el elemento lthttp-methodgt todoslos meacutetodos HTTP seraacuten seguros En este ejemplo solamente queremos asegurar losmeacutetodos POST dirigidos a la URL restresources

Autorizacioacuten en JAX-RS

Mientras que la autentificacioacuten hacer referencia a establecer y verificar la identidad del usuariola autorizacioacuten tiene que ver con los permisos iquestEl usuario X estaacute autorizado para acceder aun determinado recurso REST

JAX-RS se basa en las especificaciones Java EE y de servlets para definir la forma de autorizara los usuarios En Java EE la autorizacioacuten se realiza asociando uno o maacutes roles con un usuariodado y a continuacioacuten asignando permisos basados en dicho rol Ejemplos de roles puedenser administrador empleado Cada rol tiene asignando unos permisos de acceso adeterminados recursos por lo que asignaremos los permisos utilizando cada uno de los roles

Para poder realizar la autorizacioacuten tendremos que incluir determinadas etiquetas en el ficherode configuracioacuten webxml (tal y como ya habeacuteis visto en la asignatura de ComponentesWeb) Veaacutemoslo con un ejemplo (en el que tambieacuten incluiremos autentificacioacuten)

Volvamos a nuestra aplicacioacuten de venta de productos por internet En esta aplicacioacutenes posible crear nuevos clientes enviando la informacioacuten en formato XML a un recursoJAX-RS localizado por la anotacioacuten Path(clientes) El servicio REST esdesplegado y escaneado por la clase Application anotada con ApplicationPath(servicios) de forma que la URI completa es serviciosclientes Queremosproporcionar seguridad a nuestro servicio de clientes de forma que solamente losadministradores puedan crear nuevos clientes Veamos cuaacutel seriacutea el contenido del ficherowebxml

ltxml version=10gtltweb-appgt ltsecurity-constraintgt ltweb-resource-collectiongt ltweb-resource-namegtcreacion de clientesltweb-resource-namegt lturl-patterngtserviciosclienteslturl-patterngt lthttp-methodgtPOSTlthttp-methodgt ltweb-resource-collectiongt

ltauth-constraintgt ltrole-namegtadminltrole-namegt ltauth-constraintgt ltsecurity-constraintgt

Servicios Rest

122

ltlogin-configgt ltauth-methodgtBASICltauth-methodgt ltrealm-namegtApplicationRealmltrealm-namegt ltlogin-configgt

ltsecurity-rolegt ltrole-namegtadminltrole-namegt ltsecurity-rolegtltweb-appgt

Especificamos queacute roles tienen permiso para acceder mediante POST a la URLservicescustomers Para ello utilizamos el elemento ltauth-constraintgtdentro de ltsecurity-constraintgt Este elemento tiene uno o maacutes subelementosltrole-namegt que definen queacute roles tienen permisos de acceso definidos porltsecurity-constraintgt En nuestro ejemplo estamos dando al rol adminpermisos para acceder a la URL servicescustomers con el meacutetodo POST Si ensu lugar indicamos un ltrole-namegt con el valor cualquier usuario podriacutea accedera dicha URL En otras palabras un ltrole-namegt con el valor significa que cualquierusuario que sea capaz de autentificarse puede acceder al recursoPara cada ltrole-namegt que usemos en nuestras declaraciones ltauth-constraintsgt debemos definir el correspondiente ltsecurity-rolegt en eldescriptor de despliegue

Una limitacioacuten cuando estamos declarando las ltsecurity-contraintsgt para los recursosJAX-RS es que el elemento lturl-patterngt solamente soporta el uso de en el patroacutenurl especificado Por ejemplo rest txt

Encriptacioacuten

Por defecto la especificacioacuten de servlets no requiere un acceso a traveacutes de HTTPSSi queremos forzar un acceso HTTPS podemos especificar un elemento ltuser-data-constraintgt como parte de nuestra definicioacuten de restricciones de seguridad ( ltsecurity-constraintgt ) Vamos a modificar nuestro ejemplo anterior para forzar un acceso a traveacutesde HTTPS

ltweb-appgt ltsecurity-constraintgt

ltweb-resource-collectiongt ltweb-resource-namegtcreacion de clientesltweb-resource-namegt lturl-patterngtserviciosclienteslturl-patterngt lthttp-methodgtPOSTlthttp-methodgt ltweb-resource-collectiongt

ltauth-constraintgt ltrole-namegtadminltrole-namegt ltauth-constraintgt

ltuser-data-constraintgt

lttransport-guaranteegtCONFIDENTIALlttransport-guaranteegt ltuser-data-constraintgt ltsecurity-constraintgt

Servicios Rest

123

ltweb-appgt

Todo lo que tenemos que hacer es declarar un elemento lttransport-guaranteegtdentro de ltuser-data-constraintgt con el valor CONFIDENTIAL Si un usuariointenta acceder a una URL con el patroacuten especificado a traveacutes de HTTP seraacute redirigidoa una URL basada en HTTPS

Anotaciones JAX-RS para autorizacioacuten

Java EE define un conjunto de anotaciones para definir metadatos de autorizacioacuten Laespecificacioacuten JAX-RS sugiere aunque no es obligatorio que las implementaciones pordiferentes vendedores den soporte a dichas anotaciones Eacutestas se encuentran en elpaquete javaxannotationsecurity y son RolesAllowed DenyAll PermitAll yRunAs

La anotacioacuten RolesAllowed define los roles permitidos para ejecutar una determinadaoperacioacuten Si anotamos una clase JAX-RS define el acceso para todas las operacionesHTTP definidas en la clase JAX-RS Si anotamos un meacutetodo JAX-RS la restriccioacuten se aplicasolamente al meacutetodo que se estaacute anotando

La anotacioacuten PermitAll especifica que cualquier usuario autentificado puede invocar anuestras operaciones Al igual que RolesAllowed esta anotacioacuten puede usarse en la clasepara definir el comportamiento por defecto de toda la clase o podemos usarla en cada unode los meacutetodos Veamos un ejemplo

Path(clientes)

RolesAllowed(ADMIN CLIENTE) public class ClienteResource

GET Path(id) Produces(applicationxml) public Cliente getClienter(PathParam(id) int id)

RolesAllowed(ADMIN) POST Consumes(applicationxml) public void crearCliente(Customer cust)

PermitAll GET Produces(applicationxml) public Customer[] getClientes()

Por defecto solamente los usuarios con rol ADMIN y CLIENTE pueden ejecutar losmeacutetodos HTTP definidos en la clase ClienteResourceSobreescribimos el comportamiento por defecto Para el meacutetodo crearCliente()solamente permitimos peticiones de usuarios con rol ADMINSobreescribimos el comportamiento por defecto Para el meacutetodo getClientes() deforma que cualquier usuario autentificado puede acceder a esta operacioacuten a traveacutes de laURI correspondiente con el meacutetodo GET

Servicios Rest

124

La ventaja de utilizar anotaciones es que nos permite una mayor flexibilidad que el usodel fichero de configuracioacuten webxml pudiendo definir diferentes autorizaciones a nivel demeacutetodo

Seguridad programada

Hemos visto como utilizar una seguridad declarativa es decir basaacutendonos en meta-datosdefinidos estaacuteticamente antes de que la aplicacioacuten se ejecute JAX-RS proporciona unaforma de obtener informacioacuten de seguridad que nos permite implementar seguridad de formaprogramada en nuestras aplicaciones

Podemos utilizar la interfaz javaxwsrscoreSecurityContext para determinar laidentidad del usuario que realiza la invocacioacuten al meacutetodo proporcionando sus credencialesTambieacuten podemos comprobar si el usuario pertenece o no a un determinado rol Esto nospermite implementar seguridad de forma programada en nuestras aplicaciones

public interface SecurityContext public Principal getUserPrincipal() public boolean isUserInRole(String role) public boolean isSecure() public String getAuthenticationScheme()

El meacutetodo getUserPrincipal() devuelve un objeto de tipojavaxsecurityPrincipal que representa al usuario que actualmente estaacute realizandola peticioacuten HTTP

El meacutetodo isUserInRole() nos permite determinar si el usuario que realiza la llamadaactual pertenece a un determinado rol

El meacutetodo isSecure() devuelve cierto si la peticioacuten actual es una conexioacuten segura

El meacutetodo getAuthenticationScheme() nos indica queacute mecanismo de autentificacioacuten seha utilizado para asegurar la peticioacuten (valores tiacutepicos devueltos por el meacutetodo son BASIC DIGEST CLIENT_CERT y FORM )

Podemos acceder a una instancia de SecurityContext inyectaacutendola en un campo meacutetodosetter o un paraacutemetro de un recurso utilizando la anotacioacuten Context Veamos un ejemploSupongamos que queremos obtener un fichero de log con todos los accesos a nuestra basede datos de clientes hechas por usuarios que no son administradores

Path(clientes)public class CustomerService GET Produces(applicationxml) public Cliente[] getClientes(Context SecurityContext sec) if (secisSecure() ampamp secisUserInRole(ADMIN)) loggerlog(secgetUserPrincipal() + ha accedido a la base de datos de clientes)

Servicios Rest

125

En este ejemplo inyectamos una instancia de SecurityContext como un paraacutemetro delmeacutetodo getClientes() Utilizamos el meacutetodo SecurityContextisSecure() paradeterminar si se trata de una peticioacuten realizada a traveacutes de un canal seguro (como HTTPS) Acontinuacioacuten utilizamos el meacutetodo SecurityContextisUserInRole() para determinarsi el usuario que realiza la llamada tiene el rol ADMIN o no Finalmente imprimimos el resultadoen nuestro fichero de logs

Con la introduccioacuten del API de filtros en JAX-RS 20 podemos implementar la interfazSecurityContext y sobreescribir la peticioacuten actual sobre SecurityContext utilizandoel meacutetodo ContainerRequestContextsetSecurityContext() Lo interesante deesto es que podemos implementar nuestros propios protocolos de seguridad Por ejemplo

import javaxwsrscontainerContainerRequestContextimport javaxwsrscontainerContainerRequestFilterimport javaxwsrscontainerPreMatchingimport javaxwsrscoreSecurityContextimport javaxwsrscoreHttpHeaders

PreMatchingpublic class CustomAuth implements ContainerRequestFilter protected MyCustomerProtocolHandler customProtocol =

public void filter(ContainerRequestContext requestContext) throws IOException String authHeader = requestgetHeaderString(HttpHeadersAUTHORIZATION) SecurityContext newSecurityContext = customProtocolvalidate(authHeader) requestContextsetSecurityContext(authHeader)

Este filtro no muestra todos los detalles pero siacute la idea Extrae la cabecera Authorizationde la peticioacuten y la pasa a nuestro propio servicio customerProtocol Eacuteste devuelve unaimplementacioacuten de SecurityContext Finalmente sobreescribimos el SecurityContext pordefecto utilizando la nueva implementacioacuten

No vamos a explicar el API de filtros de JAS-RS 20 Como ya habeacuteis visto en la asignaturade Componentes Web los filtros son objetos que se interponen entre el procesamiento delas peticiones tanto del servidor como del cliente

El filtro mostrado en el ejemplo es un filtro de peticioacuten en la parte del servidor Este tipo defiltros se ejecuta antes de que se invoque a un meacutetodo JAX-RS

Servicios Rest

126

45 Ejercicios

Para los ejercicios de esta sesioacuten proporcionamos el MOacuteDULO s4-foroAvanzado quetendreacuteis que usar como plantilla para realizar las tareas planteadas

El proyecto estaacute estructurado loacutegicamente en los siguientes paquetes

bull orgexpertojavanegocio

bull orgexpertojavarest

A su vez cada uno de ellos contiene los subpaquetes api y modelo con las clasesrelacionadas con los servicios proporcionados y los datos utilizados por los serviciosrespectivamente

El API rest implementado es el siguiente

bull Recurso UsuariosResourcejava

GET usuarios proporciona un listado con los usuarios del foro

bull Subrecurso UsuarioResourcejava

GET usuarioslogin proporciona informacioacuten sobre el usuario cuyo login es login

PUT usuarioslogin actualiza los datos de un usuario

DELETE usuarioslogin borra los datos de un usuario

GET usuariosloginmensajes obtiene un listado de los mensajes de un usuario

bull Recurso MensajesResourcejava

GET mensajes proporciona un listado con los mensajes del foro

POST mensajes antildeade un mensaje nuevo en el foro

GET mensajesid proporciona informacioacuten sobre el mensaje cuyo id es id

PUT mensajesid modifica un mensaje

DELETE mensajesid borra un mensaje

Una vez desplegada la aplicacioacuten podeacuteis antildeadir datos a la base de datos del foro utilizandolos datos del fichero srcmainresourcesforosql Para ello simplemente tendreacuteis que invocarla goal Maven correspondiente desde la ventana Maven Projects gt s4-foroAvanzado gt Pluginsgt sql gt sqlexecute

En el directorio srcmainresources teneacuteis un fichero de texto ( instruccionestxt )con informacioacuten adicional sobre la implementacioacuten proporcionada

A partir de las plantillas se pide

Uso de Hateoas (1 puntos)

Vamos a antildeadir a los servicios enlaces a las operaciones que podemos realizar con cadarecurso siguiendo el estilo Hateoas

a Para los usuarios

Servicios Rest

127

bull En el listado de usuarios antildeadir a cada usuario un enlace con relacioacuten self que apuntea la direccioacuten a la que estaacute mapeado el usuario individual

bull En la operacioacuten de obtencioacuten de un usuario individual incluir los enlaces para ver elpropio usuario (self) modificarlo (usuariomodificar) borrarlo (usuarioborrar) o ver losmensajes que envioacute el usuario (usuariomensajes)

b Para los mensajes

bull En el listado de mensajes antildeadir a cada mensaje un enlace con relacioacuten self queapunte a la direccioacuten a la que estaacute mapeado el mensaje individual

bull En la operacioacuten de obtencioacuten de un mensaje individual incluir los enlaces para ver elpropio mensaje (self) modificarlo (mensajemodificar) borrarlo (mensajeborrar) o verlos datos del usuario que envioacute el mensaje (mensajeusuario)

Utiliza postman para comprobar las modificaciones realizadas

Ejercicio seguridad (1 punto)

Vamos ahora a restringir el acceso al servicio para que soacutelo usuarios registrados puedanrealizar modificaciones Se pide

a Antildeadir al usuario pepe en el ApplicationRealm de wildfly con la contrasentildea pepe yperteneciente al grupo (rol) registrado

b Configurar mediante seguridad declarativa para que las operaciones de modificacioacuten(POST PUT y DELETE) soacutelo la puedan realizar los usuarios con rol registrado Utilizarautentificacioacuten de tipo BASIC

c Ahora vamos a hacer que la modificacioacuten o borrado de usuarios soacutelo puedarealizarlas el mismo usuario que va a modificarse o borrarse Para ello utilizaremosseguridad programada En el caso de que el usuario que va a realizar lamodificacioacuten o borrado quiera borrarmodificar otros usuarios lanzaremos la excepcioacutenWebApplicationException(StatusFORBIDDEN)

d Vamos a hacer lo mismo con los mensajes Soacutelo podraacute modificar y borrar mensajes elmismo usuario que los creoacute y al publicar un nuevo mensaje forzaremos que el login delmensaje sea el del usuario que hay autentificado en el sistema

Utiliza postman para comprobar las modificaciones realizadas

Servicios Rest

128

5 Api cliente Procesamiento JSON y Pruebas

Hasta ahora hemos hablado sobre la creacioacuten de servicios web RESTful y hemos probadonuestros servicios utilizando el cliente que nos proporciona IntelliJ curl o Postman pararealizar peticiones y observar las respuestas JAX-RS 20 proporciona un API cliente parafacilitar la programacioacuten de clientes REST que presentaremos en esta sesioacuten

En sesiones anteriores hemos trabajado con representaciones de texto y xmlfundamentalmente Aquiacute hablaremos con maacutes detalle de JSON que constituye otra forma derepresentar los datos de las peticiones y respuestas de servicios REST muy extendida

Finalmente veremos coacutemo implementar pruebas sobre nuestro servicio utilizando el APIcliente y el framework junit

51 API cliente Visioacuten general

La especificacioacuten JAX-RS 20 incorpora un API cliente HTTP que facilita enormemente laimplementacioacuten de nuestros clientes RESTful y constituye una clara alternativa al uso declases Java como javanetURL libreriacuteas externas (como la de Apache) u otras solucionespropietarias

El API cliente estaacute contenido en el paquete javaxwsrsclient y estaacute disentildeado paraque se pueda utilizar de forma fluida (fluent) Esto significa como ya hemos visto que loutilizaremos encadenando una sucesioacuten de llamadas a meacutetodos del API permitieacutendonos asiacuteescribir menos liacuteneas de coacutedigo Baacutesicamente estaacute formado por tres clases principales ClientWebTarget y Response (ya hemos hablado de esta uacuteltima en sesiones anteriores)

Para acceder a un recurso REST mediante el API cliente es necesario seguir los siguientespasos

1 Obtener una instancia de la interfaz Client

2 Configurar la instancia Client a traveacutes de un target (instancia de WebTarget )

3 Crear una peticioacuten basada en el target anterior

4 Invocar la peticioacuten

Vamos a mostrar un coacutedigo ejemplo para ilustrar los pasos anteriores En este caso vamos ainvocar peticiones POST y GET sobre una URL (target) para crear y posteriormente consultarun objeto Cliente que representaremos en formato XML

Client client = ClientBuildernewClient()

WebTarget target =

clienttarget(httpexpertojavaorgclientes)

Response response = target

request()

post(Entityxml(new Cliente(Alvaro Gomez)))

responseclose()

Servicios Rest

129

Cliente cliente = targetqueryParam(nombre Alvaro Gomez) request()

get(Clienteclass) clientclose()

Obtenemos una instancia javaxwsrsclientClientCreamos un WebTargetCreamos la peticioacutenRealizamos una invocacioacuten POSTCerramos (liberamos) el input stream para esta respuesta (en el caso de que esteacutedisponible y abierto) Es una operacioacuten idempotente es decir podemos invocarlamuacuteltiples veces con el mismo efectoA partir de un WebTarget establecemos los valores de los queryParams de la URIde la peticioacuten creamos la peticioacuten y realizamos una invocacioacuten GET

A continuacioacuten explicaremos con detalle los pasos a seguir para implementar un clienteutilizando el API de JAX-RS

Obtenemos una instancia Client

La interfaz javaxwsrsclientClient es el principal punto de entrada del API ClienteDicha interfaz define las acciones e infraestructura necesarias requeridas por un cliente RESTpara consumir un servicio web RESTful Los objetos Client se crean a partir de la claseClientBuilder

Clase ClientBuilder utilizada para crear objetos Client

public abstract class ClientBuilder implements ConfigurableltClientBuildergt public static Client newClient() public static Client newClient(final Configuration configuration)

public static ClientBuilder newBuilder()

public abstract ClientBuilder sslContext(final SSLContext sslContext) public abstract ClientBuilder keyStore(final KeyStore keyStore final char[] password) public ClientBuilder keyStore(final KeyStore keyStore final String password) public abstract ClientBuilder trustStore(final KeyStore trustStore) public abstract ClientBuilder hostnameVerifier(final HostnameVerifier verifier)

public abstract Client build()

La forma maacutes sencilla de crear un objeto Client es medianteClientBuildernewClient() Este meacutetodo proporciona una instancia pre-inicializada detipo Client lista para ser usada La clase ClientBuilder nos proporciona meacutetodos adicionalescon los que podremos configurar diferentes propiedades del objeto

Veamos un ejemplo de uso de ClientBuildernewBuilder() utilizando ademaacutes algunode los meacutetodos proporcionados para configurar nuestra instancia de tipo Client que vamos

Servicios Rest

130

a crear Los meacutetodos register() y property() son meacutetodos de la interfaz Configurable(y que son implementados por ClientBuilder)

Ejemplo de uso de ClientBuilder

Client cliente = ClientBuildernewBuilder()

property(connectiontimeout 100)

sslContext(sslContext)

register(JacksonJsonProviderclass)

build()

Creamos un ClientBuilder invocando al meacutetodo estaacuteticoClientBuildernewBuilder()Asignamos una propiedad especiacutefica de la implementacioacuten concreta de JAX-RS queestemos utilizando que controla el timeout de las conexiones de los socketsEspecificamos el sslContext que queremos utilizar para gestionar las conexiones HTTPRegistramos a traveacutes del meacutetodo register() (de la interfaz Configurable) una claseanotada con Provider Dicha clase conoce coacutemo serializar objetos Java a JSONy viceversaFinalmente realizamos una llamada a build() para crear la instancia Client

Las instancias de Client gestionan conexiones con el cliente utilizando sockets y sonobjetos bastante pesados Se deberiacutean reutilizar las instancias de esta interfaz en la medida delo posible ya que la inicializacioacuten y destruccioacuten de dichas instancias consume mucho tiempoPor lo tanto por razones de rendimiento debemos limitar el nuacutemero de instancias Clienten nuestra aplicacioacuten

Client client = ClientBuildernewClient()

clientclose()

Obtenemos una instancia de tipo Client invocando al meacutetodoClientBuildernewClient()Utilizamos el meacutetodo close() para cerrar la instancia Client despueacutes de realizartodas las invocaciones sobre el target del recurso De esta forma cerramos la conexioacutende forma que se liberan sus recursos y ya no podremos seguir usaacutendola

Recuerda siempre invocar el meacutetodo close() sobre nuestros objetosClient despueacutes de que hayamos realizado todas las invocacionessobre el target dellos recursos REST A menudo los objetos Clientreutilizan conexiones por razones de rendimiento Si no los cerramosdespueacutes de utilizarlos estaremos desaprovechando recursos del sistemamuy valiosos Cerrar la conexioacuten implica cerrar el socket

Igualmente si el resultado de una invocacioacuten a un servicio rest es unainstancia de Response debemos invocar el meacutetodo close() sobredichos objetos Response para liberar la conexioacuten Liberar una conexionsignifica permitir que eacutesta esteacute disponible para otro uso por una instanciaClient Liberar la conexioacuten no implica cerrar el socket

La interfaz Client es una sub-interfaz de Configurable Esto nos permitiraacute utililizarlos meacutetodos property() y register() para cambiar la configuracioacuten y registrarcomponentes en la parte del cliente en tiempo de ejecucioacuten

Servicios Rest

131

Interfaz Client (es una subinterfaz de Configurable)

public interface Client extends ConfigurableltClientgt

public void close()

public WebTarget target(String uri) public WebTarget target(URI uri) public WebTarget target(UriBuilder uriBuilder) public WebTarget target(Link link)

Sin embargo el principal propoacutesito de Client es crear instancias de WebTarget comoveremos a continuacioacuten

Configuramos el target del cliente (URI)

La interfaz javaxwsrsclientWebTarget representa la URI especiacutefica quequeremos invocar para acceder a un recurso REST particular

Interfaz WebTarget (es una subinterfaz de Configurable)

public interface WebTarget extends ConfigurableltWebTargetgt

public URI getUri() public UriBuilder getUriBuilder()

public WebTarget path(String path) public WebTarget resolveTemplate(String name Object value) public WebTarget resolveTemplates(MapltString Objectgt templateValues) public WebTarget matrixParam(String name Object values) public WebTarget queryParam(String name Object values)

La interfaz WebTarget tiene meacutetodos para extender la URI inicial que hayamosconstruido Podemos antildeadir por ejemplo segmentos de path o paraacutemetros deconsulta invocando a los meacutetodos WebTargetpath() o WebTargetqueryParam() respectivamente Si la instancia de WebTarget contiene plantillas de paraacutemetros losmeacutetodos WebTargetresolveTemplate() pueden asignar valores a las variablescorrespondientes Por ejemplo

Ejemplo para crear la URI httpejemplocomclientes123verboso=true

WebTarget target = client

target(httpejemplocomclientesid)

resolveTemplate(id 123)

queryParam(verboso true)

Servicios Rest

132

Inicializamos un WebTarget con una URI que contiene una plantilla con un paraacutemetroid El objeto client es una instancia de la clase `ClientEl meacutetodo resolveTemplate() rellena la expresioacuten id con el valor 123Finalmente antildeadimos a la URI un paraacutemetro de consulta verboso=true

Las instancias de WebTarget son inmutables con respecto a la URI que contienen Estosignifica que los meacutetodos para especificar segmentos de path adicionales y paraacutemetrosdevuelven una nueva instancia de WebTarget Sin embargo las instancias de WebTargetson mutables respecto a su configuracioacuten Por lo tanto la configuracioacuten de objetosWebTarget no crea nuevas instancias

Veamos otro ejemplo

WebTarget base = clientetarget(httpexpertojavaorg)

WebTarget clienteURI = basepath(cliente)

clienteURIregister(MyProviderclass)

base es una instancia de WebTarget con el valor de URI httpexertojavaorgclienteURI es una instancia de WebTarget con el valor de URI httpexertojavaorgclienteConfiguramos clienteURI registrando la clase MyProvider

En este ejemplo creamos dos instancias de WebTarget La instancia clienteURI heredala configuracioacuten de base y posteriormente modificamos la configuramos registrando unaclase Provider Los cambios sobre la configuracioacuten de clienteURI no afectan a laconfiguracioacuten de base ni tampoco se crea una nueva instancia de WebTarget

Los beneficios del uso de WebTarget se hacen evidentes cuando construimos URIscomplejas por ejemplo cuando extendemos nuestra URI base con segmentos de pathadicionales o plantillas El siguiente ejemplo ilustra estas situaciones

WebTarget base = clientetarget(httpexpertojavaorg)

WebTarget saludo = basepath(hola)path(quien) Response res = saludoresolveTemplate(quien mundo)request()get()

base representa la URI httpexpertojavaorgsaludo representa la URI httpexpertojavaholaquien

En el siguiente ejemplo utilizamos una URI base y a partir de ella construimos otras URIs querepresentan servicios diferentes proporcionados por nuestro recurso REST

Client cli = ClientBuildernewClient()WebTarget base = clienttarget(httpejemplowebapi)

WebTarget lectura = basepath(leer)

WebTarget escritura = basepath(escribir)

lectura representa la uri httpejemplowebapileerescritura representa la uri httpejemplowebapiescribir

El meacutetodo WebTargetpath() crea una nueva instancia de WebTarget antildeadiendo a laURI actual el segmento de ruta que se pasa como paraacutemetro

Servicios Rest

133

Construimos y Realizamos la peticioacuten

Una vez que hemos creado y configurado convenientemente el WebTarget que representala URI que queremos invocar tenemos que construir la peticioacuten y finalmente realizarla

Para construir la peticioacuten podemos Utilizar uno de los meacutetodos WebTargetrequest() que mostramos a continuacioacuten

Interfaz WebTarget meacutetodos para comenzar a construir la peticioacuten

public interface WebTarget extends ConfigurableltWebTargetgt public InvocationBuilder request() public InvocationBuilder request(String acceptedResponseTypes) public InvocationBuilder request(MediaType acceptedResponseTypes)

Normalmente invocaremos WebTargetrequest() pasando como paraacutemetro el mediatype aceptado como respuesta en forma de String o utilizando una de las constantes dejavaxwsrscoreMediaType Los meacutetodos WebTargetrequest() devuelven unainstancia de InvocationBuilder una interfaz que proporciona meacutetodos para prepararla peticioacuten del cliente y tambieacuten para invocarla

La interface InvocationBuilder Contiene un conjunto de meacutetodos que nos permitenconstruir diferentes tipos de cabeceras de peticiones Asiacute por ejemplo proporciona variosmeacutetodos acceptXXX() para indicar diferentes tipos MIME lenguajes o encodingaceptados Tambieacuten proporciona meacutetodos cookie() para especificar cookies para enviaral servidor Finalmente proporciona meacutetodos header() para especificar diferentes valoresde cabeceras

Ejemplos de uso de esta interfaz para construir la peticioacuten

Client cli = ClientBuildernewClient()cliinvocation(LinkvalueOf(httpejemplorest))accept(applicationjson)get()si no utilizamos el meacutetodo invocation podemos hacerlo asiacuteclitarget(httpejemplorest)request(applicationjson)get()

Client cliente = ClientBuildernewClient()WebTarget miRecurso = clienttarget(httpejemplowebapimensaje) request(MediaTypeTEXT_PLAIN)

El uso de una constante MediaType es equivalente a utilizar el String que define el tipoMIME

InvocationBuilder builder = miRecursorequest(textplain)

Hemos visto que WebTarget implementa meacutetodos request() cuyosparaacutemetros especifican el tipo MIME de la cabecera Accept de la peticioacutenEl coacutedigo puede resultar maacutes legible si usamos en su lugar el meacutetodo

Servicios Rest

134

InvocationBuilderaccept() En cualquier caso es una cuestioacuten de gustospersonales

Despueacutes de determinar el media type de la respuesta invocamos la peticioacuten realizando unallamada a uno de los meacutetodos de la instancia de InvocationBuilder que se correspondecon el tipo de peticioacuten HTTP esperado por el recurso REST al que va dirigido dicha peticioacutenEstos meacutetodos son

bull get()

bull post()

bull delete()

bull put()

bull head()

bull options()

La interfaz InvocationBuilder es una subinterfaz de la interfaz SyncInvoker y es la queespecifica los meacutetodos anteriores (get post hellip) para realizar peticiones siacutencronas es decirque hasta que no nos conteste el servidor no podremos continuar procesando el coacutedigo enla parte del cliente

Las peticiones GET tienen los siguientes prototipos

Interface SyncInvoker peticiones GET siacutencronas

public interface SyncInvoker ltTgt T get(ClassltTgt responseType) ltTgt T get(GenericTypeltTgt responseType) Response get()

Los primeros dos meacutetodos geneacutericos convertiraacuten una respuesta HTTP con eacutexito a tipos Javaespeciacuteficos indicados como paraacutemetros del meacutetodo El tercero devuelve una instancia de tipoResponse Por ejemplo

Ejemplos de peticiones GET utilizando el API cliente jasx-rx 20

Client cli = ClientBuildernewClient()

peticioacuten get que devuelve una instancia de ClienteCliente cliRespuesta = clitarget(httpejemploclientes123) request(applicationjson)

get(Clienteclass)

peticioacuten get que devuelve una lista de objetos ClienteListltClientegt cliRespuesta2 = clitarget(httpejemploclientes) request(applicationxml)

get(new GenericTypeltListltClientegtgt() )

peticioacuten get que devuelve un objeto de tipo ResponseResponse respuesta = clitarget(httpejemploclientes245)

Servicios Rest

135

request(applicationjson)

get() try if (respuestagetStatus() == 200) Cliente cliRespuesta =

respuestareadEntity(Clienteclass) finally respuestaclose()

En la primera peticioacuten queremos que el servidor nos devuelva la respuesta en formatoJSON y posteriormente la convertiremos en el tipo Cliente utilizando un de loscomponentes MessageBodyReader registradosEn la segunda peticioacuten utilizamos la clase javaxwsrscoreGenericType parainformar al correspondiente MessageBodyReader del tipo de objetos de nuestra ListaPara ello creamos una clase anoacutenima a la que le pasamos como paraacutemetro el tipogeneacuterico que queremos obtenerEn la tercera peticioacuten obtenemos una instancia de Response a partir de la cual podemosobtener el cuerpo del mensaje de respuesta del servidorEl meacutetodo readEntity() asocia el tipo Java solicitado (en este caso el tipo java Cliente) yel contenido de la respuesta recibida con el correspondiente proveedor de entidades (detipo MessageBodyReader) para obtener dicho tipo Java a partir de la respuesta HTTPrecibida En sesiones anteriores hemos utilizado la clase Response desde el servicioREST para construir la respuesta que se enviacutea al cliente

Recordemos algunos de los meacutetodos que podemos utilizar desde el cliente para analizar larespuesta que hemos obtenido

Meacutetodos de la clase Response que utilizaremos desde el cliente

public abstract class Response

public abstract Object getEntity()

public abstract int getStatus()

public abstract ResponseStatusType getStatusInfo()

public abstract MultivaluedMapltString Objectgt getMetadata()

public abstract URI getLocation()

public abstract MediaType getMediaType()

public MultivaluedMapltStringObjectgt getHeaders()

public abstract ltTgt T readEntity(ClassltTgt entityType)

public abstract ltTgt T readEntity(GenericTypeltTgt entityType)

public abstract void close()

El meacutetodo getEntity() devuelve el objeto Java correspondiente al cuerpo delmensaje HTTPEl meacutetodo getStatus() devuelve el coacutedigo de respuesta HTTPEl meacutetodo getStatusInfo() devuelve la informacioacuten de estado asociada con larespuestaEl meacutetodo getMetadata() devuelve una instancia de tipo MultivaluedMap con lascabeceras de la respuesta

Servicios Rest

136

El meacutetodo getLocation() devuelve la URI de la cabecera Location de la respuestaEl meacutetodo getMediaType() devuelve el mediaType del cuerpo de la respuestaEl meacutetodo getHeaders() devuelve las cabeceras de respuesta con sus valorescorrespondientesEl meacutetodo readEntity() devuelve la entidad del cuerpo del mensaje utilizando unMessageBodyReader que soporte el mapeado del inputStream de la entidad a la claseJava especificada como paraacutemetroEl meacutetodo readEntity() tambieacuten puede devolver una clase geneacuterica si se disponedel MessageBodyReader correspondienteEl meacutetodo close() cierra el input stream correspondiente a la entidad asociada delcuerpo del mensaje (en el caso de que esteacute disponible y abierto) Tambieacuten liberacualquier otro recurso asociado con la respuesta (como por ejemplo datos posiblementealmacenados en un buffer)

Veamos otro ejemmplo Si el recurso REST espera una peticioacuten HTTP GET invocaremosel meacutetodo InvocationBuilderget() El tipo de retorno del meacutetodo deberiacuteacorresponderse con la entidad devuelta por el recurso REST que atenderaacute la peticioacuten

Client cliente = ClientBuildernewClient()WebTarget miRecurso = clientetarget(httpejemplowebapilectura)String respuesta = miRecursorequest(MediaTypeTEXT_PLAIN) get(Stringclass)

O tambieacuten podriacuteamos codificarlo como

Client cliente = ClientBuildernewClient()String respuesta = cliente target(httpejemplowebapilectura) request(MediaTypeTEXT_PLAIN) get(Stringclass)

Si el tipo de retorno de la peticioacuten GET es una coleccioacuten usaremosjavaxwsrscoreGenericTypeltTgt como paraacutemetro del meacutetodo en donde T es eltipo de la coleccioacuten

ListltPedidoAlmacengt pedidos = client target(httpejemplowebapilectura) path(pedidos) request(MediaTypeAPPLICATION_XML) get(new GenericTypeltListltPedidoAlmacengtgt() )

Si el recurso REST destinatario de la peticioacuten espera una peticioacuten de tipo HTTP POSTinvocaremos el meacutetodo InvocationBuilderpost()

Las peticiones POST tienen los siguientes prototipos

Interface SyncInvoker peticiones POST siacutencronas

public interface SyncInvoker ltTgt T post(Entityltgt entity ClassltTgt responseType)

Servicios Rest

137

ltTgt T post(Entityltgt entity GenericTypeltTgt responseType) Response post(Entityltgt entity)

Los primeros dos meacutetodos geneacutericos enviacutean una entidad (clase java + tipo MIME asociado)indicada como primer paraacutemetro del meacutetodo y como segundo paraacutemetro se indica el tipo javaal que se convertiraacute la respuesta recibida El tercero enviacutea una entidad y devuelve una instanciade tipo Response Por ejemplo

Veamos un ejemplo de invocacioacuten de peticiones POST

Ejemplo de peticioacuten POST utilizando el API cliente jasx-rx 20

Client cli = ClientBuildernewClient()Pedido pe = new PedidoAlmacen()Pedido peRespuesta = cli target() request() post(Entityentity(new Pedido() applicationjson) Pedidoclass)

En este caso estamos realizando una peticioacuten POST Como payload del mensaje enviamosun objeto Pedido representado en formato json La entidad esperada como respuesta debeser de tipo Pedido

Esto implica que en el lado del servidor el meacutetodo que atiende la peticioacuten Post tendraacute unparaacutemetro de tipo Pedido y se deberaacuten serializar los objetos de tipo Pedido a json ya que esel tipo MIME asociado a esta entidad ( especificado en la cabera Content-Type de la peticioacutenHTTP)

La clase Entity encapsula los objetos Java que queremos enviar con las peticiones GET oPOST No tiene un constructor puacuteblico En su lugar tenemos que invocar uno de sus meacutetodosestaacuteticos

Clase javaxwsrsclientEntity

public final class EntityltTgt

public static ltTgt EntityltTgt entity(T entity String mediaType)

public static ltTgt EntityltTgt entity(T entity MediaType mediaType)

public static ltTgt EntityltTgt xml(final T entity)

public static ltTgt EntityltTgt json(final T entity)

public static ltTgt EntityltTgt text(T entity)

public static EntityltFormgt form(final Form form)

El meacutetodo estaacutetico entity() crea una entidad (clase Java) con un tipo MIME asociado dadopor la cadena de caracteres mediaTypeEl meacutetodo estaacutetico entity() crea una entidad (clase Java) con un tipo MIME indicado enmediaTypeEl meacutetodo xml crea una entidad (clase Java) con el tipo MIME applicationxml

Servicios Rest

138

El meacutetodo json crea una entidad (clase Java) con el tipo MIME applicationjsomEl meacutetodo text crea una entidad (clase Java) con el tipo MIME textplainEl meacutetodo form crea una entidad (clase Java) con el tipo MIME applicationx-www-form-urlencoded

Veamos otro ejemplo de invocacioacuten POST que utiliza la clase Entity

Ejemplo de peticioacuten POST y uso de clase Entity

NumSeguimiento numSeg = client target(httpejemplowebapiescritura)

request(MediaTypeAPPLICATION_XML)

post(Entityxml(pedido) NumeroSeguimientoclass)

Especificamos como paraacutemetro de la peticioacuten request() el tipo MIME que aceptamos enla respuesta (cabecera HTTP Accept)Realizamos una peticioacuten POST El cuerpo del mensaje se crea con la llamadaEntityxml(pedido) El tipo Entity encapsula la entidad del mensaje (tipo JavaPedido) y el tipo MIME asociado (tipo MIME applicationxml)

Veamos un ejemplo en el que enviamos paraacutemetros de un formulario en una peticioacuten POST

Ejemplo de enviacuteo de datos de un formulario en una peticioacuten POST

Form form = new Form()param(nombre Pedro) param(apellido Garcia)Response response = clienttarget(httpejemploclientes) request() post(Entityform(form))responseclose()

La peticioacuten POST del coacutedigo anterior enviacutea los datos del formulario y espera recibir comorespuesta una entidad de tipo Response

El coacutedigo en el lado del servidor seraacute similar a eacuteste

Servicio rest que sirve una peticioacuten POST a partir de datos de un formulario

POSTPath(clientes)Produces(texthtml)public Response crearCliente(FormParam(nombre)String nom FormParam(apellido)String ape) creamos el nuevo cliente return Responseok(RESPONSE_OK)build()

Manejo de excepciones

Veamos queacute ocurre si se produce una excepcioacuten cuando utilizamos una forma de invocacioacutenque automaacuteticamente convierte la respuesta en el tipo especificado Supongamos el siguienteejemplo

Servicios Rest

139

Cliente cli = clienttarget(httptiendacomclientes123) request(applicationjson) get(Clienteclass)

En este escenario el framework del cliente convierte cualquier coacutedigo de error HTTP en unade las excepciones que antildeade JAX-RS 20 (BadRequesException ForbiddenExceptionhellip) yque ya hemos visto Podemos capturar dichas excepciones en nuestro coacutedigo para tratarlasadecuadamente

try Cliente cli = clienttarget(httptiendacomclientes123) request(applicationjson) get(Clienteclass) catch (NotAcceptableException notAcceptable) catch (NotFoundException notFound)

Si el servidor responde con un error HTTP no cubierto por alguna excepcioacutenespeciacutefica JAX-RS entonces se lanza una excepcioacuten de propoacutesito general La claseClientErrorException cubre cualquier coacutedigo de error en la franja del 400 La claseServerErrorException cubre cualquier coacutedigo de error en la franja del 500

Si el servidor enviacutea alguna de los coacutedigos de respuesta HTTP 3xx (clasificados como coacutedigosde la categoriacutea redireccioacuten) el API cliente lanza una RedirectionException a partir de lacual podemos obtener la URL para poder tratar la redireccioacuten nosotros mismos Por ejemplo

WebTarget target = clienttarget(httptiendacomclientes123)boolean redirected = false

Cliente cli = nulldo try cli = targetrequest(applicationjson) get(Clienteclass) catch (RedirectionException redirect) if (redirected) throw redirect redirected = true target = clienttarget(redirectgetLocation()) while (cli == null)

En este ejemplo volvemos a iterar si recibimos un coacutedigo de respuesta 3xx El coacutedigo seasegura de que soacutelo permitimos un coacutedigo de este tipo cambiando el valor de la variableredirect en el bloque en el que capturamos la exceptioacuten A continuacioacuten cambiamos elWebTarget (en el bloque catch ) al valor de la cabecera Location de la respuesta delservidor

Los coacutedigos de estado HTTP 3xx indican que es neceario realizar algunaaccioacuten adicional para que el servidor pueda completar la peticioacuten Laaccioacuten requerida puede llevarse a cabo sin necesidad de interactuar con el

Servicios Rest

140

cliente soacutelo si el meacutetodo utilizado en la segunda peticioacuten es GET o HEAD(ver httpwwww3orgProtocolsrfc2616rfc2616-sec10html)

52 Procesamiento JSON

JSON (JavaScript Object Notation) es un formato para el intercambio de datos basado en textoderivado de Javascript (Javascript disponde de una funcioacuten nativa eval() para convertirstreams JSON en objetos con propiedades que son accesibles sin necesidad de manipularninguna cadena de caracteres)

La especificacioacuten JSR 3539 proporciona un API para el procesamiento de datos JSON(parsing transformacioacuten y consultas)

La gramaacutetica de los objetos JSON es bastante simple Soacutelo se requieren dos estructurasobjetos y arrays Un objeto es un conjunto de pares nombre-valor y un array es una lista devalores JSON define siete tipos de valores string number object array true false y null

El siguiente ejemplo muestra datos JSON para un objeto que contiene pares nombre-valor

Ejemplo formato JSON

nombre John apellidos Smith edad 25 direccion calle 21 2nd Street ciudad New York codPostal 10021 telefonos [ tipo fijo numero 212 555-1234 tipo movil numero 646 555-4567 ]

El objeto anterior tiene cinco pares nombre-valor

bull Los dos primeros son nombre y apellidos con el valor de tipo String

bull El tercero es edad con el valor de tipo number

bull El cuarto es direccion con el valor de tipo object

bull El quinto es telefonos cuyo valor es de tipo array con dos objetos

JSON tiene la siguiente sintaxis

bull Los objetos estaacuten rodeados por llaves sus pares de elementos nombre-valor estaacutenseparados por una coma y el nombre y el valor de cada par estaacuten separados por dospuntos Los nombres en un objeto son de tipo String mientras que sus valores

9 httpsjcporgaboutJavacommunityprocessfinaljsr353indexhtml

Servicios Rest

141

pueden ser cualquiera de los siete tipos que ya hemos indicado incluyendo a otro objetou otro array

bull Los arrays estaacuten rodeados por corchetes [] y sus valores estaacuten separados por una coma Cada valor en un array puede ser de un tipo diferente incluyendo a otro objeto o array

bull Cuando los objetos y arrays contienen otros objetos yo arrays los datos adquieren unaestructura de aacuterbol

Los servicios web RESTful utilizan JSON habitualmente tanto en las peticiones como en lasrespuestas La cabecera HTTP utilizada para indicar que el contenido de una peticioacuten o unarespuesta es JSON es la siguiente

Content-Type applicationjson

La representacioacuten JSON es normalmente maacutes compacta que las representaciones XML debidoa que JSON no tiene etiquetas de cierre A diferencia de XML JSON no tiene un esquemade definicioacuten y validacioacuten de datos ampliamente aceptado

Actualmente las aplicaciones Java utilizan diferentes libreriacuteas para producirconsumir JSONque tienen que incluirse junto con el coacutedigo de la aplicacioacuten incrementando asiacute el tamantildeo delarchivo desplegado El API de Java para procesamiento JSON proporciona un API estaacutendarpara analizar y generar JSON de forma que las aplicaciones que utilicen dicho API sean maacutesligeras y portables

Para generar y parsear datos JSON hay dos modelos de programacioacuten que son similares alos usados para documentos XML

bull El modelo de objetos crea un aacuterbol en memoria que representa los datos JSON

bull El modelo basado en streaming utiliza un parser que lee los datos JSON elemento aelemento (uno cada vez)

Java EE incluye soporte para JSR 353 de forma que el API de java para procesamiento JSONse encuentra en los siguientes paquetes

bull El paquete javaxjson contiene interfaces para leer escribir y construir datos JSONseguacuten el modelo de objetos asiacute como otras utilidades

bull El paquete javaxjsonstream contiene una interfaz para parsear y generar datosJSON para el modelo streaming

Vamos a ver coacutemo producir y consumir datos JSON utilizando cada uno de los modelos

53 Modelo de procesamiento basado en el modelo de objetos

En este caso se crea un aacuterbol en memoria que representa los datos JSON (todos losdatos) Una vez construido el aacuterbol se puede navegar por eacutel analizarlo o modificarlo Estaaproximacioacuten es muy flexible y permite un procesamiento que requiera acceder al contenidocompleto del aacuterbol En contrapartida normalmente es maacutes lento que el modelo de streamingy requiere utilizar maacutes memoria El modelo de objetos genera una salida JSON navegandopor el aacuterbol entero de una vez

El siguiente coacutedigo muestra coacutemo crear un modelo de objetos a partir de datos JSON desdeun fichero de texto

Creacioacuten de un modelos de objetos a partir de datos JSON

Servicios Rest

142

import javaioFileReaderimport javaxjsonJsonimport javaxjsonJsonReaderimport javaxjsonJsonStructureJsonReader reader = JsoncreateReader(new FileReader(datosjsontxt))JsonStructure jsonst = readerread()

El objeto jsonst puede ser de tipo JsonObject o de tipo JsonArray dependiendo delos contenidos del fichero JsonObject y JsonArray son subtipos de JsonStructure Este objeto representa la raiacutez del aacuterbol y puede utilizarse para navegar por el aacuterbol o escribirloen un stream como datos JSON

Vamos a mostrar alguacuten ejemplo en el que utilicemos un StringReader

Objeto JSON con dos pares nombre-valor

jsonReader = JsoncreateReader(new StringReader( + manzanaroja + plaacutetanoamarillo + ))JsonObject json = jsonReaderreadObject()

jsongetString(manzana) jsongetString(plaacutetano)

El meacutetodo getString() devuelve el valor del string para la clave especificadacomo paraacutemetro Pueden utilizarse otros meacutetodos getXXX() para acceder al valorcorrespondiente de la clave en funcioacuten del tipo de dicho objeto

Un array con dos objetos cada uno de ellos con un par nombre-valor puede leerse como

Array con dos objetos

jsonReader = JsoncreateReader(new StringReader([ + manzanarojo + plaacutetanoamarillo + ]))

JsonArray jsonArray = jsonReaderreadArray()

La interfaz JsonArray tambieacuten tiene meacutetodos get para valores de tipo boolean integer y String en el iacutendice especificado (esta interfaz hereda de javautilList)

Creacioacuten de un modelos de objetos desde el coacutedigo de la aplicacioacuten

A continuacioacuten mostramos un ejemplo de coacutedigo para crear un modelo de objetos medianteprogramacioacuten

Ejemplo de creacioacuten de un modelo de objetos JSON desde programacioacuten

import javaxjsonJsonimport javaxjsonJsonObjectJsonObject modelo =

JsoncreateObjectBuilder() add(nombre Duke)

Servicios Rest

143

add(apellidos Java) add(edad 18) add(calle 100 Internet Dr) add(ciudad JavaTown) add(codPostal 12345) add(telefonos

JsoncreateArrayBuilder() add(JsoncreateObjectBuilder() add(tipo casa) add(numero 111-111-1111)) add(JsoncreateObjectBuilder() add(tipo movil) add(numero 222-222-2222))) build()

El tipo JsonObject representa un objeto JSON El meacutetodoJsoncreateObjectBuilder() crea un modelo de objetos en memoria antildeadiendoelementos desde el coacutedigo de nuestra aplicacioacutenEl meacutetodo JsoncreateArrayBuilder() crea un modelo de arrays en memoriaantildeadiendo elementos desde el coacutedigo de nuestra aplicacioacuten

El objeto modelo de tipo JsonObject representa la raiacutez del aacuterbol que es creado anidandollamadas a meacutetodos acuteadd()acute y construyendo el aacuterbol a traveacutes del meacutetodo build()

La estructura JSON generada es la siguiente

Ejemplo formato JSON generado mediante programacioacuten

nombre Duke apellidos Java edad 18 calle 100 Internet Dr ciudad JavaTown codPostal 12345 telefonos [ tipo casa numero 111-111-1111 tipo movil numero 222-222-2222 ]

Navegando por el modelo de objetos

A continuacioacuten mostramos un coacutedigo de ejemplo para navegar por el modelo de objetos

import javaxjsonJsonValueimport javaxjsonJsonObjectimport javaxjsonJsonArrayimport javaxjsonJsonNumberimport javaxjsonJsonStringpublic static void navegarPorElArbol(JsonValue arbol String clave)

Servicios Rest

144

if (clave = null) Systemoutprint(Clave + clave + ) switch(arbolgetValueType()) case OBJECT Systemoutprintln(OBJETO) JsonObject objeto = (JsonObject) arbol for (String nombre objectkeySet()) navegarPorElArbol(objectget(nombre) name) break case ARRAY Systemoutprintln(ARRAY) JsonArray array = (JsonArray) arbol for (JsonValue val array) navegarPorElArbol(val null) break case STRING JsonString st = (JsonString) arbol Systemoutprintln(STRING + stgetString()) break case NUMBER JsonNumber num = (JsonNumber) arbol Systemoutprintln(NUMBER + numtoString()) break case TRUE case FALSE case NULL Systemoutprintln(arbolgetValueType()toString()) break

El meacutetodo navegarPorElArbol() podemos usarlo con el ejemplo anterior de la siguienteforma

navegarPorElArbol(modelo OBJECT)

El meacutetodo navegarPorElArbol() tiene dos argumentos un elemento JSON y una claveLa clave se utiliza para imprimir los pares clave-valor dentro de los objetos Los elementos enel aacuterbol se representan por el tipo JsonValue Si el elemento es un objeto o un array serealiza una nueva llamada a este meacutetodo es invocada para cada elemento contenido en elobjeto o el array Si el elemento es un valor eacuteste se imprime en la salida estaacutendar

El meacutetodo JsonValuegetValueType() identifica el elemento como un objeto un arrayo un valor Para los objetos el meacutetodo JsonObjectkeySet() devuelve un conjunto deStrings que contienene las claves de los objetos y el meacutetodo JsonObjectget(Stringnombre) devuelve el valor del elemento cuya clave es nombre Para los arraysJsonArray implementa la interfaz ListltJsonValuegt Podemos utilizar bucles formejorados con el valor de SetltStringgt devuelto por JsonObjectkeySet() y coninstancias de JsonArray tal y como hemos mostrado en el ejemplo

Escritura de un modelo de objetos en un stream

Los modelos de objetos creados en los ejemplos anteriores pueden escribirse en un streamutilizando la clase JsonWriter de la siguiente forma

Servicios Rest

145

import javaioStringWriterimport javaxjsonJsonWriterStringWriter stWriter = new StringWriter()

JsonWriter jsonWriter = JsoncreateWriter(stWriter)

jsonWriterwriteObject(modelo)

jsonWriterclose()

String datosJson = stWritertoString()Systemoutprintln(datosJson)

El meacutetodo JsoncreateWriter() toma como paraacutemetro un OutputStreamEl meacutetodo JsonWriterwriteObject() escribe el objeto JsonObject en elstreamEl meacutetodo JsonWriterclose() cierra el stream de salida

Modelo de procesamiento basado en streaming

El modelo de streaming utiliza un parser basado en eventos que va leyendo los datos JSONde uno en uno El parser genera eventos y detiene el procesamiento cuando un objeto o arraycomienza o termina cuando encuentra una clave o encuentra un valor Cada elemento puedeser procesado o rechazado por el coacutedigo de la aplicacioacuten y a continuacioacuten el parser continuacuteacon el siguiente evento Esta aproximacioacuten es adecuada para un procesamiento local en elcual el procesamiento de un elemento no requiere informacioacuten del resto de los datos El modelode streaming genera una salida JSON para un determinado stream realizando una llamada auna funcioacuten con un elemento cada vez

A continuacioacuten veamos con ejemplos coacutemo utilizar el API para el modelo de streaming

bull Para leer datos JSON utilizando un parser (JsonParser)

bull Para escribir datos JSON utilizando un generador (JsonGenerator)

Lectura de datos JSON

El API para el modelo streaming es la aproximacioacuten maacutes eficiente para parsear datos JSONutilizando eventos

import javaxjsonJsonimport javaxjsonstreamJsonParserJsonParser parser = JsoncreateParser(new StringReader(datosJson))while (parserhasNext()) JsonParserEvent evento = parsernext() switch(evento) case START_ARRAY case END_ARRAY case START_OBJECT case END_OBJECT case VALUE_FALSE case VALUE_NULL case VALUE_TRUE Systemoutprintln(eventotoString())

Servicios Rest

146

break case KEY_NAME Systemoutprint(eventotoString() + + parsergetString() + - ) break case VALUE_STRING case VALUE_NUMBER Systemoutprintln(eventotoString() + + parsergetString()) break

El ejemplo consta de tres pasos

1 Obtener una instancia de un parser invocando el meacutetodo estaacuteticoJsoncreateParser()

2 Iterar sobre los eventos del parser utilizando los meacutetodos JsonParserhasNext() yJsonParsernext()

3 Realizar un procesamiento local para cada elemento

El ejemplo muestra los diez posibles tipos de eventos del parser El meacutetodoJsonParsernext() avanza al siguiente evento Para los tipos de eventos KEY_NAME VALUE_STRING y VALUE_NUMBER podemos obtener el contenido del elemento invocandoal meacutetodo JsonParsergetString() Para los eventos VALUE_NUMBER podemostambieacuten usar los siguientes meacutetodos

bull JsonParserisIntegralNumber

bull JsonParsergetInt

bull JsonParsergetLong

bull JsonParsergetBigDecimal

El parser genera los eventos START_OBJECT y END_OBJECT para un objeto JSON vaciacuteo

Para un objeto con dos pares nombre-valor

manzajaroja plaacutetanoamarillo

Mostramos los eventos generados

START_OBJECT manzajaKEY_NAMErojaVALUE_STRING plaacutetanoKEY_NAMEamarilloVALUE_STRINGEND_OBJECT

Los eventos generados para un array con dos objetos JSON seriacutean los siguientes

[START_ARRAY START_OBJECT manzajaKEY_NAMErojaVALUE_STRING END_OBJECT

Servicios Rest

147

START_OBJECT plaacutetanoKEY_NAMEamarilloVALUE_STRING END_OBJECT]END_ARRAY

Escritura de datos JSON

El siguiente coacutedigo muestra coacutemo escribir datos JSON en un fichero utilizando el API para elmodelo de streaming

Ejemplo de escritura de datos JSON con el modelo de streaming

FileWriter writer = new FileWriter(testtxt)JsonGenerator gen = JsoncreateGenerator(writer)genwriteStartObject() write(nombre Duke) write(apellidos Java) write(edad 18) write(calle 100 Internet Dr) write(ciudad JavaTown) write(codPostal 12345) writeStartArray(telefonos) writeStartObject() write(tipo casa) write(numero 111-111-1111) writeEnd() writeStartObject() write(tipo movil) write(numero 222-222-2222) writeEnd() writeEnd() writeEnd()genclose()

Este ejemplo obtiene un generador JSON invocando al meacutetodo estaacuteticoJsoncreateGenerator() que toma como paraacutemetro un output stream o un writerstream El ejemplo escribe los datos JSON en el fichero texttxt anidando llamadas a losmeacutetodos write() writeStartArray() writeStartObject() and writeEnd() El meacutetodo JsonGeneratorclose() cierra el output stream o writer stream subyacente

54 Pruebas de servicios REST

Hasta ahora hemos visto varias formas de probar nuestros servicios REST desde liacutenea decomandos con Curl desde IntelliJ con la herramienta Test RESTFul Web Service y desde elnavegador Chrome con Postman (siendo esta uacuteltima la que maacutes hemos utilizado)

Vamos a ver coacutemo implementar tests para nuestros servicios REST utilizando Maven y JUnitPara ello repasaremos algunas cuestiones baacutesicas sobre los ciclos de vida de Maven10

Ciclo de vida de Maven y tests JUnit

Un ciclo de vida en Maven es una secuencia de acciones determinada que defineel proceso de construccioacuten de un proyecto en concreto Como resultado del proceso deconstruccioacuten de un proyecto obtendremos un artefacto (fichero) de un cierto tipo (porejemplo jar war earhellip) Por lo tanto podriacuteamos decir que un ciclo de vida estaacute formado por

10 httpsmavenapacheorgref333maven-corelifecycleshtml

Servicios Rest

148

las acciones necesarias para convertir nuestros archivos fuente que constituyen el proyectoen por ejemplo un jar un warhellip

Maven propone 3 ciclos de vida es decir tres posibles secuencias de acciones que podemosutilizar (y modificar a nuestra conveniencia) para construir nuestro proyecto Dichos ciclos devida son clean site y el denominado default-lifecycle

Cada ciclo de vida estaacute formado por fases Una fase es un concepto abstracto y define el tipode acciones que se deberiacutean llevar a cabo Por ejemplo una fase del ciclo de vida por defectoes compile para referirse a las acciones que nos permiten convertir los ficheros java en losficheros class correspondientes

Cada fase estaacute formada por un conjunto de goals que son las acciones que se llevaraacuten a caboen cada una de las fases Las goals no viven de forma independiente sino que cualquiergoal siempre forma parte de un plugin Maven Podriacuteamos decir que un plugin por lo tantoes una agrupacioacuten loacutegica de una serie de goals relacionadas Por ejemplo el plugin wildflycontiene una serie de goals para desplegar re-desplegar deshacer-el-despliegue arrancarel servidor etc es decir agrupa las acciones que podemos realizar sobre el servidor wildflyUna goal se especifica siempre anteponiendo el nombre del plugin al que pertenece seguidode dos puntos por ejemplo wildflydeploy indica que se trata de la goal deploy que perteneceal plugin wildfly de maven

Pues bien por defecto Maven asocia ciertas goals a las fases de los tres ciclos de vidaCuando se ejecuta una fase de un ciclo de vida por ejemplo mvn package se ejecutan todaslas goals asociadas a todas las fases anteriores a la fase package en orden y finalmentelas goals asociadas a la fase package Por supuesto podemos alterar en cualquier momentoeste comportamiento por defecto incluyendo los plugins y goals correspondientes dentro dela etiqueta ltbuildgt en nuestro fichero de configuracioacuten pomxml

Vamos a implementar tests JUnit Los tests como ya habeacuteis visto en sesiones anterioresen el directorio srctest Algunas normas importantes son que los tests pertenezcan almismo paquete loacutegico al que pertencen las clases Java que estamos probando Por ejemplosi estamos haciendo pruebas sobre las clases del paquete orgexpertojavarest los testsdeberiacutean pertenecer al mismo paquete aunque fiacutesicamente el coacutedigo fuente y sus pruebasestaraacuten separados (el coacutedigo fuente estaraacute en srcmain y los tests en srctest)

Para realizar pruebas sobre nuestros servicios REST necesitamos que el servidor Wilfly esteacuteen marcha Tambieacuten necesitamos empaquetar el coacutedigo en un fichero war y desplegarlo enel servidor todo esto ANTES de ejecutar los tests

Las acciones para arrancar el servidor Wilfly y desplegar nuestra aplicacioacuten en eacutel NO formanparte de las acciones (o goals) incluidas por defecto en el ciclo de vida por defecto de Mavencuando nuestro proyecto tiene que empaquetarse como un war (etiqueta ltpackaginggt denuestro pomxml) Podeacuteis consultar aquiacute11 la lista de goals asociadas a las fases del ciclo devida por defecto de Maven

Por otro lado en el ciclo de vida por defecto se incluye una goal para ejecutar los testsasociada a la fase test Dicha goal es surefiretest El problema es que por defecto la fasetest se ejecuta ANTES de la fase package y por lo tanto antes de empaquetar y desplegarnuestra aplicacioacuten en Wildfly

Por lo tanto tendremos que alterar convenientemente este comportamiento por defectopara que se ejecuten las acciones de nuestro proceso de construccioacuten que necesitemos

11 httpsmavenapacheorgref333maven-coredefault-bindingshtmlPlugin_bindings_for_war_packaging

Servicios Rest

149

y en el orden en el que lo necesitemos Como ya hemos indicado antes esto lo haremosincluyendo dichas acciones en la etiqueta ltbuildgt de nuestro pomxml y configurandolasconvenientemente para asegurarnos que el orden en el que se ejecutan es el que queremos

La siguiente figura muestra parte de la secuencia de fases llevadas a cabo por Maven ensu ciclo de vida por defecto Para conseguir nuestros propoacutesitos simplemente antildeadiremosla goals wildflydeploy y la asociaremos a la fase pre-integration-test y cambiaremos lafase a la que estaacute asociada la goal surefiretest para que los tests se ejecuten DESPUEacuteS dehaber desplegado el war en Wildfly

A continuacioacuten mostramos los cambios que tenemos que realizar en el fichero de configuracioacutenpomxml

Adicioacuten de las goals wildflydeploy y surefiretest a las fases pre-integration-test ysurefiretest respectivamente

lt-- forzamos el despliegue del war generado durante la fase pre-integration-test justo despueacutes de obtener dicho war--gtltplugingt ltgroupIdgtorgwildflypluginsltgroupIdgt ltartifactIdgtwildfly-maven-pluginltartifactIdgt ltversiongt102Finalltversiongt ltconfigurationgt lthostnamegtlocalhostlthostnamegt ltportgt9990ltportgt ltconfigurationgt

Servicios Rest

150

ltexecutionsgt ltexecutiongt ltidgtwildfly-deployltidgt ltphasegtpre-integration-testltphasegt ltgoalsgt ltgoalgtdeployltgoalgt ltgoalsgt ltexecutiongt ltexecutionsgtltplugingt

lt--ejecutaremos los test JUnit en la fase integration-test inmediatamente despueacutes de la fase pre-integration-test y antes de la fase verify--gtltplugingt ltgroupIdgtorgapachemavenpluginsltgroupIdgt ltartifactIdgtmaven-surefire-pluginltartifactIdgt ltversiongt218ltversiongt ltconfigurationgt ltskipgttrueltskipgt ltconfigurationgt ltexecutionsgt ltexecutiongt ltidgtsurefire-itltidgt ltphasegtintegration-testltphasegt ltgoalsgt ltgoalgttestltgoalgt ltgoalsgt ltconfigurationgt ltskipgtfalseltskipgt ltconfigurationgt ltexecutiongt ltexecutionsgtltplugingt

Tambieacuten necesitamos incluir en el pomxml las libreriacuteas de las que depende el coacutedigo depruebas de nuestro proyecto (clases XXXXTest situadas en srctest) libreriacutea JUnit JAXB yel API cliente de JAX-RS Por lo que antildeadimos en el las dependencias correspondientes

Dependencias del coacutedigo srctest con JUnit y API cliente de JAX-RS

ltdependencygt ltgroupIdgtjunitltgroupIdgt ltartifactIdgtjunitltartifactIdgt ltversiongt412ltversiongt ltscopegttestltscopegtltdependencygtltdependencygt ltgroupIdgtorgjbossresteasyltgroupIdgt ltartifactIdgtresteasy-clientltartifactIdgt ltversiongt3013Finalltversiongt ltscopegttestltscopegtltdependencygtltdependencygt ltgroupIdgtorgjbossresteasyltgroupIdgt

Servicios Rest

151

ltartifactIdgtresteasy-jaxb-providerltartifactIdgt ltversiongt3013Finalltversiongtltdependencygt

Dado que vamos a trabajar con el API Json y dado que ejecutaremos los tests desde lamaacutequina virtual de Java y no dentro del servidor WildFly necesitamos antildeadir tambieacuten lassiguientes libreriacuteas

Dependencias del coacutedigo srctest con el API Json de jaxrs

lt--Libreriacuteas para serializardeserializar json --gtltdependencygt ltgroupIdgtorgjbossresteasyltgroupIdgt ltartifactIdgtresteasy-jackson-providerltartifactIdgt ltversiongt3013Finalltversiongtltdependencygt

lt--Jaxrs API json --gtltdependencygt ltgroupIdgtorgjbossresteasyltgroupIdgt ltartifactIdgtresteasy-json-p-providerltartifactIdgt ltversiongt3013Finalltversiongtltdependencygt

No hemos incluido en el pomxml la orden para arrancar Wildfly Vamosa hacer esto desde IntelliJ en un perfil de ejecucioacuten como ya habeacuteishecho en sesiones anteriores De esta forma podremos ver desde IntelliJla consola de logs del servidor En este caso podemos crear un perfilsolamente para arrancar el servidor Wildfly (no es necesario que se incluyael despliegue del war generado puesto que lo haremos desde la ventanaMaven Projects) Antes de iniciar el proceso de construccioacuten por lo tantotendremos que asegurarnos de que hemos arrancado Wildlfly

Con estos cambios en el pomxml y ejecutando el comando mvn verify se llevaraacuten a cabolas siguientes acciones en este orden

bull Despueacutes de compilar el proyecto obtenemos el war (fase package )

bull El war generado se despliega en el servidor de aplicaciones Wilfly (fase pre-integration-test )

bull Se ejecutan los test JUnit sobre la aplicacioacuten desplegada en el servidor (faseintegration-test )

Anotaciones JUnit y aserciones AssertThat

JUnit 4 proporciona anotaciones para forzar a que los meacutetodos anotados con Test seejecuten en el orden que nos interese (por defecto no estaacute garantizado que se ejecuten enel orden en el que se escriben)

En principio debemos programar los tests para que sean totalmente independientes unos deotros y por lo tanto el orden de ejecucioacuten no influya para nada en el resultado de su ejecucioacutentanto si se ejecuta el primero como a mitad o el uacuteltimo El no hacer los tests independientes

Servicios Rest

152

hace que el proceso de testing se alargue y complique innecesariamente ya que puede serque unos tests enmascaren en resultado de otros o que no podamos saber si ciertas partesdel coacutedigo estaacuten bien o mal implementadas hasta que los tests de los que dependemos sehayan superado con eacutexito

Auacuten asiacute y dado que muchas veces se obtienen errores por hacer asunciones en el ordende la ejecucioacuten de los tests JUnit nos permite fijar dicho orden Para ello utilizaremosla anotacioacuten FixMethodOrder indicando el tipo de ordenacioacuten como por ejemploMethodSortersNAME_ASCENDING de forma que se ejecutaraacuten los tests por ordenlexicograacutefico

Por ejemplo

Ejemplo para forzar el orden de ejecucioacuten de los test (orden lexicograacutefico)

FixMethodOrder(MethodSortersNAME_ASCENDING)public class TestMethodOrder

Test public void testB() Systemoutprintln(second)

Test public void testA() Systemoutprintln(first)

Test public void testC() Systemoutprintln(third)

En ese caso el orden de ejecucioacuten seraacute testA() a continuacioacuten testB() y finalmentetestC()

Otra aportacioacuten de JUnit 4 es la incorporacioacuten de aserciones de tipoassertThat En sesiones anteriores habeacuteis utilizado aserciones con meacutetodosAssertassertEquals(resultado_esperado resultado_real) Los nuevosmeacutetodos AssertassertThat() permiten una mayor flexibilidad a la hora de expresar lasaserciones realizadas en nuestros tests asiacute como una mayor legibilidad de los mismos Elprototipo general de las aserciones de este tipo es

assertThat([value] [matcher statement])

en donde [value] es el resultado real (valor sobre el que se quiere afirmar algo)y [matcher statement] es un Matcher u objeto que realiza operaciones deemparejamiento sobre una secuencia de caracteres seguacuten un determinado patroacuten

Por ejemplo

Ejemplos de sentencias assertThat

assertThat(x is(not(4)))

Servicios Rest

153

assertThat(responseStringJson

either(containsString(nombre))and(containsString(apellido)))

assertThat(myList hasItem(3))

Aquiacute utilizamos un matcher con el patroacuten 4 esta sentencia devuelve false si x = 4Podemos combinar varios matchers de forma que se tengan que satisfacer maacutes de unoEn este caso aplicamos el matcher sobre un conjunto de elementos

Hay varias libreriacuteas que implementan Matchers JUnit incluye parte de los matchers deHamcrest (Hamcrest es un framework para escribir objetos matcher permitiendo definir reglasde matching de forma declarativa) Otra libreriacutea interesante para realizar testing de serviciosrest que utilizan representaciones Json es la libreriacutea hamcrest-json que podemos utilizarpara realizar aserciones sobre dos objetos Json

Por ejemplo supongamos que nuestro objeto Json contiene una lista de enlaces Hateoas detipo Link Los objetos Link seraacuten serializadosdeserializados (Wildfly utiliza Jackson pararealizar estas tareas) convenientemente Cuando serializamos un objeto Link (obtenemossu representacioacuten Json) veremos ademaacutes de los objetos uri valor type valor y relvalor que son los que baacutesicamente utilizamos al crear los enlaces Hateoas otros comouriBuilder hellip paramshellip que puede que no nos interese consultar o incluso que no leshayamos asignado ninguacuten valor

Si en nuestro test queremos comprobar que el objeto Json que nos devuelve el servicio(resultado real) se corresponde con el valor esperado tendremos que comparar ambasrepresentaciones Ahora bien puede que solamente nos interese comparar ciertos valorescontenidos en el objeto Json no el objeto completo

Hacer esta comprobacioacuten elemento a elemento es bastante tediosoLa libreriacutea hamcrest-json nos proporciona lo que estamos buscandocon los meacutetodos sameJSONAs() allowingExtraUnexpectedFields() yallowingAnyArrayOrdering() de la siguiente forma

Meacutetodo para comparar dos representaciones Json ClaseukcodatumedgehamcrestjsonSameJSONAs

AssertassertThat(age43 friend_ids[16 52 23] sameJSONAs(friend_ids[52 23 16]) allowingExtraUnexpectedFields() allowingAnyArrayOrdering())

En este coacutedigo tenemos una representacioacuten formada por dos objetos uno de los cuales tienecomo valor un array de enteros Si el servicio rest devuelve un objeto Json con maacutes elementoso en otro orden en este caso el resultado de la sentencia assertThat es true Volviendo alejemplo anterior de un objeto Json que contiene enlaces Hatehoas podriacuteamos realizar lasiguiente comparacioacuten

Comparamos dos objetos Json que contienen hiperenlaces Hateoas (objetos Link)

JsonObject json_object = clienttarget(httplocalhost8080forousuarios) request(MediaTypeAPPLICATION_JSON)

get(JsonObjectclass)

String json_string = json_objecttoString()

Servicios Rest

154

JsonObject usuarios = JsoncreateObjectBuilder() add(usuarios JsoncreateArrayBuilder() add(JsoncreateObjectBuilder() add(nombre Pepe Lopez) add(links JsoncreateArrayBuilder() add(JsoncreateObjectBuilder() add(uri httplocalhost8080forousuariospepe) add(type applicationxmlapplicationjson) add(rel self)))) add(JsoncreateObjectBuilder() add(nombre Ana Garcia) add(links JsoncreateArrayBuilder() add(JsoncreateObjectBuilder() add(uri httplocalhost8080forousuariosana) add(type applicationxmlapplicationjson) add(rel self)))))

build()

AssertassertThat(json_string sameJSONAs(usuariostoString()) allowingExtraUnexpectedFields()

allowingAnyArrayOrdering())

Realizamos la llamada al servicio REST y recibimos como respuesta un objeto Json Eneste caso nuestro objeto Json estaacute formado por una lista de objetosObtenemos la representacioacuten de nuestro objeto Json (resultado real) en forma de cadenade caracteresCreamos un nuevo objeto Json con el resultado esperadoComparamos ambos objetos Si el resultado real incluye maacutes elementos que loscontenidos en json_string o en otro orden consideraremos que hemos obtenido larespuesta correcta

Para utilizar esta libreriacutea en nuestro proyecto simplemente tendremos que antildeadirla comodependencia en la configuracioacuten de nuestro pomxml

Libreriacutea para comparar objetos Json en los tests

lt--Hamcrest Json --gtltdependencygt ltgroupIdgtukcodatumedgeltgroupIdgt ltartifactIdgthamcrest-jsonltartifactIdgt ltversiongt02ltversiongtltdependencygt

Observaciones sobre los tests y algunos ejemplos de tests

Recuerda que para utilizar el API cliente necesitas utilizar instanciasjavaxwsrsclientClient que debemos cerrar siempre despueacutes de su uso paracerrar el socket asociado a la conexioacuten

Servicios Rest

155

Para ello podemos optar por Crear una uacutenica instancia Client antes de ejecutar cualquiertest (meacutetodo BeforeClass) y cerrar el socket despueacutes de ejecutar todos los tests (meacutetodoAfterClass) Crear una uacutenica instancia Client antes de ejecutar CADA test (meacutetodoBefore) y cerrar el socket despueacutes de ejecutar CADA tests (meacutetodo After)

Si el resultado de una invocacioacuten sobre la instancia Client es de tipojavaxwsrscoreResponse debemos liberar de forma expliacutecita la conexioacuten para quepueda ser usada de nuevo por dicha instancia Client

Por ejemplo supongamos que queremos realizar un test en el que realizamos una operacioacutenPOST y a continuacioacuten una operacioacuten GET para verificar que el nuevo recurso se ha antildeadididocorrectamente

Ejemplo de Test que utiliza una instancia Client para todos los tests

public class TestRESTServices private static final String BASE_URL = httplocalhost8080rest private static URI uri = UriBuilderfromUri(BASE_URL)build() private static Client client

BeforeClass public static void initClient()

client = ClientBuildernewClient()

AfterClass public static void closeClient()

clientclose()

Test public void createAndRetrieveACustomer()

Customer customer = Creamos un nuevo cliente Response response = clienttarget(uri) request() post(Entityentity(customer MediaTypeAPPLICATION_JSON)) assertEquals(ResponseStatusCREATED responsegetStatusInfo()) URI referenceURI = responsegetLocation()

responseclose()

Obtenemos el recurso que hemos antildeadido response = clienttarget(referenceURI)request()get()

Customer retrieved_customer = responsereadEntity(Customerclass) assertEquals(ResponseStatusOK responsegetStatusInfo()) assertEquals(retreivedRefgetName() rgetName())

responseclose()

Creamos una instancia Client ANTES de ejecutar cualquier testCerramos el socket asociado a la conexioacuten DESPUEacuteS de ejecutar TODOS los testsLiberamos la conexioacuten para poder reutilizarla

Servicios Rest

156

Liberamos la conexioacuten para poder reutilizarlaAhora veamos otro ejemplo en el que utilizamos una instancia Client para cada test

Ejemplo de Test que utiliza una instancia Client para CADA test

public class TestRESTServices

private Client client

Before public void setUp()

thisclient = ClientBuildernewClient()

After public void tearDown()

thisclientclose()

Test public void getAllCustomersAsJson() String uriString = httplocalhost8080restcustomers JsonArray json_array = client target(uriString) request(MediaTypeAPPLICATION_JSON) accept(MediaTypeAPPLICATION_JSON) get(JsonArrayclass)

AssertassertEquals(2 json_arraysize())

Test public void getAllCustomers() String uriString = httplocalhost8080restcustomers Consultamos los datos de todos los customers ListltCustomergt lista_usuarios = clienttarget(uriString) request(applicationjson) get(new GenericTypeltListltCustomergtgt() ) AssertassertEquals(2 lista_usuariossize())

Creamos una instancia Client ANTES de ejecutar CADA testCerramos el socket asociado a la conexioacuten DESPUEacuteS de ejecutar CADA los tests

Podemos ver en este uacuteltimo ejemplo que no es necesario liberar la conexioacuten entre usossucesivos de la instancia Client si no utilizamos la clase Response En este caso el procesose realiza de forma automaacutetica por el sistema

Finalmente comentaremos que debido a un bug en la especificacioacuten JAX-RS eldeserializado del de los objetos Link no se realiza por lo que obendremos una listade Links vaciacutea (ver httpkingsfleetblogspotcomes201405reading-and-writing-jax-rs-link-objectshtml) Podemos comprobar que si obtenemos la representacioacuten en formato texto dela entidad del mensaje dicha lista de objetos tendraacute el valor correcto

Si no utilizamos la solucioacuten propuesta en el enlace anterior deberemos usar la anotacioacutenJsonIgnoreProperties(ignoreUnknown = true) De esta forma ignoraremos el

Servicios Rest

157

deserializado de los objetos Link pero tendremos que utilizar la representacioacuten en formato decadena de caracteres del recurso json en lugar del objeto java Link asociado

Asiacute por ejemplo si nuestro recurso Customer tiene asociado una lista de objetos Link parapoder utilizar el API Cliente y acceder a la lista de enlaces usaremos la anotacioacuten anterior enla implementacioacuten de la clase Customer

JsonIgnoreProperties(ignoreUnknown = true)XmlRootElement(name=customer)public class Customer int id String name ListltLinkgt links

Servicios Rest

158

55 Ejercicios

Tests utilizando el API cliente y un mapeador de excepciones (1 punto)

Se proporciona como plantilla el MOacuteDULO IntelliJ s5-tienda con una implementacioacuten parcialde una tienda de clientes on-line Este proyecto ya contiene varios tests implementados amodo de ejemplo

Los recursos rest implementados lanzan excepciones de tipo RestException si porejemplo se intenta realizar una consulta sobre un producto yo usuario que no existe

Se ha implementado un mapeador de excepciones RestExceptionMapper quecaptura excepciones de tipo RuntimeException y devuelve una respuesta de tipoErrorMensajeBean que seraacute serializada a formato json yo formato xml (dependiendo delvalor de la cabecera Accept de la peticioacuten) con informacioacuten sobre el error producido

Implementa los siguientes dos tests test7recuperarTodosLosUsuarios() en el querealizamos una invocacioacuten GET sobre httplocalhost8080s5-tiendarestclientes Esta URIpodriacutea corresponderse con un meacutetodo anotado con GET y que devolviese una lista de todoslos clientes de la tienda Sin embargo no existe tal meacutetodo en nuestro recursos rest Verificaque dicha invocacioacuten devuelve el coacutedigo de estado 500 (Internal Server Error) y que en elcuerpo del mensaje se recibe Servicio no disponible

bull test8recuperarClienteQueNoExiste() en el que intentamos recuperar lainformacioacuten de un cliente que no exista en nuestra base de datos En este caso debemosverificar que el mensaje obtenido en formato json es el siguiente

status Not Found code 404 message El producto no se encuentra en la base de datos developerMessage error

Tests utilizando el API Json y JUnit (1 punto)

Vamos a seguir usando el proyecto s4-foroAvanzado con el que hemos trabajado en la sesioacutenanterior

Vamos a implementar algunos tests con JUnit en los que utilizaremos ademaacutes del API clienteel API Json que nos proporciona jaxrs

Para ejecutar los tests necesitamos modificar el pomxml antildeadiendo las dependenciascorrespondientes que hemos visto a lo largo de la sesioacuten y antildeadiendo las goals para que seejecuten los tests despueacutes de desplegar la aplicacioacuten en Wildfly

Proporcionamos el contenido del pomxml con las libreriacuteas y plugins que necesitaraacutes(aunque como ejercicio deberiacuteas intentar modificar la configuracioacuten tuacute mismo y luego puedescomprobar el resultado con el pomxml que se proporciona) El contenido del nuevo pomxmllo tienes en srctestresourcesnuevo-pommxl

Inicializacioacuten de los datos para los tests

Vamos a utilizar DBUnit para inicializar la BD para realizar los tests Para ello tendraacutes queantildeadir en el pomxml las dependencias necesarias (ya estaacuten antildeadidas en el fichero de

Servicios Rest

159

configuracioacuten proporcionado) En el fichero srctestresourcesforo-inicialxml encontrareacuteis elconjunto de datos con el que inicializaremos la base de datos para ejecutar nuestros tests

No es necesario (aunque es una muy buena praacutectica) que inicialicemos la BD para cada test

Implementacioacuten de los tests

Vamos a implementar los siguientes tests (que se ejecutaraacuten en en este mismo orden)

bull test1ConsultaTodosUsuarios() recuperamos los datos de todos los usuarios delforo Recuerda que previamente tienes que haber inicializado la BD con los datos del ficheroforo-inicialxml Recupera los datos en forma de JsonObject y comprueba que el nuacutemerode usuarios es el correcto Tambieacuten debes comprobar que tanto el login como los enlaceshatehoas para cada usuario estaacuten bien creados En concreto para cada usuario debesverificar que la uri (uri) el tipo mime (type) y el tipo de enlace (rel) son los correctos

bull test2CreamosMensajeDePepe() crearemos un nuevo mensaje del usuario con loginpepe Recuerda que este usuario tiene el rol registrado El mensaje tendraacute el asuntocena y el texto seraacute Mejor me voy al cine En este caso deberaacutes comprobar el valorde estado (debe ser 201) y debes recuperar (consultar con una peticioacuten REST) el mensajepara comprobar que la operacioacuten de insertar el mensaje ha tenido eacutexito

bull test3CreamosMensajeDeUsuarioNoAutorizado() creamos un nuevo mensaje deun usuario que no estaacute autorizado (por ejemplo de un usuario con login juan) En estecaso el mensaje tendraacute el asunto cena y el mensaje puede ser Pues yo tampoco voyEl resultado debe ser el coacutedigo de estado 401 ( Unauthorized)

bull test4ConsultaUsuario() Consultamos los datos del usuario pepe Recuperaremoslos datos como un JsonObject y comprobaremos que el valor de la uri para el tipo derelacioacuten self del enlace Link asociado es el correcto

  • Servicios Rest
  • Table of Contents
  • 1 Introduccioacuten a REST Disentildeo y creacioacuten de servicios RESTful
    • 11 iquestQueacute es un servicio Web
      • Servicios Web RESTful
        • 12 Fundamentos de REST
          • Recursos
          • Representacioacuten de los recursos
          • Direccionabilidad de los recursos URI
          • Uniformidad y restricciones de las interfaces
            • 13 Disentildeo de servicios Web RESTful
            • 14 Un primer servicio JAX-RS
              • Modelo de objetos
              • Modelado de URIs
              • Definicioacuten del formato de datos
                • Formato de datos para operaciones de lectura y modificacioacuten de los recursos
                • Formato de datos para operaciones de creacioacuten de los recursos
                  • Asignacioacuten de meacutetodos HTTP
                    • Visualizacioacuten de todos los Pedidos Clientes o Productos
                    • Obtencioacuten de Pedidos Clientes o Productos individuales
                    • Creacioacuten de un Pedido Cliente o Producto
                    • Actualizacioacuten de un Pedido Cliente o Producto
                    • Borrado de un Pedido Cliente o Producto
                    • Cancelacioacuten de un Pedido
                      • Implementacioacuten del servicio Creacioacuten del proyecto Maven
                      • Implementacioacuten del servicio Recursos JAX-RS
                        • Clases de nuestro dominio (entidades) Clientejava
                        • Clases de nuestro servicio RESTful ClienteResourcejava
                        • Creacioacuten de clientes
                        • Consulta de clientes
                        • Modificacioacuten de clientes
                          • Construccioacuten y despliegue del servicio
                          • Probando nuestro servicio
                            • 15 Ejercicios
                              • Servicio REST ejemplo (0 puntos)
                              • Servicio REST saludo (1 punto)
                              • Servicio REST foro (1 punto)
                                  • 2 Anotaciones baacutesicas JAX-RS El modelo de despliegue
                                    • 21 iquestCoacutemo funciona el enlazado de meacutetodos HTTP
                                    • 22 La anotacioacuten Path
                                      • Expresiones Path
                                        • Expresiones regulares
                                        • Reglas de precedencia
                                          • Paraacutemetros matrix (Matrix parameters)
                                          • Subrecursos (Subresource Locators)
                                            • Caraacutecter dinaacutemico del dispatching de peticiones
                                                • 23 Usos de las anotaciones Produces y Consumes
                                                  • Anotacioacuten Consumes
                                                  • Anotacioacuten Produces
                                                    • 24 Inyeccioacuten de paraacutemetros JAX-RS
                                                      • javaxwsrsPathParam
                                                      • Interfaz UriInfo
                                                      • javaxwsrsMatrixParam
                                                      • javaxwsrsQueryParam
                                                      • javaxwsrsFormParam
                                                      • javaxwsrsHeaderParam
                                                      • javaxwsrscoreContext
                                                      • javaxwsrsBeanParam
                                                      • Conversioacuten automaacutetica de tipos
                                                      • Valores por defecto (DefaultValue)
                                                        • 25 Configuracioacuten y despliegue de aplicaciones JAX-RS
                                                          • Configuracioacuten mediante la clase Application
                                                          • Configuracioacuten mediante un fichero webxml
                                                          • Configuracioacuten en un contenedor que no disponga de una implementacioacuten JAX-RS
                                                            • 26 Ejercicios
                                                              • Creacioacuten de un recurso creacioacuten y consulta de temas en el foro (05 puntos)
                                                              • Despliegue y pruebas del recurso (05 puntos)
                                                              • Muacuteltiples consultas de los temas del foro (05 puntos)
                                                              • Creacioacuten de subrecursos (05 puntos)
                                                                  • 3 Manejadores de contenidos Respuestas del servidor y manejo de excepciones
                                                                    • 31 Proveedores de entidades
                                                                      • Interfaz javaxwsrsextMessageBodyReader
                                                                      • Interfaz javaxwsrsextMessageBodyWriter
                                                                        • 32 Proveedores de entidad estaacutendar incluidos en JAX-RS
                                                                          • javaxwsrscoreStreamingOutput
                                                                          • javaioInputStream javaioReader
                                                                          • javaioFile
                                                                          • byte[]
                                                                          • String char[]
                                                                          • MultivaluedMapltString Stringgt y formularios de entrada
                                                                            • 33 Muacuteltiples representaciones de recursos
                                                                            • 34 Introduccioacuten a JAXB
                                                                              • Clase JAXBContext
                                                                              • Manejadores JAX-RS para JAXB
                                                                              • JAXB y JSON
                                                                                • 35 Respuestas del servidor
                                                                                  • Coacutedigos de respuesta por defecto
                                                                                    • Respuestas que indican eacutexito
                                                                                    • Respuestas que indican una situacioacuten de fallo
                                                                                      • Elaboracioacuten de respuestas con la clase Response
                                                                                        • Inclusioacuten de cookies en la respuesta
                                                                                        • El tipo enumerado de coacutedigos de estado
                                                                                        • La clase javaxwsrscoreGenericEntity
                                                                                            • 36 Manejadores de excepciones
                                                                                              • La clase javaxwsrsWebApplicationException
                                                                                              • Mapeado de excepciones
                                                                                              • Jerarquiacutea de excepciones
                                                                                                • 37 Ejercicios
                                                                                                  • Servicio REST ejemplo
                                                                                                  • Plantillas que se proporcionan
                                                                                                  • Uso de JAXB (05 puntos)
                                                                                                  • Uso de manejadores de contenidos y clase Response (075 puntos)
                                                                                                  • Manejo de excepciones (075 puntos)
                                                                                                      • 4 HATEOAS y Seguridad
                                                                                                        • 41 iquestQueacute es HATEOAS
                                                                                                        • 42 HATEOAS y Servicios Web
                                                                                                          • Enlaces Atom
                                                                                                          • Ventajas de utilizar HATEOAS con Servicios Web
                                                                                                            • Transparencia en la localizacioacuten
                                                                                                            • Desacoplamiento de los detalles de la interaccioacuten
                                                                                                            • Reduccioacuten de errores de transicioacuten de estados
                                                                                                              • Enlaces en cabeceras frente a enlaces Atom
                                                                                                                • 43 HATEOAS y JAX-RS
                                                                                                                  • Construccioacuten de URIs con UriBuilder
                                                                                                                  • URIs relativas mediante el uso de UriInfo
                                                                                                                  • Construccioacuten de enlaces (Links) en documentos XML y en cabeceras HTTP
                                                                                                                    • 44 Seguridad
                                                                                                                      • Autentificacioacuten en JAX-RS
                                                                                                                        • Creacioacuten de usuarios y roles
                                                                                                                          • Autorizacioacuten en JAX-RS
                                                                                                                          • Encriptacioacuten
                                                                                                                            • Anotaciones JAX-RS para autorizacioacuten
                                                                                                                              • Seguridad programada
                                                                                                                                • 45 Ejercicios
                                                                                                                                  • Uso de Hateoas (1 puntos)
                                                                                                                                  • Ejercicio seguridad (1 punto)
                                                                                                                                      • 5 Api cliente Procesamiento JSON y Pruebas
                                                                                                                                        • 51 API cliente Visioacuten general
                                                                                                                                          • Obtenemos una instancia Client
                                                                                                                                          • Configuramos el target del cliente (URI)
                                                                                                                                          • Construimos y Realizamos la peticioacuten
                                                                                                                                          • Manejo de excepciones
                                                                                                                                            • 52 Procesamiento JSON
                                                                                                                                            • 53 Modelo de procesamiento basado en el modelo de objetos
                                                                                                                                              • Creacioacuten de un modelos de objetos desde el coacutedigo de la aplicacioacuten
                                                                                                                                              • Navegando por el modelo de objetos
                                                                                                                                              • Escritura de un modelo de objetos en un stream
                                                                                                                                              • Modelo de procesamiento basado en streaming
                                                                                                                                                • Lectura de datos JSON
                                                                                                                                                • Escritura de datos JSON
                                                                                                                                                    • 54 Pruebas de servicios REST
                                                                                                                                                      • Ciclo de vida de Maven y tests JUnit
                                                                                                                                                      • Anotaciones JUnit y aserciones AssertThat
                                                                                                                                                      • Observaciones sobre los tests y algunos ejemplos de tests
                                                                                                                                                        • 55 Ejercicios
                                                                                                                                                          • Tests utilizando el API cliente y un mapeador de excepciones (1 punto)
                                                                                                                                                          • Tests utilizando el API Json y JUnit (1 punto)
                                                                                                                                                            • Inicializacioacuten de los datos para los tests
                                                                                                                                                              • Implementacioacuten de los tests
Page 6: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones

Servicios Rest

6

bull Debe ser un sistema uniformemente accesible (con una interfaz uniforme) Estarestriccioacuten define coacutemo debe ser la interfaz entre clientes y servidores La idea es simplificary desacoplar la arquitectura permitiendo que cada una de sus partes puede evolucionarde forma independiente Una interfaz uniforme se debe caracterizar por

Estar basada en recursos La abstraccioacuten utilizada para representar la informacioacuten ylos datos en REST es el recurso y cada recurso debe poder ser accedido medianteuna URI (Uniform Resource Identifier)

Orientada a representaciones La interaccioacuten con los servicios tiene lugar atraveacutes de las representaciones de los recursos que conforman dicho servicio Unrecurso referenciado por una URI puede tener diferentes formatos (representaciones)Diferentes plataformas requieren formatos diferentes Por ejemplo los navegadoresnecesitan HTML JavaScript requiere JSON (JavaScript Object Notation) y unaaplicacioacuten Java puede necesitar XML

Interfaz restringida Se utiliza un pequentildeo conjunto de meacutetodos bien definidos paramanipular los recursos

Uso de mensajes auto-descriptivos cada mensaje debe incluir la suficienteinformacioacuten como para describir coacutemo procesar el mensaje Por ejemplo se puedeindicar coacutemo parsear el mensaje indicando el tipo de contenido del mismo (xml htmltextohellip)

Uso de Hipermedia como maacutequina de estados de la aplicacion (HATEOAS) Lospropios formatos de los datos son los que dirigen las transiciones entre estados dela aplicacioacuten Como veremos maacutes adelante con maacutes detalle el uso de HATEOAS(Hypermedia As The Engine Of Application State) va a permitir transferir de formaexpliacutecita el estado de la aplicacion en los mensajes intercambiados y por lo tantorealizar interacciones con estado

bull Tiene que ser un sistema por capas un cliente no puede discernir si estaacute accediendodirectamente al servidor o a alguacuten intermediario Las capas intermedias van a permitirsoportar la escalabilidad asiacute como reforzar las poliacuteticas de seguridad

A continuacioacuten analizaremos algunas de las abstracciones que constituyen un sistemaRESTful recursos representaciones URIs y los tipos de peticiones HTTP que constituyen lainterfaz uniforme utilizada en las transferencias clienteservidor

Recursos

Un recurso REST es cualquier cosa que sea direccionable (y por lo tanto accesible) a traveacutesde la Web Por direccionable nos referimos a recursos que puedan ser accedidos y transferidosentre clientes y servidores Por lo tanto un recurso es una correspondencia loacutegica ytemporal con un concepto en el dominio del problema para el cual estamos implementandouna solucioacuten

Algunos ejemplos de recursos REST son

bull Una noticia de un perioacutedico

bull La temperatura de Alicante a las 400pm

bull Un valor de IVA almacenado en una base de datos

bull Una lista con el historial de las revisiones de coacutedigo en un sistema CVS

bull Un estudiante en alguna aula de alguna universidad

Servicios Rest

7

bull El resultado de una buacutesqueda de un iacutetem particular en Google

Aun cuando el mapeado de un recurso es uacutenico diferentes peticiones a un recursopueden devolver la misma representacioacuten binaria almacenada en el servidor Por ejemploconsideremos un recurso en el contexto de un sistema de publicaciones En este casouna peticioacuten de la uacuteltima revisioacuten publicada y la peticioacuten de la revisioacuten nuacutemero 12 enalguacuten momento de tiempo pueden devolver la misma representacioacuten del recurso cuandola uacuteltima revisioacuten sea efectivamente la 12 Por lo tanto cuando la uacuteltima revisioacuten publicadase incremente a la versioacuten 13 una peticioacuten a la uacuteltima revisioacuten devolveraacute la versioacuten 13 yuna peticioacuten de la revisioacuten 12 continuaraacute devolviendo la versioacuten 12 En definitiva cada unode los recursos puede ser accedido directamente y de forma independiente pero diferentespeticiones podriacutean apuntar al mismo dato

Debido a que estamos utilizando HTTP para comunicarnos podemos transferir cualquier tipode informacioacuten que pueda transportarse entre clientes y servidores Por ejemplo si realizamosuna peticioacuten de un fichero de texto de la CNN nuestro navegador mostraraacute un fichero de textoSi solicitamos una peliacutecula flash a YouTube nuestro navegador recibiraacute una peliacutecula flash Enambos casos los datos son transferidos sobre TCPIP y el navegador conoce coacutemo interpretarlos streams binarios debido a la cabecera de respuesta del protocolo HTTP Content-Type Porlo tanto en un sistema RESTful la representacioacuten de un recurso depende del tipo deseado porel cliente (tipo MIME) el cual estaacute especificado en la peticioacuten del protocolo de comunicaciones

Representacioacuten de los recursos

La representacioacuten de los recursos es lo que se enviacutea entre los servidores y clientes Unarepresentacioacuten muestra el estado temporal del dato real almacenado en alguacuten dispositivo dealmacenamiento en el momento de la peticioacuten En teacuterminos generales es un stream binariojuntamente con los metadatos que describen coacutemo dicho stream debe ser consumido por elcliente yo servidor (los metadatos tambieacuten puden contener informacioacuten extra sobre el recursocomo por ejemplo informacioacuten de validacioacuten y encriptacioacuten o coacutedigo extra para ser ejecutadodinaacutemicamente)

A traveacutes del ciclo de vida de un servicio web pueden haber varios clientes solicitando recursosClientes diferentes son capaces de consumir diferentes representaciones del mismo recursoPor lo tanto una representacioacuten puede tener varias formas como por ejemplo una imagen untexto un fichero XML o un fichero JSON pero tienen que estar disponibles en la misma URL

Para respuestas generadas para humanos a traveacutes de un navegador una representacioacutentiacutepica tiene la forma de paacutegina HTML Para respuestas automaacuteticas de otros servicios web lalegibilidad no es importante y puede utilizarse una representacioacuten mucho maacutes eficiente comopor ejemplo XML

El lenguaje para el intercambio de informacioacuten con el servicio queda a eleccioacuten deldesarrollador A continuacioacuten mostramos algunos formatos comunes que podemos utilizarpara intercambiar esta informacioacuten

Table 1 Ejemplos de formatos utilizados por los servicios REST

Formato Tipo MIME

Texto plano textplain

HTML texthtml

XML applicationxml

JSON applicationjson

Servicios Rest

8

De especial intereacutes es el formato JSON Se trata de un lenguaje ligero de intercambio deinformacioacuten que puede utilizarse en lugar de XML (que resulta considerablemente maacutespesado) para aplicaciones AJAX De hecho en Javascript puede leerse este tipo de formatosimplemente utilizando el meacutetodo eval()

Direccionabilidad de los recursos URI

Una URI o Uniform Resource Identifier en un servicio web RESTful es un hiper-enlace aun recurso y es la uacutenica forma de intercambiar representaciones entre clientes y servidoresUn servicio web RESTful expone un conjunto de recursos que identifican los objetivos de lainteraccioacuten con sus clientes

El conjunto de restricciones REST no impone que las URIs deban ser hiper-enlacesSimplemente hablamos de hiper-enlaces porque estamos utilizando la Web para crearservicios web Si estuvieacutesemos utilizando un conjunto diferente de tecnologiacuteas soportadasuna URI RESTful podriacutea ser algo completamente diferente Sin embargo la idea dedireccionabilidad debe permanecer

En un sistema REST la URI no cambia a lo largo del tiempo ya que la implementacioacutende la arquitectura es la que gestiona los servicios localiza los recursos negocia lasrepresentaciones y enviacutea respuestas con los recursos solicitados Y lo que es maacutes importantesi hubiese un cambio en la estructura del dispositivo de almacenamiento en el lado del servidor(por ejemplo un cambio de servidores de bases de datos) nuestras URIs seguiraacuten siendo lasmismas y seraacuten vaacutelidas mientras el servicio web siga estando en marcha o el contexto delrecurso no cambie

Sin las restricciones REST los recursos se acceden por su localizacioacutenlas direcciones web tiacutepicas son URIs fijas Si por ejemplo renombramosun fichero en el servidor la URI seraacute diferente si movemos el fichero a undirectorio diferente la URI tambieacuten seraacute diferente

El formato de una URI se estandariza como sigue

schemehostportpathqueryStringfragment

En donde

bull scheme es el protocolo que estamos utilizando para comunicarnos con el servidor Paraservicios REST normalmente el protocolo seraacute http o https

bull El teacutermino host es un nombre DNS o una direccioacuten IP

bull A continuacioacuten se puede indicar de forma opcional un puerto (mediante port ) que es unvalor numeacuterico El host y el port representan la localizacioacuten de nuestro recurso en la red

bull Seguidamente aparece una expresioacuten path que es un conjunto de segmentos de textodelimitados por el caraacutecter (pensemos en la expresioacuten path como en una lista dedirectorios de un fichero en nuestra maacutequina)

bull Esta expresioacuten puede ir seguida opcionalmente por una queryString El caraacutecter )separa el path de la queryString Esta uacuteltima es una lista de paraacutemetros representadoscomo pares nombrevalor Cada par estaacute delimitado por el caraacutecter amp

La uacuteltima parte de la URI es el fragment delimitado por el caraacutecter Normalmente seutiliza para apuntar a cierto lugar del documento al que estamos accediendo

Servicios Rest

9

En una URI no todos los caracteres estaacuten permitidos de forma que algunos caracteres secodificaraacuten de acuerdo a las siguientes reglas

bull Los caracteres a-z A-Z 0-9 - y _ permanecen igual

bull El caracter espacio se convierte en el caraacutecter +

bull El resto de caracteres se codifican como una secuencia de bits siguiendo un esquema decodificacioacuten hexadecimal de forma que cada dos diacutegitos hexadecimales van precedidospor el caraacutecter

Un ejemplo de URI podriacutea ser eacuteste

httpexpertojavauaesrecursosclientesapellido=MartinezampcodPostal=02115

En el ejemplo anterior el host viene dado por expertojavauaes el path o ruta de acceso alrecurso es recursosclientes y hemos especificado los paraacutemetros apellido y codPostal conlos valores Martinez y 02115 respectivamente

Si por ejemplo en nuestra aplicacioacuten tenemos informacioacuten de clientes podriacuteamos acceder ala lista correspondiente mediante una URL como la siguiente

httpexpertojavauaesrecursosclientes

Esto nos devolveraacute la lista de clientes en el formato que el desarrollador del servicio hayadecidido Hay que destacar por lo tanto que en este caso debe haber un entendimiento entreel consumidor y el productor del servicio de forma que el primero comprenda el lenguajeutilizado por el segundo

La URL anterior nos podriacutea devolver un documento como el siguiente

ltxml version=10gtltclientesgt ltclientegthttpexpertojavauaesrecursosclientes1ltclientegt ltclientegthttpexpertojavauaesrecursosclientes2ltclientegt ltclientegthttpexpertojavauaesrecursosclientes4ltclientegt ltclientegthttpexpertojavauaesrecursosclientes6ltclientegtltclientesgt

En este documento se muestra la lista de clientes registrados en la aplicacioacuten cada uno deellos representado tambieacuten por una URL Accediendo a estas URLs a su vez podremosobtener informacioacuten sobre cada curso concreto o bien modificarlo

Uniformidad y restricciones de las interfaces

Ya hemos introducido los conceptos de recursos y sus representaciones Hemos dichoque los recursos son correspondencias (mappings) de los estados reales de las entidadesque son intercambiados entre los clientes y servidores Tambieacuten hemos dicho que lasrepresentaciones son negociadas entre los clientes y servidores a traveacutes del protocolo decomunicacioacuten en tiempo de ejecucioacuten (a traveacutes de HTTP) A continuacioacuten veremos con detalle

Servicios Rest

10

lo que significa el intercambio de estas representaciones y lo que implica para los clientes yservidores el realizar acciones sobre dichos recursos

El desarrollo de servicios web REST es similar al desarrollo de aplicaciones web Sin embargola diferencia fundamental entre el desarrollo de aplicaciones web tradicionales y las maacutesmodernas es coacutemo pensamos sobre las acciones a realizar sobre nuestras abstraccionesde datos De forma maacutes concreta el desarrollo moderno estaacute centrado en el concepto denombres (intercambio de recursos) el desarrollo tradicional estaacute centrado en el conceptode verbos (acciones remotas a realizar sobre los datos) Con la primera forma estamosimplementando un servicio web RESTful con la segunda un servicio similar a una llamada aprocedimiento remoto- RPC) Y lo que es maacutes un servicio RESTful modifica el estado delos datos a traveacutes de la representacioacuten de los recursos (por el contrario una llamada aun servicio RPC oculta la representacioacuten de los datos y en su lugar enviacutea comandos paramodificar el estado de los datos en el lado del servidor) Finalmente en el desarrollo modernode aplicaciones web limitamos la ambiguumledad en el disentildeo y la implementacioacuten debido aque tenemos cuatro acciones especiacuteficas que podemos realizar sobre los recursos CreateRetrieve Update y Delete (CRUD) Por otro lado en el desarrollo tradicional de aplicacionesweb podemos tener otras acciones con nombres o implementaciones no estaacutendar

A continuacioacuten indicamos la correspondencia entre las acciones CRUD sobre los datos y losmeacutetodos HTTP asociados

Table 2 Operaciones REST sobre los recursos

Accioacuten sobre los datos Protocolo HTTP equivalente

CREATE POST

RETRIEVE GET

UPDATE PUT

DELETE DELETE

El principio de uniformidad de la interfaz de acceso a recursos es fundamental y quizaacute el maacutesdifiacutecil de seguir por los programadores acostumbrados al modelo RPC (Remote ProcedureCall) La idea subyacente es utilizar uacutenicamente un conjunto finito y claramente establecidode operaciones para la interaccioacuten con los servicios Esto significa que no tendremos unparaacutemetro accioacuten en nuestra URI y que soacutelo utilizaremos los meacutetodos HTTP para accedera nuestros servicios Cada uno de los meacutetodos tiene un propoacutesito y significado especiacuteficosque mostramos a continuacioacuten

GETGET es una operacioacuten soacutelo de lectura Se utiliza para recuperar informacioacuten especiacuteficadel servidor Tambieacuten se trata de una operacioacuten idempotente y segura Idempotentesignifica que no importa cuaacutentas veces invoquemos esta operacioacuten el resultado (queobservaremos como usuarios) debe ser siempre el mismo Segura significa que unaoperacioacuten GET no cambia el estado del servidor en modo alguno es decir no debe exhibirninguacuten efecto lateral en el servidor Por ejemplo el hecho de leer un documento HTMLno deberiacutea cambiar el estado de dicho documento

PUTLa operacioacuten PUT solicita al servidor el almacenar el cuerpo del mensaje enviado condicha operacioacuten en la direccioacuten proporcionada en el mensaje HTTP Normalmente semodela como una insercioacuten o actualizacioacuten (nosotros la utilizaremos solamente comoactualizacioacuten) Es una propiedad idempotente Cuando se utiliza PUT el cliente conoce

Servicios Rest

11

la identidad del recurso que estaacute creando o actualizando Es idempotente porque enviar elmismo mensaje PUT maacutes de una vez no tiene ninguacuten efecto sobre el servicio subyacenteUna analogiacutea podriacutea ser un documento de texto que estemos editando No importa cuaacutentasveces pulsemos el botoacuten de grabar el fichero que contiene el documento loacutegicamenteseraacute el mismo documento

DELETEEsta operacioacuten se utiliza para eliminar recursos Tambieacuten es idempotente

POSTPost es la uacutenica operacioacuten HTTP que no es idempotente ni segura Cada peticioacuten POSTpuede modificar el servicio de forma exclusiva Se puede enviar o no informacioacutencon la peticioacuten Tambieacuten podemos recibir o no informacioacuten con la respuesta Paraimplementar servicios REST es deseable enviar informacioacuten con la peticioacuten y tambieacutenrecibir informacioacuten con la respuesta

Adicionalmente podemos utilizar otras dos operaciones HTTP

HEADEs una operacioacuten exactamente igual que GET excepto que en lugar de devolver un cuerpode mensaje solamente devuelve un coacutedigo de respuesta y alguna cabecera asociada conla peticioacuten

OPTIONSSe utiliza para solicitar informacioacuten sobre las opciones disponibles sobre un recurso en elque estamos interesados Esto permite al cliente determinar las capacidades del servidory del recurso sin tener que realizar ninguna peticioacuten que provoque una accioacuten sobre elrecurso o la recuperacioacuten del mismo

PATCHSe utiliza para para realiza reemplazos (actualizaciones) parciales de un documento yaque la operacioacuten PUT soacutelo permite una actualizacioacuten completa del recurso (y requiereindicar una representacioacuten completa del mismo) Es uacutetil cuando el recurso a modificares complejo y solamente queremos actualizar parte de su contenido En este caso solonecesitamos indicar la parte que queremos cambiar

13 Disentildeo de servicios Web RESTful

El disentildeo de servicios RESTful no es muy diferente del disentildeo de aplicaciones webtradicionales tenemos requerimientos de negocio tenemos usuarios que quieren realizaroperaciones sobre los datos y tenemos restricciones hardware que van a condicionar nuestraarquitectura software La principal diferencia reside en el hecho de que tenemos que buscara partir de los requerimientos cuaacuteles van a ser los recursos que van a ser accedidos a traveacutesde los servicios sin preocuparnos de queacute operaciones o acciones especiacuteficas van a poderserealizar sobre dichos recursos (el proceso de disentildeo depende de los nombres no de losverbos)

Podemos resumir los principios de disentildeo de servicios web RESTful en los siguientes cuatropasos

1 Elicitacioacuten de requerimientos y creacioacuten del modelo de objetos Este paso es similar aldisentildeo orientado a objetos El resultado del proceso puede ser un modelo de clases UML

2 Identificacioacuten de recursos Este paso consiste en identificar los objetos de nuestromodelo sin preocuparnos de las operaciones concretas a realizar sobre dichos objetos

Servicios Rest

12

3 Definicioacuten de las URIs Para satisfacer el principio de direccionabilidad de los recursostendremos que definir las URIs que representaraacuten los endpoints de nuestros servicios yque constituiraacuten los puntos de entrada de los mismos

4 Definicioacuten de la representacioacuten de los recursos Puesto que los sistemas REST estaacutenorientados a la representacioacuten tendremos que definir el formato de los datos queutilizaremos para intercambiar informacioacuten entre nuestros servicios y clientes

5 Definicioacuten de los meacutetodos de acceso a los recursos Finalmente tendremos que decidirqueacute meacutetodos HTTP nos permitiraacuten acceder a las URIs que queremos exponer asiacutecomo queacute haraacute cada meacutetodo Es muy importante que en este paso nos cintildeamos alas restricciones que definen los principios RESTful que hemos indicado en apartadosanteriores

14 Un primer servicio JAX-RS

Vamos a ilustrar los pasos anteriores con un ejemplo concretamente definiremos una interfazRESTful para un sistema sencillo de gestioacuten de pedidos de un hipoteacutetico comercio por internetLos potenciales clientes de nuestro sistema podraacuten realizar compras modificar pedidosexistentes en nuestro sistema asiacute como visualizar sus datos personales o la informacioacuten sobrelos productos que son ofertados por el comercio

Modelo de objetos

A partir de los requerimientos del sistema obtenemos el modelo de objetos El modelode objetos de nuestro sistema de ventas por internet es bastante sencillo Cada pedidoen el sistema representa una uacutenica transaccioacuten de compra y estaacute asociada con un clienteparticular Los pedidos estaraacuten formados por una o maacutes liacuteneas de pedido Las liacuteneas de pedidorepresentan el tipo y el nuacutemero de unidades del producto adquirido

Basaacutendonos en esta descripcioacuten de nuestro sistema podemos extraer que los objetos denuestro modelo son Pedido Cliente LineaPedido y Producto Cada objeto de nuestromodelo tiene un identificador uacutenico representado por la propiedad id dada por un valor detipo entero La siguiente figura muestra un diagrama UML de nuestro modelo

Estamos interesados en consultar todos los pedidos realizados asiacute como cada pedido deforma individual Tambieacuten queremos poder realizar nuevos pedidos asiacute como actualizar

Servicios Rest

13

pedidos existentes El objeto ServicioPedidos representa las operaciones que queremosrealizar sobre nuestos objetos Pedido Cliente LineaPedido y Producto

Modelado de URIs

Lo primero que haremos para crear nuestra interfaz distribuida es definir y poner nombre acada uno de los endpoints de nuestro sistema En un sistemam RESTful los endpoints seraacutenlos recursos del sistema que identificaremos mediante URIs

En nuestro modelo de objetos queremos poder interactuar con Pedidos Clientes y ProductosEacutestos seraacuten por lo tanto nuestros recursos de nivel maacutes alto Por otro lado estamosinteresados en obtener una lista de cada uno de estos elementos de alto nivel asiacute comointeractuar con los elementos indiduales de cada tipo El objeto LineaPedido es un objetoagregado del objeto Pedido por lo que no lo consideraremos com un recurso de nivel superiorMaacutes adelante veremos que podremos exponerlo como un subrecurso de un Pedido particularpero por ahora asumiremos que estaacute oculto por el formato de nuestros datos Seguacuten estouna posible lista de URIs que expondraacute nuestro sistema podriacutea ser

bull pedidos

bull pedidosid

bull productos

bull productosid

bull clientes

bull clientesid

Fiacutejate que hemos representado como URIs los nombres en nuestromodelo de objetos Recuerda que las URIS no deberiacutean utilizarse comomini-mecanismos de RPC ni deberiacutean identificar operaciones En vez deeso tenemos que utilizar una combinacioacuten de meacutetodos HTTP y de datos(recursos) para modelar las operaciones de nuestro sistema RESTful

Definicioacuten del formato de datos

Una de las cosas maacutes importantes que tenemos que hacer cuando definimos la interfazRESTful es determinar coacutemo se representaraacuten los recursos que seraacuten accedidos por losusuarios de nuestro API REST Quizaacute XML sea el formato maacutes popular de la web y puede serprocesado por la mayor parte de los lenguajes de programacioacuten Como veremos maacutes adelanteJSON es otro formato popular menos verboso que XML y que puede ser interpretadodirectamente por JavaScript (lo cual es perfecto para aplicaciones Ajax por ejemplo) Porahora utilizaremos el formato XML en nuestro ejemplo

Generalmente tendriacuteamos que definir un esquema XML para cada representacioacuten quequeramos transimitir a traves de la red Un esquema XML define la gramaacutetica del formato dedatos Por simplicidad vamos a omitir la creacioacuten de esquemas asumiendo que los ejemplosque proporcionamos se adhieren a sus correspondientes esquemas

A continuacioacuten distinguiremos entre dos formatos de datos uno para las operaciones delectura y actualizacioacuten y otro para la operacioacuten de creacioacuten de recursos

Formato de datos para operaciones de lectura y modificacioacuten de los recursos

Las representaciones de los recursos Pedido Cliente y Producto tendraacuten un elemento XMLen comuacuten al que denominaremos link

Servicios Rest

14

ltlink rel=self href=httporgexpertojavagt

El elemento (o etiqueta) link indica a los clientes que obtengan un documento XML comorepresentacioacuten del recurso doacutende pueden interaccionar en la red con dicho recurso enparticular El atributo self le indica al cliente queacute relacioacuten tiene dicho enlace con la URIdel recurso al que apunta (informacioacuten contenida en el atributo href ) El valor self indicaque estaacute apuntando a siacute mismo Maacutes adelante veremos la utilidad del elemento link cuandoagreguemos informacioacuten en documentos XML maacutes grandes

El formato de representacioacuten del recurso Cliente podriacutea ser

ltcliente id=8gt ltlink rel=self href=httporgexpertojavaclientes8gt ltnombregtPedroltnombregt ltapellidosgtGarcia Perezltapellidosgt ltdirecciongtCalle del Pino 5ltdirecciongt ltcodPostalgt08888ltcodPostalgt ltciudadgtMadridltciudadgtltclientegt

El formato de representacioacuten del recurso Producto podriacutea ser

ltproducto id=34gt ltlink rel=self href=httporgexpertojavaproductos34gt ltnombregtiPhone 6ltnombregt ltpreciogt800ltpreciogt ltcantidadgt1ltcantidadgtltproductogt

Finalmente el formato de la representacioacuten del recurso Pedido podriacutea ser

ltpedido id=233gt ltlink rel=self href=httporgexpertojavapedidos233gt lttotalgt800lttotalgt ltfechagtDecember 22 2014 0656ltfechagt ltcliente id=8gt ltlink rel=self href=httporgexpertojavaclientes8gt ltnombregtPedroltnombregt ltapellidosgtGarcia Perezltapellidosgt ltdirecciongtCalle del Pino 5ltdirecciongt ltcodPostalgt08888ltcodPostalgt ltciudadgtMadridltciudadgt ltclientegt ltlineasPedidogt ltlineaPedido id=1gt ltproducto id=34gt ltlink rel=self href=httporgexpertojavaproductos34gt ltnombregtiPhone 6ltnombregt

Servicios Rest

15

ltpreciogt800ltpreciogt ltcantidadgt1ltcantidadgt ltproductogt ltlineaPedidogt ltlineasPedidogtltpedidogt

El formato de datos de un Pedido tiene en un primer nivel la informacioacuten del total conel importe total del pedido asiacute como la fecha en la que se hizo dicho pedido Pedido es unbuen ejemplo de composicioacuten de datos ya que un pedido incluye informacioacuten sobre el Clientey el Productos Aquiacute es donde el elemento ltlinkgt puede resultar particularmente uacutetil Si elusuario estaacute interesado en interaccionar con el Cliente que ha realizado el pedido o en uno delos productos del mismo se proporciona la URI necesaria para interactuar con cada uno dedichos recursos De esta forma cuando el usuario del API consulte un pedido podraacute ademaacutesacceder a informacioacuten adicional relacionada con la consulta realizada

Formato de datos para operaciones de creacioacuten de los recursos

Cuando estamos creando nuevos Pedidos Clientes o Productos no tiene mucho sentidoincluir un atributo id y un elemento link en nuestro documento XML El servidor seraacute elencargado de crear los ids cuando inserte nuestro nuevo objeto en la base de datos Tampococonocemos la URI del nuevo objeto creado ya que seraacute el servidor el encargado de generarloPor lo tanto para crear un nuevo Producto el formato de la informacioacuten podriacutea ser el siguiente

ltproductogt ltlink rel=self href=httporgexpertojavaclientes8gt ltnombregtiPhoneltnombregt ltpreciogt800ltpreciogtltproductogt

Asignacioacuten de meacutetodos HTTP

Finalmente tendremos que decidir queacute meacutetodos HTTP expondremos en nuestro servicio paracada uno de los recursos asiacute como definir queacute haraacuten dichos meacutetodos Es muy importanteno asignar funcionaldad a un meacutetodo HTTP que sobrepase los liacutemites impuestos por laespecificacioacuten de dicho meacutetodo Por ejemplo una operacioacuten GET sobre un recurso concretodeberiacutea ser de soacutelo lectura No deberiacutea cambiar el estado del recurso cuando invoquemos laoperacioacuten GET sobre eacutel Si no seguimos de forma estricta la semaacutentica de los meacutetodos HTTPlos clientes asiacute como cualquier otra herramienta administrativa no pueden hacer asuncionessobre nuestros servicios de forma que nuestro sistema se vuelve maacutes complejo

Veamos para cada uno de los meacutetodos de nuestro modelo de objetos cuales seraacuten las URIsy meacutetodos HTTP que usaremos para representarlos

Visualizacioacuten de todos los Pedidos Clientes o Productos

Los tres objetos de nuestro modelo Pedidos Clientes y Productos son accedidos ymanipulados de forma similar Los usuarios pueden estar interesados en ver todos losPedidos Clientes o Productos en el sistema Las siguientes URIs representan dichos objetoscomo un grupo

bull pedidos

Servicios Rest

16

bull productos

bull clientes

Para obtener una lista de Pedidos Clientes o Productos el cliente remoto realizara unallamada al meacutetodo HTTP GET sobre la URI que representa el grupo de objetos Un ejemplode peticioacuten podriacutea ser la siguiente

GET productos HTTP11

Nuestro servicio responderaacute con los datos que representan todos los Pedidos de nuestrosistema Una respuesta podriacutea ser eacutesta

HTTP11 200 OKContent-Type applicationxml

ltproductosgt ltproducto id=111gt ltlink rel=self href=httporgexpertojavaproductos111gt ltnombregtiPhoneltnombregt ltpreciogt64899ltpreciogt ltproductogt ltproducto id=222gt ltlink rel=self href=httporgexpertojavaproductos222gt ltnombregtMacbookltnombregt ltpreciogt159999ltpreciogt ltproductogt ltproductosgt

Un problema que puede darse con esta peticioacuten es que tengamos miles de Pedidos Clienteso Productos en nuestro sistema por lo que podemos sobrecargar a nuestro cliente y afectarnegativamente a los tiempos de respuesta Para mitigar esta problema permitiremos que elusuario especifique unos paraacutemetros en la URI para limitar el tamantildeo del conjunto de datosque se va a devolver

GET pedidosstartIndex=0ampsize=5 HTTP11GET productosstartIndex=0ampsize=5 HTTP11GET clientesstartIndex=0ampsize=5 HTTP11

En las oacuterdenes anteriores hemos definido dos paraacutemetros de peticioacuten startIndex ysize El primero de ellos es un iacutendice numeacuterico que representa a partir de queacute posicioacuten enla lista de Pedidos Clientes o Productos comenzaremos a enviar la informacioacuten al clienteEl paraacutemetro size especifica cuaacutentos de estos objetos de la lista queremos que nos seandevueltos

Estos paraacutemetros seraacuten opcionales de forma que el cliente no tiene que especificarlos en suURI

Obtencioacuten de Pedidos Clientes o Productos individuales

Ya hemos comentado previamente que podriacuteamos utilizar las siguientes URIs para obtenerPedidos Clientes o Productos

Servicios Rest

17

bull pedidosid

bull productosid

bull clientesid

En este caso usaremos el meacutetodo HTTP GET para recuperar objetos individuales en elsistema Cada invocacioacuten GET devolveraacute la informacioacuten del correspondiente objeto Porejemplo

GET pedidos233 HTTP11

Para esta peticioacuten el cliente estaacute interesado en obtener una representacioacuten del Pedido conidentificador 233 Las peticiones GET para Productos y Clientes podriacutean funcionar de formasimilar El mensaje de respuesta podriacutea parecerse a algo como esto

HTTP11 200 OKContent-Type applicationxml

ltpedido id=233gtltpedidogt

El coacutedigo de respuesta es 200 OK indicando que la peticioacuten ha tenido eacutexito La cabeceraContent-Type especifica el formato del cuerpo de nuestro mensaje como XML y finalmenteobtenemos la representacioacuten del Pedido solicitado

Creacioacuten de un Pedido Cliente o Producto

Para crear un Pedido Cliente o Producto utilizaremos el meacutetodo POST En este caso el clienteenviacutea una representacioacuten del nuevo objeto que se prentende crear a la URI padre de surepresentacioacuten y por lo tanto podremos omitir el identificador del recurso Por ejemplo

Peticioacuten POST para crear un pedidio

POST pedidos HTTP11Content-Type applicationxml

ltpedidogt lttotalgt19902lttotalgt ltfechagtDecember 22 2008 0656ltfechagt ltpedidogt

El servicio recibe el mensaje POST procesa la XML y crea un nuevo pedido en la basede datos utilizando un identificador generado de forma uacutenica Si bien esta aproximacioacutenfunciona perfectamente se le pueden plantear varias cuestiones al usuario iquestQueacute ocurre siel usuario quiere visualizar modificar o eliminar el pedido que acaba de crear iquestCuaacutel es elidentificador del nuevo recurso iquestCuaacutel es la URI que podemos utilizar para interactuar con elnuevo recurso Para resolver estas cuestiones antildeadiremos alguna informacioacuten al mensajede respuesta HTTP El cliente podriacutea recibir un mensaje similar a eacuteste

Respuesta de una peticioacuten POST para crear un pedido

HTTP11 201 Created

Servicios Rest

18

Content-Type applicationxmlLocation httporgexpertojavapedidos233

ltpedido id=233gt ltlink rel=self href=httporgexpertojavapedidos233gt lttotalgt19902lttotalgt ltfechagtDecember 22 2008 0656ltfechagt ltpedidogt

HTTP requiere que si POST crea un nuevo recurso se debe responder con un coacutedigo 201Created Tambieacuten se requieer que la cabecera Location en el mensaje de respuestaproporcione una URI al usuario que ha hecho la peticioacuten para que eacuteste pueda interactuar conla Peticioacuten que acaba de crear (por ejemplo para modificar dicho Pedido) Es opcional porparte del servidor devolver en la respuesta la representacioacuten del nuevo recurso creado Ennuestro ejemplo optamos por devolver una representacioacuten XML de la Peticion creada con elidentificador del atributo asiacute como el elemento link

Actualizacioacuten de un Pedido Cliente o Producto

Para realizar modificaciones sobre los recursos que ya hemos creado utilizaremos el meacutetodoPUT En este caso un ejemplo de peticioacuten podriacutea ser eacutesta

Peticioacuten PUT para modificar un pedidio

PUT pedidos233 HTTP11Content-Type applicationxml

ltproducto id=111gt ltnombregtiPhoneltnombregt ltpreciogt64999ltpreciogtltproductogt

Tal y como he hemos indicado anteriormente la operacioacuten PUT es idempotente Lo quesignifica que no importa cuaacutentas veces solicitemos la peticioacuten PUT el producto subyacentesigue permaneciendo con el mismo estado final

Cuando un recurso se modifica mediante PUT la especificacioacuten HTTP requiere que el servidorenviacutee un coacutedigo de respuesta 200 OK y un cuerpo de mensaje de respuesta o bien el coacutedigo204 No Content sin ninguacuten cuerpo de mensaje en la respuesta

En nuestro caso devolveremos un coacutedigo de estado 204 y un mensaje sin cuerpo derespuesta

RECUERDA Es importante NO confundir POST con PUTMuchas veces se confunden los meacutetodos PUT y POST El significado deestos meacutetodos es el siguiente

bull POST Publica datos en un determinado recurso El recurso debe existirpreviamente y los datos enviados son antildeadidos a eacutel Por ejemplo paraantildeadir nuevos pedidos con POST hemos visto que debiacuteamos hacerlocon el recurso lista de pedidos (pedidos) ya que la URI del nuevopedido todaviacutea no existe La operacioacuten NO es idempotente es decir si

Servicios Rest

19

antildeadimos varias veces el mismo alumno apareceraacute repetido en nuestralista de pedidos con URIs distintas

bull PUT Hace que el recurso indicado tome como contenido los datosenviados El recurso podriacutea no existir previamente y en caso de queexistiese seriacutea sobrescrito con la nueva informacioacuten A diferencia dePOST PUT es idempotente Muacuteltiples llamadas ideacutenticas a la mismaaccioacuten PUT siempre dejaraacuten el recurso en el mismo estado Laaccioacuten se realiza sobre la URI concreta que queremos establecer (porejemplo pedidos215) de forma que varias llamadas consecutivas conlos mismos datos tendraacuten el mismo efecto que realizar soacutelo una deellas

Borrado de un Pedido Cliente o Producto

Modelaremos el borrado de los recursos utilizando el meacutetodo HTTP DELETE El usuariosimplemente invocaraacute el meacutetodo DELETE sobre la URI que representa el objeto que queremoseliminar Este meacutetodo haraacute que dicho recurso ya no exista en nuestro sistema

Cuando eliminamos un recurso con DELETE la especificacioacuten requiere que se enviacutee un coacutedigode respuesta 200 OK y un cuerpo de mensaje de respuesta o bien un coacutedigo de respuesta204 No Content sin un cuerpo de mensaje de respuesta

En nuestro caso devolveremos un coacutedigo de estado 204 y un mensaje sin cuerpo derespuesta

Cancelacioacuten de un Pedido

Hasta ahora las operaciones de nuestro modelo de objetos encajan bastante bien enla especificacioacuten de los correspondientes meacutetodos HTTP Hemos utilzado GET para leerdatos PUT para realizar modificaciones POST para crear nuevos recursos y DELETE paraeliminarlos En nuestro sistema los Pedidos pueden eliminarse o tambieacuten cancelarse Yahemos comentado que el borrado de un recurso lo elimina completamente de nuestra basede datos La operacioacuten de cancelacioacuten solamente cambia el estado del Pedido y lo siguemanteniendo en el sistema iquestCoacutemo podriacuteamos modelar esta operacioacuten

Cuando modelamos una interfaz RESTful para las operaciones de nuestro modelo de objetosdeberiacuteamos plantearnos la siguiente pregunta iquestla operacioacuten es un estado del recurso Sila respuesta es siacute entonces deberiacuteamos modelar esta operacioacuten dentro del formato de losdatos

La cancelacioacuten de un pedido es un ejemplo perfecto de esto que acabamos de decir La claveestaacute en que esta operacioacuten en realidad es un estado especiacutefico del Pedido eacuteste puede estarcancelado o no Cuando un usuario accede a un Pedido puede desear conocer si el Pedidoha sido o no cancelado Por lo tanto la informacioacuten sobre la cancelacioacuten deberiacutea formar partedel formato de datos de un Pedido Asiacute antildeadiremos un nuevo elemento a la informacioacuten delPedido

ltpedido id=233gt ltlink rel=self href=httporgexpertojavapedidos233gt lttotalgt19902lttotalgt ltfechagtDecember 22 2008 0656ltfechagt ltcanceladogtfalseltcanceladogt

Servicios Rest

20

ltpedidogt

Ya que el estado cancelado se modela en el propio formato de datos modelaremos la accioacutende cancelacioacuten con una operacioacuten HTTP PUT que ya conocemos

PUT pedidos233 HTTP11Content-Type applicationxml

ltpedido id=233gt ltlink rel=self href=httporgexpertojavapedidos233gt lttotalgt19902lttotalgt ltfechagtDecember 22 2008 0656ltfechagt ltcanceladogttrueltcanceladogt ltpedidogt

En este ejemplo modificamos la representacioacuten del Pedido con el elemento ltcanceladogtcon valor true

Este patroacuten de modelado no siempre sirve en todos los casos Por ejemplo imaginemosque queremos ampliar el sistema de forma que borremos del sistema todos los pedidoscancelados No podemos modelar esta operacioacuten de la misma manera que la de cancelacioacutenya que esta operacioacuten no cambia el estado de nuestra aplicacioacuten (no es en siacute misma un estadode la aplicacioacuten)

Para resolver este problema podemos modelar esta nueva operacioacuten como un subrecursode pedidos y realizar un borrado de los pedidos cancelados mediante el meacutetodo HTTP POSTde dicho subrecurso de la siguiente forma

POST pedidoseliminacion HTTP11

Un efecto interesante de lo que acabamos de hacer es que puesto que ahora eliminaciones una URI podemos hacer que la interfaz de nuestro servicios RESTful evolucionen conel tiempo Por ejemplo la orden GET pedidoseliminacion podriacutea devolver la uacuteltima fechaen la que se procedioacute a eliminar todos los pedidos cancelados asiacute como queacute pedidosfueron cancelados iquestY si queremos antildeadir alguacuten criterio a la hora de realizar el borradode pedidos cancelados Podriacuteamos introducir paraacutemetros para indicar que soacutelo queremoseliminar aquellos pedidos que esteacuten cancelados en una fecha anterior a una dada Comovemos podemos mantener una interfaz uniforme y centildeirnos a las operaciones HTTP tal ycomo estaacuten especificadas y a la vez dotar de una gran flexiblidad a la interfaz de nuestrosistema RESTful Hablaremos con maacutes detalle de los subrecursos en la siguiente sesioacuten

Implementacioacuten del servicio Creacioacuten del proyecto Maven

Vamos a utilizar Maven para crear la estructura del proyecto que contendraacute la implementacioacutende nuestro servicio Rest Inicialmente podemos utilizar el mismo arquetipo con el que habeacuteistrabajado en sesiones anteriores Y a continuacioacuten modificaremos la configuracioacuten del ficheropomxml para implementar nuestros servicios

Una opcioacuten es generar la estructura del proyecto directamente desde liacutenea de comandos Elcomando es el siguiente (recuerda que debes escribirlo en una misma liacutenea Los caracteres

Servicios Rest

21

que aparecen en el comando no forman parte del mismo simplemente indican que no sedebe pulsar el retorno de carro)

mvn --batch-mode archetypegenerate -DarchetypeGroupId=orgcodehausmojoarchetypes -DarchetypeArtifactId=webapp-javaee7 -DgroupId=orgexpertojava -DartifactId=ejemplo-rest

En donde

bull archetypeGroupId y archetypeArtifactId son los nombres del groupId yartifactId del arquetipo Maven que nos va a generar la plantilla para nuestro proyecto

bull groupId y artifactId son los nombres que asignamos como groupId y artifactId denuestro proyecto En este caso hemos elegido los valores orgexpertojava y ejemplo-restrespectivamente

Si utilizamos IntelliJ para crear el proyecto tenemos que

1 Crear un nuevo proyecto (New Project)

2 Elegir el tipo de proyecto Maven

3 Crear el proyecto Maven a partir de un arquetipo con las siguientes coordenadas

bull GroupId orgcodehausmojoarchetypes

bull ArtifactId webapp-javaee7

bull Version 11

4 Indicar las coordenadas de nuestro proyecto

bull GroupId orgexpertojava

bull ArtifactId ejemplo-rest

bull Version 10-SNAPSHOT

5 Confirmamos los datos introducidos

6 Para finalizar especificamos el nombre de nuestro proyecto en IntelliJ

bull Project Name ejemplo-rest (este valor tambieacuten identificaraacute el nombre del moacutedulo enIntelliJ)

7 Por comodidad marcaremos Enable autoimport para importar automaacuteticamente cualquiercambio en el proyecto

Una vez que hemos creado el proyecto con IntelliJ el paso siguiente es cambiar laconfiguracioacuten del pommxl que nos ha generado el arquetipo para incluir las propiedadesdependencias pluginshellip que necesitaremos para implementar nuestros recursos REST

Como ya sabemos el fichero pomxml contiene la configuracioacuten que utiliza Maven paraconstruir el proyecto A continuacioacuten indicamos las modificaciones en el fichero pomxmlgenerado inicialmente para adecuarlo a nuestras necesidades particulares

bull Cambiamos las propiedades del proyecto (etiqueta ltpropertiesgt ) por

Servicios Rest

22

Propiedades del proyecto

ltpropertiesgt ltprojectbuildsourceEncodinggtUTF-8ltprojectbuildsourceEncodinggtltpropertiesgt

bull Indicamos las dependencias del proyecto (etiqueta ltdependenciesgt en donde seincluyen las libreriacuteas necesarias para la construccioacuten del proyecto) En nuestro casonecesitamos incluir la libreriacutea javaxjavaee-web-api70 que contiene el apiestaacutendar de javaee 7 Marcamos el aacutembito de la libreriacutea (etiqueta ltscopegt ) comoprovided Con esto estamos indicando que soacutelo necesitaremos el jar correspondientepara compilar el proyecto y por lo tanto no incluiremos dicho jar en el fichero wargenerado para nuestra aplicacioacuten ya que dicha libreriacutea ya estaraacute disponible desde elservidor de aplicaciones en el que residiraacute nuestra aplicacioacuten

Libreriacuteas utilizadas para construir el proyecto (dependencias)

ltdependenciesgt ltdependencygt ltgroupIdgtjavaxltgroupIdgt ltartifactIdgtjavaee-web-apiltartifactIdgt ltversiongt70ltversiongt ltscopegtprovidedltscopegt ltdependencygtltdependenciesgt

bull A continuacioacuten configuramos la construccioacuten del proyecto (etiqueta ltbuildgt ) de lasiguiente forma (cambiamos la configuracioacuten original por la que mostramos a continuacioacuten)

Configuracioacuten de la construccioacuten del proyecto

ltbuildgt lt-- Especificamos el nombre del war que seraacute usado como context root cuando despleguemos la aplicacioacuten --gt ltfinalNamegt$projectartifactIdltfinalNamegt

ltpluginsgt lt-- Compilador de java Utilizaremos la versioacuten 17 --gt ltplugingt ltgroupIdgtorgapachemavenpluginsltgroupIdgt ltartifactIdgtmaven-compiler-pluginltartifactIdgt ltversiongt31ltversiongt ltconfigurationgt ltsourcegt17ltsourcegt lttargetgt17lttargetgt ltconfigurationgt ltplugingt

lt-- Servidor de aplicaciones wildfly --gt ltplugingt ltgroupIdgtorgwildflypluginsltgroupIdgt ltartifactIdgtwildfly-maven-pluginltartifactIdgt ltversiongt102Finalltversiongt ltconfigurationgt lthostnamegtlocalhostlthostnamegt

Servicios Rest

23

ltportgt9990ltportgt ltconfigurationgt ltplugingt

lt-- Cuando generamos el war no es necesario que el fichero webxml esteacute presente --gt ltplugingt ltgroupIdgtorgapachemavenpluginsltgroupIdgt ltartifactIdgtmaven-war-pluginltartifactIdgt ltversiongt23ltversiongt ltconfigurationgt ltfailOnMissingWebXmlgtfalseltfailOnMissingWebXmlgt ltconfigurationgt ltplugingt ltpluginsgtltbuildgt

Implementacioacuten del servicio Recursos JAX-RS

Una vez que tenemos la estructura del proyecto implementaremos los recursos de nuestraaplicacioacuten que seraacuten clases Java que utilizaraacuten anotaciones JAX-RS para enlazar y mapearpeticiones HTTP especiacuteficas a meacutetodos java los cuales serviraacuten dichas peticiones Eneste caso vamos a ilustrar con un ejemplo una posible implementacioacuten para el recursoCliente Tenemos que diferenciar entre las clases java que representaraacuten entidades denuestro dominio (objetos java que representan elementos de nuestro negocio y que seraacutenalmacenados tiacutepicamente en una base de datos) de nuestros recursos JAX-RS que tambieacutenseraacuten clases java anotadas y que utilizaraacuten objetos de nuestro dominio para llevar a cabo lasoperaciones expuestas en el API RESTful que hemos disentildeado

Asiacute por ejemplo implementaremos las clases

bull Clientejava representa una entidad del dominio Contiene atributos y suscorrespondientes getters y setters

bull ClienteResourcejava representa las operaciones RESTful sobre nuestro recurso Clienteque hemos definido en esta sesioacuten Es una clase java con anotaciones JAX-RS que nospermitiraacute insertar modificar borrar consultar un cliente asiacute como consultar la lista declientes de nuestro sistema

Clases de nuestro dominio (entidades) Clientejava

La clase que representa nuestra entidad del dominio Cliente es una clase java plana con suscorrespondientes atributos y getters y setters

Implementacioacuten del dominio clientejava

package orgexpertojavadomain

XmlRootElement(name=cliente)XmlAccessorType(XmlAccessTypeFIELD)public class Cliente private int id private String nombre private String apellidos private String direccion

Servicios Rest

24

private String codPostal private String ciudad

public int getId() return id public void setId(int id) thisid = id

public String getNombre() return nombre public void setNombre(String nom) thisnombre = nom

public String getApellidos() return apellidos public void setApellidos(String apellidos) thisapellidos = apellidos

public String getDireccion() return direccion public void setDireccion(String dir) thisdireccion = dir

public String getCodPostal() return codPostal public void setCodPostal(String cp) thiscodPostal = cp

public String getCiudad() return ciudad public void setCiudad(String ciudad) thisciudad = ciudad

Hemos anotado la clase Cliente con XmlRootElement y XmlAccessorType Hablaremos de estas anotaciones en sesiones posteriores las cuales se encargan delserializadodeserializado del cuerpo del mensaje (en formato xml o json) a nuestra clase javaCliente

Clases de nuestro servicio RESTful ClienteResourcejava

Una vez definido el objeto de nuestro dominio que representaraacute un objeto Cliente vamos a vercoacutemo implementar nuestros servicio JAX-RS para que diferentes usuarios de forma remotapuedan interactuar con nuestra base de datos de clientes

La implementacioacuten del servicio es lo que se denomina una resource class que no es maacutesque una clase java que utiliza anotaciones JAX-RS

Por defecto una nueva instancia de nuestra clase de recursos se crea para cada peticioacuten aese recurso Es lo que se conoce como un objeto per-request Esto implica que se crea unobjeto Java para procesar cada peticioacuten de entrada y se destruye automaacuteticamente cuandola peticioacuten se ha servido Per-request tambieacuten implica sin estado ya que no se guarda elestado del servicio entre peticiones

Comencemos con la implementacioacuten del servicio

package orgexpertojavaservices

import

Path(clientes)public class ClienteResource

private static MapltInteger Clientegt clienteDB = new ConcurrentHashMapltInteger Clientegt()

Servicios Rest

25

private static AtomicInteger idContador = new AtomicInteger()

Podemos observar que ClientResource es una clase java planay que no implementaninguna interfaz JAX-RS particular La anotacioacuten javaxwsrsPath indica que laclase ClienteResource es un servicio JAX-RS Todas las clases que queramos que seanreconocidas como servicios JAX-RS tienen que tener esta anotacioacuten Fiacutejate que estaanotacioacuten tiene el valor clientes Este valor representa la raiacutez relativa de la URI de nuestroservicio RESTful Si la URI absoluta de nuestro servidor es por ejemplo httpexpertojavaorglos meacutetodos expuestos por nuestra clase ClienteResource estariacutean disponibles bajo la URIhttpexpertojavaorgclientes

En nuestra clase definimos un Mapa para el campo ClienteDB quealmacenaraacute en memoria a los objetos Cliente de nuestro sistema Utilizamos unjavautilconcurrentConcurrentHashMap como tipo de clienteDB ya que nuestro recursoseraacute accedido concurrentemente por los usuarios de nuestro servicio rest El campoidContador lo utilizaremos para generar nuevos identificadores de nuestros objetos Clientecreados El tipo de este campo es javautilconcurrentatomicAtomicInteger para garantizarque siempre generaremos un identificador uacutenico aunque tengamos peticiones concurrentes

Justificacioacuten del caracter static de los atributosComo nuestros objetos seraacuten de tipo per-request el runtime de JAX-RScrearaacute una instancia de ClienteResource para cada pecioacuten que se realicesobre nuestro servicio La maacutequina virtual de java ejecutaraacute cada peticioacutena nuestro servicio en un hilo (thread) diferente permitiendo asiacute el accesoconcurrente a nuestro recurso Puesto que hemos decidido almacenaren memoria la informacioacuten de los clientes necesitamos que los atributosclienteDB y idContador sean static para que todas las instancias deClienteResource tengan acceso a la lista de clientes en memoria y nohaya problemas de concurrencia En realidad lo que estamos haciendocon eacutesto es permitir que el servicio guarde el estado entre peticiones Enun sistema real ClienteResource probablemente interactuacutee con una basede datos para almacenar y recuperar la informacioacuten de los clientes y porlo tanto no necesitaremos guardar el estado entre peticiones

Una mejor solucioacuten seriacutea no utilizar variables estaacuteticas y definir nuestroservicio como singleton Si hacemos eacutesto solamente se creariacutea unainstancia de clienteResource y estariacuteamos manteniendo el estado delas peticiones En la siguiente sesioacuten explicaremos coacutemo configurar unservicio como singleton Por simplicidad de momento optaremos por laopcioacuten de que los objetos RESTful sean per-request

Creacioacuten de clientes

Para implementar la creacioacuten de un nuevo cliente utilizamos el mismo modelo que hemosdisentildeado previamente Una peticioacuten HTTP POST enviacutea un documento XML que representaal cliente que queremos crear

El coacutedigo para crear nuevos clientes en nuestro sistema podriacutea ser eacuteste

POST

Consumes(applicationxml)

public Response crearCliente(Cliente cli)

Servicios Rest

26

el paraacutemetro cli se instancia con los datos del cliente del body del mensaje HTTP idContador++ clisetId(idContadorincrementAndGet())

clienteDBput(cligetId() cli)

Systemoutprintln(Cliente creado + cligetId()) return Responsecreated(URIcreate(clientes

+ cligetId()))build()

se recibe una peticioacuten POSTel cuerpo de la peticioacuten debe tener formato xmlcontiene la informacioacuten del documento xml del cuerpo de la peticioacuten de entradase antildeade el nuevo objeto Cliente a nuestro mapa de clientes (clienteDB)este meacutetodo se ejectua en el servidor por lo que el mensaje soacutelo seraacute visible por ejemplosi consultamos los mensajes generados por el servidor durante la ejecucioacuten el meacutetododevuelve un coacutedigo de respuesta 201 Created junto con una cabecera Locationapuntando a la URI absoluta del cliente que acabamos de crear

Vamos a explicar la implementacioacuten con maacutes detalle

Para enlazar peticiones HTTP POST con el meacutetodo crearCliente() lo anotamos conla anotacioacuten javaxwsrsPOST La anotacioacuten Path combinada con la anotacioacutenPOST enlaza todas las peticiones POST dirigidas a la URI relativa clientes al meacutetodo JavacrearCliente()

La anotacioacuten javaxwsrsConsumes aplicada a crearCliente() especifica queacute media typeespera el meacutetodo en el cuerpo del mensaje HTTP de entrada Si el cliente incluye en su peticioacutenPOST un media type diferente de XML se enviacutea un coacutedigo de error al cliente

El meacutetodo crearCliente() tiene un paraacutemetro de tipo Cliente En JAX-RS cualquier paraacutemetrono anotado con anotaciones JAX-RS se considera que es una representacioacuten del cuerpodel mensaje de la peticioacuten de entrada HTTP Las anotaciones que hemos introducido en laclase Cliente de nuestro dominio realizan el trabajo de convertir el documento xml contenidoen el cuerpo de la peticioacuten htpp de entrada en una instancia de nuestra clase Cliente

Solamente UNO de los paraacutemetros del meacutetodo Java puede representar elcuerpo del mensaje de la peticioacuten HTTP Esto significa que el resto deparaacutemetros deben anotarse con alguna anotacioacuten JAX-RS que veremosmaacutes adelante

El meacutetodo crearCliente() devuelve una respuesta de tipo javaxwsrscoreResponse El meacutetodo estaacutetico Responsecreated() crea un objeto Response que contiene un coacutedigode estado 201 Created Tambieacuten antildeade una cabecera Location con un valor similar ahttpexpertojavaorgclientes123 dependiendo del valor del valor de base de la raiacutez dela URI del servidor y el identificador generado para el objeto Cliente (en este caso se habriacuteagenerado el identificador 123) Maacutes adelante explicaremos con detalle el uso de la claseResponse

Consulta de clientes

A continuacioacuten mostramos un posible coacutedigo para consultar la informacioacuten de un cliente

GET

Servicios Rest

27

Path(id)Produces(applicationxml)public Cliente recuperarClienteId(PathParam(id) int id) final Cliente cli = clienteDBget(id) if (cli == null) throw new WebApplicationException(ResponseStatusNOT_FOUND) return new Cliente(cligetId() cligetNombre() cligetApellidos() cligetDireccion() cligetCodPostal() cligetCiudad())

En este caso anotamos el meacutetodo recuperarClienteId() con la anotacioacutenjavaxwsrsGET para enlazar las operaciones HTTP GET con este meacutetodo Java

Tambieacuten anotamos recuperarClienteId() con la anotacioacuten javaxwsrsPRODUCES Estaanotacioacuten indica a JAX-RS que valor tiene la cabecera HTTP Content-Type en la respuestaproporcionada por la operacioacuten GET En este caso estamos indicando que seraacute de tipoapplicationxml

En la implementacioacuten del meacutetodo utilizamos el paraacutemetro id para consultar si existe unobjeto Cliente en nuestro mapa clienteDB Si dicho cliente no existe lanzaremos la excepcioacutenjavaxwsrsWebApplictionException Esta excepcioacuten provocaraacute que el coacutedigo derespuesta HTTP sea 404 Not Found y significa que el recurso cliente requerido no existeDiscutiremos maacutes adelante el tema del manejo de excepciones

Modificacioacuten de clientes

Vamos a mostrar coacutemo seriacutea el coacutedigo para modificar un cliente

PUTPath(id)Consumes(applicationxml)public void modificarCliente(PathParam(id) int id Cliente nuevoCli)

Cliente actual = clienteDBget(id) if (actual == null) throw new WebApplicationException(ResponseStatusNOT_FOUND)

actualsetNombre(nuevoCligetNombre()) actualsetApellidos(nuevoCligetApellidos()) actualsetDireccion(nuevoCligetDireccion()) actualsetCodPostal(nuevoCligetCodPostal()) actualsetCiudad(nuevoCligetCiudad())

Anotamos el meacutetodo modificarCliente() con javaxwsrsPUT para enlazar laspeticiones HTTP PUT a este meacutetodo Al igual que hemos hecho con recuperarClienteId() elmeacutetodo modificarCliente() estaacute anotado adicionalmente con Path de forma que podamosatender peticiones a traveacutes de las URIs clientesid

El meacutetodo modificarCliente() tiene dos paraacutemetros El primero es un paraacutemetro id querepresenta el objeto Cliente que estamos modificando Al igual que ocurriacutea con el meacutetodorecuperarClienteId() utilizamos la anotacioacuten PathParam para extraer el identificador a

Servicios Rest

28

partir de la URI de la peticioacuten de entrada El segundo paraacutemetro es un objeto Cliente querepresenta el cuerpo del mensaje de entrada ya que no tiene ninguna anotacioacuten JAX-RS

El meacutetodo intenta encontrar un objeto Cliente en nuestro mapa clienteDB Si no existeprovocamos una WebApplicationException que enviaraacute una respuesta al usuario con elcoacutedigo 404 Not Found Si el objeto Cliente existe modificamos nuestro objeto Clienteexistente con los nuevos valores que obtenemos de la peticioacuten de entrada

Construccioacuten y despliegue del servicio

Una vez implementado nuestro servicio RESTful necesitamos poner en marcha el procesode construccioacuten El proceso de construccioacuten compilaraacutehellip empaquetaraacute hellip y finalmente nospermitiraacute desplegar nuestro servicio en el servidor de aplicaciones

Para poder empaquetar nuestro servicio RESTful como un war que se desplegaraacute en elservidor de aplicaciones vamos a incluir un proveedor de servicios JAX-RS en el descriptorde despliegue de nuestra aplicacioacuten (fichero webxml) En la siguiente sesioacuten justificaremos laexistencia de dicho proveedor (que seraacute un servlet) y explicaremos el modelo de desplieguede los servicios JAX-RS Los pasos a seguir desde IntelliJ para configurar el despliegue denuestro servicio son

bull Antildeadimos el directorio WEB-INF como subdirectorio de webapp

bull Nos vamos a FileProject StructurehellipFacetsWeb y antildeadimos el fichero webxml (enel panel Deployment descriptors pulsamos sobre + y seleccionamos webxml) Editamoseste fichero para antildeadir el servlet que serviraacute las peticiones de nuestros servicios RESTindicando cuaacutel seraacute la ruta en la que estaraacuten disponibles dichos servicios (en nuestroejemplo indicaremos la ruta rest) Dicha ruta es relativa a la ruta del contexto de nuestraaplicacioacuten y que por defecto es el nombre del artefacto war desplegado que hemosindicado en la etiqueta ltfinalNamegt dentro del ltbuildgt del fichero de configuracioacuten deMaven (pomxml)

Contenido del fichero webxml

ltweb-app version=30 xmlns=httpjavasuncomxmlnsjavaee xmlnsxsi=httpwwww3org2001XMLSchema-instance xsischemaLocation=httpjavasuncomxmlnsjavaee httpjavasuncomxmlnsjavaeeweb-app_3_0xsdgt lt-- One of the way of activating REST Services is adding these lines the server is responsible for adding the corresponding servlet automatically if the src folder has the Annotations to receive REST invocation--gt ltservlet-mappinggt ltservlet-namegtjavaxwsrscoreApplicationltservlet-namegt lturl-patterngtrestlturl-patterngt ltservlet-mappinggtltweb-appgt

A continuacioacuten ya estamos en disposicioacuten de iniciar la construccioacuten del proyecto con Mavenpara compilar empaquetar y desplegar nuestro servicio en Wildfly

Si utilizamos el terminal la secuencia de pasos para empaquetar y desplegar nuestroproyecto seriacutean

cd ejemplo-rest

Servicios Rest

29

mvn package

usrlocalwildfly-821Finalbinstandalonesh

mvn wildflydeploy

Nos situamos en el directorio que contiene el pomxml de nuestro proyectoEmpaquetamos el proyecto (obtendremos el war)Arrancamos el servidor wildflyDesplegamos el war generado en el servidor wildfly

Secuencia correcta de acciones

En ejecuciones posteriores despueacutes de realizar modificaciones en nuestro coacutedigo esrecomendable ejecutar mvn clean previamente al empaquetado del proyecto

Por lo tanto y suponiendo que el servidor de aplicaciones ya estaacute en marcha lasecuencia de acciones (comandos maven) que deberiacuteamos realizar para asegurarnosde que vamos a ejecutar exactamente la aplicacioacuten con los uacuteltimos cambios quehayamos introducido son

bull mvn wildflyundeploy

bull mvn clean

bull mvn package

bull mvn wildflydeploy

Tambieacuten podriacuteamos realizar todas estas acciones con un uacutenico comando maven

bull mvn wildflyundeploy clean package wildflydeploy

Si utilizamos IntelliJ antildeadiremos un nuevo elemento de configuracioacuten de ejecucioacuten desdeRunEdit Configurations Pulsamos el icono + y antildeadimos la configuracioacuten de tipo JBosssServerLocal Podemos ponerle por ejemplo como nombre Wilfdly start A continuacioacutenconfiguramos la ruta del servidor wildfly como usrlocalwildfly-821Final

Cuando lancemos este elemento de ejecucioacuten desde IntelliJ automaacuteticamente seconstruiraacute el proyecto (obtendremos el war) y arrancaremos wildfly Para desplegarel war utlizaremos la ventana Maven Projects y haremos doble click sobre ejemplo-restPluginswildflywildflydeploy

Probando nuestro servicio

Podemos probar nuestro servicio de varias formas Vamos a mostrar como hacerlodirectamente desde liacutenea de comandos utilizando IntelliJ o bien utilizando la herramientaPostman (que teneacuteis disponible desde el navegador Chrome)

Invocacioacuten del servicio desde liacutenea de comandosUtilizaremos la herramienta curl Por ejemplo para realizar una insercioacuten de un cliente elcomando seriacutea

Servicios Rest

30

curl -i -H Accept applicationxml -H Content-Type applicationxml -X POST -d clientexml httplocalhost8080ejemplo-restrestclientes

En donde

-iTambieacuten se puede utilizar la opcioacuten equivalente --include Indica que se debe incluirlas cabeceras HTTP en la respuesta recibida Recuerda que la peticioacuten POST devuelveen la cabecera Location el enlace del nuevo recurso creado (puede hacerlo en unacabedera Location o como un campo ltlinkgt del elemento creado en el cuerpo delmensaje lo veremos maacutes adelante) Esta informacioacuten seraacute necesaria para poder consultarla informacioacuten del nuevo cliente creado

-HIndica un par cabecera_valor_ En nuestro caso lo utilizamos para especificar los valoresde las cabeceras HTTP Accept y Content-Type

-XIndica el meacutetodo a invocar (GET POST PUThellip)

-dTambieacuten se puede utilizar --data Indica cuaacuteles son los datos enviados en el mensajede entrada en una peticioacuten POST Si los datos especificados van precedidos por estamos indicando que dichos datos estaacuten en un fichero Por ejemplo en la orden anteriorescribimos en el fichero clientexml los datos del cliente que queremos antildeadir en nuestrosistema

El contenido del fichero clientexml podriacutea ser eacuteste

ltxml version=10 encoding=UTF-8gtltclientesgt ltclientegt ltnombregtPepe ltnombregt ltapellidosgtGarcia Lopezltapellido1gt ltdirecciongtCalle del pino 3ltapellido2gt ltcodPostalgt0001ltcodPostalgt ltciudadgtAlicanteltciudadgt ltclientegtltclientesgt

Finalmente en la orden indicamos la URI a la que queremos acceder en este caso

httplocalhost8080ejemplo-restrestclientes

Una vez insertado el cliente podemos recuperar el cliente utilizando el enlace que se incluyeen la cabecera de respuesta Location

curl -i -H Accept applicationxml -H Content-Type applicationxml -X GET httplocalhost8080ejemplo-restrestclientes1

Servicios Rest

31

Invocacioacuten del servicio desde IntelliJIntelliJ nos proporciona una herramienta para probar servicios REST desde ToolsTestRESTful Web Service Desde esta nueva ventana podremos invocar al servicio RESTindicando el tipo de peticioacuten HTTP asiacute como las cabeceras y cuerpo de la peticioacuten

La siguiente figura muestra la elaboracioacuten de una peticioacuten POST a nuestro servicio REST

A continuacioacuten mostramos la ejecucioacuten de una peticioacuten GET

Cuando realizamos una peticioacuten POST debemos indicar el contenido del cuerpo del mensajeEn la siguiente figura observamos que tenemos varias opciones disponibles como por ejemploteclear directamente dicho contenido (opcioacuten Text) o bien subir dicha informacioacuten desdeun fichero en nuestro disco duro (opcioacuten File Contents) Podemos ver que hemos elegido estauacuteltima opcioacuten para probar nuestro servicio

Invocacioacuten del servicio desde PostmanOtra alternativa sencilla para probar nuestro servicio REST es la herramienta postmanque podemos lanzar desde el navegador en nuestro caso Chrome

Accederemos a la aplicacioacuten desde la barra de marcadores seleccionando Aplicaciones yy a continuacioacuten pulsaremos sobre el icono Postman

El aspecto de la herramienta es el que mostramos a continuacioacuten

Servicios Rest

32

Postman a diferencia de las alternativas anteriores nos permitiraacute guardar un historial depeticiones de forma que podamos repetir la ejecucioacuten de nuestros tests exactamente de lamisma forma aunque no de forma automaacutetica sino que tenemos que lanzar manualmentecada test que queramos volver a ejecutar

Tambieacuten podemos crear colecciones que no son maacutes que carpetas que contienen unconjunto de peticiones de nuestro historial Por ejemplo podemos crear la coleccioacuten s1-rest-ejercicio1 en donde guardaremos todas las peticiones que hayamos hecho sobre el ejercicio1 de la primera sesioacuten de rest

Podeacuteis crearos una cuenta gratuita para almacener y gestionar vuestras peticiones rest Unavez que tengaacuteis creadas varias colecciones Postman nos permite guardarlas en nuestrodisco duro en formato json

Servicios Rest

33

15 Ejercicios

Antes de empezar a crear los proyectos debes descargarte el repositorio git java_uaejercicios-rest-expertojava en el que vas a implementar los ejercicios relativos a laasignatura de Servicios REST El proceso es el mismo que el seguido en sesiones anteriores

1 Accedemos al repositorio y realizamos un Fork en nuestra cuenta personal (asiacute podremostener una copia con permisos de escritura)

2 Realizamos un Clone en nuestra maacutequina

$ git clone httpsbitbucketorgltalumnogtejercicios-rest-expertojava

De esta forma se crea en nuestro ordenador el directorio ejercicios-rest-expertojavay se descarga en eacutel un proyecto IntelliJ en el que iremos antildeadiendo MOacuteDULOS para cada unode los ejercicios Contiene tambieacuten el fichero gitignore asiacute como diferentes moacutedulos conlas plantillas que vayamos a necesitar para realizar los ejercicios

A partir de este momento se puede trabajar con dicho proyecto y realizar Commit y Pushcuando sea oportuno

$ cd ejercicios-rest-expertojava$ git add $ git commit -a -m Mensaje de commit$ git push origin master

Los MOacuteDULOS IntelliJ que iremos antildeadiendo tendraacuten todos el prefijo sx- siendo x elnuacutemero de la sesioacuten correspondiente (por ejemplo s1-ejercicio s2-otroEjerciciohellip)

Servicio REST ejemplo (0 puntos)

Para familiarizarnos con las peticiones http POST PUT GET DELETE se proporciona elMOacuteDULO s1-ejemplo-rest con la implementacioacuten de un servicio rest que podeacuteis probar biendesde liacutenea de comandos con la utilidad curl desde el navegador con la herramienta postman o bien desde IntelliJ con el cliente REST incluido en el IDE

En el directorio srcmainresources de dicho moacutedulo teneacuteis un fichero de texto(instruccionestxt) con las instrucciones para construir desplegar y probar la aplicacioacuten deejemplo

Servicio REST saludo (1 punto)

Vamos a implementar un primer servicio RESTful muy sencillo Para ello seguiremos lassiguientes indicaciones

bull Creamos un MOacuteDULO Maven con IntelliJ (desde el directorio ejercicios-rest-expertojava ) con el arquetipo webapp-javaee7 tal y como hemos visto en losapuntes de la sesioacuten Las coordenadas del artefacto Maven seraacuten

GroupId orgexpertojava

ArtifactId s1-saludo-rest

Servicios Rest

34

version 10-SNAPSHOT

bull Configuramos el pommxl del proyecto para poder compilar empaquetar y desplegarnuestro servicio Consulta los apuntes para ver cuaacutel debe ser el contenido de las etiquetasltpropertiesgt ltdependenciesgt y ltbuildgt

bull Creamos la carpeta WEB-INF y antildeadimos el fichero de configuracioacuten webxml tal y comohemos visto en los apuntes (esto seraacute necesario para configurar el despliegue) En estecaso queremos mapear los servicios REST contenidos en el paquete orgexpertojavaal directorio recursos dentro de nuestro contexto (recuerda que el contexto de nuestraaplicacioacuten web vendraacute dado por el valor de la etiqueta ltfinalNamegt anidada dentro deltbuildgt)

bull Creamos un recurso de nombre HolaMundoResource que se mapee a la direccioacuten holamundo Implementar un meacutetodo de forma que al acceder a eacutel por GET nos devuelvaen texto plano (textplain) el mensaje Hola mundo Una vez desplegada la aplicacioacutenen el servidor WildFly prueba el servicio mediante la utilidad Postman desde ChromeComprobar que la invocacioacuten

GET httplocalhost8080saludo-restholamundo

Devuelve como cuerpo del mensaje Hola mundo

bull Vamos a antildeadir un segmento variable a la ruta Implementa un meacutetodo GET nuevode forma que si accedemos a holamundonombre antildeade el nombre indicado al saludo(separado por un espacio en blanco y seguido por )

Una vez desplegada la aplicacioacuten en el servidor WildFly prueba el servicio mediante la utilidadTest RESTFul Web Service de IntelliJ o con Postman Comprobar que la invocacioacuten

GET httplocalhost8080saludo-restholamundopepe

Devuelve como cuerpo del mensaje Hola mundo pepe

bull Hacer que se pueda cambiar el saludo mediante un meacutetodo PUT El nuevo saludo llegaraacutetambieacuten como texto plano en el cuerpo de la peticioacuten y posteriores invocaciones a losmeacutetodos GET utilizaraacuten el nuevo saludo Almacenaremos el nuevo saludo en una variableestaacutetica de nuestro recurso iquestQueacute pasa si no lo es (lo hemos explicado en los apuntespuedes hacer la prueba para ver queacute ocurre si la variable no es estaacutetica)

Una vez desplegada la aplicacioacuten en el servidor WildFly prueba el servicio con Postmano bien mediante la utilidad Test RESTFul Web Service de IntelliJ Realizar las siguientesinvocaciones (en este orden)

PUT httplocalhost8080saludo-restholamundoy en el cuerpo del mensaje Buenos diacuteas

GET httplocalhost8080saludo-restholamundoGET httplocalhost8080saludo-restholamundopepe

Servicios Rest

35

La segunda invocacioacuten debe devolver como cuerpo del mensaje Buenos dias Al ejecutarla tercera invocacioacuten el cuerpo del mensaje de respuesta deberiacutea ser Buenos diasPepe

Servicio REST foro (1 punto)

Vamos a implementar un servicio RESTful que contemple las cuatro operaciones baacutesicas(GET PUT POST y DELETE) Se trata de un foro con en el que los usuarios pueden intervenirde forma anoacutenima en diferentes conversaciones

Primero debes crear un nuevo moacutedulo Maven configurar el pomxml asiacute como el ficherowebxml de la misma forma que hemos hecho en el ejercicio anterior pero para esteejercicio

bull Las coordenadas del moacutedulo Maven seraacuten

GroupId orgexpertojava

ArtifactId s1-foro-rest

version 10-SNAPSHOT

bull Nuestros servicios REST estaraacuten disponibles en la URI httplocalhost8080s1-foro-rest

El foro estaraacute formado por diferentes mensajes Por lo tanto el modelo del dominio de nuestraaplicacioacuten estaraacute formado por la clase Mensaje que contendraacute un identificador y una cadenade caracteres que representaraacute el contenido del mensaje (recuerda que debes implementarlos correspondientes getters y setters)

Por simplicidad vamos a almacenar los mensajes de nuestro foro en memoria Estos estaraacutendisponibles desde la clase DatosEnMemoria que contendraacute la variable estaacutetica

static MapltInteger Mensajegt datos = new HashMapltInteger Mensajegt()

Los servicios que proporcionaraacute el foro estaraacuten implementados en la claseMensajeResource Se accederaacute a ellos traveacutes de la ruta relativa a la raiacutez de nuestrosservicios mensajes Concretamente podremos realizar las siguientes operaciones

bull Antildeadir un nuevo mensaje al foro con la URI relativa a la raiacutez de nuestros servicios mensajes El texto del mensaje estaraacute en el cuerpo de la peticioacuten y el tipo MIME asociadoseraacute textplain (contenido de la cabecera Content-type de la peticioacuten HTTP)Nuestra respuesta debe incluir en la cabecera Location de la respuesta HTTP la URIdel nuevo recurso creado Utiliza para ello la clase Response tal y como hemos mostradoen el coacutedigo de ejemplo proporcionado para el ejercicio anterior Hablaremos con detallesobre esta clase en sesiones posteriores

Recuerda que para acceder al cuerpo de la peticioacuten basta con definir unparaacutemetro de tipo String JAX-RS automaacuteticamente lo instanciaraacute a partirdel cuerpo de la peticioacuten y lo convertiraacute en un objeto de tipo String

bull Modificar un mensaje determinado con un identificador con valor id a traveacutes de la URIrelativa a la raiacutez de nuestros servicios mensajesid ( id debe ser por tanto unsegmento de ruta variable) Si no existe ninguacuten mensaje con el identificador id se lanzaraacutela excepcioacuten WebApplicationException(ResponseStatusNOT_FOUND)

Servicios Rest

36

bull Borrar un mensaje determinado con un identificador con valor id atraveacutes de la URI relativa a la raiacutez de nuestros servicios mensajesid Igual que en el caso anterior si el identificador proporcionado no secorresponde con el de ninguacuten mensaje del foro se lanzaraacute la excepcioacutenWebApplicationException(ResponseStatusNOT_FOUND)

bull Consultar todos los mensajes del foro (la URI relativa seraacute mensajes ) El resultado semostraraacute en tantas liacuteneas como mensajes Cada mensaje iraacute precedido de su identificadorTambieacuten se informaraacute del nuacutemero total de mensajes en el foro (La respuesta seraacute unacadena de caracteres Al final del ejercicio mostramos un ejemplo de mensaje de respuestapara esta operacioacuten)

bull Consultar un mensaje determinado con un identificador con valor id a traveacutes dela URI relativa a la raiacutez de nuestros servicios mensajesid Si el identificadorproporcionado no se corresponde con el de ninguacuten mensaje del foro se lanzaraacute laexcepcioacuten WebApplicationException(ResponseStatusNOT_FOUND)

Prueba el servicio utilizando Postman o el cliente de IntelliJ para servicios REST con lassiguientes entradas

bull Crea los mensajes Mensaje numero 1 Mensaje numero 2 Mensaje numero 3 en esteorden

bull Consulta los mensajes del foro El resultado debe ser

1 Mensaje numero 1

2 Mensaje numero 2

3 Mensaje numero 3

Numero total de mensajes = 3

bull Cambia el mensaje con identificador 2 por Nuevo mensaje numero 2

bull Consulta los mensajes del foro El resultado debe ser

1 Mensaje numero 1

2 Nuevo Mensaje numero 2

3 Mensaje numero 3

Numero total de mensajes = 3

bull Borra el mensaje con identificador 3

bull Consulta el mensaje con el identificador 3 Se debe obtener una respuesta 404 NotFound

bull Consulta los mensajes del foro El resultado debe ser

1 Mensaje numero 1

2 Nuevo Mensaje numero 2

Numero total de mensajes = 2

bull Antildeade el mensaje Mensaje final Vuelve a consultar los mensajes el resultado debe ser

1 Mensaje numero 1

Servicios Rest

37

2 Nuevo Mensaje numero 2

4 Mensaje final

Numero total de mensajes = 3

Para evitar problemas con el id generado si hemos borrado mensajeslo maacutes sencillo es que el identificador vaya incrementaacutendose siemprecon cada nuevo mensaje Esto puede hacer que queden huecos en lanumeracioacuten como en el ejemplo anterior

Servicios Rest

38

2 Anotaciones baacutesicas JAX-RS El modelo de despliegue

Ya hemos visto como crear un servicio REST baacutesico Ahora se trata de analizar con maacutesdetalle aspectos fundamentales sobre la implementacioacuten de los servicios Comenzaremos pordetallar los usos de la anotacioacuten Path que es la que nos permite etiquetar una clase Javacomo un recurso REST sobre el que podremos realizar las operaciones que hemos identificadoen la sesioacuten anterior Tambieacuten hablaremos algo maacutes sobre las anotaciones Produces yConsumes que ya hemos utilizado para implementar nuestro primer servicio

En segundo lugar hablaremos sobre la extraccioacuten de informacioacuten de las peticiones HTTP ycoacutemo podemos inyectar esa informacioacuten en nuestro coacutedigo java Esto nos permitiraacute servir laspeticiones sin tener que escribir demasiado coacutedigo adicional

Finalmente explicaremos maacutes detenidamente coacutemo configurar el despliegue de nuestraaplicacioacuten REST de forma que sea portable

21 iquestCoacutemo funciona el enlazado de meacutetodos HTTP

JAX-RS define cinco anotaciones que se corresponden con operaciones HTTP especiacuteficas

bull javaxwsrsGET

bull javaxwsrsPUT

bull javaxwsrsPOST

bull javaxwsrsDELETE

bull javaxwsrsHEAD

En la sesioacuten anterior ya hemos utilizado estas anotaciones para hacer corresponder (enlazar)peticiones HTTP GET con un meacutetodo Java concreto

Por ejemplo

Path(clientes)public class ServicioCliente

GET Produces(applicationxml) public String getTodosLosClientes()

En este coacutedigo la anotacioacuten GET indica al runtime JAX-RS que el meacutetodo javagetTodosLosClientes() atiende peticiones HTTP GET dirigidas a la URI clientes

Soacutelamente se puede utilizar una de las anotaciones anteriores para unmismo meacutetodo Si se aplica maacutes de uno se produce un error durante eldespliegue de la aplicacioacuten

Es interesante conocer que cada una de estas anotaciones a su vez estaacute anotada con otrasanotaciones (podriacuteamos llamarlas meta anotaciones) Por ejemplo la implementacioacuten de laanotacioacuten GET tiene este aspecto

package javaxwsrsimport

Servicios Rest

39

Target(ElementTypeMETHOD)Retention(RetentionPolicyRUNTIME)HttpMethod(HttpMethodGET)public interface GET

GET en siacute mismo no tiene ninguacuten significado especial para el proveedor JAX-RS (runtimede JAX-RS) Lo que hace que la anotacioacuten GET sea significativo para el runtime de JAX-RSes el valor de la meta anotacioacuten javaxwsrsHttpMethod (en este caso HttpMethodGET )Este valor es el que realmente decide que un determinado meacutetodo Java se enlace con undeterminado meacutetodo HTTP

iquestCuaacuteles son las implicaciones de eacutesto Pues que podemos crear nuevas anotaciones quepodemos enlazar a otros meacutetodos HTTP que no sean GET POST PUT DELETE o HEAD Deesta forma podriacuteamos permitir que diferentes tipos de clientes que hacen uso de la operacioacutenHTTP LOCK puedan ser atendidos por nuestro servicio REST (como por ejemplo un clienteWebDAV )

22 La anotacioacuten Path

La anotacioacuten Path identifica la plantilla de path para la URI del recurso al que se accede yse puede especificar a nivel de clase o a nivel de meacutetodo de dicho recurso

El valor de una anotacioacuten Path es una expresioacuten que denota una URI relativa a la URIbase del servidor en el que se despliega el recurso a la raiz del contexto de la aplicacioacuten yal patroacuten URL al que responde el runtime de JAX-RS

Un segmento de la URI es cada una de las subcadenas delimitadas por que aparecenen dicha URI Por ejemplo la URI httpejemploclientescomclientesviprecientes contiene 4segmentos de ruta ejemploclientescom clientes vip y recientes

La anotacioacuten Path no es necesario que contenga una ruta que empieceo termine con el caraacutecter El runtime de JAX-RS analiza igualmente laexpresioacuten indicada como valor de Path

Para que una clase Java sea identificada como una clase que puede atender peticiones HTTPeacutesta tiene que estar anotada con al menos la expresioacuten Path() Este tipo de clasesse denominan recursos JAX-RS raiacutez

Para recibir una peticioacuten un meacutetodo Java debe tener al menos una anotacioacuten de meacutetodoHTTP como por ejemplo javaxwsrsGET Este meacutetodo no requiere tener ninguna anotacioacutenPath adicional Por ejemplo

Path(pedidos)public class PedidoResource GET public String getTodosLosPedidos()

Una peticioacuten HTTP GET pedidos se delegaraacute en el meacutetodo getTodosLosPedidos()

Servicios Rest

40

Podemos aplicar tambieacuten Path a un meacutetodo Java Si hacemos esto la expresioacuten de laanotacioacuten Path de la clase se concatenaraacute con la expresioacuten de la anotacioacuten Path delmeacutetodo Por ejemplo

Path(pedidos)public class PedidoResource

GET Path(noPagados) public String getPedidosNoPagados()

De esta forma una peticioacuten GET pedidosnoPagados se delegaraacute en el meacutetodogetPedidosNoPagados()

Podemos tener anotaciones Path para cada meacutetodo que seraacuten relativos a la ruta indicadaen la anotacioacuten Path de la definicioacuten de la clase Por ejemplo la siguiente clase de recursosirve peticiones a la URI pedidos

Path(pedidos)public class PedidoResource

GET public String getPedidos()

Si quisieacuteramos proporcionar el servicio en la URI pedidosincidencias por ejemplono necesitamos una nueva definicioacuten de clase y podriacuteamos anotar un nuevo meacutetodogetIncidenciasPedidos() de la siguiente forma

Path(pedidos)public class PedidoResource

GET public String getPedidos()

GET Path(incidencias) public String getIncidenciasPedidos()

Ahora tenemos una clase de recurso que gestiona peticiones para pedidos y para pedidosincidencias

Expresiones Path

El valor de una anotacioacuten Path puede ser una cadena de caracteres o tambieacutenpuede contener expresiones maacutes complejas si es necesario nos referiremos a ellas comoexpresiones Path

Servicios Rest

41

Una expresioacuten Path puede incluir variables que se indican entre llaves que seraacuten sustituidasen tiempo de ejecucioacuten dependiendo del valor que se indique en la llamada al recurso Asiacutepor ejemplo si tenemos la siguiente anotacioacuten

GETPath(clientesid)

y el usuario realiza la llamada

GET httporgexpertojavacontextorestclientesPedro

la peticioacuten se delegaraacute en el meacutetodo que esteacute anotado con las anotaciones anteriores y el valorde id seraacute instanciado en tiempo de ejecucioacuten a Pedro

Para obtener el valor del nombre del cliente utilizaremos la anotacioacuten PathParam en losparaacutemetros del meacutetodo de la siguiente forma

GETPath(clientesnombre)public String getClientePorNombre(PathParam(nombre) String nombre)

Una expresioacuten Path puede tener maacutes de una variable cada una figuraraacute entre llaves Porejemplo si utilizamos la siguiente expresioacuten Path

Path(nombre1nombre2)public class MiResource

podremos atender peticiones dirigidas a URIs que respondan a la plantilla

httporgexpertojavacontextorecursosnombre1nombre2

como por ejemplo

httporgexpertojavacontextorecursosPedroLopez

Las expresiones Path pueden incluir maacutes de una variable para referenciar un segmento deruta Por ejemplo

Path()public class ClienteResource GET Path(clientesapellido1-apellido2) public String getCliente(PathParam(apellido1) String ape1 PathParam(apellido2) String ape2)

Servicios Rest

42

Una peticioacuten del tipo

GET httporgexpertojavacontextoclientesPedro-Lopez

seraacute procesada por el meacutetodo getCliente()

Expresiones regulares

Las anotaciones Path pueden contener expresiones regulares (asociadas a las variables)Por ejemplo si nuestro meacutetodo getClienteId() tiene un paraacutemetro de tipo entero podemosrestringir las peticiones para tratar solamente aquellas URIs que contengan diacutegitos en elsegmento de ruta que nos interese

Path(clientes)public class ClienteResource GET Path(id d+) solo soporta diacutegitos public String getClienteId(PathParam(id) int id)

Si la URI de la peticioacuten de entrada no satisface ninguna expresioacuten regular de ninguno de losmetodos del recurso entonces se devolveraacute el coacutedigo de error 404 Not Found

El formato para especificar expresiones regulares para las variables del path es

nombre-variable [ expresion-regular ]

El uso de expresiones regulares es opcional Si no se proporciona una expresioacuten regularpor defecto se admite cualquier caraacutecter En teacuterminos de una expresioacuten regular la expresioacutenregular por defecto seriacutea

[^]+

Por ejemplo si queremos aceptar solamente nombres que comiencen por una letra y acontinuacioacuten puedan contener una letra o un diacutegito lo expresariacuteamos como

Path(clientes)public class ClienteResource GET Path(nombre [a-zA-Z][a-zA-Z_0-9]) public String getClienteNombre(PathParam(nombre) string nom)

Servicios Rest

43

De esta forma la URI clientesaaa no seriacutea vaacutelida la URI clientesa9 activariacutea elmeacutetodo getClienteNombre() y la URI clientes89 activariacutea el meacutetodo getClienteId()

Las expresiones regulares no se limitan a un soacutelo segmento de la URI Por ejemplo

Path(clientes)public class ClienteResource GET Path(id +) public String getCliente(PathParam(id) String id)

GET Path(id +direccion) public String getDireccion(PathParam(id) String id)

La expresioacuten regular + indica que estaacuten permitidos cualquier nuacutemero de caracteres Asiacutepor ejemplo la peticioacuten GET clientespedrolopez podriacutea delegarse en el meacutetodogetClientes()

El meacutetodo getDireccion() tiene asociada una expresioacuten maacutes especiacutefica la cual puedemapearse con cualquier cadena de caracteres que termine con direccion Seguacuten eacutestola peticioacuten GET clientespedrolopezdireccion podriacutea delegarse en el meacutetodogetDireccion()

Reglas de precedencia

En el ejemplo anterior acabamos de ver que las expresiones Path para getCliente() ygetDireccion() son ambiguas Una peticioacuten GET clientespedrolopezdireccionpodriacutea mapearse con cualquiera de los dos meacutetodos La especificacioacuten JAX-RS define lassiguientes reglas para priorizar el mapeado de expresiones regulares

bull El primer criterio para ordenar las acciones de mapeado es el nuacutemero de caracteres literalesque contiene la expresioacuten Path teniendo prioridad aquellas con un mayor nuacutemero decaracteres literales El patroacuten de la URI para el meacutetodo getCliente() tiene 10 caraacutecteresliterales clientes El patroacuten para el meacutetodo getDireccion() tiene 19 clientesdireccion Por lo tanto se elegiriacutea primero el meacutetodo getDireccion()

bull El segundo criterio es el nuacutemero de variables en expresiones Path (por ejemplo id oid +) Teniendo precedencia las patrones con un mayor nuacutemero de variables

bull El tercer criterio es el nuacutemero de variables que tienen asociadas expresiones regulares(tambieacuten en orden descendente)

A continuacioacuten mostramos una lista de expresiones Path ordenadas en orden descendentede prioridad

1 clientesidnombredireccion

2 clientesid +direccion

3 clientesiddireccion

4 clientesid +

Servicios Rest

44

Las expresiones 13 se analizariacutean primero ya que tienen maacutes caracteres literales que laexpresioacuten nuacutemero 4 Si bien las expresiones 13 tienen el mismo nuacutemero de caracteresliterales La expresioacuten 1 se analizariacutea antes que las otras dos debido a la segunda regla (tienemaacutes variables) Las expresiones 2 y 3 tienen el mismo nuacutemero de caracteres literales y elmismo nuacutemero de variables pero la expresioacuten 2 tiene una variable con una expresioacuten regularasociada

Estas reglas de ordenacioacuten no son perfectas Es posible que siga habiendo ambiguumledadespero cubren el 90 de los casos Si el disentildeo de nuestra aplicacioacuten presenta ambiguumledadesaplicando estas reglas es bastante probable que hayamos complicado dicho disentildeo y seriacuteaconveniente revisarlo y refactorizar nuestro esquema de URIs

Paraacutemetros matrix (Matrix parameters)

Los paraacutemetros matrix con pares nombre-valor incluidos como parte de la URI Aparecen alfinal de un segmento de la URI (segmento de ruta) y estaacuten delimitados por el caraacutecter Por ejemplo

httpejemplocochescomseatibizacolor=black2006

En la ruta anterior el paraacutemetro matrix aparece despueacutes del segmento de ruta ibiza Su nombrees color y el valor asociado es black

Un paraacutemetro matrix es diferente de lo que denominamos paraacutemetro de consulta (queryparameter) ya que los paraacutemetros matrix representan atributos de ciertos segmentos de laURI y se utilizan para propoacutesitos de identificacioacuten Pensemos en ellos como adjetivos Losparaacutemetros de consulta por otro lado siempre aparecen al final de la URI y siemprepertenecen al recurso completo que estemos referenciando

Los paraacutemetros matrix son ignorados cuando el runtime de JAX-RS realiza el matching de laspeticiones de entrada a meacutetodos de recursos REST De hecho es ilegal incluir paraacutemetrosmatrix en las expresiones Path Por ejemplo

Path(seat)public class SeatService

GET Path(ibizaanyo) Produces(imagejpeg) public Response getIbizaImagen(PathParam(anyo) String anyo)

Si la peticioacuten de entrada es GET seatibizacolor=black2009 el meacutetodo getIbizaImagen()seriacutea elegido por el proveedor de JAX-RS para servir la peticioacuten de entrada y seriacutea invocadoLos paraacutemetros matrix NO se consideran parte del proceso de matching debido a quenormalmente son atributos variables de la peticioacuten

Subrecursos (Subresource Locators)

Acabamos de ver la capacidad de JAX-RS para hacer corresponder de forma estaacuteticaa traveacutes de la anotacioacuten Path URIs especificadas en la entrada de la peticioacuten conmeacutetodos Java especiacuteficos JAX-RS tambieacuten nos permitiraacute de forma dinaacutemica servir nosotros

Servicios Rest

45

mismos las peticiones a traveacutes de los denominados subresource locators (localizadores desubrecursos)

Los subresource locators son meacutetodos Java anotados con Path pero sin anotacionesGET PUT hellip Este tipo de meacutetodos devuelven un objeto que es en siacute mismo un servicioJAX-RS que sabe coacutemo servir el resto de la peticioacuten Vamos a describir mejor este conceptocon un ejemplo

Supongamos que queremos extender nuestro servicio que proporciona informacioacuten sobre losclientes Disponemos de diferentes bases de datos de clientes seguacuten regiones geograacuteficasQueremos antildeadir esta informacioacuten en nuestro esquema de URIs pero desacoplando labuacutesqueda del servidor de base de datos de la consulta particular de un cliente en concretoAntildeadiremos la informacioacuten de la zona geograacutefica en la siguiente expresioacuten Path

clienteszona-dbclienteId

A continuacioacuten definimos la clase ZonasClienteResource que delegaraacute en la claseClienteResource que ya teniacuteamos definida

Path(clientes)public class ZonasClienteResource

Path(zona-db) public ClienteResource getBaseDeDatos(PathParam(zona) String db) devuelve una instancia dependiendo del paraacutemetro db ClienteResource resource = localizaClienteResource(db) return resource

protected ClienteResource localizaClienteResource(String db)

La clase ZonasClienteResource es nuestro recurso raiacutez Dicha clase no atiende ningunapeticioacuten HTTP directamente Nuestro recurso raiacutez procesa el segmento de URI que hacereferencia a la base de datos en donde buscar a nuestro cliente y devuelve una instancia dedicha base de datos (o maacutes propiamente dicho del objeto con en que accederemos a dichabase de datos) El proveedor de JAX-RS utiliza dicha instancia para servir el resto de lapeticioacuten

public class ClienteResource private MapltInteger Clientegt clienteDB = new ConcurrentHashMapltInteger Clientegt() private AtomicInteger idContador = new AtomicInteger()

public ClienteResource(MapltInteger Clientegt clienteDB) thisclienteDB = clienteDB

POST Consumes(applicationxml)

Servicios Rest

46

public Response crearCliente(InputStream is)

GET Path(id) Produces(applicationxml) public Cliente recuperarClienteId(PathParam(id) int id)

PUT Path(id) Consumes(applicationxml) public void modificarCliente(PathParam(id) int id Cliente cli)

Si un usuario enviacutea la peticioacuten GET clientesnorteamerica-db333 el proveedorJAX-RS primero realizaraacute un matching de la expresioacuten sobre el meacutetodoZonasClienteResourcegetBaseDeDatos() A continuacioacuten procesaraacute el resto de la peticioacuten(333) a traveacutes del meacutetodo ClienteResourcerecuperarClienteId()

Podemos observar que la nueva clase ClienteResource ademaacutes de tener un nuevoconstructor ya no estaacute anotada con Path Esto implica que ya no es un recurso de nuestrosistema es un subrecurso y no debe ser registrada en el runtime de JAX-RS a traveacutes de laclase Application (como veremos maacutes adelante)

Veamos otro ejemplo Supongamos que tenemos un conjunto de alumnos del que podemosobtener el listado completo de alumnos y antildeadir nuevos alumnos pero ademaacutes queremos quecada alumno individual pueda consultarse modificarse o borrarse Una forma sencilla de trataresto es dividir el coacutedigo en un recurso (lista de alumnos) y un subrecurso (alumno individual)de la siguiente forma

Path(alumnos)public class AlumnosResource

Context UriInfo uriInfo

GET Produces(MediaTypeAPPLICATION_XML MediaTypeAPPLICATION_JSON) public ListltAlumnoBeangt getAlumnos() return FactoriaDaosgetAlumnoDao()getAlumnos()

POST Consumes(MediaTypeAPPLICATION_JSON) public void addAlumno(AlumnoBean alumno) throws IOException String dni = FactoriaDaosgetAlumnoDao()addAlumno(alumno) URI uri = uriInfogetAbsolutePathBuilder()path(dni)build(dni) Responsecreated(uri)build()

Path(alumno) public AlumnoResource getAlumno( PathParam(alumno) String dni)

Servicios Rest

47

return new AlumnoResource(uriInfo dni)

Vemos que en este recurso inyectamos informacioacuten sobre la URI solicitada como variable deinstancia (utilizando la anotacioacuten Context de la que hablaremos maacutes adelante) Para elconjunto de alumnos ofrecemos dos operaciones obtener la lista de alumnos y antildeadir unnuevo alumno a la lista la cual devuelve como respuesta la URI que nos da acceso al recursoque acabamos de antildeadir

Sin embargo lo maacutes destacable es el uacuteltimo meacutetodo Eacuteste se ejecutaraacute cuando antildeadamos a laruta el identificador de un alumno (por ejemplo alumnos15 ) En este caso lo que hace esdevolver un subrecurso (AlumnoResource) para asiacute tratar un alumno individual (destacamosque el nombre estaacute en singular para distinguirlo del recurso anterior que representa elconjunto)

Cuando hacemos esto estamos delegando en el nuevo Recurso para tratar la peticioacuten

public class AlumnoResource

UriInfo uriInfo

String dni

public AlumnoResource(UriInfo uriInfo String dni) thisuriInfo = uriInfo thisdni = dni

GET Produces(MediaTypeAPPLICATION_XMLMediaTypeAPPLICATION_JSON) public AlumnoBean getAlumno() AlumnoBean alumno = FactoriaDaosgetAlumnoDao()getAlumno(dni) if(alumno==null) throw new WebApplicationException(StatusNOT_FOUND) return alumno

PUT Consumes(MediaTypeAPPLICATION_XML) public Response setAlumno(AlumnoBean alumno) El DNI del alumno debe coincidir con el de la URI alumnosetDni(dni)

if(FactoriaDaosgetAlumnoDao()getAlumno(dni) = null) FactoriaDaosgetAlumnoDao()updateAlumno(alumno) return ResponsenoContent()build() else FactoriaDaosgetAlumnoDao()addAlumno(alumno) return Responsecreated(uriInfogetAbsolutePath())build()

Servicios Rest

48

DELETE public void deleteAlumno() FactoriaDaosgetAlumnoDao()deleteAlumno(dni)

Este recurso ya no es un recurso raiacutez mapeado a una ruta determinada (podemos ver que laclase no lleva la anotacioacuten Path) sino que es creado desde otro recurso Es por lo tantoun subrecurso

Como ya hemos visto los subrecursos nos permiten simplificar la forma de trabajar conconjuntos de recursos definiendo en un uacutenico meacutetodo la ruta de acceso a un recurso individualen lugar de tenerlo que hacer de forma independiente para cada operacioacuten

Ademaacutes este disentildeo modular de los recursos nos va a permitir reutilizar determinadosrecursos dentro de otros Por ejemplo dentro del recurso de un alumno podriacuteamos ver la listade asignaturas en las que se ha matriculado y reutilizar el subrecurso encargado de acceder alas asignaturas para poder acceder a sus datos a partir del recurso del alumno No deberemosabusar de esta caracteriacutestica ya que si creamos relaciones ciacuteclicas perdemos la caracteriacutesticadeseable de los servicios REST de que cada recurso estaacute asignado a una uacutenica URI

En un subrecurso NO podemos inyectar objetos de contexto mediantela anotacioacuten Context ya que no estamos en un recurso raiacutez Porese motivo en el ejemplo hemos proporcionado el objeto UriInfo enel constructor del subrecurso De forma alternativa tambieacuten podriacuteamoshaber inyectado este objeto como paraacutemetro de sus meacutetodos en ese casosi que habriacutea sido posible la inyeccioacuten

Caraacutecter dinaacutemico del dispatching de peticiones

En los ejemplos anteriores hemos ilustrado el concepto de subresource locator aunque nohemos mostrado completamente su caraacutecter dinaacutemico Asiacute si volvemos al primero de ellosel meacutetodo ZonasClienteResourcegetBaseDeDatos() puede devolver cualquier instancia decualquier clase En tiempo de ejecucioacuten el proveedor JAX-RS buscaraacute el interior de estainstancia meacutetodos de recurso que puedan gestionar la peticioacuten

Supongamos que tenemos dos bases de datos de clientes con diferentes tipos deidentificadores Una de ellas utiliza una clave numeacuterica La otra utiliza una clave formada porel nombre y apellidos Necesitamos tener dos clases diferentes para extraer la informacioacutenadecuada de la URI de la peticioacuten Cambiaremos la implementacioacuten de la siguiente forma

Path(clientes)public class ZonasClienteResourceResource protected ClienteResource europa = new ClienteResource() protected OtraClaveClienteResource norteamerica = new OtraClaveClienteResource()

Path(zona-db) public Object getBaseDeDatos(PathParam(zona) String db) if (dbequals(europa)) return europa else if (dbequals(norteamerica)) return northamerica

Servicios Rest

49

else return null

En lugar de devolver una instancia de ClienteResource el meacutetodo getBaseDeDatos() devuelveuna instancia de javalangObject JAX-RS analizaraacute la instancia devuelta para ver coacutemoprocesar el resto de la peticioacuten

Ahora si un usuario enviacutea la peticioacuten GET clienteseuropa-db333 se utilizaraacute la claseClienteResource para servir el resto de la peticioacuten Si la peticioacuten es GET clientesnorteamerica-dbjohn-smith utilizaremos el nuevo subrecurso OtraClaveClienteResource

public class OtraClaveClienteResource private MapltString Clientegt clienteDB = new ConcurrentHashMapltString Clientegt() GET Path(nombre-apellidos) Produces(applicationxml) public Cliente getCliente(PathParam(nombre) String nombre PathParam(apellidos) String apelllidos)

PUT Path(nombre-apellidos) Consumes(applicationxml) public void actualizaCliente()PathParam(nombre) String nombre PathParam(apellidos) String apelllidos Cliente cli)

23 Usos de las anotaciones Produces y Consumes

La informacioacuten enviada a un recurso y posteriormente devuelta al cliente que realizoacute la peticioacutense especifica con la cabecera HTTP Media-Type tanto en la peticioacuten como en la respuestaComo ya hemos visto podemos especificar que representaciones de los recursos (valor deMedia_Type) son capaces de aceptar yo producir nuestros servicios mediante las siguientesanotaciones

bull javaxwsrsConsumes

bull javaxwsrsProduces

La ausencia de dichas anotaciones es equivalente a incluirlas con el valor de media type es decir su ausencia implica que se soporta (acepta) cualquier tipo de representacioacuten

Anotacioacuten Consumes

Esta anotacioacuten funciona conjuntamente con POST y PUT Le indica al framework (libreriacuteasJAX-RS) a queacute meacutetodo se debe delegar la peticioacuten de entrada Especiacuteficamente el clientefija la cabecera HTTP Content-Type y el framework delega la peticioacuten al correspondientemeacutetodo capaz de manejar dicho contenido Un ejemplo de anotacioacuten con PUT es lasiguiente

Servicios Rest

50

Path(pedidos)public class PedidoResource PUT Consumes(applicationxml) public void modificarPedido(Pedido representation)

Si Consumes se aplica a la clase por defecto los meacutetodos correspondientes aceptan lostipos especificados de tipo MIME Si se aplica a nivel de meacutetodo se ignora cualquier anotacioacutenConsumes a nivel de clase para dicho meacutetodo

En este ejemplo le estamos indicando al framework que el meacutetodo modificarPedido() aceptaun recurso cuya representacioacuten (tipo MIME) es applicationxml (y que se almacenaraacute en lavariable representation hablaremos de ello en la siguiente sesioacuten) Por lo tanto un clienteque se conecte al servicio web a traveacutes de la URI pedidos debe enviar una peticioacuten HTTPPUT conteniendo el valor de applicationxml como tipo MIME de la cabecera HTTPContent-Type y el cuerpo (body) del mensaje HTTP debe ser por tanto un documentoxml vaacutelido

Si no hay meacutetodos de recurso que puedan responder al tipo MIME solicitado (tipo MIMEespecificado en la anotacioacuten Consumes del servicio) se le devolveraacute al cliente un coacutedigoHTTP 415 (Unsupported Media Type) Si el meacutetodo que consume la representacioacutenindicada como tipo MIME no devuelve ninguna representacioacuten se enviaraacute un el coacutedigo HTTP204 (No content) A continuacioacuten mostramos un ejemplo en el que sucede eacutesto

POSTConsumes(applicationxml)public void creaPedido(Pedido pedido) Crea y almacena un nuevo _Pedido_

Podemos ver que el meacutetodo consume una representacioacuten en texto plano pero devuelvevoid es decir no devuelve ninguna representacioacuten En este caso se enviacutea el coacutedigo de estadoHTTP 204 No content en la respuesta

Un recurso puede aceptar diferentes tipos de entradas Asiacute podemos utilizar la anotacioacutenPUT con maacutes de un meacutetodo para gestionar las repuestas con tipos MIME diferentes Porejemplo podriacuteamos tener un meacutetodo para aceptar estructuras XML y otro para aceptarestructuras JSON

Path(pedidos)public class PedidoResource PUT Consumes(applicationxml) public void modificarPedidoXML(InputStream pedido) PUT Consumes(applicationjson) public void modificarPedidoJson(InputStream pedido)

Servicios Rest

51

Anotacioacuten Produces

Esta anotacioacuten funciona conjuntamente con GET POST y PUT Indica al frameworkqueacute tipo de representacioacuten se enviacutea de vuelta al cliente

De forma maacutes especiacutefica el cliente enviacutea una peticioacuten HTTP junto con una cabecera HTTPAccept que se mapea directamente con el Content-Type que el meacutetodo produce Por lo tantosi el valor de la cabecera Accept HTTP es applicationxml el meacutetodo que gestiona la peticioacutendevuelve un stream de tipo MIME applicationxml Esta anotacioacuten tambieacuten puede utilizarse enmaacutes de un meacutetodo en la misma clase de recurso Un ejemplo que devuelve representacionesXML y JSON seriacutea el siguiente

Path(pedidos)public class PedidoResource

GET Produces(applicationxml) public String getPedidoXml()

GET Produces(applicationjson) public String getPedidoJson()

Si un cliente solicita una peticioacuten a una URI con un tipo MIME no soportadopor el recurso el framework JAX-RS lanza la excepcioacuten adecuadaconcretamente el runtime de JAX-RS enviacutea de vuelta un error HTTP 406Not acceptable

Se puede declarar maacutes de un tipo en la misma declaracioacuten Produces como por ejemplo

Produces(applicationxml applicationjson)public String getPedidosXmlOJson()

El meacutetodo getPedidosXmlOJson() seraacute invocado si cualquiera de los dos tipos MIMEespecificados en la anotacioacuten Produces son aceptables (la cabecera Accept de la peticioacutenHTTP indica queacute representacioacuten es aceptable) Si ambas representaciones son igualmenteaceptables se elegiraacute la primera

En lugar de especificar los tipos MIME como cadenas detexto en Consumes y Produces podemos utilizar lasconstantes definidas en la clase javaxwsrscoreMediaTypecomo por ejemplo MediaTypeAPPLICATION_XML oMediaTypeAPPLICATION_JSON en lugar de applicationxml yapplicationjson

24 Inyeccioacuten de paraacutemetros JAX-RS

Buena parte del trabajo de JAX-RS es el extraer informacioacuten de una peticioacuten HTTP einyectarla en un meacutetodo Java Podemos estar interesados en un fragmento de la URI de

Servicios Rest

52

entrada en los paraacutemetros de peticioacutenhellip El cliente tambieacuten podriacutea enviar informacioacuten en lascabeceras de la peticioacuten A continuacioacuten indicamos una lista con algunas de las anotacionesque podemos utilizar para inyectar informacioacuten de las peticiones HTTP

bull javaxwsrsPathParam

bull javaxwsrsMatrixParam

bull javaxwsrsQueryParam

bull javaxwsrsFormParam

bull javaxwsrsHeaderParam

bull javaxwsrsContext

bull javaxwsrsBeanParam

Habitualmente estas anotaciones se utilizan en los paraacutemetros de un meacutetodo de recurso JAX-RX Cuando el proveedor de JAX-RS recibe una peticioacuten HTTP busca un meacutetodo Java quepueda servir dicha peticioacuten Si el meacutetodo Java tiene paraacutemetros anotados con alguna de estasanotaciones extraeraacute la informacioacuten de la peticioacuten HTTP y la pasaraacute como un paraacutemetrocuando se invoque el meacutetodo

javaxwsrsPathParam

Ya la hemos utilizado en la sesioacuten anterior PathParam nos permite inyectar el valor de losparaacutemetros de la URI definidos en expresiones Path Recordemos el ejemplo

Path(clientes)public class ClienteResource

GET Path(id) Produces(applicationxml) public Cliente recuperarClienteId(PathParam(id) int id)

Podemos referenciar maacutes de un paraacutemetro en el path de la URI en nuestros meacutetodo javaPor ejemplo supongamos que estamos utilizando el nombre y apellidos para identificar a uncliente en nuestra clase de recurso

Path(clientes)public class ClienteResource

GET Path(nombre-apellidos) Produces(applicationxml) public Cliente recuperarClienteId(PathParam(nombre) String nom PathParam(apellidos) String ape)

Servicios Rest

53

En ocasiones un parameacutetro de path de la URI puede repetirse en diferentes expresionesPath que conforman el patroacuten de matching completo para un meacutetodo de un recurso (porejemplo puede repetirse en la expresioacuten Path de la clase y de un meacutetodo) En estos casos laanotacioacuten PathParam siempre referencia el paraacutemetro path final Asiacute en el siguiente coacutedigo

Path(clientesid)public class ClienteResource

GET Path(direccionid) Produces(textplain) public String getDireccion(PathParam(id) String direccionId)

Si nuestra peticioacuten HTTP es GET clientes123direccion456 el paraacutemetro direccionIddel meacutetodo getDireccion() tendriacutea el valor inyectado de 456

Interfaz UriInfo

Podemos disponer ademaacutes de un API maacutes general para consultar y extraer informacioacuten sobrelas peticiones URI de entrada Se trata de la interfaz javaxwsrscoreUriInfo

public interface UriInfo public javanetURI getAbsolutePath() public UriBuilder getAbsolutePathBuilder()

public javanetURI getBaseUri() public UriBuilder getBaseUriBuilder()

public String getPath() public ListltPathSegmentgt getPathSegments() public MultivaluedMapltString Stringgt getPathParameters()

Los meacutetodos getAbsolutePathBuilder() y getAbsolutePath() devuelven la ruta absoluta dela peticioacuten HTTP en forma de UriBuilder y URI respectivamente

Los meacutetodos getBaseUri() y getBaseUriBuilder() devuelven la ruta base de la aplicacioacuten(ruta raiz de nuestros servicios rest) en forma de UriBuilder y URI respectivamente

El meacutetodo UriInfogetPath() permite obtener la ruta relativa de nuestros servicios RESTutilizada para realizar el matching con nuestra peticioacuten de entrada (es la ruta de la peticioacutenactual relativa a la ruta base de la peticioacuten rest)

El meacutetodo UriInfogetPathSegments() divide la ruta relativa de nuestro servicio REST enuna serie de objetos PathSegment (segmentos de ruta delimitados por )

El meacutetodo UriInfogetPathParameters() devuelve un objeto de tipo MultivaluedMap con todoslos paraacutemetros del path definidos en todas las expresiones Path de nuestra peticioacuten rest

Servicios Rest

54

Por ejemplo si la ruta de nuestra petcioacuten http es httplocalhost8080contextorestclientes2(siendo contexto la ruta raiacutez del war desplegado y rest la ruta de servicio de jax-rs)

bull la ruta absoluta (meacutetodo getAbsolutePath()) seriacutea httplocalhost8080contextorestclientes2

bull la ruta base (meacutetodo getBaseUri) seriacutea httplocalhost8080contextorest

bull la ruta relativa a la ruta base (meacutetodo getPath()) seriacutea clientes2

bull el nuacutemero de segmentos de la peticioacuten rest (meacutetodo getPathSegments()) seriacutean 2 clientesy 2

Podemos inyectar una instancia de la interfaz UriInfo utilizando la anotacioacutenjavaxwsrscoreContext A continuacioacuten mostramos un ejemplo

Path(cochesmarca)public class CarResource

GET Path(modeloanyo) Produces(imagejpeg) public Response getImagen(Context UriInfo info) String fabricado = infogetPathParameters()getFirst(marca) PathSegment modelo = infogetPathSegments()get(2) String color = modelogetMatrixParameteres()getFirst(color)

En este ejemplo inyectamos una instancia de UriInfo como paraacutemetro del meacutetodo getImagen()A continuacioacuten hacemos uso de dicha instancia para extraer informacioacuten de la URI

Recuerda que tambieacuten podriacuteamos inyectar una instancia de UriInfo en unavariable de instancia de la clase raiacutez de nuestro recurso

El meacutetodo CarResourcegetImagen() utiliza la interfazjavaxwsrscorePathSegment que como ya hemos indicado representa unsegmento de ruta

package javaxwsrscorepublic interface PathSegment String getPath() MultivaluedMapltString Stringgt getMatrixParameters()

El meacutetodo PathSegmentgetPath() devuelve el valor de la cadena de caracteres del segmentode ruta actual sin considerar niguacuten paraacutemetro matrix que pudiese contener

El meacutetodo PathSegmentgetMatrixParameters() devuelve un mapa con todos losparaacutemetros matrix aplicados a un segmento de ruta

Supongamos que realizamos la siguiente peticioacuten http para el coacutedigo anterior (claseCarResource)

Servicios Rest

55

GET cochesseatleoncolor=rojo2015

Esta peticioacuten es delegada en el meacutetodo ClarResourcegetImagen() La ruta contiene 4segmentos coches seat leon y 2015 La variable _modelo tomaraacute el valor leon y la variablecolor se instanciaraacute con el valor rojo

javaxwsrsMatrixParam

La especificacioacuten JAX-RS nos permite inyectar una matriz de valores de paraacutemetros a traveacutesde la anotacioacuten javaxwsrsMatrixParam

Path(cochesmarca)public class CarResource

GET Path(modeloanyo) Produces(imagejpeg) public Response getImagen(PathParam(marca) String marca PathParam(modelo) String modelo MatrixParam(color) String color)

El uso de la anotacioacuten MatrixParam simplifica nuestro coacutedigo y lo hace algo maacutes legibleSi por ejemplo la peticioacuten de entrada es

GET cochesseatibizacolor=black2009

entonces el paraacutemetro color del meacutetodo CarResourcegetImagen() tomariacutea el valor black

javaxwsrsQueryParam

La anotacioacuten javaxwsrsQueryParam nos permite inyectar paraacutemetros de consulta(query parameters) de la URI en los valores de los paraacutemetros de los meacutetodos java denuestros recursos Por ejemplo supongamos que queremos consultar informacioacuten de nuestrosclientes y queremos recuperar un subconjunto de clientes de nuestra base de datos NuestraURI de peticioacuten podriacutea ser algo asiacute

GET clientesinicio=0amptotal=10

El paraacutemetro de consulta inicio representa el iacutendice (o posicioacuten) del primer cliente quequeremos consultar y el paraacutemetro total representa cuaacutentos clientes en total queremosobtener como respuesta Una implementacioacuten del servicio RESTful podriacutea contener elsiguiente coacutedigo

Path(clientes)public class ClienteResource GET Produces(applicationxml) public String getClientes(QueryParam(inicio) int inicio

Servicios Rest

56

QueryParam(total) int total)

En este ejemplo el paraacutemetro inicio tomariacutea el valor 0 y el paraacutemetro total tomariacutea elvalor 10 (JAX-RS convierte automaacuteticamente las cadenas de caracteres de los paraacutemetrosde consulta en enteros)

javaxwsrsFormParam

La anotacioacuten javaxwsrsFormParam se utiliza para acceder al cuerpo del mensajede la peticioacuten HTTP de entrada cuyo valor de Content-Type es applicationx-www-form-urlencoded Es decir se utiliza para acceder a entradas individuales de un formulario HTMLPor ejemplo supongamos que para registrar a nuevos clientes en el sistema tenemos querellenar el siguiente formulario

ltFORM action=httpejemplocomclientes method=postgt ltPgt Nombre ltINPUT type=text name=nombregtltBRgt Apellido ltINPUT type=text name=apellidogtltBRgt ltINPUT type=submit value=Sendgt ltPgtltFORMgt

La ejecucioacuten de este coacutedigo inyectaraacute los valores del formulario como paraacutemetros de nuestromeacutetodo Java que representa el servicio de la siguiente forma

Path(clientes)public class ClienteResource POST public void crearCliente(FormParam(nombre) String nom FormParam(apellido) String ape)

Aquiacute estamos inyectando los valores de nombre y apellidos del formulario HTML en losparaacutemetors nom y ape del meacutetodo java crearCliente() Los datos del formulario viajan atraveacutes de la red codificados como URL-encoded Cuando se utiliza la anotacioacuten FormParamJAX-RS decodifica de forma automaacutetica las entradas del fomulario antes de inyectar susvalores

Asiacute por ejemplo si tecleamos los valores Maria Luisa y_Perlado_ como valores en loscampos de texto nombre y apellido del formulario el cuerpo de nuestro mensaje HTTP seraacutenombre=Maria20Luisaapellido=Perlado Este mensaje seraacute recibido por nuestro meacutetodoque extraeraacute los valores correspondientes y los instanciaraacute en los paraacutemetros nom y ape delmeacutetodo _ClienteResourcecrearCliente()

javaxwsrsHeaderParam

La anotacioacuten javaxwsrsHeaderParam se utiliza para inyectar valores de lascabeceras de las peticiones HTTP Por ejemplo si estamos interesados en la paacutegina web quenos ha referenciado o enlazado con nuestro servicio web podriacuteamos acceder a la cabeceraHTTP Referer utilizando la anotacioacuten HeaderParam de la siguiente forma

Servicios Rest

57

Path(miservicio)public class MiServicio GET Produces(texthtml) public String get(HeaderParam(Referer) String referer)

De forma alternativa podemos acceder de forma programativa a todas las cabeceras de lapeticioacuten de entrada utilizando la interfaz javaxwsrscoreHttpHeaders

public interface HttpHeaders public ListltStringgt getRequestHeader(String name) public MultivaluedMapltString Stringgt getRequestHeaders()

El meacutetodo getRequestHeader() permite acceder a una cabecera en concreto y el meacutetodogetRequestHeaders() nos proporciona un objeto de tipo Map que representa todas lascabeceras A continuacioacuten mostramos un ejemplo que accede a todas las cabeceras de lapeticioacuten HTTP de entrada

Path(miservicio)public class MiServicio GET Produces(texthtml) public String get(Context HttpHeaders cabeceras) String referer = headersgetRequestHeader(Referer)get(0) for (String header headersgetRequestHeaders()keySet()) Systemoutprintln(Se ha utilizado esta cabecera + header)

javaxwsrscoreContext

Dentro de nuestros recursos JAX-RS podemos inyectar determinados objetos coninformacioacuten sobre el contexto de JAX-RS sobre el contexto de servlets o sobreelementos de la peticioacuten recibida desde el cliente Para ello utilizaremos la anotacioacutenjavaxwsrscoreContext

En los ejemplos de esta sesioacuten ya hemos visto como utilizarla para inyectar objetos de tipoUriInfo y HttpHeaders

A continuacioacuten mostramos un ejemplo en el que podos obtener detalles sobre el contextodel despliegue de la aplicacion asi como del contexto de peticiones individuales utilizando laanotacion Context

Implementacioacuten de un servicio que muestra informacioacuten sobre el contexto de la peticioacuten

Path(orders)

Servicios Rest

58

public class PedidoResource

Context Application app

Context UriInfo uri

Context HttpHeaders headers

Context Request request

Context SecurityContext security

Context Providers providers

GET Produces(applicationxml) public ListltOrdergt getAll(QueryParam(start)int from QueryParam(end)int to) (appgetClasses()) (urigetPath()) (headersgetRequestHeader(HttpHeadersACCEPT)) (headersgetCookies()) (requestgetMethod()) (securityisSecure())

Application proporciona acceso a la informacioacuten de la configuracioacuten de la aplicacioacuten(clase Application)UriInfo proporciona acceso a la URI de la peticioacutenHttpHeaders proporciona acceso a las cabeceras de la peticioacuten HTTP La anotacioacutenHeaderParam puede tambieacuten utilizarse para enlazar una cabecera HTTP a unparaacutemetro de un meacutetodo de nuestro recurso a un campo del mismo o a una propiedadde un beanRequest se utiliza para procesar la respuestas tiacutepicamente se usa juntamente con laclase Response para construir la respuesta de forma dinaacutemicaSecurityContext proporciona acceso a la informacioacuten de la peticioacuten actual relacionadacon la seguridadProviders proporciona informacioacuten sobre la buacutesqueda del runtime de las instancias deproveedores utilizando un conjunto de criterios de buacutesqueda

Con respecto a contexto de servlets podremos inyectar informacioacuten de ServletContextServletConfig HttpServletRequest y HttpServletResponse Debemos recordar que losrecursos JAX-RS son invocados por un servlet dentro de una aplicacioacuten web por lo quepodemos necesitar tener acceso a la informacioacuten del contexto de servlets Por ejemplo sinecesitamos acceder a la ruta en disco donde tenemos los datos de nuestra aplicacioacuten webtendremos que inyectar el objeto ServletContext

GETProduces(imagejpeg)public InputStream getImagen(Context ServletContext sc) return scgetResourceAsStream(fotos + nif + jpg)

javaxwsrsBeanParam

La anotacioacuten javaxwsrsBeanParam nos permite inyectar una clase especiacutefica cuyosmeacutetodos o atributos esteacuten anotados con alguna de las anotaciones de inyeccioacuten de paraacutemetrosxxxParam que hemos visto en esta sesioacuten Por ejemplo supongamos esta clase

Servicios Rest

59

public class ClienteInput FormParam(nombre) String nombre

FormParam(apellido) String apellido

HeaderParam(Content-Type) String contentType

public String getFirstName()

La clase ClienteInput es un simple POJO (Plain Old Java Object) que contiene el nombrey apellidos de un cliente asiacute como el tipo de contenido del mismo Podemos dejar que JAX-RScree inicialice e inyecte esta clase usando la anotacioacuten BeanParam de la siguiente forma

Path(clientes)public class ClienteResource POST public void crearCliente(BeanParam ClienteInput newCust)

El runtime de JAX-RS analizaraacute los paraacutemetros anotados con BeanParam para inyectarlas anotaciones correspondientes y asignar el valor que corresponda En este ejemplo la claseClienteInput contendraacute dos valores de un formulario de entrada y uno de los valores de lacabecera de la peticioacuten De esta forma nos podemos evitar una larga lista de paraacutemetros enel meacutetodo crearCliente() (en este caso son soacutelo tres pero podriacutean ser muchos maacutes)

Conversioacuten automaacutetica de tipos

Todas las anotaciones que hemos visto referencian varias partes de la peticioacuten HTTP Todasellas se representan como una cadena de caracteres en dicha peticioacuten HTTP JAX-RS puedeconvertir esta cadena de caracteres en cualquier tipo Java siempre y cuando se cumpla almenos uno de estos casos

1 Se trata de un tipo primitivo Los tipos int short float double byte char y booleanpertenecen a esta categoriacutea

2 Se trata de una clase Java que tiene un constructor con un uacutenico paraacutemetro de tipo String

3 Se trata de una clase Java que tiene un meacutetodo estaacutetico denominado valueOf() que tomaun uacutenico String como argumento y devuelve una instancia de la clase

4 Es una clase de tipo javautilListltTgt javautilSetltTgt o javautilSortedSetltTgt en dondeT es un tipo que satisface los criterios 2 oacute 3 o es un String Por ejemplo ListltDoublegtSetltStringgt o SortedSetltIntegergt

Si el runtime JAX-RS falla al convertir una cadena de caracteres en el tipo Java especificadose considera un error del cliente Si se produce este fallo durante el procesamiento de unainyeccioacuten de tipo MatrixParam QueryParam o PathParam se devuelve al clienteun error 404 Not found Si el fallo tiene lugar con el procesamiento de las inyecciones

Servicios Rest

60

HeaderParam o CookieParam (esta uacuteltima no la hemos visto) entonces se enviacutea al clienteel eror 400 Bad Request

Valores por defecto (DefaultValue)

Suele ser habitual que algunos de los paraacutemetros proporcionados en las peticiones a serviciosRESTful sean opcionales Cuando un cliente no proporciona esta informacioacuten opcional en lapeticioacuten JAX-RS inyectaraacute por defecto un valor null si se trata de un objeto o un valor ceroen el caso de tipos primitivos

Estos valores por defecto no siempre son los que necesitamos para nuestro servicioPara solucionar este problema podemos definir nuestro propio valor por defecto para losparaacutemetros que sean opcionales utilizando la anotacioacuten javaxwsrsDefaultValue

Consideremos el ejemplo anterior relativo a la recuperacioacuten de la informacioacuten de unsubconjunto de clientes de nuestra base de datos Para ello utilizaacutebamos dos paraacutemetros deconsulta para indicar el iacutendice del primer elemento asiacute como el nuacutemero total de elementosque estamos interesados en recuperar En este caso no queremos que el cliente tengaque especificar siempre estos paraacutemetros al realizar la peticion Usaremos la anotacioacutenDefaultValue para indicar los valores por defecto que nos interese

Path(clientes)public class ClienteResource GET Produces(applicationxml) public String getClientes( DefaultValue(0) QueryParam(inicio) int inicio DefaultValue(10) QueryParam(total) int total)

Hemos usado DefaultValue para especificar un iacutendice de comienzo con valor cero y untamantildeo del subconjunto de los datos de la respuesta JAX-RS utilizaraacute las reglas de conversioacutende cadenas de caracteres que acabamos de indicar para convertir el valor del paraacutemetro enel tipo Java que especifiquemos

25 Configuracioacuten y despliegue de aplicaciones JAX-RS

Como ya hemos visto en la sesioacuten anterior implementamos nuestros servicios REST utilizandoel API de Java JAX-RS (especificacioacuten JSR-339) Una aplicacioacuten JAX-RS consiste en unoo maacutes recursos y cero o maacutes proveedores En este apartado vamos a describir ciertosaspectos aplicados a las aplicaciones JAX-RS como un todo concretamente a la configuracioacuteny tambieacuten a la publicacioacuten de las mismas cuando utilizamos un servidor de aplicacionesJavaEE 7 o bien un contenedor de servlets 30 que incluyan una implementacioacuten del APIJAX-RS Tambieacuten indicaremos coacutemo configurar el despliegue en el caso de no disponer comomiacutenimo de un contenedor de servlets 30

Configuracioacuten mediante la clase Application

Tanto los recursos (clases anotadas con Path) como los proveedores que conforman nuestraaplicacioacuten JAX-RS pueden configurarse utilizando una subclase de Application Cuandohablamos de configuracioacuten nos estamos refiriendo en este caso a definir los mecanismospara localizar las clases que representan los recursos asiacute como a los proveedores

Servicios Rest

61

Un proveedor es una clase que implementa una o algunade las siguientes interfaces JAX-RS MesssageBodyReaderMessageBodyWriter ContextResolverltTgt y ExceptionMapperltTgt Lasdos primeras permiten crear proveedores de entidades (entity providers)la tercera es un proveedor de contexto (context provider) y la uacuteltima unproveedor de mapeado de excepciones (exception mapping provider) Lasclases que actuacutean como proveedores estaacuten anotadas con Providerpara que puedan ser identificadas automaacuteticamente por el runtime JAX-RS

El uso de una subclase de Application para configurar nuestros servicios REST constituyela forma maacutes sencilla de desplegar los servicios JAX-RS en un servidor de aplicacionescertificado como Java EE (en este caso Wildfly cumple con este requisito) o un contenedorstandalone de Servlet 3 (como por ejemplo Tomcat)

Pasemos a conocer la clase javaxwsrscoreApplication El uso de la claseApplication es la uacutenica forma portable de decirle a JAX-RS queacute servicios web (clasesanotadas con Path) asiacute como queacute otros elementos como filtros interceptoreshellip queremospublicar (desplegar)

La clase Application se define como

package javaxwsrscore

import javautilCollectionsimport javautilSet

public abstract class Application private static final SetltObjectgt emptySet = CollectionsemptySet()

public abstract SetltClassltgtgt getClasses()

public SetltObjectgt getSingletons() return emptySet

La clase Application es muy simple Como ya hemos indicado su propoacutesito es proporcionaruna lista de clases y objetos que queremos desplegar

El meacutetodo getClasses() devuelve una lista de clases de servicios web y proveedores JAX-RS Cualquier servicio JAX-RS devuelto por este meacutetodo sigue el modelo per-request queya hemos introducido en la sesioacuten anterior Cuando la implementacioacuten de JAX-RS determinaque una peticioacuten HTTP necesita ser procesada por un meacutetodo de una de estas clases secrearaacute una instancia de dicha clase durante la peticioacuten y se destruiraacute al finalizar la mismaEn este caso estamos delegando en el runtime JAX-RS la creacioacuten de los objetos Lasclases proveedoras son instanciadas por el contenedor JAX-RS y registradas una uacutenica vezpor aplicacioacuten

El meacutetodo getSingletons() devuelve una lista de servicios y proveedores web JAX-RSya instanciados Nosotros como programadores de las aplicaciones somos responsablesde crear estos objetos El runtime JAX-RS iteraraacute a traveacutes de la lista de objetos y los registraraacuteinternamente

Servicios Rest

62

Un ejemplo de uso de una subclase de Application podriacutea ser eacuteste

package orgexpertojava

import javaxwsrscoreApplicationimport javaxwsrsApplicationPath

ApplicationPath(rest)public class ComercioApplication extends Application

public SetltClassltgtgt getClasses() HashSetltClassltgtgt set = new HashSetltClassltgtgt() setadd(ClienteResourceclass) setadd(PedidoResourceclass) return set

public SetltObjectgt getSingletons() JsonWriter json = new JsonWriter() TarjetaCreditoResource servicio = new TarjetaCreditoResource()

HashSetltObjectgt set = new HashSet() setadd(json) setadd(servicio) return set

La anotacioacuten ApplicationPath define la base URL de la ruta para todos nuestrosservicios JAX-RS desplegados Asiacute por ejemplo accederemos a todos nuestros serviciosJAX-RS seraacuten desde la ruta rest cuando los ejecutemos En el ejemplo anterior estamosindicando que ClienteResource y PedidoResource son servicios per-request El meacutetodogetSingletons() devuelve el servicio de tipo TarjetaCreditoResource asiacute como el proveedorJsonWriter (que implementa la interfaz MessageBodyWriter)

Si tenemos al menos una implementacioacuten de la clase Application anotada conApplicationPath esta seraacute detectada y desplegada automaacuteticamente por el servidor deaplicaciones

Podemos aprovechar completamente esta capacidad para escanear y detectarautomaacuteticamente nuestros servicios si tenemos implementada una subclase de Applicationpero dejamos que getSingletons() devuelva el conjunto vaciacuteo y no indicamos nada en elmeacutetodo getClasses() de esta forma

package orgexpertojava

import javaxwsrsApplicationPathimport javaxwsrscoreApplication

ApplicationPath(rest)public class ComercioApplication extends Application

Servicios Rest

63

En este caso el servidor de aplicaciones se encargaraacute de buscar en el directorio WEB-INFclasses y en cualquier fichero jar dentro del directorio WEB-INFlib A continuacioacuten antildeadiraacutecualquier clase anotada con Path o Provider a la lista de cosas que necesitan serdesplegadas y registradas en el runtime JAX-RS

Los servicios REST son atendidos por un servlet que es especiacutefico de la implementacioacutenJAX-RS utilizada por el servidor de aplicaciones El servidor wildfly utiliza la implementacioacutende JAX-RS 20 denomindada resteasy (otra implementacioacuten muy utilizada es jersey porejemplo con el servidor de aplicaciones Glassfish) El runtime de JAX-RS contiene un servletinicializado con un paraacutemetro de inicializacioacuten de tipo javaxwsrsApplication cuyo valor seraacuteinstanciado automaacuteticamente por el servidor de aplicaciones con el nombre de la subclasede Application que sea detectada en el war de nuestra aplicacioacuten

Configuracioacuten mediante un fichero webxml

En la sesioacuten anterior no hemos utilizado de forma expliacutecita la clase Application para configurarel despliegue En su lugar hemos indicado esta informacioacuten en el fichero webxml

ltweb-app version=30 xmlns=httpjavasuncomxmlnsjavaee xmlnsxsi=httpwwww3org2001XMLSchema-instance xsischemaLocation=httpjavasuncomxmlnsjavaee httpjavasuncomxmlnsjavaeeweb-app_3_0xsdgt lt-- Con estas liacuteneas el servidor es el responsable de antildeadir el servlet correspondiente de forma automaacutetica Si en nuestro war tenemos clases anotadas con anotaciones JAX-RS para recibir invocaciones REST eacutestas seraacuten detectadas y registradas--gt ltservlet-mappinggt ltservlet-namegtjavaxwsrscoreApplicationltservlet-namegt lturl-patterngtrestlturl-patterngt ltservlet-mappinggtltweb-appgt

Esta configuracioacuten es equivalente a incluir una subclase de Application sin sobreescribir losmeacutetodos correspondientes En este caso se antildeade de forma dinaacutemica el servlet que sirvelas peticiones REST con el nombre javaxwsrscoreApplication de forma que se detectenautomaacuteticamente todas las clases de recursos y clases proveedoras empaquetadas en el warde la aplicacioacuten

Configuracioacuten en un contenedor que no disponga de una implementacioacuten JAX-RS

Si queremos hacer el despliegue sobre servidores de aplicaciones o servidores web queden soporte a una especificacioacuten de servlets con una versioacuten inferior a la 30 tendremosque configurar MANUALMENTE el fichero webxml para que cargue el servlet de nuestraimplementacioacuten propietaria de JAX-RS (cuyos ficheros jar deberemos incluir en el directorioWEB-INFlib de nuestro war) Un ejemplo de configuracioacuten podriacutea ser eacuteste

Configuracioacuten del fichero webxml (directorio de fuentes webappWEB-INFwebxml)

ltxml version=10gtltweb-appgt ltservletgt

Servicios Rest

64

ltservlet-namegtJAXRSltservlet-namegt ltservlet-classgt orgjbossresteasypluginsserverservletHttpServletDispatcher ltservlet-classgt ltinit-paramgt ltparam-namegt javaxwsrsApplication ltparam-namegt ltparam-valuegt orgexpertoJavaComercioApplication ltparam-valuegt ltinit-paramgt ltservletgt

ltservlet-mappinggt ltservlet-namegtJAXRSltservlet-namegt lturl-patterngtrestlturl-patterngt ltservlet-mappinggtltweb-appgt

En la configuracioacuten anterior estamos indicando de forma expliacutecita el servlet JAX-RS que recibelas peticiones REST que a su vez utilizaraacute la clase Application para detectar queacute servicios yproveedores REST seraacuten desplegados en el servidor

Tambieacuten seraacute necesario incluir la libreriacutea con la implementacioacuten JAX-RS 20 de formaexpliacutecita en el war generado (recordemos que para ello tendremos que utilizar la etiquetaltscopegtcompileltscopegt para que se antildeadan los jar correspondientes)

Libreriacutea con la implementacioacuten de JAX-RS 20

ltdependencygt ltgroupIdgtjavaxltgroupIdgt ltartifactIdgtjavaee-web-apiltartifactIdgt ltversiongt70ltversiongt ltscopegtcompileltscopegtltdependencygt

Servicios Rest

65

26 Ejercicios

Para esta sesioacuten antildeadiremos un nuevo moacutedulo en el que implementaremos un servicio restincorporando los conceptos que hemos explicado durante la sesioacuten En concreto

bull Creamos un moacutedulo Maven con IntelliJ (desde el directorio ejercicios-rest-expertojava ) con el arquetipo webapp-javaee7 tal y como hemos visto en losapuntes de la sesioacuten Las coordenadas del artefacto Maven seraacuten

GroupId orgexpertojava

ArtifactId s2-foro-nuevo

version 10-SNAPSHOT

bull Configuramos el pommxl del proyecto para poder compilar empaquetar y desplegarnuestro servicio en el servidor de aplicaciones Wildfly Consulta los apuntes para ver cuaacuteldebe ser el contenido de las etiquetas ltpropertiesgt ltdependenciesgt y ltbuildgt

bull Vamos a estructurar los fuentes (directorio srcmainjava) de nuestro proyecto en lossiguientes paquetes

orgexpertojavadatos contendraacute clases relacionadas con los datos a los que accedenuestra aplicacioacuten rest Por simplicidad almacenaremos en memoria los datos denuestra aplicacioacuten

orgexpertojavamodelo contiene las clases de nuestro modelo de objetos que seraacutenclases java con atributos y sus correspondientes getters y setters

orgexpertojavarest contiene los recursos JAX-RS que implementan nuestrosservicios rest asiacute como las clases necesarias para automatizar el despliegue de dichosrecursos

Creacioacuten de un recurso creacioacuten y consulta de temas en el foro (05 puntos)

Vamos a crear un recurso JAX-RS al que denominaremos TemasResource (en el paqueteorgexpertojavarest ) En el siguiente ejercicio al configurar la aplicacioacuten haremos que esterecurso sea un singleton Nuestro recurso gestionaraacute sus propios datos en memoria Porejemplo podemos utilizar un atributo private de tipo HashMap en el que almacenaremos lostemas cada uno con un identificador numeacuterico como clave Tambieacuten necesitaremos un atributopara generar las claves para cada uno de los temas Por ejemplo

private MapltInteger Temagt temasDB = new HashMapltInteger Temagt()private int contadorTemas = 0

Fiacutejate que si utilizamos los tipos HashMap e int podemos tener problemasde concurrencia si muacuteltiples usuarios estaacuten realizando peticiones paracrear yo consultar los temas del foro En una situacioacuten real deberiacuteamosutilizar en su lugar los tipos ConcurrentHasMap y AtomicInteger paraevitar el que dos usuarios intentaran crear un nuevo tema con la mismaclave perdieacutendose asiacute uno de los dos temas creados Al tratarse de unejercicio en el que solamente tendremos un cliente no nos plantearaacuteninguacuten problema el trabajar con HashMap e int por lo que podeacuteis elegircualquiera de las dos opciones para realizar el ejercicio

Servicios Rest

66

bull Nuestro recurso estaraacute accesible en el servidor en la ruta temas (relativa a la raiacutez delcontexto de nuestra aplicacioacuten y a la ruta de nuestro servlet JAX-RS que determinaremoscon la anotacioacuten ApplicationPath de nuestra clase Application)

bull En el paquete orgexpertojavamodelo crearemos la clase Tema con los atributosprivados

int idString nombre

y sus correspondientes getters y setters

setId() getId()setNombre() getNombre()

bull Implementamos un primer meacutetodo en el recurso TemasResource denominadocreaTema() para poder crear un nuevo tema en el foro Dicho meacutetodo atenderaacutepeticiones POST a nuestro servicio Los datos de entrada (cadena de caracteres querespresenta el nombre del tema) se pasan a traveacutes de un formulario html en el que tenemosuna uacutenica entrada denominada nombre

Puedes incluir el siguiente contenido en el fichero indexhtml para introducir los datosdesde el navegador

ltDOCTYPE htmlgtlthtmlgtltheadgt lttitlegtStart Pagelttitlegt ltmeta http-equiv=Content-Type content=texthtml charset=UTF-8gtltheadgtltbodygtlth1gtAlta de temas en el foro lth1gtltform action=s2-foro-nuevoresttemas method=postgt Nombre del tema ltinput type=text name=nombre gtltbr gt

ltinput type=submit value=Enviar gtltformgtltbodygtlthtmlgt

Cada nuevo Tema creado se antildeadiraacute a nuestra base de datos en memoria temasDB juntocon un identificador numeacuterico (que se iraacute incrementando para cada nueva instancia creada)

bull Implementamos un segundo meacutetodo para consultar los temas creados en el foro Elmeacutetodo se denominaraacute verTemasTodos() y devuelve (en formato texto) todos los temasactualmente creados Dado que puede haber un gran nuacutemero de ellos vamos a permitirque el usuario decida cuaacutentos elementos como maacuteximo quiere consultar a partir de unaposicioacuten determinada Por defecto si no se indica esta informacioacuten se mostraraacuten comomaacuteximo los primeros 8 temas registrados en el foro Si el identificador a partir del cualqueremos iniciar la consulta es mayor que el nuacutemero de temas almacenados entoncesdevolveremos la cadena No es posible atender la consulta Ejemplos de URIs que aceptadicho meacutetodo son

Servicios Rest

67

temas

en este caso y suponiendo que hayamos creado solamente los tres temas del apartadoanterior el resultado seriacutea

Listado de temas del 1 al 81 animales2 plantas3 ab

temasinicio=2amptotal=2

el resultado seriacutea

Listado de temas del 2 al 32 plantas3 ab

temasinicio=7amptotal=1

el resultado seriacutea

No es posible atender la consulta

Como ya hemos comentado las URIs indicadas en este ejercicio sonrelativas a la raiacutez del contexto de nuestra aplicacioacuten y a la ruta especificadapara nuestros servicios rest Recuerda que si has configurado el pomxmlcomo en la sesioacuten anterior la raiacutez del contexto de la aplicacioacuten vendraacutedada por el valor de la etiqueta ltfinalNamegt anidada en ltbuildgt Ennuestro caso deberiacutea ser s2-foro-nuevo Maacutes adelante fijaremos la rutade nuestros servicios rest como rest Por ejemplo la URI completa parael uacuteltimo apartado seriacutea httplocalhost8080s2-foro-nuevoresttemasinicio=7amptotal=1

Despliegue y pruebas del recurso (05 puntos)

Vamos a construir y desplegar nuestro servicio en el servidor de aplicaciones Para ello vamosa utilizar una subclase de Application que antildeadiremos en el paquete orgexpertojavarestLa ruta en la que se van a servir nuestras peticiones rest seraacute rest Fiacutejate que el recursoque hemos creado es el encargado de gestionar (crear modificarhellip) sus propios datosPor lo tanto necesitamos que nuestro recurso REST sea un singleton Implementa la claseForoApplication y realiza la construccioacuten y despliegue del proyecto A continuacioacutenprueba el servicio utilizando postman Puedes probar la insercioacuten de temas utilizando tambieacutenel formulario a traveacutes de la URI httplocalhost8080s2-foro-nuevo Podemos utilizar lasentradas del apartado anterior de forma que comprobemos que se crean correctamentelos temas animales plantas y ab y que obtenemos los listados correctos tanto si noindicamos el inicio y total de elementos como si decidimos mostrar los temas desde el 2 hastael 3

Servicios Rest

68

Cuando utilices el cliente IntelliJ para probar meacutetodos POST debesproporcionar un Request Body no vaciacuteo En este caso como en lapropia URI incluimos el contenido del mensaje que es el nombre del temaque queremos antildeadir al foro tendraacutes que seleccionar Text aunque norellenemos el campo correspondiente De no hacerlo asiacute obtendremoscomo respuesta un cuerpo de mensaje vaciacuteo y la cabecera de respuestaHTTP11 415 Unsupported Media Type

Muacuteltiples consultas de los temas del foro (05 puntos)

Implementa tres nuevas consultas de los temas del foro de forma que

bull Se pueda realizar una consulta de un tema concreto a partir de su identificador numeacuterico(el meacutetodo solamente debe admitir identificadores formados por uno o maacutes diacutegitos) Si eltema consultado no existe se debe devolver una excepcioacuten con la cabecera de respuestaHTTP11 404 Not Found Por ejemplo

temas2

Debe devolver lo siguiente

Ver el tema 2plantas

temas4

Obtenemos como respuesta un cuerpo de mensaje vaciacuteo y la cabecera de respuestaHTTP11 404 Not Found

bull Se pueda realizar una consulta de los temas que comiencen por uno de los siguientescaracteres a b c oacute d Por ejemplo teniendo en cuenta que hemos introducido los temasanteriores

temasa

Debe devolver lo siguiente

Listado de temas que comienzan por aanimales

temasd

Debe devolver Listado de temas que comienzan por d

bull Se pueda realizar una consulta de los temas que contengan una subcadena de caracteresPor ejemplo teniendo en cuenta que hemos introducido los temas anteriores

temasma + Debe devolver lo siguiente

Listado de temas que contienen la subcadena maanimales

Servicios Rest

69

Creacioacuten de subrecursos (05 puntos)

Vamos a crear el subrecurso MensajesResource (en el paquete orgexpertojavarest)de forma que este recurso gestione la creacioacuten y consulta de mensajes para cada unode los temas del foro Este subrecurso debe atender peticiones desde rutas del tipotemasidentificadorTemamensajes siendo identificadorTema la clave numeacutericaasociada a uno de los temas almacenados

bull En este caso nuestro subrecurso no seraacute un singleton por lo que necesitaremos almacenarlos mensajes en otra clase diferente (ya que crearemos una nueva instancia del recursopara cada peticioacuten) La clase DatosEnMemoria (en el paquete orgexpertojavadatos)seraacute la encargada de almacenar en memoria la informacioacuten de los mensajes publicadospara cada tema Por ejemplo puedes utilizar los siguientes campos estaacuteticos paragestionar los mensajes

public static MapltMensaje Stringgt mensajesDB = new HashMapltMensaje Stringgt()

La clave seraacute el propio mensaje (objeto Mensaje que se asociaraacute al tema correspondiente)

public static int contadorMen = 0

Como ya hemos comentado puedes usar ConcurrentHashMap yAtomicInteger en lugar de los tipos anteriores para evitar problemas deconcurrencia

bull En el paquete orgexpertojavadatos crearemos la clase Mensaje con los atributosprivados

int idString textoString autor=anonimo

y sus correspondientes getters y setters

setId() getId()setTexto() getTexto()setAutor() getAutor()

bull Vamos a crear un meacutetodo para poder realizar la publicacioacuten de un mensaje de texto en elforo en uno de los temas ya creados Independientemente del tipo de peticioacuten realizadasobre los mensajes si el tema indicado en la URI no existe lanzaremos la excepcioacutenWebApplicationException(ResponseStatusNOT_FOUND) Veamos alguacuten ejemplo

Deberemos poder realizar una peticioacuten POST a temas1mensajes con el cuerpo demensaje = Mensaje numero 1 El mensaje creado por defecto tendraacute asociado el autoranonimo

Servicios Rest

70

Si realizamos una peticioacuten para antildeadir un mensaje a la URI temas9mensajesdeberiacuteamos obtener como cabecera de respuesta HTTP11 404 Not Foundindependientemente del cuerpo del mensaje

bull Vamos a crear un meacutetodo para realizar una consulta de todos los mensajes publicados enun tema concreto Por ejemplo

Una peticioacuten GET a temas1mensajes deberiacutea dar como resultado

Lista de mensajes para el tema animales1 Mensaje anonimo

Si realizamos una peticioacuten GET a la URI temas9mensajes deberiacuteamos obtenercomo cabecera de respuesta HTTP11 404 Not Found independientemente delcuerpo del mensaje

bull Finalmente vamos a antildeadir dos nuevos meacutetodos para (a) antildeadir un nuevo mensajeen un tema concreto indicando el autor del mensaje Como restriccioacuten el nombre delautor deberaacute estar formado solamente por caracteres alfabeacuteticos utilizando mayuacutesculas ominuacutesculas y como miacutenimo tiene que tener un caracter y (b) consultar todos los mensajesque un determinado autor ha publicado en el foro en un tema determinado

Una peticioacuten POST a la URI temas1mensajespepe con el cuerpo de mensaje convalor mensaje de pepe deberiacutea crear un nuevo mensaje para el tema con identificador2 y devolver como resultado el nuevo id (yo la URI del nuevo recurso en la cabecerade respuesta Location si seguimos la ortodoxia REST) En caso de que devolvamos laURI del nuevo recurso podemos utilizar la orden

return Responsecreated(uriInfogetAbsolutePathBuilder()

segment(StringvalueOf(id))

build())

build()

Obtenemos el path absoluto de la uri que nos ha invocadoAntildeadimos el identificador id del nuevo recurso creadoConstruimos la nueva URIConstruimos el objeto Response

Veremos coacutemo manipular objetos de tipo Response en sesiones posteriores

Recuerda que para acceder al cuerpo de la peticioacuten basta con definir unparaacutemetro de tipo String JAX-RS automaacuteticamente lo instanciaraacute con elcuerpo de la peticioacuten como una cadena

bull Una peticioacuten GET a la URI temas1mensajesanonimo dariacutea como resultado

Lista de mensajes tema= animales y autor= anonimo

1 Mensaje anonimo

bull Una peticioacuten GET a la URI temas1mensajes dariacutea como resultado

Lista de mensajes para el tema animales

Servicios Rest

71

1 Mensaje anonimo2 mensaje de pepe

bull Una peticioacuten GET a la URI temas1mensajesroberto dariacutea como resultado

Lista de mensajes tema= animales y autor= roberto

Servicios Rest

72

3 Manejadores de contenidos Respuestas del servidor ymanejo de excepciones

En la sesioacuten anterior hemos hablado de coacutemo inyectar informacioacuten contenida en las cabecerasde las peticiones HTTP ahora nos detendremos en el cuerpo del mensaje tanto de la peticioacutencomo de la respuesta En el caso de las peticiones explicaremos el proceso de transformarlos datos de entrada en objetos Java para poder ser procesados por nuestros serviciosCon respecto a las respuestas proporcionadas por nuestros servicios analizaremos tanto loscoacutedigos de respuesta por defecto como la elaboracioacuten de respuestas complejas y manejo deexcepciones

31 Proveedores de entidades

JAX-RS define lo que se denominan proveedores de entidades que son clases queproporcionan servicios de mapeado entre las representaciones del cuerpo del mensaje HTTPy los correspondientes tipos java que utilizaremos en nuestros recursos (paraacutemetros en losmeacutetodos o bien como tipo de la respuesta de los mismos) Las entidades tambieacuten se conocencon el nombre de message payload o simplemente como payload y representan elcontenido del cuerpo del mensaje HTTP

ProvidersEl runtime de JAX-RS puede extenderse (ampliarse) utilizandoclases proveedoras (providers) suministradas por nuestra aplicacioacutenConcretamente JAX-RS nos proporciona un conjunto de interfaces quepodemos implementar en nuestra aplicacioacuten creando asiacute dichas clasesproveedoras de entidades (entity providers) La especificacioacuten de JAX-RS define un proveedor como una clase que implementa una o maacutesinterfaces JAX-RS (de entre un conjunto determinado) y que puedenanotarse con provider para ser descubiertas de forma automaacuteticapor el runtime de JAX-RS

Nuestra aplicacioacuten puede proporcionar su propio mapeado entre representaciones(tipos MIME) del mensaje de entrada y tipos Java implementando las interfacesMessageBodyWriter y MessageBodyReader convirtieacutendose asiacute en clases proveedorasde entidades (entity providers) Por ejemplo podemos tener nuestro propio proveedor deentidades para el formato XML o JSON de forma que utilizando las libreriacuteas de java paraprocesamiento XML o JSON (Java API for XML Processing JAXP1 y Java API for JSONProcessing JSON-P2) implementemos el serializadodeserializado del cuerpo del mensajeHTTP de entrada cuando eacuteste presente los tipos MIME applicationxml o application_jsonLas clases que realizan dichos mapeados son clases entity provider

Interfaz javaxwsrsextMessageBodyReader

La interfaz MessageBodyReader define el contrato entre el runtime de JAX-RS y loscomponentes que proporcionan servicios de mapeado desde diferentes representaciones(indicadas como tipos mime) al tipo Java correspondiente Cualquier clase que quieraproporcionar dicho servicio debe implementar la interfaz MessageBodyReader y debeanotarse con Provider para poder ser detectada de forma automaacutetica por el runtime deJAX-RS

1 httpswwwjcporgenjsrdetailsummaryid=2062 httpsjcporgenjsrdetailid=353

Servicios Rest

73

La secuencia loacutegica de pasos seguidos por una implementacioacuten de JAX-RS cuando se mapeael cuerpo de un mensaje HTTP de entrada a un paraacutemetro de un meacutetodo Java es la siguiente

1 Se obtiene el media type de la peticioacuten (valor de la cabecera HTTP Content-Type ) Sila peticioacuten no contiene una cabecera Content-Type se usaraacute applicationoctet-stream

2 Se identifica el tipo java del paraacutemetro cuyo valor seraacute mapeado desde el cuerpo delmensaje

3 Se localiza la clase MessageBodyReader que soporta el media type de la peticioacuten y seusa su meacutetodo readFrom() para mapear el contenido del cuerpo del mensaje HTTP enel tipo Java que corresponda

4 Si no es posible encontrar el MessageBodyReader adecuado se genera la excepcioacutenNotSupportedException con el coacutedigo 405

Interfaz javaxwsrsextMessageBodyWriter

La interfaz MessageBodyWriter define el contrato entre el runtime de JAX-RS ylos componentes que proporcionan servicios de mapeado desde un tipo Java a unarepresentacioacuten determinada Cualquier clase que quiera proporcionar dicho servicio debeimplementar la interfaz MessageBodyWriter y debe anotarse con Provider para poderser detectada de forma automaacutetica por el runtime de JAX-RS

La secuencia loacutegica de pasos seguidos por una implementacioacuten de JAX-RS cuando se mapeaun valor de retorno de un meacutetodo del recurso a una entidad del cuerpo de un mensaje HTTPes la siguiente

1 Se obtiene el objeto que seraacute mapeado a la entidad del cuerpo del mensaje

2 Se determina el media type de la respuesta

3 Se localiza la clase MessageBodyWriter que soporta el objeto que seraacute mapeado a laentidad del cuerpo del mensaje HTTP y se utiliza su meacutetodo writeTo() para realizardicho mapeado

4 Si no es posible encontrar el MessageBodyWriter adecuado se generala excepcioacuten InternalServerErrorException (que es una subclase deWebApplicationException ) con el coacutedigo 500

32 Proveedores de entidad estaacutendar incluidos en JAX-RS

Cualquier implementacioacuten de JAX-RS debe incluir un conjunto de implementaciones deMessageBodyReader y MessageBodyWriter de forma predeterminada para ciertascombinaciones de tipos Java y media types

Table 3 Proveedores de entidades estaacutendar de una implementacioacuten JAX-RS

Tipo Java Media Type

byte[] (Cualquier media type)

javalangString (Cualquier media type)

javaioInputStream (Cualquier media type)

Servicios Rest

74

Tipo Java Media Type

javaioReader (Cualquier media type)

javaioFile (Cualquier media type)

javaxactivationDataSource (Cualquier media type)

javaxxmltransformSource textxml applicationxml application+xml(tipos basados en xml)

javaxxmlbindJAXBElement andapplication-supplied JAXB classes

textxml applicationxml application+xml(tipos basados en xml)

MultivaluedMapltStringStringgt applicationx-www-form-urlencoded(Contenido de formularios)

StreamingOutput (Cualquier media type) (SoacuteloMessageBodyWriter )

javalangBoolean javalangCharacterjavalangNumber

textplain

A continuacioacuten comentaremos algunos de estos proveedores de entidades estaacutendar oconversores por defecto que permiten convertir el cuerpo del mensaje HTTP a objetos Javade diferentes tipos y viceversa

javaxwsrscoreStreamingOutput

StreamingOutput es una interfaz callback que implementamos cuando queremos tratar comoun flujo continuo (streaming) el cuerpo de la respuesta Constituye una alternativa ligera aluso de MessageBodyWriter

public interface StreamingOutput void write(OutputStream output) throws IOException WebApplicationException

Implementamos una instancia de esta interfaz y la utilizamos como tipo de retorno denuestros meacutetodos de recursos Cuando el runtime de JAX-RS estaacute listo para escribir elcuerpo de respuesta del mensaje se invoca al meacutetodo write() de la instancia deStreamingOutput Veamos un ejemplo

Path(miservicio) public class MiServicio GET Produces(textplain) StreamingOutput get() return new StreamingOutput() public void write(OutputStream output) throws IOException WebApplicationException outputwrite(hello worldgetBytes())

Hemos utilizado una clase interna anoacutenima que implementa la interfaz StreamingOutputen lugar de crear una clase puacuteblica separada La razoacuten de utilizar una clase interna es porque

Servicios Rest

75

en este caso al contener tan pocas liacuteneas de coacutedigo resulta beneficioso mantener dicha loacutegicadentro del meacutetodo del recurso JAX-RS de forma que el coacutedigo sea maacutes faacutecil de seguirNormalmente no tendremos necesidad de reutilizar la loacutegica implementada en otros meacutetodospor lo que no tiene demasiado sentido crear otra clase especiacutefica

iquestY por queacute no inyectamos un OutputStream directamente iquestPor queacute necesitamos un objetocallback La razoacuten es que asiacute dejamos que el runtime de JAX-RS maneje la salida de lamanera que quiera Por ejemplo por razones de rendimiento puede ser conveniente que JAX-RS utilice un thread para responder diferente del thread de peticioacuten

javaioInputStream javaioReader

Para leer el cuerpo de un mensaje de entrada podemos utilizar las clases InputStream oReader Por ejemplo

Path()public class MiServicio PUT Path(dato) public void modificaDato(InputStream is) byte[] bytes = readFromStream(is) String input = new String(bytes) Systemoutprintln(input)

private byte[] readFromStream(InputStream stream) throws IOException ByteArrayOutputStream baos = new ByteArrayOutputStream() byte[] buffer = new byte[1000] int wasRead = 0 do wasRead = streamread(buffer) if (wasRead gt 0) baoswrite(buffer 0 wasRead) while (wasRead gt -1) return baostoByteArray()

En este caso estamos leyendo bytes a partir de un javaioInputStream para convertirloen una cadena de caracteres que mostramos por pantalla

En el siguiente ejemplo creamos un javaioLineNumberReader a partir de un objetoReader e imprimimos cada liacutenea del cuerpo del mensaje de entrada

PUTPath(maslineas)public void putMasLineas(Reader reader) LineNumberReader lineReader = new LineNumberReader(reader) do String line = lineReaderreadLine() if (line = null) Systemoutprintln(line)

Servicios Rest

76

while (line = null)

No estamos limitados solamente a utilizar instancias de InputStream yo Reader paraleer el cuerpo de los mensajes de entrada Tambieacuten podemos devolver dichos objetos comorespuesta Por ejemplo

Path(fichero)public class FicheroServicio private static final String basePath =

GET Path(rutafichero ) Produces(textplain) public InputStream getFichero(PathParam(rutafichero) String path) FileInputStream is = new FileInputStream(basePath + path) return is

Aquiacute estamos inyectando un valor PathParam para crear una referencia a un fichero realde nuestro disco duro Creamos una instancia de javaioFileInputStream a partir delvalor de la ruta inyectada como paraacutemetro y la devolvemos como cuerpo de nuestro mensajede respuesta La implementacioacuten de JAX-RS leeraacute la respuesta de este stream de entrada y laalmacenaraacute en un buffer para posteriormente escribirla de forma incremental en el stream desalida de la respuesta En este caso debemos especificar la anotacioacuten Produces para quela implementacioacuten de JAX-RS conozca el valor que debe asignar a la cabecera Content-Type en la respuesta

javaioFile

Se pueden utilizar instancias de la clase javaioFile para entrada y salida decualquier MIME-TYPE (especificado en Content-Type yo Accept y en las anotacionesProduces yo Consumes ) El siguiente coacutedigo por ejemplo devuelve una referencia aun fichero en nuestro disco

Path(fichero)public class FicheroServicio private static final String baseRuta =

GET Path(rutafichero ) Produces(textplain) public File getFichero(PathParam(rutafichero) String ruta) return new File(baseRuta + ruta)

En este caso inyectamos el valor de la ruta del fichero con la anotacioacuten PathParam A partirde dicha ruta creamos un objeto javaioFile y lo devolvemos como cuerpo del mensaje

Servicios Rest

77

de respuesta La implementacioacuten JAX-RS leeraacute la informacioacuten abriendo un InputStreambasado en esta referencia al fichero y la escribiraacute en un buffer Posteriormente y de formaincremental volveraacute a escribir el contenido del buffer en el stream de salida de la respuesta Aligual que en el ejemplo anterior debemos especificar la anotacioacuten Produces para que JAX-RS sepa coacutemo rellenar la cabecera Content-Type de la respuesta

Tambieacuten podemos inyectar instancias de javaioFile a partir del cuerpo del mensaje dela peticioacuten Por ejemplo

POSTPath(masdatos)public void post(File fichero) Reader reader = new Reader(new FileInputStream(fichero)) LineNumberReader lineReader = new LineNumberReader(reader) do String line = lineReaderreadLine() if (line = null) Systemoutprintln(line) while (line = null)

En este caso la implementacioacuten de JAX-RS crea un fichero temporal en el disco para laentrada Lee la informacioacuten desde el buffer de la red y guarda los bytes leiacutedos en este ficherotemporal En el ejemplo los datos leiacutedos desde la red estaacuten representados por el el objetoFile inyectado por el runtime de JAX-RS (recuerda que soacutelo puede haber un paraacutemetro sinanotaciones en los meacutetodos del recurso y que eacuteste representa el cuerpo del mensaje de lapeticioacuten HTTP) A continuacioacuten el meacutetodo post() crea un javaioFileInputStreama partir del objeto File inyectado Finalmente utilizamos eacuteste stream de entrada para crearun objeto LineNumberReader y mostrar los datos por la consola

byte[]

Podemos utilizar un array de bytes como entrada y salida para cualquier tipo especificadocomo media-type A continuacioacuten mostramos un ejemplo

Path()public class MiServicio GET Produces(textplain) public byte[] get() return hello worldgetBytes()

POST Consumes(textplain) public void post(byte[] bytes) Systemoutprintln(new String(bytes))

Para cualquier meacutetodo de recurso JAX-RS que devuelva un array de bytes debemosespecificar la anotacioacuten Produces para que JAX-RS sepa queacute valor asignar a la cabeceraContent-Type

Servicios Rest

78

String char[]

La mayor parte de formatos en internet estaacuten basados en texto JAX-RS puede convertircualquier formato basado en texto a un String o a cualquier array de caracteres Porejemplo

Path()public class MiServicio GET Produces(applicationxml) public String get() return ltcustomergtltnamegtSergio Garcialtnamegtltcustomergt

POST Consumes(textplain) public void post(String str) Systemoutprintln(str)

Para cualquier meacutetodo de recurso JAX-RS que devuelva un Sring o un array de caracteresdebemos especificar la anotacioacuten Produces para que JAX-RS sepa que valor asignar a lacabecera Content-Type

MultivaluedMapltString Stringgt y formularios de entrada

Los formularios HTML son usados habitualmente para enviar datos a servidores web Losdatos del formulario estaacuten codificados con el media type applicationx-www-form-urlencoded Ya hemos visto como utilizar la anotacioacuten FormParam para inyectarparaacutemetros individuales de un formulario de las peticiones de entrada Tambieacuten podremosinyectar una instancia de MultivaluedMapltString Stringgt que representa todos losdatos del formulario enviado en la peticioacuten Por ejemplo

Path() public class MiServicio POST Consumes(applicationx-www-form-urlencoded) Produces(applicationx-www-form-urlencoded) public MultivaluedMapltStringStringgt post( MultivaluedMapltString Stringgt form) el formulario tiene los campos fieldName1 y fieldName2 Systemoutprintln(formgetFirst(fieldName1)) Systemoutprintln(formgetFirst(fieldName2)) return form

En este coacutedigo nuestro meacutetodo post() acepta peticiones POST y recibe unMultivaluedMapltStringStringgt que contiene todos los datos de nuestro formularioEn este caso tambieacuten devolvemos una instancia de un formulario como respuesta

Los datos del formulario pueden representarse en el cuerpo de la petcioacuten como paresnombre=valor separados por amp Por ejemplo

Servicios Rest

79

fieldName1=valor20con20espaciosampfielName2=otroValor

Los espacios en blanco se codifican como 20 No es necesario poner comillas

33 Muacuteltiples representaciones de recursos

Por defecto un recurso RESTful se produce o consume con el tipo MIME Un recursoRESTful puede restringir los media types que soporta tanto en la peticioacuten como en larespuesta utilizando las anotaciones Consumes y Produces respectivamente Estasanotaciones pueden especificarse como ya hemos visto a nivel de clase o de meacutetodo derecurso Las anotaciones especificadas sobre el meacutetodo prevalecen sobre las de la clase Laausencia de estas anotaciones es equivalente a su inclusioacuten con el tipo MIME () es decirsu ausencia implica que se soporta cualquier tipo

A continuacioacuten mostramos un ejemplo en el que un Pedido puede producirse tanto en formatoxml como en formato json

GETPath(id)Produces(applicationxml applicationjson)public Pedido getPedido(PathParam(id)int id)

El meacutetodo getPedido() puede generar ambas representaciones para el pedido El tipo exactode la respuesta viene determinado por la cabecera HTTP Accept de la peticioacuten

Otro ejemplo en el que pueden consumirse varios tipos MIME puede ser el siguiente

POSTPath(id)Consumes(applicationxml applicationjson)public Pedido addPedido(PathParam(id)int id)

En este caso el formato consumido vendraacute dado por el valor de la cabecera HTTP Content-Type de la peticioacuten

JAX-RS 20 nos permite indicar la preferencia por un media type en el lado del servidorutilizando el paraacutemetro qs (quality on service) qs toma valores entre 0000 y 1000 eindica la calidad relativa de una representacioacuten comparado con el resto de representacionesdisponibles Una representacioacuten con un valor de qs de 0000 nunca seraacute elegido Unarepresentacioacuten sin valor para el paraacutemetro qs se asume que dicho valor es 1000

Ejemplo

POSTPath(id)Consumes(applicationxml qs=075 applicationjson qs=1)public Pedido addPedido(PathParam(id)int id)

Si un cliente realiza una peticioacuten y no manifiesta ninguna preferencia por ningunarepresentacioacuten en particular o con una cabecera Accept con valor application entonces el

Servicios Rest

80

servidor seleccionaraacute la representacioacuten con el valor de qs maacutes alto (en este caso applicationjson) Los valores de qs son relativos y como tales solamente son comparables con otrosvalores qs dentro de la misma instancia de la anotacioacuten Consumes (o Produces)

Los clientes pueden indicar tambieacuten sus preferencias utilizando otro factor relativo de calidaden forma de paraacutemetro denominado q El valor del paraacutemetro q se utiliza para ordenar elconjunto de tipos aceptados q toma valores entre 0000 y 1000 (maacutexima preferencia) Al igualque antes Los valores de q son relativos y como tales solamente son comparables con otrosvalores q dentro de la misma cabecera Accept o Content-type

Las preferencias del servidor (valores de los paraacutemetros qs) soacutelo se tienenen cuenta si el cliente acepta muacuteltiples media types con el mismo valorde q

Veamos un ejemplo

GETPath(id)Produces(applicationxml qs=1 applicationjson qs=075)public Pedido getPedido(PathParam(id)int id)

Supongamos que un cliente lanza una petcioacuten GET con una valor para la cabecera Acceptde application q=05 texthtml En este caso el servidor determina que los tipos MIMEapplicationxml y applicationjson tienen la misma preferencia por parte del cliente (con valorde 05) por lo tanto el servidor elegiraacute la representacioacuten applicationjson ya que tiene un valorde qs mayor

34 Introduccioacuten a JAXB

JAXB (Java Architecture for XML Binding) es una especificacioacuten Java antigua (JSR 2223) yno estaacute definida por JAX-RS JAXB es un framework de anotaciones que mapea clases Java aXML y esquemas XML Es extremadamente uacutetil debido a que en lugar de interactuar con unarepresentacioacuten abstracta de un documento XML podemos trabajar con objetos Java realesque estaacuten maacutes cercanos al dominio que estamos modelando JAX-RS proporciona soportepara JAXB pero antes de revisar los manejadores de contenidos JAXB incuidos con JAX-RSveamos una pequentildea introduccioacuten al framework JAXB

Como ya hemos dicho si queremos mapear una clase Java existente a XML podemos utilizarJAXB a traveacutes de un conjunto de anotaciones Veaacutemoslo mejor con un ejemplo

XmlRootElement(name=cliente)XmlAccessorType(XmlAccessTypeFIELD)public class Cliente XmlAttribute protected int id

XmlElement protected String nombre

public Customer()

3 httpsjcporgaboutJavacommunityprocessmreljsr222index2html

Servicios Rest

81

public int getId() return thisid public void setId(int id) thisid = id

public String getNombre() return thisnombre public void setNombre(String nombre thisnombre = nombre

La anotacioacuten javaxxmlbindannotationXmlRootElement se utiliza en clasesjava para denotar que representan elementos XML (etiqueta XML raiacutez) En este caso estamosdiciendo que la clase Java representa un documento XML que tiene como etiqueta raiacutezltclientegt Las clases java anotadas con XmlRootElement se denomina beansJAXB

La anotacioacuten javaxxmlbindannotationXmlAttribute la hemos asociado alcampo id de nuestra clase Cliente Esta anotacioacuten indica que el campo id de la clasedebe mapearse como el atributo id del elemento raiacutez ltclientegt del documento XML Laanotacioacuten XmlAttribute tiene un atributo name de forma que podemos especificar elnombre exacto del atributo XML dentro del documento Por defecto tiene el mismo nombreque el campo anotado

Hemos utilizado la anotacioacuten javaxxmlbindannotationXmlElement en el camponombre de la clase Cliente Esta anotacioacuten indica a JAXB que debe mapearse el camponombre como el elemento ltnombregt anidado en la etiqueta raiacutez ltclientegt Igual queantes podemos especificar el nombre concreto del elememto XML Por defecto toma el mismonombre que el correspondiente campo anotado

La anotacioacuten javaxxmlbindannotationXmlAccessorType permite controlar laserializacioacuten por defecto de los atributos de la clase Esta anotacioacuten soacutelo puede ser usadaconjuntamente con XmlRootElement (y alguna otra anotacioacuten que no mostramos aquiacute)Hemos usado como valor XmlAccessTypeFIELD lo que significa que por defecto se debenserializar todos los campos (fields) de la clase (esteacuten anotados o no) y las propiedades(properties) de la clase que tengan anotaciones JAXB (a menos que la anotacioacuten seaXMLTransient)

Si alguno de los campos de la clase no tiene anotaciones JAXB asociadas por defecto seserializaraacuten como elementos (etiquetas) en el documento XML correspondiente Seguacuten ladocumentacioacuten4 de JAXB un campo es una variable de instancia no estaacutetica (normalmenteprivada)

Las propiedades de la clase vienen dadas por las combinaciones gettersetter de los atributosde la clase El coacutedigo anterior tiene dos propiedades nombre (dado por el par getNombresetNombre_) e id (par getIdsetId) Normalmente se anotan los meacutetodos getter Dichaspropiedades no estaacuten anotadas por lo que JAXB no las serializaraacute

Al proceso de serializar (convertir) un objeto Java en un documento XMLse le denomina marshalling El proceso inverso la conversioacuten de XML aobjetos Java se denomina unmarshalling

Con las anotaciones anteriores un ejemplo de una instancia de nuestra clase Cliente conun id de 42 y el valor de nombre Pablo Martinez tendriacutea el siguiente aspecto

ltcliente id=42gt

4 httpsjaxbjavanetnonav226docsapi

Servicios Rest

82

ltnombreCompletogtPablo MartinezltnombreCompletogtltclientegt

Observamos que se han serializado los campos (variables de instancia de la clase)

Si no especificamos la anotacioacuten XmlAccessorType por defecto se utilizaraacute

XmlAccessorType(XmlAccessTypePUBLIC_MEMBER)

El valor XmlAccessTypePUBLIC_MEMBER indica a JAXB que se deben serializar todoslos campos puacuteblicos de la clase todos los campos anotados y todas las propiedades (paresgettersetter) a menos que esteacuten anotadas con XMLTransient

Tambieacuten podriacuteamos utilizar

XmlAccessorType(XmlAccessTypeNONE)

En este caso la anotacioacuten XmlAccessTypeNONE indica soacutelo se deben serializar aquellaspropiedades yo campos de la clase que esteacuten anotados

A continuacioacuten indicamos en forma de tabla el uso de los diferentes XmlAccessType

Table 4 Valores utilizados para XmlAccessorType() conjuntamentecon XmlRootElement()

Valor Significado

XmlAccessTypePUBLIC_MEMBER Serializacioacuten por defecto si no se especificaXmlAccessorType() Serializa laspropiedades (pares gettersetter) y campospuacuteblicos a menos que esteacuten anotados conXMLTransient Si alguacuten campo nopuacuteblico estaacute anotado tambieacuten se serializa

XmlAccessTypeFIELD Serializa todos los campos (puacuteblicos oprivados) a menos que esteacuten anotados conXMLTransient

XmlAccessTypeNONE Solamente serializa aquellos camposy propiedades que esteacuten anotadas conanotaciones JAXB

XmlAccessTypePROPERTY Serializa cada par gettersetter a menosque esteacuten anotados con XMLTransient Si alguacuten campo (no puacuteblico) estaacute anotadotambieacuten se serializa

Podemos utilizar la anotacioacuten XmlElement para anidar otras clases anotadas con JAXBPor ejemplo supongamos que queremos antildeadir una clase Direccion a nuestra claseCliente

XmlRootElement(name=direccion)

Servicios Rest

83

XmlAccessorType(XmlAccessTypeFIELD)public class Direccion XmlElement protected String calle

XmlElement protected String cludad

XmlElement protected String codPostal

getters y setters

Simplemente tendriacuteamos que antildeadir el campo de tipo Direccion a nuestra clase Clientede la siguiente forma

XmlRootElement(name=cliente)XmlAccessorType(XmlAccessTypeFIELD)public class Cliente XmlAttribute protected int id

XmlElement protected String nombreCompleto

XmlElement protected Direccion direccion

public Customer()

getters y setters

En este caso una instancia de un Cliente con valores id =56 nombre =RicardoLopez calle =calle del oso 35 ciudad =Alicante y coacutedigo_postal =01010 seriacuteaserializado como

ltcliente id=56gt ltnombregtRicardo Lopezltnombregt ltdirecciongt ltcallegtcalle del oso 35ltcallegt ltciudadgtAlicanteltciudadgt ltcodPostalgt01010ltcodPostalgt ltdirecciongt ltclientegt

Veamos otro ejemplo Supongamos que tenemos el recurso EstadoResource con meacutetodosque responden a peticiones http GET y PUT

Path(estado)

Servicios Rest

84

public class EstadoResource

private static EstadoBean estadoBean = new EstadoBean()

GET Produces(applicationxml) public EstadoBean getEstado() return estadoBean

PUT Consumes(applicationxml) public void setEstado(EstadoBean estado) thisestadoBean = estado

En este caso la clase EstadoBean debe utilizar anotaciones JAXB para poder ser convertidaautomaacuteticamente por el runtime de JAX-RS en un documento XML y viceversa

XmlRootElement(name = estadoImpresora)public class EstadoBean

public String estado = Idle public int tonerRestante = 25 public ListltTareaBeangt tareas = new ArrayListltTareaBeangt()

Por defecto la anotacioacuten XmlRootElement realiza la serializacioacuten de la claseEstadoBean en formato xml utilizando los campos puacuteblicos y propiedades definidas en laclase ( estado tonerRestante y tareas ) Vemos que el campo tareas a su vez esuna coleccioacuten de elementos de tipo TareaBean que tambieacuten necesitan ser serializados Acontinuacioacuten mostramos la implementacioacuten de la clase TareaBeanjava

XmlRootElement(name = tarea)public class TareaBean public String nombre public String estado public int paginas

public TareaBean()

public TareaBean(String nombre String estado int paginas) thisnombre = nombre thisestado = estado thispaginas = paginas

Para serializar la clase es necesario que la clase tenga un constructor sin paraacutemetros

Servicios Rest

85

Si accedemos al servicio anterior nos devolveraacute la informacioacuten sobre el estado de la siguienteforma (resultado devuelto por el meacutetodo getEstado() anotado con GET)

ltxml version=10 encoding=UTF-8 standalone=yesgtltestadoImpresoragt ltestadogtIdleltestadogt lttonerRestantegt25lttonerRestantegt lttareasgt ltnombregttextodocltnombregt ltestadogtimprimiendoltestadogt ltpaginasgt13ltpaginasgt lttareasgt lttareasgt ltnombregttexto2docltnombregt ltestadogten esperaltestadogt ltpaginasgt5ltpaginasgt lttareasgtltestadoImpresoragt

Podemos observar que se utiliza el elemento xml lttareasgt para representar cada una de lasinstancias de TareaBean las cuales forman parte de de la lista del campo tareas de nuestraclase EstadoBean

Vamos a usar las anotaciones XmlAttribute y XmlElement de la siguiente forma

XmlRootElement(name=estadoImpresora)public class EstadoBean XmlAttribute(name=valor) public String estado = Idle XmlAttribute(name=toner) public int tonerRestante = 25 XmlElement(name=tarea) public ListltTareaBeangt tareas = new ArrayListltTareaBeangt()

En este caso el XML resultante quedariacutea de la siguiente forma

ltestadoImpresora valor=Idle toner=25gt lttareagt ltnombregttextodocltnombregt ltestadogtimprimiendoltestadogt ltpaginasgt13ltpaginasgt lttareagt lttareagt ltnombregttexto2docltnombregt ltestadogten esperaltestadogt ltpaginasgt5ltpaginasgt lttareagtltestadoImpresoragt

Servicios Rest

86

Si no se indica lo contrario por defecto se convierten los campos a elementos del XML

En caso de que los campos no sean puacuteblicos etiquetaremos los getterscorrespondiente (propiedades de la clase)

Hemos visto que para las listas el nombre que especificamos en XmlElement se utilizapara nombrar cada elemento de la lista Si queremos que ademaacutes se incluya un elemento queenvuelva a toda la lista podemos utilizar la etiqueta XmlElementWrapper

XmlRootElement(name=estadoImpresora)public class EstadoBean

XmlAttribute(name=valor) public String estado = Idle

XmlAttribute(name=toner) public int tonerRestante = 25

XmlElementWrapper(name=tareas) XmlElement(name=tarea) public ListltTareaBeangt tareas = new ArrayListltTareaBeangt()

En este caso tendremos un XML como el que se muestra a continuacioacuten

ltestadoImpresora valor=Idle toner=25gt lttareasgt lttareagt ltnombregttextodocltnombregt ltestadogtimprimiendoltestadogt ltpaginasgt13ltpaginasgt lttareagt lttareagt ltnombregttexto2docltnombregt ltestadogten esperaltestadogt ltpaginasgt5ltpaginasgt lttareagt lttareasgtltestadoImpresoragt

Para etiquetar una lista tambieacuten podemos especificar distintos tipos de elemento seguacutenel tipo de objeto contenido en la lista Por ejemplo supongamos que en el ejemploanterior la clase TareaBean fuese una clase abstracta que tiene dos posible subclasesTareaSistemaBean y TareaUsuarioBean Podriacuteamos especificar una etiqueta distintapara cada elemento de la lista seguacuten el tipo de objeto del que se trate con la etiquetaXmlElements de la siguiente forma

XmlElementWrapper(name=tareas) XmlElements( XmlElement(name=usuariotype=TareaUsuarioBeanclass

Servicios Rest

87

XmlElement(name=sistematype=TareaSistemaBeanclass) public ListltTareaBeangt tareas = new ArrayListltTareaBeangt()

De esta forma podriacuteamos tener un XML como el siguiente

ltestadoImpresora valor=Idle toner=25gt lttareasgt ltusuariogt ltnombregttextodocltnombregt ltestadogtimprimiendoltestadogt ltpaginasgt13ltpaginasgt ltusuariogt ltsistemagt ltnombregttexto2docltnombregt ltestadogten esperaltestadogt ltpaginasgt5ltpaginasgt ltsistemagt lttareasgtltestadoImpresoragt

Hemos visto que por defecto se serializan todos los campos Si queremos excluir algunode ellos de la serializacioacuten podemos hacerlo anotaacutendolo con XmlTransient Comoalternativa podemos cambiar el comportamiento por defecto de la serializacioacuten de la claseetiquetaacutendola con XmlAccessorType Por ejemplo

XmlAccessorType(NONE)XmlRootElement(name=estadoImpresora)public class EstadoBean

En este uacuteltimo caso especificando como tipo NONE no se serializaraacute por defectoninguacuten campo soacutelo aquellos que hayamos anotado expliacutecitamente con XmlElement oXmlAttribute Los campos y propiedades (getters) anotados con estas etiquetas seserializaraacuten siempre Ademaacutes de ellos tambieacuten podriacuteamos especificar que se serialicenpor defecto todos los campos puacuteblicos y los getters ( PUBLIC_MEMBER ) todos los getters( PROPERTY ) o todos los campos ya sean puacuteblicos o privados ( FIELD ) Todos los que seserialicen por defecto sin especificar ninguna etiqueta lo haraacuten como elemento

Por uacuteltimo si nos interesa que toda la representacioacuten del objeto venga dada uacutenicamente porel valor de uno de sus campos podemos etiquetar dicho campo con XmlValue

Clase JAXBContext

Para serializar clases Java a y desde XML es necesario interactuar con la clasejavaxxmlbindJAXBContext Esta clase nos permite inspeccionar las clasesJava para comprender la estructura de nuestras clases anotadas Dichas clasesse utilizan como factoriacuteas para las interfaces javaxxmlbindMarshaller yjavaxxmlbindUnmarshaller Las instancias de Marshaller se utilizan para creardocumentos XML a partir de objetos Java Las instancias de Unmarshaller se utilizan para crearobjetos Java a partir de documentos XML A continuacioacuten mostramos un ejemplo de uso de

Servicios Rest

88

JAXB para convertir una instancia de la clase Cliente que hemos definido anteriormentea formato XML para posteriormente volver a crear el objeto de tipo Cliente

Cliente cliente = new Cliente()clientesetId(42)

clientesetNombre(Lucia Arg)

JAXBContext ctx = JAXBContextnewInstance(Clienteclass) StringWriter writer = new StringWriter()

ctxcreateMarshaller()marshal(cliente writer)

String modString = writertoString()

cliente = (Cliente)ctxcreateUnmarshaller()

unmarshal(new StringReader(modString))

Creamos e inicializamos una instancia de tipo ClienteInicializamos JAXBContext para que pueda analizar la clase `ClienteUtilizamos una instancia de Marshaller para escribir el objeto Cliente como unString de Java ( StringWriter es un stream de caracteres que utilizaremos paraconstruir un String )Utilizamos una instancia de Unmarshaller para recrear el objeto Cliente a partir delString que hemos obtenido en el paso anterior

La clase JAXBContext constituye un punto en entrada al API JAXB paralos clientes de nuestros servicios RESTful Proporciona una abstraccioacutenpara gestionar el enlazado (binding) de informacioacuten XMLJava necesariapara implementar las operaciones de marshalling y unmarshalling

bull Unmarshalling La clase Unmarshaller proporciona a la aplicacioacutencliente la capacidad para convertir datos XML en un aacuterbol de objetosJava

bull Marshalling La clase Marshaller proporciona a la aplicacioacutencliente la capacidad para convertir un aacuterbol con contenidos Java denuevo en datos XML

Una vez que hemos proporcionado una visioacuten general sobre coacutemo funciona JAXB vamos aver coacutemo se integra con JAX-RS

Manejadores JAX-RS para JAXB

La especificacioacuten de JAX-RS indica que cualquier implementacioacuten debe soportar deforma automaacutetica el proceso de marshalling y unmarshalling de clases anotadas conXmlRootElement A continuacioacuten mostramos un ejemplo de la implementacioacuten de unservicio que hace uso de dichos manejadores Para ello utilizamos la clase Cliente que hemosanotado previamente con XmlRootElement y que hemos mostrado en apartados anteriores

Path(clientes)public class ClienteResource GET Path(id)

Servicios Rest

89

Produces(applicationxml) public Cliente getCliente(PathParam(id) int id) Cliente cli = findCliente(id) return cust

POST Consumes(applicationxml) public void crearCliente(Cliente cli)

Como podemos ver una vez que aplicamos las anotaciones JAXB a nuestras clases Java ( eneste caso a la clase Cliente ) es muy sencillo intercambiar documentos XML entre el clientey nuestro servicio web Los manejadores JAXB incluidos en la implementacioacuten de JAX-RSgestionaraacuten el marshallingunmarshalling de cualquier clase con anotaciones JAXB para losvalores de Content-Type applicationxml textxml o application+xml Por defecto tambieacuten se encargan de la creacioacuten e inicializacioacuten de instancias JAXBContext Debido a que la creacioacuten de las instancias JAXBContext puede ser cara la implementacioacutende JAX-RS normalmente las guarda despueacutes de la primera inicializacioacuten

JAXB y JSON

JAXB es lo suficientemente flexible como para soportar otros formatos ademaacutes de XMLAunque la especificacioacuten de JAX-RS no lo requiere muchas implementaciones de JAX-RSincluyen adaptadores de JAXB para soportar el formato JSON ademaacutes de XML JSON es unformato basado en texto que puede ser interpretado directamente por Javascript De hechoes el formato de intercambio preferido por las aplicaciones Ajax

JSON es un formato mucho maacutes simple que XML Los objetos estaacuten entre llaves ycontienen pares de clavevalor separados por comas Los valores pueden ser cadenas decaracteres booleanos ( true o false ) valores numeacutericos o arrays de los tipos anteriores

Supongamos que tenemos la siguiente descripcioacuten de un producto en formato XML

ltxml version=10 encoding=UTF-8gtltproductogt ltidgt1ltidgt ltnombregtiPadltnombregt ltdescripciongtDispositivo moacutevilltdescripciongt ltpreciogt500ltpreciogtltproductogt

La representacioacuten JSON equivalente seriacutea

id1 nombreiPad descripcionDispositivo moacutevil precio500

Servicios Rest

90

El formato JSON asociado por ejemplo al siguiente objeto Cliente

ltcliente id=56gt ltnombregtRicardo Lopezltnombregt ltdirecciongt ltcallegtcalle del oso 35ltcallegt ltciudadgtAlicanteltciudadgt ltcodPostalgt01010ltcodPostalgt ltdirecciongt ltclientegt

quedariacutea como sigue

nombre Ricardo Lopez direccion calle calle del oso 35 ciudad Alicante codPostal 01010

Como vemos en el ejemplo el formato JSON consiste baacutesicamente en objetos situados entrellaves los cuales estaacuten formados por pares clavevalor separadas por comas Cada clavey valor estaacute separado por Los valores pueden cadenas de caracteres booleanos (true ofalse) valores numeacutericos o vectores de los tipos anteriores (los vectores estaacuten delimitadospor corchetes)

Podemos antildeadir el formato applicationjson o bienMediaTypeAPPLICATION_JSON a la anotacioacuten Produces en nuestros meacutetodos derecurso para generar respuestas en formato JSON

GETPath(get)Produces(applicationxmlapplicationjson)public Producto getProducto()

En este ejemplo se elegiraacute el formato JSON en la respuesta si el cliente realiza una peticioacutenGET que incluye en la cabecera

Accept applicationjson

El tipo de respuesta es de tipo Producto En este caso Producto debe ser un bean JAXBes decir una clase anotada con XmlRootElement

Los meacutetodos de recurso pueden aceptar tambieacuten datos JSON para clases con anotacionesJAXB

POSTPath(producto)Consumes(applicationxmlapplicationjson)

Servicios Rest

91

public Response crearProducto(Producto prod)

En este caso el cliente deberiacutea incluir la siguiente cabecera cuando realice la peticioacuten POSTque incluya los datos JSON anteriores en el cuerpo del mensaje

Content-Type applicationjson

Hablaremos con maacutes detalle del formato JSON en una sesioacuten posterior

Finalmente y como resumen de lo anterior

JAXB

Para dar soporte al serializado y deserializado XML se utilizan beans JAXBPorejemplo las clases anteriores EstadoBean TareaBean Cliente y Productoson ejemplos de beans JAXB La clase que se serializaraacute como un recurso XMLo JSON se tiene que anotar con XmlRootElement Si en alguacuten elemento deun recurso se retorna un elemento de esa clase y se etiqueta con los tiposProduces(MediaTypeAPPLICATION_XMLMediatypeAPPLICATION_JSON )eacuteste se serializa automaacuteticamente utilizando el tipo de representacioacuten aceptada por elcliente Se puede consultar el artiacuteculo de Lars Vogel5 para maacutes informacioacuten

35 Respuestas del servidor

Vamos a explicar cuaacutel es el comportamiento por defecto de los meacutetodos de recursos JAX-RS en particular veremos cuaacuteles son los coacutedigos de respuesta HTTP por defecto teniendo encuenta situaciones de eacutexito asiacute como de fallo

Dado que en ocasiones vamos a tener que enviar cabeceras de respuesta especiacuteficas antecondiciones de error complejas tambieacuten vamos a explicar coacutemo podemos elaborar respuestascomplejas utilizando el API JAX-RS

Coacutedigos de respuesta por defecto

Los coacutedigos de respuesta por defecto se corresponden con el comportamiento indicado en laespecificacioacuten6 de la definicioacuten de los meacutetodos HTTP 11 Vamos a examinar dichos coacutedigosde respuesta con el siguiente ejemplo de recurso JAX-RS

Path(clientes)public class ClienteResource

Path(id) GET Produces(applicationxml) public Cliente getCliente(PathParam(id) int id)

5 httpwwwvogelladearticlesJAXBarticlehtml6 httpwwww3orgProtocolsrfc2616rfc2616-sec9html

Servicios Rest

92

POST Produces(applicationxml) Consumes(applicationxml) public Cliente crearCliente(Cliente nuevoCli)

PUT Path(id) Consumes(applicationxml) public void updateCliente(PathParam(id) int id Cliente cli)

Path(id) DELETE public void borrarCliente(PathParam(id) int id)

Respuestas que indican eacutexito

Los nuacutemeros de coacutedigo de respuestas HTTP con eacutexito se situacutean en el rango de 200 a 399

bull Para los meacutetodos crearCliente() y getCliente() se devolveraacute una respuestaHTTP con el coacutedigo 200 OK si el objeto Cliente que devuelven dichos meacutetodos noes null

bull Para los meacutetodos crearCliente() y getCliente() se devolveraacute una respuestaHTTP con el coacutedigo 204 No Content si el objeto Cliente que devuelven dichosmeacutetodos es null El coacutedigo de respuesta 204 no indica una condicioacuten de error Solamenteavisa al cliente de que todo ha ido bien pero que el mensaje de respuesta no contienenada en el cuerpo de la misma Seguacuten eacutesto si un meacutetodo de un recurso devuelve void por defecto se devuelve el coacutedigo de respuesta 204 No content Este es el caso para losmeacutetodos updateCliente() y borrarCliente() de nuestro ejemplo

La especificacioacuten HTTP es bastante consistente para los meacutetodos PUT POST GET yDELETE Si una respuesta exitosa HTTP contiene informacioacuten en el cuerpo del mensaje derespuesta entonces el coacutedigo de respuesta es 200 OK Si el cuerpo del mensaje estaacute vaciacuteoentonces se debe devolver 204 No Content

Respuestas que indican una situacioacuten de fallo

Es habitual que las respuestas fallidas se programen de forma que se lance una excepcioacutenLo veremos en un apartado posterior Aquiacute comentaremos algunas condiciones de error pordefecto

Los nuacutemeros de coacutedigo de error de respuesta estaacutendar en HTTP se situacutean en el rango entre400 y 599 En nuestro ejemplo si un cliente se equivoca tecleando la URI y eacutesta queda porejemplo como httphellipcliente entonces el servidor no encontraraacute ninguacuten meacutetodo delrecurso que pueda servir dicha peticioacuten (la URI correcta seriacutea httphellipclientes ) Eneste caso se enviaraacute como respuesta el coacutedigo 404 Not Found

Para los meacutetodos getCliente() y crearCliente() de nuestro ejemplo si el clientesolicita una respuesta con el tipo MIME texthtml entonces la implementacioacuten de JAX-RS devolveraacute automaacuteticamente 406 Not Acceptable con un mensaje de respuesta con elcuerpo de dicho mensaje vaciacuteo Esta respuesta indica que JAX-RS puede encontrar una rutade URI relativa que coincide con la peticioacuten pero no encuentra ninguacuten meacutetodo del recursoque devuelva la respuesta con ese tipo MIME

Servicios Rest

93

Si el cliente invoca una peticioacuten HTTP sobre una URI vaacutelida para la que no se puedeencontrar un meacutetodo de recurso asociado entonces el runtime de JAX-RS devolveraacute el coacutedigo405 Method Not Allowed Asiacute en nuestro ejemplo si el cliente solicita una operacioacuten PUTGET o DELETE sobre la URI clientes obtendraacute como respuesta 405 Method NotAllowed puesto que POST es el uacutenico meacutetodo HTTP que puede dar soporte a dicha URILa implementacioacuten de JAX-RS tambieacuten devolveraacute una cabecera de respuesta Allow con lalista de meacutetodos HTTP que pueden dar soporte a dicha URI Por lo tanto si nuestra aplicacioacutencliente realiza la siguiente peticioacuten de entrada

GET clientes

el servidor devolveraacute la siguiente respuesta

HTTP11 405 Method Not AllowedAllow POST

Elaboracioacuten de respuestas con la clase Response

Como ya hemos visto por ejemplo para peticiones GET si todo va bien se estaraacute devolviendoun coacutedigo de respuesta 200 (Ok) junto con el contenido especificado en el tipo de datosutilizado en cada caso Si devolvemos void el coacutedigo de respuesta seraacute 204 (No Content)

Sin embargo en ocasiones el servicio web que estamos disentildeando no puede implementarseutilizando el comportamiento por defecto de peticioacutenrespuesta inherente a JAX-RS En estoscasos necesitaremos controlar de forma expliacutecita la respuesta que se le enviacutea al cliente (cuerpodel mensaje de la respuesta HTTP) Por ejemplo cuando creamos un nuevo recurso con POSTdeberiacuteamos devolver 201 (Created) Para tener control sobre este coacutedigo nuestros recursosJAX-RS podraacuten devolver instancias de javaxwsrscoreResponse

GETProduces(MediaTypeAPPLICATION_XML)public Response getClientes() ClientesBean clientes = obtenerClientes()

return Responseok(clientes)build()

POSTConsumes(MediaTypeAPPLICATION_XML)public Response addCliente(ClienteBean cliente Context UriInfo uriInfo)

String id = insertarCliente(cliente) URI uri = uriInfo getAbsolutePathBuilder() path(id)

build(id)

return Responsecreated(uri)build()

Al crear una respuesta con Response podemos especificar una entidad que podraacute serun objeto de cualquiera de los tipos vistos anteriormente y que representa los datos a

Servicios Rest

94

devolver como contenido Por ejemplo cuando indicamos ok(clientes) estamos creandouna respuesta con coacutedigo 200 (Ok) y con el contenido generado por nuestro beanJAXB clientes Esto seraacute equivalente a haber devuelto directamente ClientesBean comorespuesta pero con la ventaja de que en este caso podemos controlar el coacutedigo de estadode la respuestaInsertamos una instancia de ClienteBean en la base de datos y obtenemos la claveasociada a dicho clienteEn la sesioacuten anterior hemos hablado de la interfaz uriInfo Es una interfaz inyectableque proporciona acceso a informacioacuten sobre la URI de la aplicacioacuten o la URI de laspeticiones recibidas En este caso estamos construyendo una nueva URI formada porla ruta absoluta de la peticioacuten de entrada http POST antildeadieacutendole la plantilla id yfinalmente sustituyendo el paraacutemetro de la plantilla por el valor id Supongamos quela peticioacuten POST contiene la uri httplocalhost8080recursosclientes Y que el valor de id es 8 El valor de la variable uri seraacute por tanto httplocalhost8080recursosclientes8Devolvemos la respuesta incluyendo la URI anterior en la cabecera HTTP `Location

Veamos con maacutes detalle la clase Response

public abstract class Response

public abstract Object getEntity()

public abstract int getStatus()

public abstract MultivaluedMapltString Objectgt getMetadata()

public abstract URI getLocation()

public abstract MediaType getMediaType()

public abstract void close()

El meacutetodo getEntity() devuelve el objeto Java correspondiente al cuerpo delmensaje HTTPEl meacutetodo getStatus() devuelve el coacutedigo de respuesta HTTPEl meacutetodo getMetadata() devuelve una instancia de tipo MultivaluedMap con lascabeceras de la respuestaEl meacutetodo getLocation() devuelve la URI de la cabecera Location de la respuestaEl meacutetodo getMediaType() devuelve el mediaType del cuerpo de la respuestaEl meacutetodo close() cierra el input stream correspondiente a la entidad asociada delcuerpo del mensaje (en el caso de que esteacute disponible y abierto) Tambieacuten liberacualquier otro recurso asociado con la respuesta (como por ejemplo datos posiblementealmacenados en un buffer)

Estos meacutetodos tiacutepicamente seraacuten invocados dese el cliente tal y como veremos maacutes adelantecuando expliquemos el API cliente

Los objetos Response no pueden crearse directamente Tienen que crearse a partir deinstancias de javaxwsrscoreResponseResponseBuilder devueltas por uno delos siguientes meacutetodos estaacuteticos de Response

public abstract class Response public abstract void close() public static ResponseBuilder status(Status status) public static ResponseBuilder status(int status) public static ResponseBuilder ok()

Servicios Rest

95

public static ResponseBuilder ok(Object entity) public static ResponseBuilder ok(Object entity MediaType type) public static ResponseBuilder ok(Object entity String type) public static ResponseBuilder ok(Object entity Variant var) public static ResponseBuilder serverError() public static ResponseBuilder created(URI location) public static ResponseBuilder noContent() public static ResponseBuilder notModified() public static ResponseBuilder notModified(EntityTag tag) public static ResponseBuilder notModified(String tag) public static ResponseBuilder seeOther(URI location) public static ResponseBuilder temporaryRedirect(URI location) public static ResponseBuilder notAcceptable(ListltVariantgt variants) public static ResponseBuilder fromResponse(Response response)

Veamos por ejemplo el meacutetodo ok()

ResponseBuilder ok(Object entity MediaType type)

Este meacutetodo recibe como paraacutemetros un objeto Java que queremos convertir en una respuestaHTTP y el Content-Type de dicha respuesta Como valor de retorno se obtiene unainstancia de tipo ResponseBuilder pre-inicializada con un coacutedigo de estado de 200 OK

El meacutetodo created() devuelve un ResponseBuilder para un recurso creado y asigna a lacabecera Location de la respuesta el valor de la URI proporionado como paraacutemetro

La clase ResponseBuilder es una factoriacutea utilizada para crear instancias individuales detipo Response

public static abstract class ResponseBuilder public abstract Response build() public abstract ResponseBuilder clone()

public abstract ResponseBuilder status(int status) public ResponseBuilder status(Status status)

public abstract ResponseBuilder entity(Object entity) public abstract ResponseBuilder type(MediaType type) public abstract ResponseBuilder type(String type)

public abstract ResponseBuilder variant(Variant variant) public abstract ResponseBuilder variants(ListltVariantgt variants)

public abstract ResponseBuilder language(String language) public abstract ResponseBuilder language(Locale language)

public abstract ResponseBuilder location(URI location) public abstract ResponseResponseBuilder header(String name Object value) public abstract ResponseResponseBuilder link(URI uri String rel)

Servicios Rest

96

public abstract ResponseResponseBuilder link(String uri String rel)

Vamos a mostrar un ejemplo sobre coacutemo crear respuestas utilizando un objeto Response En este caso el meacutetodo getLibro() devuelve un String que representa el libro en el queestaacute interesado nuestro cliente

Path(libro)public class LibroServicio GET Path(restfuljava) Produces(textplain) public Response getLibro()

String libro =

ResponseBuilder builder = Responseok(libro)

builderlanguage(fr)header(Some-Header some value)

return builderbuild()

Recuperamos los datos del libro solicitado En este caso vamos a devolver una cadenade caracteres que representa el libro en el que estamos interesadosInicializamos el cuerpo de la respuesta utilizando el meacutetodo Responseok() El coacutedigode estado de ResponseBuilder se inicializa de forma automaacutetica con 200Usamos el meacutetodo ResponseBuilderlanguage() para asignar el valorde la cabecera Content-Languaje a franceacutes Tambieacuten usamos el meacutetodoResponseBuilderheader() para asignar un valor concreto a otra cabeceraFinalmente creamos y devolvemos el objeto Response usando el meacutetodoResponseBuilderbuild()

Un detalle que es interesante destacar en este coacutedigo es que no indicamos ninguacuten valor parael Content-Type de la respuesta Debido a que ya hemos especificado esta informacioacutenen la anotacioacuten Produces del meacutetodo el runtime de JAX-RS devolveraacute el valor adecuadodel media type de la respuesta por nosotros

Inclusioacuten de cookies en la respuesta

JAX-RS proporciona la clase javaxwsrscoreNewCookie que utilizaremos para crearnuevos valores de cookies y enviarlos en las respuestas

Para poder incluir las cookies en nuestro objeto Response primero crearemoslas instancias correspondientes de tipo NewCookie las pasaremos al meacutetodoResponseBuildercookie() Por ejemplo

Path(myservice)public class MyService GET public Response get()

NewCookie cookie = new NewCookie(nombre pepe)

ResponseBuilder builder = Responseok(hola textplain) return buildercookie(cookie)build()

Servicios Rest

97

Creamos una nueva cookie con el nombre de clave nombre y le asignamos el valorpepeEn este caso al no indicar el tipo mime de la respuesta con la anotacioacuten Producesnecesitamos indicarlo (en este caso como un paraacutemetro del meacutetodo ok() )

El tipo enumerado de coacutedigos de estado

JAX-RS proporciona el tipo enumerado javaxwsrscoreStatus para representarcoacutedigos de respuesta especiacuteficos

public enum Status OK(200 OK) CREATED(201 Created) ACCEPTED(202 Accepted) NO_CONTENT(204 No Content) MOVED_PERMANENTLY(301 Moved Permanently) SEE_OTHER(303 See Other) NOT_MODIFIED(304 Not Modified) TEMPORARY_REDIRECT(307 Temporary Redirect) BAD_REQUEST(400 Bad Request) UNAUTHORIZED(401 Unauthorized) FORBIDDEN(403 Forbidden) NOT_FOUND(404 Not Found) NOT_ACCEPTABLE(406 Not Acceptable) CONFLICT(409 Conflict) GONE(410 Gone) PRECONDITION_FAILED(412 Precondition Failed) UNSUPPORTED_MEDIA_TYPE(415 Unsupported Media Type) INTERNAL_SERVER_ERROR(500 Internal Server Error) NOT_IMPLEMENTED(501 Not Implemented) SERVICE_UNAVAILABLE(503 Service Unavailable) public enum Family INFORMATIONAL SUCCESSFUL REDIRECTION CLIENT_ERROR SERVER_ERROR OTHER

public Family getFamily() public int getStatusCode() public static Status fromStatusCode(final int statusCode)

Cada valor del tipo Status se asocia con una familia especiacutefica de coacutedigos de respuestaHTTP Estas familias se identifican por el enumerado StatusFamily

bull Los coacutedigos en el rango del 100 se consideran informacionales

bull Los coacutedigos en el rango del 200 se consideran exitosos

bull Los coacutedigos en el rango del 300 son coacutedigos con eacutexito pero dentro de la categoriacutearedireccioacuten

bull Los coacutedigos de error pertenecen a los ragos 400 y 500 En el rango de 400 se consideranerrores del cliente y en el rango de 500 son errores del servidor

Servicios Rest

98

Tanto el meacutetodo Responsestatus() como ResponseBuilderstatus() puedenaceptar un valor enumerado de tipo Status Por ejemplo

DELETEResponse delete() return Responsestatus(StatusGONE)build()

En este caso estamos indicando al cliente que lo que queremos borrar ya no existe (410)

La clase javaxwsrscoreGenericEntity

Cuando estamos creando objetos de tipo Response se nos plantea un problema cuandoqueremos devolver tipos geneacutericos ya que el manejador JAXB necesita extraer la informacioacutendel tipo parametrizado de la respuesta en tiempo de ejecucioacuten Para estos casos JAX-RS proporciona la clase javaxwsrscoreGenericEntity Veamos su uso con unejemplo

GETProduces(applicationxml)public Response getListaClientes() ListltClientegt list = new ArrayListltClientegt() listadd(new Cliente())

GenericEntity entity =

new GenericEntityltListltClientegtgt(list)

return Responseok(entity)build()

La clase GenericEntity es tambieacuten una clase geneacuterica Lo que hacemos escrear una clase anoacutenima que extiende GenericEntity inicializando la plantilla deGenericEntity con el tipo geneacuterico que estemos utilizando

36 Manejadores de excepciones

Vamos a explicar coacutemo podemos tratar las excepciones en nuestros servicios RESTful

Los errores pueden enviarse al cliente bien creando y devolviendo el objeto Responseadecuado o lanzando una excepcioacuten Podemos lanzar cualquier tipo de excepcioacutentanto las denominadas checked (clases que heredan de javalangException ) comolas excepciones unchecked (clases que extienden javalangRuntimeException )Las excepciones generadas son manejadas por el runtime de JAX-RS si tenemosregistrado un mapper de excepciones Dichos mappers (o mapeadores) de excepcionespueden convertir una excepcioacuten en una respuesta HTTP Si las excepciones no estaacutengestionadas por un mapper eacutestas se propagan y se gestionan por el contenedor (deservlets) en el que se estaacute ejecutando JAX-RS JAX-RS proporciona tambieacuten la clasejavaxwsrsWebApplicationException Esta excepcioacuten puede lanzarse por elcoacutedigo de nuestra aplicacioacuten y seraacute procesado automaacuteticamente por JAX-RS sin necesidadde disponer de forma expliacutecita de ninguacuten mapper Vamos a ver coacutemo utilizar esta clase

Servicios Rest

99

La clase javaxwsrsWebApplicationException

JAX-RS incluye una excepcioacuten unchecked que podemos lanzar desde nuestraaplicacioacuten RESTful (ver la documentacioacuten del httpdocsoraclecomjavaee7apijavaxwsrsWebApplicationExceptionhtml [API]) Esta excepcioacuten se puede pre-inicializar con un objetoResponse o con un coacutedigo de estado particular

Clase javaxwsrsWebApplicationException

public class WebApplicationException extends RuntimeException Constructores public WebApplicationException()

public WebApplicationException(Response response) public WebApplicationException(int status)

public WebApplicationException(ResponseStatus status)

public WebApplicationException(String message) public WebApplicationException(Throwable cause) public WebApplicationException(Throwable cause Response response) public WebApplicationException(Throwable cause int status) public WebApplicationException(Throwable cause ResponseStatus status)

public Response getResponse() ]

Podemos crear una instancia a partir de un objeto ResponseCreacioacuten de una instancia a partir de un coacutedigo de estadoCreacioacuten de una instancia a partir de un String Por defecto se incluye el coacutedigo de estado500 El String que se pasa como paraacutemetro se almacena para su posterior recuperacioacutena traveacutes del mensaje `getMessage()

Cuando JAX-RS detecta que se ha lanzado la excepcioacuten WebApplicationException lacaptura y realiza una llamada al meacutetodo WebApplicationExceptiongetResponse()para obtener un objeto Response que enviaraacute al cliente Si la aplicacioacuten ha inicializado laexcepcioacuten WebApplicationException con un coacutedigo de estado o un objeto Response dicho coacutedigo o Response se utilizaraacuten para crear la respuesta HTTP real En otro caso laexcepcioacuten WebApplicationException devolveraacute el cliente el coacutedigo de respuesta 500Internal Server Error

Por ejemplo supongamos que tenemos un servicio web que permite a los usuarios solicitarinformacioacuten de nuestros clientes utilizando una representacioacuten XML

Path(clientes)public class ClienteResource GET Path(id) Produces(applicationxml) public Cliente getCliente(PathParam(id) int id) Cliente cli = recuperarCliente(id) if (cli == null) throw new WebApplicationException(ResponseStatusNOT_FOUND) return cli

Servicios Rest

100

En este ejemplo si no encontramos una instancia de Cliente con el id proporcionadolanzaremos una WebApplicationException que provocaraacute que se le enviacutee al clientecomo coacutedigo de respuesta 404 Not Found

Mapeado de excepciones

Normalmente las aplicaciones tienen que tratar con multitud de excepciones lanzadas desdenuestro coacutedigo de aplicacioacuten o por frameworks de terceros Dejar que el servlet JAX-RSque reside en el servidor maneje la excepcioacuten no nos proporciona demasiada flexibilidadSi capturamos y redirigimos todas estas excepciones a WebApplicationExceptionpodriacutea resultar bastante tedioso De forma alternativa podemos implementar y registrarinstancias de javaxwsrsextExceptionMapper Estos objetos saben coacutemo mapearuna excepcioacuten lanzada por la aplicacioacuten a un objeto Response

public interface ExceptionMapperltE extends Throwablegt Response toResponse(E exception)

Las clases que implementan la interfaz ExceptionMapperltTgt son proveedores demappings de excepciones (Exception Mapping Providers) y mapean una excepcioacuten runtimeo checked a una instancia de Response

Cuando un recurso JAX-RS lanza una excepcioacuten para la que existe un proveedor de mappingde excepciones eacuteste uacuteltimo se utiliza para devolver una instancia de Response Estarespuesta resultante se procesa como si hubiese sido generada por el recurso

Por ejemplo una excepcioacuten bastante utilizada por aplicaciones de bases de datos que utilizanJPA (Java Persistence Api) es javaxpersistenceEntityNotFoundException Estaexcepcioacuten se lanza cuando JPA no puede encontrar un objeto particular en la base de datosEn lugar de escribir coacutedigo que maneje dicha excepcioacuten de forma expliacutecita podemos escribirun ExceptionMapper para que gestione dicha excepcioacuten por nosotros Veaacutemos coacutemo

Provider public class EntityNotFoundMapper

implements ExceptionMapperltEntityNotFoundExceptiongt

public Response toResponse(EntityNotFoundException e)

return Responsestatus(ResponseStatusNOT_FOUND)build()

Nuestra implementacioacuten de ExceptionMapper debe anotarse con Provider Estole indica al runtime de JAX-RS que esta clase es un componente RESTLa clase que implementa ExceptionMapper debe proporcionar el tipo que se quiereparametrizar JAX-RS utiliza esta informacioacuten para emparejar las excepciones de tipoEntityNotFoundException con nuestra clase EntityNotFoundMapperEl meacutetodo toResponse() recibe la excepcioacuten lanzada ycrea un objeto Response que se utilizaraacute para construir la respuesta HTTP

Servicios Rest

101

Otro ejemplo es la excepcioacuten EJBException lanzada por aplicaciones que utilizan EJBs

Jerarquiacutea de excepciones

JAX-RS 20 proporciona una jerarquiacutea de excepciones para varias condiciones deerror para las peticiones HTTP La idea es que en lugar de crear una instancia deWebApplicationException e inicializarla con un coacutedigo de estado especiacutefico podemosuilizar en su lugar una de las excepciones de la jeraquiacutea (clases que heredan de la claseWebApplicationException) Por ejemplo podemos cambiar el coacutedigo anterior que utilizabaWebApplicationException y en su lugar usar javaxwsrsNotFoundException

Path(clientes)public class ClienteResource GET Path(id) Produces(applicationxml) public Cliente getCliente(PathParam(id) int id) Cliente cli = recuperarCliente(id) if (cli == null) throw new NotFoundException() return cli

Al igual que el resto de excepciones de la jerarquiacutea la excepcioacuten NotFoundExceptionhereda de WebApplicationException La siguiente tabla muestra algunas excepcionesque podemos utilizar todas ellas del paquete javaxwsrs Las que incluyen un coacutedigo deestado en el rango de 400 son subclases de ClientErrorException y como ya hemosindicado representan errores en las peticiones Las que presentan un coacutedigo de estado enel rango de 500 son subclases de ServerErrorException y representan errores delservidor

Table 5 Jerarquiacutea de excepciones JAX-RS

Excepcioacuten Coacutedigo de estado Descripcioacuten

BadRequestException 400 Mensaje mal formado

NotAuthorizedException 401 Fallo de autenticacioacuten

ForbiddenException 403 Acceso no permitido

NotFoundException 404 No se ha podido encontrar elrecurso

NotAllowedException 405 Meacutetodo HTTP no soportado

NotAcceptableException 406 Media type solicitado porel cliente no soportado(cabecera Accept de lapeticion)

NotSupportedException 415 El cliente ha incluido unMedia type no soportado(cabecera Content-Type dela peticion)

Servicios Rest

102

Excepcioacuten Coacutedigo de estado Descripcioacuten

InternalServerErrorException 500 Error general del servidor

ServiceUnavailableException 503 El servidor estaacute ocupadoo temporalmente fuera deservicio

bull La excepcioacuten BadRequestException se utiliza cuando el cliente enviacutea algo al servidorque eacuteste no puede interpretar Ejemplos de escenarios concretos que provocan que elruntime JAX-RS son cuando una peticioacuten PUT o POST contiene un cuerpo del mensaje conun documento XML o JSON mal formado de forma que falle el parsing del documento ocuando no puede convertir un valor especificado en la cabecera o cookie al tipo deseadoPor ejemplo

HeaderParam(Cabecera-Particular) int cabeceraCookieParam(miCookie) int cookie

Si el valor de la cabecera HTTP de la peticioacuten o el valor miCookie no puede convertirseen un entero se lanzaraacute la excepcioacuten BadRequestException

bull La excepcioacuten NotAuthorizedException (coacutedigo 401) se usa cuando queremosescribir nuestros propios protocolos de autorizacioacuten y queremos indicar al cliente que eacutestenecesita autenticarse con el servidor

bull La excepcioacuten ForbiddenException (coacutedigo 403)se usa generalmente cuando elcliente realiza una invocacioacuten para la que no tiene permisos de acceso Esto ocurrenormalmente debido a que el cliente no tiene el rol requerido

bull La excepcioacuten NotFoundException (coacutedigo 404) se usa cuando queremos comunicar alcliente que el recurso que estaacute solicitando no existe Esta excepcioacuten tambieacuten se generaraacute deforma automaacutetica por el runtime de JAX-RS cuando a eacuteste no le sea posible inyectar un valoren un PathParam QueryParam o MatrixParam Al igual que hemos comentado paraBadRequestException esto puede ocurrir si intentamos convertir el valor del paraacutemetroa un tipo que no admite esta conversioacuten

bull La excepcioacuten NotAllowedException (coacutedigo 405) se usa cuando el meacutetodo HTTP queel cliente estaacute intentando invocar no estaacute soportado por el recurso al que el cliente estaacuteaccediendo El runtime de JAX-RS lanza automaacuteticamente esta excepcioacuten si no encuentraninguacuten meacutetodo que pueda emparejar con el meacutetodo HTTP invocado

bull La excepcioacuten NotAcceptableException (coacutedigo 406) se usa cuando un cliente estaacutesolicitando un formato especiacutefico a traveacutes de la cabecera Accept El runtime de JAX-RSlanza automaacuteticamente esta excepcioacuten si no hay un meacutetodo con una anotacioacuten Producesque sea compatible con la cabecera Accept del cliente

bull La excepcioacuten NotSupportedException (coacutedigo 415)se usa cuando un cliente estaacuteenviando una representacioacuten que el servidor no comprende El runtime de JAX-RS lanzaautomaacuteticamente esta excepcioacuten si no hay un meacutetodo con una anotacioacuten Consumes quecoincida con el valor de la cabecera Content-Type de la peticioacuten

bull La excepcioacuten InternalServerErrorException (coacutedigo 500)es una excepcioacuten depropoacutesito general lanzada por el servidor Si en nuestra aplicacioacuten queremos lanzar estaexcepcioacuten deberiacuteamos hacerlo si se ha producido una condicioacuten de error que realmenteno encaja en ninguna de las situaciones que hemos visto El runtime de JAX-RS lanzaautomaacuteticamente esta excepcioacuten si falla un MessageBodyWriter o si se lanza algunaexcepcioacuten desde alguacuten ExceptionMapper

Servicios Rest

103

bull La excepcioacuten ServiceUnavailableException (coacutedigo 503)se usa cuando el servidorestaacute ocupado o temporalmente fuera de servicio En la mayoriacutea de los casos es suficientecon que el cliente vuelva a intentar realizar la llamada un tiempo maacutes tarde

Servicios Rest

104

37 Ejercicios

Servicio REST ejemplo

Para familiarizarnos con las el uso de diferentes manejadores de contenidos y manejo deexcepciones proporcionamos el moacutedulo el MOacuteDULO s3-ejemplo-rest con la implementacioacutende un servicio rest sencillo que podeacuteis probar con la herramienta postman

En el directorio srcmainresources de dicho moacutedulo teneacuteis un fichero de texto con lasinstrucciones (instruccionestxt) para construir desplegar y probar la aplicacioacuten de ejemplo

Plantillas que se proporcionan

Para esta sesioacuten proporcionamos un proyecto como plantilla con el nombre s3-filmotecaque tendraacutes utilizar como punto de partida para aplicar lo que hemos aprendido en esta sesioacuten

Se trata de una implementacioacuten parcial para gestionar una filmoteca con informacioacuten depeliacuteculas y actores

La estructura loacutegica del proyecto proporcionado es la siguiente

bull Paquete orgexpertojavadomain es la capa que contiene los objetos del dominiode la aplicacioacuten Por simplicidad no usamos una base de datos real sino que trabajamoscon datos en memoria

bull Paquete orgexpertojavaservice contiene la implementacioacuten de los servicios denuestra aplicacioacuten que seraacuten accedidos desde la capa rest

bull Paquete orgexpertojavarest constituye la capa rest de nuestra aplicacioacuten Estacapa es cliente de la capa de servicios

En la carpeta srcmainresources teneacuteis un fichero de texto (instruccionestxt) coninformacioacuten detallada sobre el API rest implementado

Uso de JAXB (05 puntos)

Utiliza las anotaciones JAXB oportunas para realizar el serializado de las entidades java a xmly json de forma que la lista de peliacuteculas de la filmoteca en formato xml sea

Peticioacuten rest GET peliculas

ltxml version=10 encoding=UTF-8 standalone=yesgtltpeliculasgt ltpelicula duracion=120 estreno=2015-12-08T000000+0100 id=1gt ltdirectorgtStanley Kubrickltdirectorgt lttitulogtEl resplandorlttitulogt ltpeliculagt ltpelicula duracion=155 estreno=2015-18-07T000000+0100 id=2gt ltdirectorgtDirector 2ltdirectorgt lttitulogtPelicula 2lttitulogt ltpeliculagtltpeliculasgt

Servicios Rest

105

Por defecto JAXB serializa los tipos Date con el formato ISO 80617

para los contenidos en el cuerpo de la petioacutenrespuesta Dicho formatoes YYYY-MM-DD (seguido de HHMMSS) Por otro lado si antildeadimosactores a las peliacuteculas eacutestos deberaacuten mostrarse bajo la etiquetaltactoresgt tal y como mostramos en el siguiente ejemplo

Los datos mostrados para una peliacutecula en formato xml tienen que presentar el siguienteaspecto

sourcejava] Peticioacuten rest GET peliculas1

ltxml version=10 encoding=UTF-8 standalone=yesgtltpelicula duracion=120 estreno=2015-12-08T000000+0100 id=1gt ltactoresgt ltactor nombre=Jack Nicholson personaje=Jack Torrancegt ltactoresgt ltdirectorgtStanley Kubrickltdirectorgt lttitulogtEl resplandorlttitulogtltpeliculagt

Puedes comprobar si has hecho bien el ejercicio utilizando Postman

Uso de manejadores de contenidos y clase Response (075 puntos)

Implementa una nueva peticioacuten POST que reciba los datos de una nueva peliacutecula desdeun formulario Recuerda que los datos de un formulario pueden ponerse en el cuerpo de lapeticioacuten HTTP como pares nombre=valor separados por amp Los espacios en blanco secodifican como 20 No es necesario poner comillas Por ejemplo

nombre1=valor20con20espaciosampnombre2=valor

Se proporciona el fichero indexhtml con un formulario para utilizarlo como alternativa apostman Para acceder al formulario usaremos httplocalhost8080s3-filmoteca

Modifica las peticiones POST sobre peliacuteculas y actores de forma que devuelvan enla cabecera Location la URI del nuevo recurso creado Por ejemplo

httplocalhost8080s3-filmotecapeliculas3

en el caso de una nueva peliacutecula y

httplocalhost8080s3-filmotecapeliculas3actoresSigourney20Weaver

si por ejemplo hemos antildeadido un nuevo actor a la peliacutecula con id=3

Modifica los meacutetodos GET para que devuelvan el estado 204 No Content en los casos enlos que la peliacutecula yo actor consultado no exista

7 httpseswikipediaorgwikiISO_8601

Servicios Rest

106

Modifica los meacutetodos GET para que devuelvan el estado 404 Not Found en los casos enlos que las listas de peliacuteculas yo actores esteacuten vaciacuteas

Implementa el coacutedigo para antildeadir un nuevo actor Tendraacutes que obtener la informacioacuten de lapeliacutecula y nombre del actor de la URI de la peticioacuten

Puedes comprobar si has hecho bien el ejercicio utilizando Postman

Manejo de excepciones (075 puntos)

Modifica el meacutetodo addPelicula de la capa de servicio (paquete orgexpertojavaservice)para que lance una excepcioacuten de tipo ServiceException con el mensaje El tiacutetulo de la peliacuteculano puede ser nulo ni vaciacuteo cuando se intente antildeadir una peliacutecula con un tiacutetulo con valor nullo vaciacuteo

El meacutetodo addPelicula debe lanzar tambieacuten una excepcioacuten de tipo ServiceException conel mensaje La peliacutecula ya existe cuando se intente antildeadir una peliacutecula con un tiacutetulo que yaexiste

Modifica el meacutetodo addActor de la capa de servicio para que lance las excepciones de tipoServiceException con los mensajes El tiacutetulo de la peliacutecula no puede ser nulo ni vaciacuteo cuandose intente antildeadir un actor a una peliacutecula cuyo tiacutetulo no existe o bien el mensaje EL actor yaexiste si intentamos antildeadir un actor a una peliacutecula que ya habiacuteamos antildeadido previamente

Implementa un mapper para capturar las excepciones de la capa de servicio de forma quese devuelva el estado 500 Internal Server error y como entidad del cuerpo de la respuestael mensaje asociado a las excepciones generadas por el servicio (El tiacutetulo de la peliacutecula nopuede ser nulo ni vaciacuteo EL actor ya existe hellip) La nueva clase pertenceraacute a la capa restPuedes ponerle el nombre ServiceExceptionMapper

Puedes comprobar si has hecho bien el ejercicio utilizando Postman

Servicios Rest

107

4 HATEOAS y Seguridad

En esta sesioacuten trataremos uno de los principios REST de obligado cumplimiento para poderhablar de un servicio RESTful Nos referimos a HATEOAS Hasta ahora hemos visto coacutemo losclientes pueden cambiar el estado de los recursos (el nombre del recurso se especifica en laURI de la peticioacuten) a traveacutes de los contenidos del cuerpo del mensaje o utilizando paraacutemetroso cabeceras de peticioacuten A su vez los servicios comunican el estado resultante de la peticioacuten alos clientes a traveacutes del contenido del cuerpo del mensaje coacutedigos de respuesta y cabecerasde respuesta Pues bien teniendo en cuenta lo anterior HATEOAS hace referencia a quecuando sea necesario tambieacuten deben incluirse los enlaces a los recursos (URI) en el cuerpode la respuesta (o en las cabeceras) para asiacute poder recuperar el recurso en cuestioacuten o losrecursos relacionados

En esta sesioacuten tambieacuten explicaremos algunos conceptos baacutesicos para poder dotar deseguridad a nuestros servicios REST

41 iquestQueacute es HATEOAS

Comuacutenmente se hace referencia a Internet como la Web (web significa red telarantildea) debidoa que la informacioacuten estaacute interconectada mediante una serie de hiperenlaces embebidosdentro de los documentos HTML Estos enlaces crean una especie de hilos o hebras entrelos sitios web relacionados en Internet Una consecuencia de ello es que los humanos puedennavegar por la Web buscando elementos de informacioacuten relacionados de su intereacutes haciendoclick en los diferentes enlaces desde sus navegadores Los motores de buacutesqueda puedentrepar o desplazarse por estos enlaces y crear iacutendices enormes de datos susceptibles deser buscados Sin ellos Internet no podriacutea tener la propiedad de ser escalable No habriacuteaforma de indexar faacutecilmente la informacioacuten y el registro de sitios web seriacutea un proceso manualbastante tedioso

Ademaacutes de los enlaces (links) otra caracteriacutestica fundamental de Internet es HTML Enocasiones un sitio web nos solicita que rellenemos alguna informacioacuten para compraralgo o registrarnos en alguacuten servicio El servidor nos indica a nosotros como clientes queacuteinformacioacuten necesitamos proporcionar para completar una accioacuten descrita en la paacutegina webque estamos viendo El navegador nos muestra la paacutegina web en un formado que podemosentender faacutecilmente Nosotros leemos la paacutegina web y rellenamos y enviamos el formulario Unformulario HTML es un formato de datos interesante debido a que auto-describe la interaccioacutenentre el cliente y el servidor

El principio arquitectoacutenico que describe el proceso de enlazado (linking) y el enviacuteo deformularios se denomina HATEOAS Las siglas del teacutermino HATEOAS significan HypermediaAs The Engine Of Application State (es decir el uso de Hipermedia como mecanismo demaacutequina de estados de la aplicacioacuten) La idea de HATEOAS es que el formato de los datosproporciona informacioacuten extra sobre coacutemo cambiar el estado de nuestra aplicacioacuten En laWeb los enlaces HTML nos permiten cambiar el estado de nuestro navegador Por ejemplocuando estamos leyendo una paacutegina web un enlace nos indica queacute posibles documentos(estados) podemos ver a continuacioacuten Cuando hacemos click sobre un enlace el estado delnavegador cambia al visitar y mostrar una nueva paacutegina web Los formularios HTML por otraparte nos proporcionan una forma de cambiar el estado de un recurso especiacutefico de nuestroservidor Por uacuteltimo cuando compramos algo en Internet por ejemplo estamos creando dosnuevos recursos en el servicio una transaccioacuten con tarjeta de creacutedito y una orden de compra

Servicios Rest

108

42 HATEOAS y Servicios Web

Cuando aplicamos HATEOAS a los servicios web la idea es incluir enlaces en nuestrosdocumentos XML o JSON La mayoriacutea de las aplicaciones RESTful basadas en XML utilizanel formato Atom Syndication Format8 para implementar HATEOAS

Enlaces Atom

Los enlaces Atom constituyen un mecanismo estaacutendar para incluir enlaces (links) en nuestrosdocumentos XML Veamos un ejemplo

ltclientesgt ltlink rel=next href=httpejemplocomclientesinicio=2amptotal=2 type=applicationxmlgt ltcliente id=123gt ltnombregtJuan Garcialtnombregt ltclientegt ltcliente id=332gt ltnombregtPablo Bozoltnombregt ltclientegtltclientesgt

El documento anterior representa una lista de clientes y el elemento ltlinkgt que contieneun enlace indica la forma de obtener los siguientes clientes de la lista

Un enlace Atom es simplemente un elemento XML (elemento ltlinkgt ) con unos atributosespeciacuteficos

bull El atributo rel Se utiliza para indicar la relacioacuten del enlace con el elemento XML en elque anidamos dicho enlace Es el nombre loacutegico utilizado para referenciar el enlace Esteatributo tiene el mismo significado para la URL que estamos enlazando que la etiquetaHTML ltagt tiene para la URL sobre la que estamos haciendo click con el ratoacuten en elnavegador Si el enlace hace referencia al propio elemento XML en el que incluimos elenlace entonces asignaremos el valor del atributo self

bull El atributo href es la URL a la que podemos acceder para obtener nueva informacioacuten ocambiar el estado de nuestra aplicacioacuten

bull El atributo type indica el media type asociado con el recurso al que apunta la URL

Cuando un cliente recibe un documento con enlaces Atom eacuteste busca la relacioacuten en la queestaacute interesado (atributo rel ) e invoca la URI indicada en el atributo href

Ventajas de utilizar HATEOAS con Servicios Web

Resulta bastante obvio por queacute los enlaces y los formularios tienen mucho que ver en laprevalencia de la Web Con un navegador tenemos una ventana a todo un mundo deinformacioacuten y servicios Las maacutequinas de buacutesqueda rastrean Internet e indexan sitios webpara que todos los datos esteacuten al alcance de nuestros dedos Esto es posible debido a quela Web es auto-descriptiva Cuando accedemos a un documento conocemos coacutemo recuperarinformacioacuten adicional siguiendo los enlaces situados en dicho documento Por ejemplo

8 httpwwww3org2005Atom

Servicios Rest

109

conocemos coacutemo realizar una compra en Amazon debido a que los formularios HTML nosindican coacutemo hacerlo

Cuando los clientes son maacutequinas en lugar de personas (los servicios Web tambieacuten seconocen como Web para maacutequinas frente a la Web para humanos proporcionada por elacceso a un servidor web a traveacutes de un navegador) el tema es algo diferente puesto que lasmaacutequinas no pueden tomar decisiones sobre la marcha cosa que los humanos siacute puedenhacer Las maacutequinas requieren que los programadores les digan coacutemo interpretar los datosrecibidos desde un servicio y coacutemo realizar transiciones entre estados como resultado de lasinteracciones entre clientes y servidores

En este sentido HATEOAS proporciona algunas ventajas importantes para contribuir a quelos clientes sepan coacutemo utilizar los servicios a la vez que acceden a los mismos Vamos acomentar algunas de ellas

Transparencia en la localizacioacuten

En un sistema RESTful gracias a HATEOAS soacutelo es necesario hacer puacuteblicas unas pocasURIs Los servicios y la informacioacuten son representados con enlaces que estaacuten embebidosen los formatos de los datos devueltos por las URIs puacuteblicas Los clientes necesitan conocerlos nombres loacutegicos de los enlaces para buscar a traveacutes de ellos pero no necesitan conocerlas ubicaciones reales en la red de los servicios a los que acceden

Los enlaces proporcionan un nivel de indireccioacuten de forma que los servicios subyacentespueden cambiar sus localizaciones en la red sin alterar la loacutegica ni el coacutedigo del cliente

Desacoplamiento de los detalles de la interaccioacuten

Consideremos una peticioacuten que nos devuelve una lista de clientes en una base de datosGET clientes Si nuestra base de datos tiene miles de datos probablemente noquerremos devolver todos ellos de una soacutela vez Lo que podemos hacer es definir una vistaen nuestra base de datos utilizando paraacutemetros de consulta por ejemplo

customersinicio=indiceInicioamptotal=numeroElementosDevueltos

El paraacutemetro inicio identifica el iacutendice inicial de nuestra lista de clientes El paraacutemetrototal especifica cuaacutentos clientes queremos que nos sean devueltos como respuesta

Lo que estamos haciendo en realidad es incrementar la cantidad de conocimiento que elcliente debe tener predefinido para interactuar con el servicio (es decir no soacutelo necesita saberla URI sino ademaacutes conocer la existencia de estos paraacutemetros) Supongamos que en el futuroel servidor decide que necesita cambiar la forma en la que se accede al nuacutemero de datossolicitados por el cliente Si el servidor cambia la interfaz los clientes antiguos dejaraacuten defuncionar a menos que cambien su coacutedigo

En lugar de publicar la interfaz REST anterior para obtener datos de los clientes podemosincluir dicha informacioacuten en el documento de respuesta por ejemplo

ltclientesgt ltlink rel=next href=httpejemplocomclientesinicio=2amptotal=2 type=applicationxmlgt ltcliente id=123gt

Servicios Rest

110

ltnombregtJuan Garcialtnombregt ltclientegt ltcliente id=332gt ltnombregtPablo Bozoltnombregt ltclientegtltclientesgt

Cuando incluimos un enlace Atom en un documento estamos asignando un nombre loacutegicoa una transicioacuten de estados En el ejemplo anterior la transicioacuten de estados es el siguienteconjunto de clientes a los que podemos acceder En lugar de tener que recordar cuaacuteles sonlos paraacutemetros de la URI que tenemos que utilizar en la siguiente invocacioacuten para obtener maacutesclientes lo uacutenico que tenemos que hacer es seguir el enlace proporcionado El cliente notiene que contabilizar en ninguacuten sitio la interaccioacuten ni tiene que recordar queacute seccioacuten de labase de datos estamos consultando actualmente

Ademaacutes el XML devuelto es auto-contenido iquestQueacute pasa si tenemos que pasar estedocumento a un tercero Tendriacuteamos que decirle que se trata de una vista parcial de la basede datos y especificar el iacutedice de inicio Al incluir el enlace en el documento ya no es necesarioproporcionar dicha informacioacuten adicional ya que forma parte del propio documento

Reduccioacuten de errores de transicioacuten de estados

Los enlaces no se utilizan solamente como un mecanismo para agregar informacioacuten denavegacioacuten Tambieacuten se utilizan para cambiar el estado de los recursos Pensemos en unaaplicacioacuten de comercio web a la que podemos acceder con la URI pedidos333

ltpedido id=333gt ltcliente id=123gtltclientegt ltimportegt9999ltimportegt ltlineas-pedidogt ltlineas-pedidogtltpedidogt

Supongamos que un cliente quiere cancelar su pedido Podriacutea simplemente invocar la peticioacutenHTTP DELETE pedidos333 Esta no es siempre la mejor opcioacuten ya que normalmenteel sistema necesitaraacute retener el pedido para propoacutesitos de almacenaje Por ello podriacuteamosconsiderar una nueva representacioacuten del pedido con un elemento cancelado a true

PUT pedidos333 HTTP11Content-Type applicationxmlltpedido id=333gt ltcliente id=123gtltclientegt ltimportegt9999ltimportegt ltcanceladogttrueltcanceladogt ltlineas-pedidogt ltlineas-pedidogtltpedidogt

Pero iquestqueacute ocurre si el pedido no puede cancelarse Podemos tener un cierto estado ennuestro proceso de pedidos en donde esta accioacuten no estaacute permitida Por ejemplo si el pedido

Servicios Rest

111

ya ha sido enviado entonces no puede cancelarse En este caso realmente no hay niguacutencoacutedigo de estado HTTP de respuesta que represente esta situacioacuten Una mejor aproximacioacutenes incluir un enlace para poder realizar la cancelacioacuten

ltpedido id=333gt ltcliente id=123gtltclientegt ltimportegt9999ltimportegt ltcanceladogtfalseltcanceladogt ltlink rel=cancelar href=httpejemplocompedidos333canceladogt ltlineas-pedidogt ltlineas-pedidogtltpedidogt

El cliente podriacutea invocar la orden GET pedidos333 y obtener el documento XMLque representa el pedido Si el documento contiene el enlace cancelar entonces el clientepuede cambiar el estado del pedido a cancelado enviando una orden PUT vaciacutea a la URIreferenciada en el enlace Si el documento no contiene el enlace el cliente sabe que estaoperacioacuten no es posible Esto permite que el servicio web controle en tiempo real la forma enla que el cliente interactua con el sistema

Enlaces en cabeceras frente a enlaces Atom

Una alternativa al uso de enlaces Atom en el cuerpo de la respuesta es utilizar enlaces enlas cabeceras de la respuesta (httptoolsietforghtmlrfc5988) Vamos a explicar eacutesto con unejemplo

Consideremos el ejemplo de cancelacioacuten de un pedido que acabamos de ver En lugar deutilizar un enlace Atom para especificar si se permite o no la cancelacioacuten del pedido podemosutilizar la cabecera Link (es uno de los posibles campos que podemos incluir como cabeceraen una respuesta HTTP)) De esta forma si un usuario enviacutea la peticioacuten GET pedidos333 recibiraacute la siguiente respuesta HTTP

HTTP11 200 OKContent-Type applicationxmlLink lthttpejemplocompedidos333canceladogt rel=cancel

ltpedido id=333gt ltpedidogt

La cabecera Link tiene las mismas caracteriacutesticas que un enlace Atom La URI estaacute entrelos signos lt y gt y estaacute seguida por uno o maacutes atributos delimitados por El atributo reles obligatorio y tiene el mismo significado que el correspondiente atributo Atom com el mismonombre En el ejemplo no se muestra pero podriacuteamos especificar el media type utilizando elatributo type

43 HATEOAS y JAX-RS

JAX-RS no proporciona mucho soporte para implementar HATEOAS HATEOAS se definepor la aplicacioacuten por lo que no hay mucho que pueda aportar ninguacuten framework Lo que siacute

Servicios Rest

112

proporciona JAX-RS son algunas clases que podemos utilizar para construir las URIs de losenlaces HATEOAS

Construccioacuten de URIs con UriBuilder

Una clase que podemos utilizar es javaxwsrscoreUriBuilder Esta clase nospermite construir URIs elemento a elemento y tambieacuten permite incluir plantillas de paraacutemetros(segmentos de ruta variables)

Clase UriBuilder meacutetodos para instanciar objetos de la clase

public abstract class UriBuilder public static UriBuilder fromUri(URI uri) throws IllegalArgumentException public static UriBuilder fromUri(String uri) throws IllegalArgumentException public static UriBuilder fromPath(String path) throws IllegalArgumentException public static UriBuilder fromResource(Classltgt resource) throws IllegalArgumentException public static UriBuilder fromLink(Link link) throws IllegalArgumentException

Las instancias de UriBuilder se obtienen a partir de meacutetodos estaacuteticos con la formafromXXX() Podemos inicializarlas a partir de una URI una cadena de caracteres o laanotacioacuten Path de una clase de recurso

Para extraer modificar yo componer una URI se pueden utilizar meacutetodos como

Clase UriBuilder meacutetodos para manipular las URIs

public abstract UriBuilder clone() crea una copia

crea una copia con la informacioacuten de un objeto URIpublic abstract UriBuilder uri(URI uri) throws IllegalArgumentException

meacutetodos para asignarmodificar valores de los atributos de los objetos UriBuilderpublic abstract UriBuilder scheme(String scheme) throws IllegalArgumentExceptionpublic abstract UriBuilder userInfo(String ui)public abstract UriBuilder host(String host) throws IllegalArgumentExceptionpublic abstract UriBuilder port(int port) throws IllegalArgumentExceptionpublic abstract UriBuilder replacePath(String path)

meacutetodos que antildeaden elementos a nuestra URIpublic abstract UriBuilder path(String path)public abstract UriBuilder segment(String segments)public abstract UriBuilder matrixParam(String name Object values)public abstract UriBuilder queryParam(String name Object values)

Servicios Rest

113

meacutetodo que instancia el valor de una plantilla de la URIpublic abstract UriBuilder resolveTemplate(String name Object value)

Los meacutetodos build() construyen la URI Eacutesta puede contener plantillas de paraacutemetros( segmentos de ruta variables) que deberemos inicializar utilizando pares nombrevalor o bienuna lista de valores que reemplazaraacuten a los paraacutemetros de la plantilla en el orden en el queaparezcan

Clase UriBuilder meacutetodos buildXXX() para construir las URIs

public abstract URI buildFromMap(MapltString extends Objectgt values) throws IllegalArgumentException UriBuilderException

public abstract URI build(Object values) throws IllegalArgumentException UriBuilderException

Veamos alguacuten ejemplo que muestra coacutemo crear inicializar componer y construir una URIutilizando un UriBuilder

UriBuilder builder = UriBuilderfromPath(clientesid)builderscheme(http) host(hostname) queryParam(param=param)

Con este coacutedigo estamos definiendo una URI como

httphostnameclientesidparam=param

Puesto que tenemos plantillas de paraacutemetros necesitamos inicializarlos con valores quepasaremos como argumentos para crear la URI final Si queremos reutilizar la URI quecontiene las plantillas deberiacuteamos realizar una llamada a clone() antes de llamar al meacutetodobuild() ya que eacuteste reemplazaraacute los paraacutemetros de las plantillas en la estructura internadel objeto

UriBuilder clone = builderclone()URI uri = clonebuild(ejemplocom 333 valor)

El coacutedigo anterior dariacutea lugar a la siguiente URI

httpejemplocomclientes333param=valor

Tambieacuten podemos definir un objeto de tipo Map que contenga los valores de las plantillas

MapltString Objectgt map = new HashMapltString Objectgt()

Servicios Rest

114

mapput(hostname ejemplocom)mapput(id 333)mapput(param valor)UriBuilder clone = builderclone()URI uri = clonebuildFromMap(map)

Otro ejemplo interesante es el de crear una URI a partir de las expresiones Path definidasen las clases JAX-RS anotadas A continuacioacuten mostramos el coacutedigo

Path(clientes)public class ServicioClientes

Path(id) public Cliente getCliente(PathParam(id) int id)

Podemos referenciar esta clase y el meacutetodo getCliente() a traveacutes de la claseUriBuilder de la siguiente forma

UriBuilder builder = UriBuilderfromResource(ServicioClientesclass)builderhost(hostname)builderpath(ServicioClientesclass getCliente)

El coacutedigo anterior define la siguiente plantilla para la URI

httphostnameclientesid

A partir de esta plantilla podremos construir la URI utilizando alguno de los meacutetodos`buildXXX()

Tambieacuten podemos querer utilizar UriBuilder para crear URIS a partir de plantillas Para ellodisponemos de meacutetodos resolveTemplateXXX() que nos facilitan el trabajo

Clase UriBuilder meacutetodos resolveTemplateXXX() para crear URIs a partir de plantillas

public abstract UriBuilder resolveTemplate(String name Object value)public abstract UriBuilder resolveTemplate(String name Object value boolean encodeSlashInPath)public abstract UriBuilder resolveTemplateFromEncoded(String nameObject value)public abstract UriBuilder resolveTemplates(MapltString Objectgt templateValues)public abstract UriBuilder resolveTemplates( MapltStringObjectgt templateValues boolean encodeSlashInPath) throws IllegalArgumentExceptionpublic abstract UriBuilder resolveTemplatesFromEncoded( MapltString Objectgt templateValues) Devuelve la URI de la plantilla como una cadena de caracterespublic abstract String toTemplate()

Servicios Rest

115

Funcionan de forma similar a los meacutetodos build() y se utilizan para resolver parcialmentelas plantillas contenidas en la URI Cada uno de los meacutetodos devuelve una nueva instanciade UriBuilder de forma que podemos encadenar varias llamadas para resolver todas lasplantillas de la URI Finalmente usaremos el meacutetodo toTemplate() para obtener la nuevaplantilla en forma de String

String original = httphostidString nuevaPlantilla = UriBuilderfromUri(original) resolveTemplate(host localhost) toTemplate()

El valor de nuevaPlantilla para el coacutedigo anterior seriacutea httplocalhostid

URIs relativas mediante el uso de UriInfo

Cuando estamos escribiendo servicios que distribuyen enlaces hay cierta informacioacuten queprobablemente no conozcamos cuando estamos escribiendo el coacutedigo Por ejemplo podemosno conocer todaviacutea los hostnames de los enlaces o incluso los base paths de las URIs en elcaso de que estemos enlazando con otros servicios REST

JAX-RS proporciona una forma sencilla de solucionar estos problemas utilizando la interfazjavaxwsrscoreUriInfo Ya hemos introducido algunas caracteriacutesticas de estainterfaz en sesiones anteriores Ademaacutes de poder consultar informacioacuten baacutesica de la rutatambieacuten podemos obtener instancias de UriBuilder preinicializadas con la URI base utilizadapara definir los servicios JAX-RS o la URI utilizada para invocar la peticioacuten HTTP actual

public interface UriInfo public URI getRequestUri() public UriBuilder getRequestUriBuilder() public URI getAbsolutePath() public UriBuilder getAbsolutePathBuilder() public URI getBaseUri() public UriBuilder getBaseUriBuilder()

Por ejemplo supongamos que tenemos un servicio que permite acceder a Clientes desdeuna base de datos En lugar de tener una URI base que devuelva todos los clientes en unuacutenico documento podemos incluir los enlaces previo y sigiente de forma que podamosnavegar por los datos Vamos a mostrar coacutemo crear estos enlaces utilizando la URI parainvocar la peticioacuten

Path(clientes)public class ServicioClientes GET Produces(applicationxml)

public String getCustomers(Context UriInfo uriInfo)

UriBuilder nextLinkBuilder = uriInfogetAbsolutePathBuilder() nextLinkBuilderqueryParam(inicio 5) nextLinkBuilderqueryParam(total 10) URI next = nextLinkBuilderbuild() rellenar el resto del documento

Servicios Rest

116

Para acceder a la instancia UriInfo que representa al peticioacuten usamos la anotacioacutenjavaxwsrscoreContext para inyectarla como un paraacutemetro del meacutetodo delrecurso RESTObtenemos un UriBuilder preininicializado con la URI utilizada para acceder alservicio

Para el coacutedigo anterior y dependiendo de coacutemo se despliegue el servicio la URI creada podriacuteaser

httporgexpertojavajaxrsclientesinicio=5amptotal=10

Construccioacuten de enlaces (Links) en documentos XML y en cabeceras HTTP

JAX-RS proporciona cierto soporte para construir los enlaces y devolverlos en las cabecerasde respuesta o bien incluirlos en los documentos XML Para ello podemos utilizar las clasesjavawsrscoreLink y javawsrscoreLinkBuilder

Clase abstracta javaxwsrscoreLink

public abstract class Link public abstract URI getUri() public abstract UriBuilder getUriBuilder() public abstract String getRel() public abstract ListltStringgt getRels() public abstract String getTitle() public abstract String getType() public abstract MapltString Stringgt getParams() public abstract String toString()

Link es una clase abstracta que representa todos los metadatos contenidos en una cabecerao en un enlace Atom El meacutetodo getUri() representa el atributo href del enlace Atom Elmeacutetodo getRel() representa el atributo rel y asiacute sucesivamente Podemos referenciar atodos los atributos a traveacutes del meacutetodo getParams() Finalmente el meacutetodo toString()convertiraacute la instancia Link en una cadena de caracteres con el formato de una cabeceraLink

Para crear instancias de Link utilizaremos un LinkBuilder que crearemos con algunode estos meacutetodos

public abstract class Link public static Builder fromUri(URI uri) public static Builder fromUri(String uri) public static Builder fromUriBuilder(UriBuilder uriBuilder) public static Builder fromLink(Link link) public static Builder fromPath(String path) public static Builder fromResource(Classltgt resource) public static Builder fromMethod(Classltgt resource String method)

Servicios Rest

117

Los meacutetodos fromXXX() funcionan de forma similar a UriBuilderfromXXX() Todosinicializan el UriBuilder subyacente que utilizaremos para construir el atributo href delenlace

Los meacutetodos link() uri() y uriBuilder() nos permiten sobreescribir la URIsubyacente del enlace que estamos creando

public abstract class Link interface Builder public Builder link(Link link) public Builder link(String link) public Builder uri(URI uri) public Builder uri(String uri) public Builder uriBuilder(UriBuilder uriBuilder)

Los siguientes meacutetodos nos permiten asignar valores a varios atributos del enlace que estamosconstruyendo

public Builder rel(String rel) public Builder title(String title) public Builder type(String type) public Builder param(String name String value)

Finalmente eacutel meacutetodo build() nos permitiraacute construir el enlace

public Link build(Object values)

El objeto LinkBuilder tiene asociado una UriBuilder subyacente Los valorespasados como paraacutemetros del meacutetodo build() son utilizados por el UriBuilder paracrear una URI para el enlace Veamos un ejemplo

Link link = LinkfromUri(httphostraizclientesid) rel(update)type(textplain) build(localhost 1234)

Si realizamos una llamada a toString() sobre la instancia del enlace ( link )obtendremos lo siguiente

httplocalhostraizclientes1234gt rel=update type=textplain

A continuacioacuten mostramos dos ejemplos que muestran coacutemo crear instancias Link en lascabeceras y en el cuerpo de la respuesta como un enlace Atom

Escritura de enlaces en cabeceras HTTP

Servicios Rest

118

PathGETResponse get() Link link = LinkfromUri(abc)build() Response response = ResponsenoContent() links(link) build() return response

Inclusioacuten de un enlace Atom en el documento XMl de respuesta

import javaxwsrscoreLink

XmlRootElementpublic class Cliente private String nombre private ListltLinkgt enlaces = new ArrayListltLinkgt()

XmlElement public String getNombre() return nombre

public void setNombre(String nom) thisnombre = nom

XmlElement(name = enlace)

XmlJavaTypeAdapter(LinkJaxbAdapterclass) public ListltLinkgt getEnlaces() return enlaces

La clase Link contiene tambieacuten un JaxbAdapter con una implementacioacuten de la claseJAXB XmlAdapter que mapea los objetos JAX-RS de tipo Link a un valor quepuede ser serializado y deserializado por JAXB

El coacutedigo de este ejemplo permite construir cualquier enlace y antildeadirlo a la clase Clientede nuestro dominio Los enlaces seraacuten convertidos a elementos XML que se incluiraacuten en eldocumento XML de respuesta

44 Seguridad

Es importante que los servicios rest permitan un acceso seguro a los datos y funcionalidadesque proporcionan Especialmente para servicios que permiten la realizacioacuten de actualizacionesen los datos Tambieacuten es interesante asegurarnos de que terceros no lean nuestros mensajese incluso permitir que ciertos usuarios accedan a determinadas funcionalidades pero a otrasno

Ademaacutes de la especificacioacuten JAX-RS podemos aprovechar los servicios de seguridad quenos ofrece la web y Java EE y utilizarla en nuestros servicios REST Estos incluyen

AutentificacioacutenHace referencia a la validacioacuten de la identidad del cliente que accede a los serviciosNormalmente implica la comprobacioacuten de si el cliente ha proporcionado unos credenciales

Servicios Rest

119

vaacutelidos tales como el password En este sentido podemos utilizar los mecanismos quenos proporciona la web y las facilidades del contenedor de servlets de Java EE paraconfigurar los protocolos de autentificacioacuten

AutorizacioacutenUna vea que el cliente se ha autenticado (ha validado su identidad) querraacute interactuarcon nuestro servicio REST La autorizacioacuten hace referencia a decidir si un cierto usuariopuede acceder e invocar un determinado meacutetodo sobre una determinada URI Por ejemplopodemos habilitar el acceso a operaciones PUTPOSTDELETE para ciertos usuarios peropara otros no En este caso utilizaremos las facilidades que nos propociona el contenedorde servlets de Java EE para realizar autorizaciones

EncriptadoCuando un cliente estaacute interaccionando con un servicio REST es posible que alguienintercepte los mensajes y los lea si la conexioacuten HTTP no es segura Los datos sensiblesdeberiacutean protegerse con servicios criptograacuteficos tales como SSL

Autentificacioacuten en JAX-RS

Hay varios protocolos de autentificacioacuten En este caso vamos a ver coacutemo realizar unaautenticacioacuten baacutesica sobre HTTP (y que ya habeacuteis utilizado para servlets) Este tipo deautentificacioacuten requiere enviar un nombre de usuario y password codificados como Base-64en una cabecera de la peticioacuten al servidor El servidor comprueba si existe dicho usuario en elsistema y verifica el password enviado Veaacutemoslo con un ejemplo

Supongamos que un cliente no autorizado quiere acceder a nuestros servicios REST

GET clientes333 HTTP11

Ya que la peticioacuten no contiene informacioacuten de autentificacioacuten el servidor deberiacutea responderla siguiente respuesta

HTTP11 401 UnauthorizedWWW-Autenticate Basic realm=Cliente Realm

La respuesta 401 nos indica que el cliente no estaacute autorizado a acceder a dicha URI Lacabecera WWW-Autenticate especifica queacute protocolo de autentificacioacuten se deberiacutea usarEn este caso Basic significa que se deberiacutea utilizar una autentificacioacuten de tipo Basic Elatributo realm identifica una coleccioacuten de recursos seguros en un sitio web En este ejemploindica que solamente estaacuten autorizados a acceder al meacutetodo GET a traveacutes de la URI anteriortodos aquellos uarios que pertenezcan al realm Cliente Realm y seraacuten autentificados porel servidor mediante una autentificacioacuten baacutesica

Para poder realizar la autentificacioacuten el cliente debe enviar una peticioacuten que incluya lacabecera Authorization cuyo valor sea Basic seguido de la siguiente cadena decaracteres loginpassword codificada en Base64 (el valor de login y password representael login y password del usuario) Por ejemplo supongamos que el nombre del usuario esfelipe y el password es locking la cadena felipelocking codificada como Base64es ZmVsaXBlOmxvY2tpbmc= Por lo tanto nuestra peticioacuten deberiacutea ser la siguiente

GET clientes333 HTTP11

Servicios Rest

120

Authorization Basic ZmVsaXBlOmxvY2tpbmc=

El cliente deberiacutea enviar esta cabecera con todas y cada una de las peticiones que haga alservidor

El inconveniente de esta aproximacioacuten es que si la peticioacuten es interceptada por alguna entidadhostil en la red el hacker puede obtner faacutecilmente el usuario y el passwork y utilizarlos parahacer sus propias peticiones Utilizando una conexioacuten HTTP encriptada (HTTPS) se solucionaeste problema

Creacioacuten de usuarios y roles

Para poder utilizar la autentificacioacuten baacutesica necesitamos tener creados previamente los realmsen el servidor de aplicaciones Wildfly y registrar los usuarios que pertenecen a dichos realmsLa forma de hacerlo es ideacutentica a lo que ya habeacuteis visto en la asignatura de ComponentesWeb (a traveacutes del comando add-usersh )

Utilizaremos el realm por defecto ApplicationRealm de Wildfly que nos permitiraacute ademaacutescontrolar la autorizacioacuten mediante la asignacioacuten de roles a usuarios

Lo uacutenico que tendremos que hacer es antildeadir los usuarios a dicho realm a traveacutes de laherramienta $WILDFLY_HOMEbinadd-usersh

Al ejecutarla desde liacutenea de comandos deberemos elegir el ream ApplicationRealm eintroducir los datos para cada nuevo usuario que queramos antildeadir indicando su loginpassword y el grupo (rol) al que queremos que pertenezca dicho usuario

Los datos sobre los nuevos usuarios creados se almacenan en los ficheros application-usersproperties y application-rolesproperties tanto en el directorio$WILDFLY_HOMEstandaloneconfiguration como en $WILDFLY_HOMEdomainconfiguration

Una vez creados los usuarios tendremos que incluir en el fichero de configuracioacuten webxml la siguiente informacioacuten

ltweb-appgt

ltlogin-configgt ltauth-methodgtBASICltauth-methodgt

ltrealm-namegtApplicationRealmltrealm-namegt ltlogin-configgt

ltsecurity-constraintgt ltweb-resource-collectiongt ltweb-resource-namegtcustomer creationltweb-resource-namegt

lturl-patterngtrestresourceslturl-patterngt

lthttp-methodgtPOSTlthttp-methodgt ltweb-resource-collectiongt ltsecurity-constraintgt ltweb-appgt

Servicios Rest

121

El elemento ltlogin-configgt define coacutemo queremos autentificar nuestro despliegueEl subelemento ltauth-methodgt puede tomar los valores BASIC DIGEST orCLIENT_CERT correspondieacutendose con la autentificacioacuten Basic Digest y ClientCertificate respectivamenteEl valor de la etiqueta ltrealm-namegt es el que se mostraraacute como valor del atributorealm de la cabecera WWW-Autenticate si intentamos acceder al recurso sin incluirnuestras credenciales en la peticioacutenEl elemento ltlogin-configgt realmente NO activa la autentificacioacuten Por defectocualquier cliente puede acceder a cualquier URL proporcionada por nuestra aplicacioacutenweb sin restricciones Para forzar la autentificacioacuten debemos especificar el patroacuten URLque queremos asegurar (elemento lturl-patterngt )El elemento lthttp-methodgt nos indica que solamente queremos asegurar laspeticiones POST sobre esta URL Si no incluimos el elemento lthttp-methodgt todoslos meacutetodos HTTP seraacuten seguros En este ejemplo solamente queremos asegurar losmeacutetodos POST dirigidos a la URL restresources

Autorizacioacuten en JAX-RS

Mientras que la autentificacioacuten hacer referencia a establecer y verificar la identidad del usuariola autorizacioacuten tiene que ver con los permisos iquestEl usuario X estaacute autorizado para acceder aun determinado recurso REST

JAX-RS se basa en las especificaciones Java EE y de servlets para definir la forma de autorizara los usuarios En Java EE la autorizacioacuten se realiza asociando uno o maacutes roles con un usuariodado y a continuacioacuten asignando permisos basados en dicho rol Ejemplos de roles puedenser administrador empleado Cada rol tiene asignando unos permisos de acceso adeterminados recursos por lo que asignaremos los permisos utilizando cada uno de los roles

Para poder realizar la autorizacioacuten tendremos que incluir determinadas etiquetas en el ficherode configuracioacuten webxml (tal y como ya habeacuteis visto en la asignatura de ComponentesWeb) Veaacutemoslo con un ejemplo (en el que tambieacuten incluiremos autentificacioacuten)

Volvamos a nuestra aplicacioacuten de venta de productos por internet En esta aplicacioacutenes posible crear nuevos clientes enviando la informacioacuten en formato XML a un recursoJAX-RS localizado por la anotacioacuten Path(clientes) El servicio REST esdesplegado y escaneado por la clase Application anotada con ApplicationPath(servicios) de forma que la URI completa es serviciosclientes Queremosproporcionar seguridad a nuestro servicio de clientes de forma que solamente losadministradores puedan crear nuevos clientes Veamos cuaacutel seriacutea el contenido del ficherowebxml

ltxml version=10gtltweb-appgt ltsecurity-constraintgt ltweb-resource-collectiongt ltweb-resource-namegtcreacion de clientesltweb-resource-namegt lturl-patterngtserviciosclienteslturl-patterngt lthttp-methodgtPOSTlthttp-methodgt ltweb-resource-collectiongt

ltauth-constraintgt ltrole-namegtadminltrole-namegt ltauth-constraintgt ltsecurity-constraintgt

Servicios Rest

122

ltlogin-configgt ltauth-methodgtBASICltauth-methodgt ltrealm-namegtApplicationRealmltrealm-namegt ltlogin-configgt

ltsecurity-rolegt ltrole-namegtadminltrole-namegt ltsecurity-rolegtltweb-appgt

Especificamos queacute roles tienen permiso para acceder mediante POST a la URLservicescustomers Para ello utilizamos el elemento ltauth-constraintgtdentro de ltsecurity-constraintgt Este elemento tiene uno o maacutes subelementosltrole-namegt que definen queacute roles tienen permisos de acceso definidos porltsecurity-constraintgt En nuestro ejemplo estamos dando al rol adminpermisos para acceder a la URL servicescustomers con el meacutetodo POST Si ensu lugar indicamos un ltrole-namegt con el valor cualquier usuario podriacutea accedera dicha URL En otras palabras un ltrole-namegt con el valor significa que cualquierusuario que sea capaz de autentificarse puede acceder al recursoPara cada ltrole-namegt que usemos en nuestras declaraciones ltauth-constraintsgt debemos definir el correspondiente ltsecurity-rolegt en eldescriptor de despliegue

Una limitacioacuten cuando estamos declarando las ltsecurity-contraintsgt para los recursosJAX-RS es que el elemento lturl-patterngt solamente soporta el uso de en el patroacutenurl especificado Por ejemplo rest txt

Encriptacioacuten

Por defecto la especificacioacuten de servlets no requiere un acceso a traveacutes de HTTPSSi queremos forzar un acceso HTTPS podemos especificar un elemento ltuser-data-constraintgt como parte de nuestra definicioacuten de restricciones de seguridad ( ltsecurity-constraintgt ) Vamos a modificar nuestro ejemplo anterior para forzar un acceso a traveacutesde HTTPS

ltweb-appgt ltsecurity-constraintgt

ltweb-resource-collectiongt ltweb-resource-namegtcreacion de clientesltweb-resource-namegt lturl-patterngtserviciosclienteslturl-patterngt lthttp-methodgtPOSTlthttp-methodgt ltweb-resource-collectiongt

ltauth-constraintgt ltrole-namegtadminltrole-namegt ltauth-constraintgt

ltuser-data-constraintgt

lttransport-guaranteegtCONFIDENTIALlttransport-guaranteegt ltuser-data-constraintgt ltsecurity-constraintgt

Servicios Rest

123

ltweb-appgt

Todo lo que tenemos que hacer es declarar un elemento lttransport-guaranteegtdentro de ltuser-data-constraintgt con el valor CONFIDENTIAL Si un usuariointenta acceder a una URL con el patroacuten especificado a traveacutes de HTTP seraacute redirigidoa una URL basada en HTTPS

Anotaciones JAX-RS para autorizacioacuten

Java EE define un conjunto de anotaciones para definir metadatos de autorizacioacuten Laespecificacioacuten JAX-RS sugiere aunque no es obligatorio que las implementaciones pordiferentes vendedores den soporte a dichas anotaciones Eacutestas se encuentran en elpaquete javaxannotationsecurity y son RolesAllowed DenyAll PermitAll yRunAs

La anotacioacuten RolesAllowed define los roles permitidos para ejecutar una determinadaoperacioacuten Si anotamos una clase JAX-RS define el acceso para todas las operacionesHTTP definidas en la clase JAX-RS Si anotamos un meacutetodo JAX-RS la restriccioacuten se aplicasolamente al meacutetodo que se estaacute anotando

La anotacioacuten PermitAll especifica que cualquier usuario autentificado puede invocar anuestras operaciones Al igual que RolesAllowed esta anotacioacuten puede usarse en la clasepara definir el comportamiento por defecto de toda la clase o podemos usarla en cada unode los meacutetodos Veamos un ejemplo

Path(clientes)

RolesAllowed(ADMIN CLIENTE) public class ClienteResource

GET Path(id) Produces(applicationxml) public Cliente getClienter(PathParam(id) int id)

RolesAllowed(ADMIN) POST Consumes(applicationxml) public void crearCliente(Customer cust)

PermitAll GET Produces(applicationxml) public Customer[] getClientes()

Por defecto solamente los usuarios con rol ADMIN y CLIENTE pueden ejecutar losmeacutetodos HTTP definidos en la clase ClienteResourceSobreescribimos el comportamiento por defecto Para el meacutetodo crearCliente()solamente permitimos peticiones de usuarios con rol ADMINSobreescribimos el comportamiento por defecto Para el meacutetodo getClientes() deforma que cualquier usuario autentificado puede acceder a esta operacioacuten a traveacutes de laURI correspondiente con el meacutetodo GET

Servicios Rest

124

La ventaja de utilizar anotaciones es que nos permite una mayor flexibilidad que el usodel fichero de configuracioacuten webxml pudiendo definir diferentes autorizaciones a nivel demeacutetodo

Seguridad programada

Hemos visto como utilizar una seguridad declarativa es decir basaacutendonos en meta-datosdefinidos estaacuteticamente antes de que la aplicacioacuten se ejecute JAX-RS proporciona unaforma de obtener informacioacuten de seguridad que nos permite implementar seguridad de formaprogramada en nuestras aplicaciones

Podemos utilizar la interfaz javaxwsrscoreSecurityContext para determinar laidentidad del usuario que realiza la invocacioacuten al meacutetodo proporcionando sus credencialesTambieacuten podemos comprobar si el usuario pertenece o no a un determinado rol Esto nospermite implementar seguridad de forma programada en nuestras aplicaciones

public interface SecurityContext public Principal getUserPrincipal() public boolean isUserInRole(String role) public boolean isSecure() public String getAuthenticationScheme()

El meacutetodo getUserPrincipal() devuelve un objeto de tipojavaxsecurityPrincipal que representa al usuario que actualmente estaacute realizandola peticioacuten HTTP

El meacutetodo isUserInRole() nos permite determinar si el usuario que realiza la llamadaactual pertenece a un determinado rol

El meacutetodo isSecure() devuelve cierto si la peticioacuten actual es una conexioacuten segura

El meacutetodo getAuthenticationScheme() nos indica queacute mecanismo de autentificacioacuten seha utilizado para asegurar la peticioacuten (valores tiacutepicos devueltos por el meacutetodo son BASIC DIGEST CLIENT_CERT y FORM )

Podemos acceder a una instancia de SecurityContext inyectaacutendola en un campo meacutetodosetter o un paraacutemetro de un recurso utilizando la anotacioacuten Context Veamos un ejemploSupongamos que queremos obtener un fichero de log con todos los accesos a nuestra basede datos de clientes hechas por usuarios que no son administradores

Path(clientes)public class CustomerService GET Produces(applicationxml) public Cliente[] getClientes(Context SecurityContext sec) if (secisSecure() ampamp secisUserInRole(ADMIN)) loggerlog(secgetUserPrincipal() + ha accedido a la base de datos de clientes)

Servicios Rest

125

En este ejemplo inyectamos una instancia de SecurityContext como un paraacutemetro delmeacutetodo getClientes() Utilizamos el meacutetodo SecurityContextisSecure() paradeterminar si se trata de una peticioacuten realizada a traveacutes de un canal seguro (como HTTPS) Acontinuacioacuten utilizamos el meacutetodo SecurityContextisUserInRole() para determinarsi el usuario que realiza la llamada tiene el rol ADMIN o no Finalmente imprimimos el resultadoen nuestro fichero de logs

Con la introduccioacuten del API de filtros en JAX-RS 20 podemos implementar la interfazSecurityContext y sobreescribir la peticioacuten actual sobre SecurityContext utilizandoel meacutetodo ContainerRequestContextsetSecurityContext() Lo interesante deesto es que podemos implementar nuestros propios protocolos de seguridad Por ejemplo

import javaxwsrscontainerContainerRequestContextimport javaxwsrscontainerContainerRequestFilterimport javaxwsrscontainerPreMatchingimport javaxwsrscoreSecurityContextimport javaxwsrscoreHttpHeaders

PreMatchingpublic class CustomAuth implements ContainerRequestFilter protected MyCustomerProtocolHandler customProtocol =

public void filter(ContainerRequestContext requestContext) throws IOException String authHeader = requestgetHeaderString(HttpHeadersAUTHORIZATION) SecurityContext newSecurityContext = customProtocolvalidate(authHeader) requestContextsetSecurityContext(authHeader)

Este filtro no muestra todos los detalles pero siacute la idea Extrae la cabecera Authorizationde la peticioacuten y la pasa a nuestro propio servicio customerProtocol Eacuteste devuelve unaimplementacioacuten de SecurityContext Finalmente sobreescribimos el SecurityContext pordefecto utilizando la nueva implementacioacuten

No vamos a explicar el API de filtros de JAS-RS 20 Como ya habeacuteis visto en la asignaturade Componentes Web los filtros son objetos que se interponen entre el procesamiento delas peticiones tanto del servidor como del cliente

El filtro mostrado en el ejemplo es un filtro de peticioacuten en la parte del servidor Este tipo defiltros se ejecuta antes de que se invoque a un meacutetodo JAX-RS

Servicios Rest

126

45 Ejercicios

Para los ejercicios de esta sesioacuten proporcionamos el MOacuteDULO s4-foroAvanzado quetendreacuteis que usar como plantilla para realizar las tareas planteadas

El proyecto estaacute estructurado loacutegicamente en los siguientes paquetes

bull orgexpertojavanegocio

bull orgexpertojavarest

A su vez cada uno de ellos contiene los subpaquetes api y modelo con las clasesrelacionadas con los servicios proporcionados y los datos utilizados por los serviciosrespectivamente

El API rest implementado es el siguiente

bull Recurso UsuariosResourcejava

GET usuarios proporciona un listado con los usuarios del foro

bull Subrecurso UsuarioResourcejava

GET usuarioslogin proporciona informacioacuten sobre el usuario cuyo login es login

PUT usuarioslogin actualiza los datos de un usuario

DELETE usuarioslogin borra los datos de un usuario

GET usuariosloginmensajes obtiene un listado de los mensajes de un usuario

bull Recurso MensajesResourcejava

GET mensajes proporciona un listado con los mensajes del foro

POST mensajes antildeade un mensaje nuevo en el foro

GET mensajesid proporciona informacioacuten sobre el mensaje cuyo id es id

PUT mensajesid modifica un mensaje

DELETE mensajesid borra un mensaje

Una vez desplegada la aplicacioacuten podeacuteis antildeadir datos a la base de datos del foro utilizandolos datos del fichero srcmainresourcesforosql Para ello simplemente tendreacuteis que invocarla goal Maven correspondiente desde la ventana Maven Projects gt s4-foroAvanzado gt Pluginsgt sql gt sqlexecute

En el directorio srcmainresources teneacuteis un fichero de texto ( instruccionestxt )con informacioacuten adicional sobre la implementacioacuten proporcionada

A partir de las plantillas se pide

Uso de Hateoas (1 puntos)

Vamos a antildeadir a los servicios enlaces a las operaciones que podemos realizar con cadarecurso siguiendo el estilo Hateoas

a Para los usuarios

Servicios Rest

127

bull En el listado de usuarios antildeadir a cada usuario un enlace con relacioacuten self que apuntea la direccioacuten a la que estaacute mapeado el usuario individual

bull En la operacioacuten de obtencioacuten de un usuario individual incluir los enlaces para ver elpropio usuario (self) modificarlo (usuariomodificar) borrarlo (usuarioborrar) o ver losmensajes que envioacute el usuario (usuariomensajes)

b Para los mensajes

bull En el listado de mensajes antildeadir a cada mensaje un enlace con relacioacuten self queapunte a la direccioacuten a la que estaacute mapeado el mensaje individual

bull En la operacioacuten de obtencioacuten de un mensaje individual incluir los enlaces para ver elpropio mensaje (self) modificarlo (mensajemodificar) borrarlo (mensajeborrar) o verlos datos del usuario que envioacute el mensaje (mensajeusuario)

Utiliza postman para comprobar las modificaciones realizadas

Ejercicio seguridad (1 punto)

Vamos ahora a restringir el acceso al servicio para que soacutelo usuarios registrados puedanrealizar modificaciones Se pide

a Antildeadir al usuario pepe en el ApplicationRealm de wildfly con la contrasentildea pepe yperteneciente al grupo (rol) registrado

b Configurar mediante seguridad declarativa para que las operaciones de modificacioacuten(POST PUT y DELETE) soacutelo la puedan realizar los usuarios con rol registrado Utilizarautentificacioacuten de tipo BASIC

c Ahora vamos a hacer que la modificacioacuten o borrado de usuarios soacutelo puedarealizarlas el mismo usuario que va a modificarse o borrarse Para ello utilizaremosseguridad programada En el caso de que el usuario que va a realizar lamodificacioacuten o borrado quiera borrarmodificar otros usuarios lanzaremos la excepcioacutenWebApplicationException(StatusFORBIDDEN)

d Vamos a hacer lo mismo con los mensajes Soacutelo podraacute modificar y borrar mensajes elmismo usuario que los creoacute y al publicar un nuevo mensaje forzaremos que el login delmensaje sea el del usuario que hay autentificado en el sistema

Utiliza postman para comprobar las modificaciones realizadas

Servicios Rest

128

5 Api cliente Procesamiento JSON y Pruebas

Hasta ahora hemos hablado sobre la creacioacuten de servicios web RESTful y hemos probadonuestros servicios utilizando el cliente que nos proporciona IntelliJ curl o Postman pararealizar peticiones y observar las respuestas JAX-RS 20 proporciona un API cliente parafacilitar la programacioacuten de clientes REST que presentaremos en esta sesioacuten

En sesiones anteriores hemos trabajado con representaciones de texto y xmlfundamentalmente Aquiacute hablaremos con maacutes detalle de JSON que constituye otra forma derepresentar los datos de las peticiones y respuestas de servicios REST muy extendida

Finalmente veremos coacutemo implementar pruebas sobre nuestro servicio utilizando el APIcliente y el framework junit

51 API cliente Visioacuten general

La especificacioacuten JAX-RS 20 incorpora un API cliente HTTP que facilita enormemente laimplementacioacuten de nuestros clientes RESTful y constituye una clara alternativa al uso declases Java como javanetURL libreriacuteas externas (como la de Apache) u otras solucionespropietarias

El API cliente estaacute contenido en el paquete javaxwsrsclient y estaacute disentildeado paraque se pueda utilizar de forma fluida (fluent) Esto significa como ya hemos visto que loutilizaremos encadenando una sucesioacuten de llamadas a meacutetodos del API permitieacutendonos asiacuteescribir menos liacuteneas de coacutedigo Baacutesicamente estaacute formado por tres clases principales ClientWebTarget y Response (ya hemos hablado de esta uacuteltima en sesiones anteriores)

Para acceder a un recurso REST mediante el API cliente es necesario seguir los siguientespasos

1 Obtener una instancia de la interfaz Client

2 Configurar la instancia Client a traveacutes de un target (instancia de WebTarget )

3 Crear una peticioacuten basada en el target anterior

4 Invocar la peticioacuten

Vamos a mostrar un coacutedigo ejemplo para ilustrar los pasos anteriores En este caso vamos ainvocar peticiones POST y GET sobre una URL (target) para crear y posteriormente consultarun objeto Cliente que representaremos en formato XML

Client client = ClientBuildernewClient()

WebTarget target =

clienttarget(httpexpertojavaorgclientes)

Response response = target

request()

post(Entityxml(new Cliente(Alvaro Gomez)))

responseclose()

Servicios Rest

129

Cliente cliente = targetqueryParam(nombre Alvaro Gomez) request()

get(Clienteclass) clientclose()

Obtenemos una instancia javaxwsrsclientClientCreamos un WebTargetCreamos la peticioacutenRealizamos una invocacioacuten POSTCerramos (liberamos) el input stream para esta respuesta (en el caso de que esteacutedisponible y abierto) Es una operacioacuten idempotente es decir podemos invocarlamuacuteltiples veces con el mismo efectoA partir de un WebTarget establecemos los valores de los queryParams de la URIde la peticioacuten creamos la peticioacuten y realizamos una invocacioacuten GET

A continuacioacuten explicaremos con detalle los pasos a seguir para implementar un clienteutilizando el API de JAX-RS

Obtenemos una instancia Client

La interfaz javaxwsrsclientClient es el principal punto de entrada del API ClienteDicha interfaz define las acciones e infraestructura necesarias requeridas por un cliente RESTpara consumir un servicio web RESTful Los objetos Client se crean a partir de la claseClientBuilder

Clase ClientBuilder utilizada para crear objetos Client

public abstract class ClientBuilder implements ConfigurableltClientBuildergt public static Client newClient() public static Client newClient(final Configuration configuration)

public static ClientBuilder newBuilder()

public abstract ClientBuilder sslContext(final SSLContext sslContext) public abstract ClientBuilder keyStore(final KeyStore keyStore final char[] password) public ClientBuilder keyStore(final KeyStore keyStore final String password) public abstract ClientBuilder trustStore(final KeyStore trustStore) public abstract ClientBuilder hostnameVerifier(final HostnameVerifier verifier)

public abstract Client build()

La forma maacutes sencilla de crear un objeto Client es medianteClientBuildernewClient() Este meacutetodo proporciona una instancia pre-inicializada detipo Client lista para ser usada La clase ClientBuilder nos proporciona meacutetodos adicionalescon los que podremos configurar diferentes propiedades del objeto

Veamos un ejemplo de uso de ClientBuildernewBuilder() utilizando ademaacutes algunode los meacutetodos proporcionados para configurar nuestra instancia de tipo Client que vamos

Servicios Rest

130

a crear Los meacutetodos register() y property() son meacutetodos de la interfaz Configurable(y que son implementados por ClientBuilder)

Ejemplo de uso de ClientBuilder

Client cliente = ClientBuildernewBuilder()

property(connectiontimeout 100)

sslContext(sslContext)

register(JacksonJsonProviderclass)

build()

Creamos un ClientBuilder invocando al meacutetodo estaacuteticoClientBuildernewBuilder()Asignamos una propiedad especiacutefica de la implementacioacuten concreta de JAX-RS queestemos utilizando que controla el timeout de las conexiones de los socketsEspecificamos el sslContext que queremos utilizar para gestionar las conexiones HTTPRegistramos a traveacutes del meacutetodo register() (de la interfaz Configurable) una claseanotada con Provider Dicha clase conoce coacutemo serializar objetos Java a JSONy viceversaFinalmente realizamos una llamada a build() para crear la instancia Client

Las instancias de Client gestionan conexiones con el cliente utilizando sockets y sonobjetos bastante pesados Se deberiacutean reutilizar las instancias de esta interfaz en la medida delo posible ya que la inicializacioacuten y destruccioacuten de dichas instancias consume mucho tiempoPor lo tanto por razones de rendimiento debemos limitar el nuacutemero de instancias Clienten nuestra aplicacioacuten

Client client = ClientBuildernewClient()

clientclose()

Obtenemos una instancia de tipo Client invocando al meacutetodoClientBuildernewClient()Utilizamos el meacutetodo close() para cerrar la instancia Client despueacutes de realizartodas las invocaciones sobre el target del recurso De esta forma cerramos la conexioacutende forma que se liberan sus recursos y ya no podremos seguir usaacutendola

Recuerda siempre invocar el meacutetodo close() sobre nuestros objetosClient despueacutes de que hayamos realizado todas las invocacionessobre el target dellos recursos REST A menudo los objetos Clientreutilizan conexiones por razones de rendimiento Si no los cerramosdespueacutes de utilizarlos estaremos desaprovechando recursos del sistemamuy valiosos Cerrar la conexioacuten implica cerrar el socket

Igualmente si el resultado de una invocacioacuten a un servicio rest es unainstancia de Response debemos invocar el meacutetodo close() sobredichos objetos Response para liberar la conexioacuten Liberar una conexionsignifica permitir que eacutesta esteacute disponible para otro uso por una instanciaClient Liberar la conexioacuten no implica cerrar el socket

La interfaz Client es una sub-interfaz de Configurable Esto nos permitiraacute utililizarlos meacutetodos property() y register() para cambiar la configuracioacuten y registrarcomponentes en la parte del cliente en tiempo de ejecucioacuten

Servicios Rest

131

Interfaz Client (es una subinterfaz de Configurable)

public interface Client extends ConfigurableltClientgt

public void close()

public WebTarget target(String uri) public WebTarget target(URI uri) public WebTarget target(UriBuilder uriBuilder) public WebTarget target(Link link)

Sin embargo el principal propoacutesito de Client es crear instancias de WebTarget comoveremos a continuacioacuten

Configuramos el target del cliente (URI)

La interfaz javaxwsrsclientWebTarget representa la URI especiacutefica quequeremos invocar para acceder a un recurso REST particular

Interfaz WebTarget (es una subinterfaz de Configurable)

public interface WebTarget extends ConfigurableltWebTargetgt

public URI getUri() public UriBuilder getUriBuilder()

public WebTarget path(String path) public WebTarget resolveTemplate(String name Object value) public WebTarget resolveTemplates(MapltString Objectgt templateValues) public WebTarget matrixParam(String name Object values) public WebTarget queryParam(String name Object values)

La interfaz WebTarget tiene meacutetodos para extender la URI inicial que hayamosconstruido Podemos antildeadir por ejemplo segmentos de path o paraacutemetros deconsulta invocando a los meacutetodos WebTargetpath() o WebTargetqueryParam() respectivamente Si la instancia de WebTarget contiene plantillas de paraacutemetros losmeacutetodos WebTargetresolveTemplate() pueden asignar valores a las variablescorrespondientes Por ejemplo

Ejemplo para crear la URI httpejemplocomclientes123verboso=true

WebTarget target = client

target(httpejemplocomclientesid)

resolveTemplate(id 123)

queryParam(verboso true)

Servicios Rest

132

Inicializamos un WebTarget con una URI que contiene una plantilla con un paraacutemetroid El objeto client es una instancia de la clase `ClientEl meacutetodo resolveTemplate() rellena la expresioacuten id con el valor 123Finalmente antildeadimos a la URI un paraacutemetro de consulta verboso=true

Las instancias de WebTarget son inmutables con respecto a la URI que contienen Estosignifica que los meacutetodos para especificar segmentos de path adicionales y paraacutemetrosdevuelven una nueva instancia de WebTarget Sin embargo las instancias de WebTargetson mutables respecto a su configuracioacuten Por lo tanto la configuracioacuten de objetosWebTarget no crea nuevas instancias

Veamos otro ejemplo

WebTarget base = clientetarget(httpexpertojavaorg)

WebTarget clienteURI = basepath(cliente)

clienteURIregister(MyProviderclass)

base es una instancia de WebTarget con el valor de URI httpexertojavaorgclienteURI es una instancia de WebTarget con el valor de URI httpexertojavaorgclienteConfiguramos clienteURI registrando la clase MyProvider

En este ejemplo creamos dos instancias de WebTarget La instancia clienteURI heredala configuracioacuten de base y posteriormente modificamos la configuramos registrando unaclase Provider Los cambios sobre la configuracioacuten de clienteURI no afectan a laconfiguracioacuten de base ni tampoco se crea una nueva instancia de WebTarget

Los beneficios del uso de WebTarget se hacen evidentes cuando construimos URIscomplejas por ejemplo cuando extendemos nuestra URI base con segmentos de pathadicionales o plantillas El siguiente ejemplo ilustra estas situaciones

WebTarget base = clientetarget(httpexpertojavaorg)

WebTarget saludo = basepath(hola)path(quien) Response res = saludoresolveTemplate(quien mundo)request()get()

base representa la URI httpexpertojavaorgsaludo representa la URI httpexpertojavaholaquien

En el siguiente ejemplo utilizamos una URI base y a partir de ella construimos otras URIs querepresentan servicios diferentes proporcionados por nuestro recurso REST

Client cli = ClientBuildernewClient()WebTarget base = clienttarget(httpejemplowebapi)

WebTarget lectura = basepath(leer)

WebTarget escritura = basepath(escribir)

lectura representa la uri httpejemplowebapileerescritura representa la uri httpejemplowebapiescribir

El meacutetodo WebTargetpath() crea una nueva instancia de WebTarget antildeadiendo a laURI actual el segmento de ruta que se pasa como paraacutemetro

Servicios Rest

133

Construimos y Realizamos la peticioacuten

Una vez que hemos creado y configurado convenientemente el WebTarget que representala URI que queremos invocar tenemos que construir la peticioacuten y finalmente realizarla

Para construir la peticioacuten podemos Utilizar uno de los meacutetodos WebTargetrequest() que mostramos a continuacioacuten

Interfaz WebTarget meacutetodos para comenzar a construir la peticioacuten

public interface WebTarget extends ConfigurableltWebTargetgt public InvocationBuilder request() public InvocationBuilder request(String acceptedResponseTypes) public InvocationBuilder request(MediaType acceptedResponseTypes)

Normalmente invocaremos WebTargetrequest() pasando como paraacutemetro el mediatype aceptado como respuesta en forma de String o utilizando una de las constantes dejavaxwsrscoreMediaType Los meacutetodos WebTargetrequest() devuelven unainstancia de InvocationBuilder una interfaz que proporciona meacutetodos para prepararla peticioacuten del cliente y tambieacuten para invocarla

La interface InvocationBuilder Contiene un conjunto de meacutetodos que nos permitenconstruir diferentes tipos de cabeceras de peticiones Asiacute por ejemplo proporciona variosmeacutetodos acceptXXX() para indicar diferentes tipos MIME lenguajes o encodingaceptados Tambieacuten proporciona meacutetodos cookie() para especificar cookies para enviaral servidor Finalmente proporciona meacutetodos header() para especificar diferentes valoresde cabeceras

Ejemplos de uso de esta interfaz para construir la peticioacuten

Client cli = ClientBuildernewClient()cliinvocation(LinkvalueOf(httpejemplorest))accept(applicationjson)get()si no utilizamos el meacutetodo invocation podemos hacerlo asiacuteclitarget(httpejemplorest)request(applicationjson)get()

Client cliente = ClientBuildernewClient()WebTarget miRecurso = clienttarget(httpejemplowebapimensaje) request(MediaTypeTEXT_PLAIN)

El uso de una constante MediaType es equivalente a utilizar el String que define el tipoMIME

InvocationBuilder builder = miRecursorequest(textplain)

Hemos visto que WebTarget implementa meacutetodos request() cuyosparaacutemetros especifican el tipo MIME de la cabecera Accept de la peticioacutenEl coacutedigo puede resultar maacutes legible si usamos en su lugar el meacutetodo

Servicios Rest

134

InvocationBuilderaccept() En cualquier caso es una cuestioacuten de gustospersonales

Despueacutes de determinar el media type de la respuesta invocamos la peticioacuten realizando unallamada a uno de los meacutetodos de la instancia de InvocationBuilder que se correspondecon el tipo de peticioacuten HTTP esperado por el recurso REST al que va dirigido dicha peticioacutenEstos meacutetodos son

bull get()

bull post()

bull delete()

bull put()

bull head()

bull options()

La interfaz InvocationBuilder es una subinterfaz de la interfaz SyncInvoker y es la queespecifica los meacutetodos anteriores (get post hellip) para realizar peticiones siacutencronas es decirque hasta que no nos conteste el servidor no podremos continuar procesando el coacutedigo enla parte del cliente

Las peticiones GET tienen los siguientes prototipos

Interface SyncInvoker peticiones GET siacutencronas

public interface SyncInvoker ltTgt T get(ClassltTgt responseType) ltTgt T get(GenericTypeltTgt responseType) Response get()

Los primeros dos meacutetodos geneacutericos convertiraacuten una respuesta HTTP con eacutexito a tipos Javaespeciacuteficos indicados como paraacutemetros del meacutetodo El tercero devuelve una instancia de tipoResponse Por ejemplo

Ejemplos de peticiones GET utilizando el API cliente jasx-rx 20

Client cli = ClientBuildernewClient()

peticioacuten get que devuelve una instancia de ClienteCliente cliRespuesta = clitarget(httpejemploclientes123) request(applicationjson)

get(Clienteclass)

peticioacuten get que devuelve una lista de objetos ClienteListltClientegt cliRespuesta2 = clitarget(httpejemploclientes) request(applicationxml)

get(new GenericTypeltListltClientegtgt() )

peticioacuten get que devuelve un objeto de tipo ResponseResponse respuesta = clitarget(httpejemploclientes245)

Servicios Rest

135

request(applicationjson)

get() try if (respuestagetStatus() == 200) Cliente cliRespuesta =

respuestareadEntity(Clienteclass) finally respuestaclose()

En la primera peticioacuten queremos que el servidor nos devuelva la respuesta en formatoJSON y posteriormente la convertiremos en el tipo Cliente utilizando un de loscomponentes MessageBodyReader registradosEn la segunda peticioacuten utilizamos la clase javaxwsrscoreGenericType parainformar al correspondiente MessageBodyReader del tipo de objetos de nuestra ListaPara ello creamos una clase anoacutenima a la que le pasamos como paraacutemetro el tipogeneacuterico que queremos obtenerEn la tercera peticioacuten obtenemos una instancia de Response a partir de la cual podemosobtener el cuerpo del mensaje de respuesta del servidorEl meacutetodo readEntity() asocia el tipo Java solicitado (en este caso el tipo java Cliente) yel contenido de la respuesta recibida con el correspondiente proveedor de entidades (detipo MessageBodyReader) para obtener dicho tipo Java a partir de la respuesta HTTPrecibida En sesiones anteriores hemos utilizado la clase Response desde el servicioREST para construir la respuesta que se enviacutea al cliente

Recordemos algunos de los meacutetodos que podemos utilizar desde el cliente para analizar larespuesta que hemos obtenido

Meacutetodos de la clase Response que utilizaremos desde el cliente

public abstract class Response

public abstract Object getEntity()

public abstract int getStatus()

public abstract ResponseStatusType getStatusInfo()

public abstract MultivaluedMapltString Objectgt getMetadata()

public abstract URI getLocation()

public abstract MediaType getMediaType()

public MultivaluedMapltStringObjectgt getHeaders()

public abstract ltTgt T readEntity(ClassltTgt entityType)

public abstract ltTgt T readEntity(GenericTypeltTgt entityType)

public abstract void close()

El meacutetodo getEntity() devuelve el objeto Java correspondiente al cuerpo delmensaje HTTPEl meacutetodo getStatus() devuelve el coacutedigo de respuesta HTTPEl meacutetodo getStatusInfo() devuelve la informacioacuten de estado asociada con larespuestaEl meacutetodo getMetadata() devuelve una instancia de tipo MultivaluedMap con lascabeceras de la respuesta

Servicios Rest

136

El meacutetodo getLocation() devuelve la URI de la cabecera Location de la respuestaEl meacutetodo getMediaType() devuelve el mediaType del cuerpo de la respuestaEl meacutetodo getHeaders() devuelve las cabeceras de respuesta con sus valorescorrespondientesEl meacutetodo readEntity() devuelve la entidad del cuerpo del mensaje utilizando unMessageBodyReader que soporte el mapeado del inputStream de la entidad a la claseJava especificada como paraacutemetroEl meacutetodo readEntity() tambieacuten puede devolver una clase geneacuterica si se disponedel MessageBodyReader correspondienteEl meacutetodo close() cierra el input stream correspondiente a la entidad asociada delcuerpo del mensaje (en el caso de que esteacute disponible y abierto) Tambieacuten liberacualquier otro recurso asociado con la respuesta (como por ejemplo datos posiblementealmacenados en un buffer)

Veamos otro ejemmplo Si el recurso REST espera una peticioacuten HTTP GET invocaremosel meacutetodo InvocationBuilderget() El tipo de retorno del meacutetodo deberiacuteacorresponderse con la entidad devuelta por el recurso REST que atenderaacute la peticioacuten

Client cliente = ClientBuildernewClient()WebTarget miRecurso = clientetarget(httpejemplowebapilectura)String respuesta = miRecursorequest(MediaTypeTEXT_PLAIN) get(Stringclass)

O tambieacuten podriacuteamos codificarlo como

Client cliente = ClientBuildernewClient()String respuesta = cliente target(httpejemplowebapilectura) request(MediaTypeTEXT_PLAIN) get(Stringclass)

Si el tipo de retorno de la peticioacuten GET es una coleccioacuten usaremosjavaxwsrscoreGenericTypeltTgt como paraacutemetro del meacutetodo en donde T es eltipo de la coleccioacuten

ListltPedidoAlmacengt pedidos = client target(httpejemplowebapilectura) path(pedidos) request(MediaTypeAPPLICATION_XML) get(new GenericTypeltListltPedidoAlmacengtgt() )

Si el recurso REST destinatario de la peticioacuten espera una peticioacuten de tipo HTTP POSTinvocaremos el meacutetodo InvocationBuilderpost()

Las peticiones POST tienen los siguientes prototipos

Interface SyncInvoker peticiones POST siacutencronas

public interface SyncInvoker ltTgt T post(Entityltgt entity ClassltTgt responseType)

Servicios Rest

137

ltTgt T post(Entityltgt entity GenericTypeltTgt responseType) Response post(Entityltgt entity)

Los primeros dos meacutetodos geneacutericos enviacutean una entidad (clase java + tipo MIME asociado)indicada como primer paraacutemetro del meacutetodo y como segundo paraacutemetro se indica el tipo javaal que se convertiraacute la respuesta recibida El tercero enviacutea una entidad y devuelve una instanciade tipo Response Por ejemplo

Veamos un ejemplo de invocacioacuten de peticiones POST

Ejemplo de peticioacuten POST utilizando el API cliente jasx-rx 20

Client cli = ClientBuildernewClient()Pedido pe = new PedidoAlmacen()Pedido peRespuesta = cli target() request() post(Entityentity(new Pedido() applicationjson) Pedidoclass)

En este caso estamos realizando una peticioacuten POST Como payload del mensaje enviamosun objeto Pedido representado en formato json La entidad esperada como respuesta debeser de tipo Pedido

Esto implica que en el lado del servidor el meacutetodo que atiende la peticioacuten Post tendraacute unparaacutemetro de tipo Pedido y se deberaacuten serializar los objetos de tipo Pedido a json ya que esel tipo MIME asociado a esta entidad ( especificado en la cabera Content-Type de la peticioacutenHTTP)

La clase Entity encapsula los objetos Java que queremos enviar con las peticiones GET oPOST No tiene un constructor puacuteblico En su lugar tenemos que invocar uno de sus meacutetodosestaacuteticos

Clase javaxwsrsclientEntity

public final class EntityltTgt

public static ltTgt EntityltTgt entity(T entity String mediaType)

public static ltTgt EntityltTgt entity(T entity MediaType mediaType)

public static ltTgt EntityltTgt xml(final T entity)

public static ltTgt EntityltTgt json(final T entity)

public static ltTgt EntityltTgt text(T entity)

public static EntityltFormgt form(final Form form)

El meacutetodo estaacutetico entity() crea una entidad (clase Java) con un tipo MIME asociado dadopor la cadena de caracteres mediaTypeEl meacutetodo estaacutetico entity() crea una entidad (clase Java) con un tipo MIME indicado enmediaTypeEl meacutetodo xml crea una entidad (clase Java) con el tipo MIME applicationxml

Servicios Rest

138

El meacutetodo json crea una entidad (clase Java) con el tipo MIME applicationjsomEl meacutetodo text crea una entidad (clase Java) con el tipo MIME textplainEl meacutetodo form crea una entidad (clase Java) con el tipo MIME applicationx-www-form-urlencoded

Veamos otro ejemplo de invocacioacuten POST que utiliza la clase Entity

Ejemplo de peticioacuten POST y uso de clase Entity

NumSeguimiento numSeg = client target(httpejemplowebapiescritura)

request(MediaTypeAPPLICATION_XML)

post(Entityxml(pedido) NumeroSeguimientoclass)

Especificamos como paraacutemetro de la peticioacuten request() el tipo MIME que aceptamos enla respuesta (cabecera HTTP Accept)Realizamos una peticioacuten POST El cuerpo del mensaje se crea con la llamadaEntityxml(pedido) El tipo Entity encapsula la entidad del mensaje (tipo JavaPedido) y el tipo MIME asociado (tipo MIME applicationxml)

Veamos un ejemplo en el que enviamos paraacutemetros de un formulario en una peticioacuten POST

Ejemplo de enviacuteo de datos de un formulario en una peticioacuten POST

Form form = new Form()param(nombre Pedro) param(apellido Garcia)Response response = clienttarget(httpejemploclientes) request() post(Entityform(form))responseclose()

La peticioacuten POST del coacutedigo anterior enviacutea los datos del formulario y espera recibir comorespuesta una entidad de tipo Response

El coacutedigo en el lado del servidor seraacute similar a eacuteste

Servicio rest que sirve una peticioacuten POST a partir de datos de un formulario

POSTPath(clientes)Produces(texthtml)public Response crearCliente(FormParam(nombre)String nom FormParam(apellido)String ape) creamos el nuevo cliente return Responseok(RESPONSE_OK)build()

Manejo de excepciones

Veamos queacute ocurre si se produce una excepcioacuten cuando utilizamos una forma de invocacioacutenque automaacuteticamente convierte la respuesta en el tipo especificado Supongamos el siguienteejemplo

Servicios Rest

139

Cliente cli = clienttarget(httptiendacomclientes123) request(applicationjson) get(Clienteclass)

En este escenario el framework del cliente convierte cualquier coacutedigo de error HTTP en unade las excepciones que antildeade JAX-RS 20 (BadRequesException ForbiddenExceptionhellip) yque ya hemos visto Podemos capturar dichas excepciones en nuestro coacutedigo para tratarlasadecuadamente

try Cliente cli = clienttarget(httptiendacomclientes123) request(applicationjson) get(Clienteclass) catch (NotAcceptableException notAcceptable) catch (NotFoundException notFound)

Si el servidor responde con un error HTTP no cubierto por alguna excepcioacutenespeciacutefica JAX-RS entonces se lanza una excepcioacuten de propoacutesito general La claseClientErrorException cubre cualquier coacutedigo de error en la franja del 400 La claseServerErrorException cubre cualquier coacutedigo de error en la franja del 500

Si el servidor enviacutea alguna de los coacutedigos de respuesta HTTP 3xx (clasificados como coacutedigosde la categoriacutea redireccioacuten) el API cliente lanza una RedirectionException a partir de lacual podemos obtener la URL para poder tratar la redireccioacuten nosotros mismos Por ejemplo

WebTarget target = clienttarget(httptiendacomclientes123)boolean redirected = false

Cliente cli = nulldo try cli = targetrequest(applicationjson) get(Clienteclass) catch (RedirectionException redirect) if (redirected) throw redirect redirected = true target = clienttarget(redirectgetLocation()) while (cli == null)

En este ejemplo volvemos a iterar si recibimos un coacutedigo de respuesta 3xx El coacutedigo seasegura de que soacutelo permitimos un coacutedigo de este tipo cambiando el valor de la variableredirect en el bloque en el que capturamos la exceptioacuten A continuacioacuten cambiamos elWebTarget (en el bloque catch ) al valor de la cabecera Location de la respuesta delservidor

Los coacutedigos de estado HTTP 3xx indican que es neceario realizar algunaaccioacuten adicional para que el servidor pueda completar la peticioacuten Laaccioacuten requerida puede llevarse a cabo sin necesidad de interactuar con el

Servicios Rest

140

cliente soacutelo si el meacutetodo utilizado en la segunda peticioacuten es GET o HEAD(ver httpwwww3orgProtocolsrfc2616rfc2616-sec10html)

52 Procesamiento JSON

JSON (JavaScript Object Notation) es un formato para el intercambio de datos basado en textoderivado de Javascript (Javascript disponde de una funcioacuten nativa eval() para convertirstreams JSON en objetos con propiedades que son accesibles sin necesidad de manipularninguna cadena de caracteres)

La especificacioacuten JSR 3539 proporciona un API para el procesamiento de datos JSON(parsing transformacioacuten y consultas)

La gramaacutetica de los objetos JSON es bastante simple Soacutelo se requieren dos estructurasobjetos y arrays Un objeto es un conjunto de pares nombre-valor y un array es una lista devalores JSON define siete tipos de valores string number object array true false y null

El siguiente ejemplo muestra datos JSON para un objeto que contiene pares nombre-valor

Ejemplo formato JSON

nombre John apellidos Smith edad 25 direccion calle 21 2nd Street ciudad New York codPostal 10021 telefonos [ tipo fijo numero 212 555-1234 tipo movil numero 646 555-4567 ]

El objeto anterior tiene cinco pares nombre-valor

bull Los dos primeros son nombre y apellidos con el valor de tipo String

bull El tercero es edad con el valor de tipo number

bull El cuarto es direccion con el valor de tipo object

bull El quinto es telefonos cuyo valor es de tipo array con dos objetos

JSON tiene la siguiente sintaxis

bull Los objetos estaacuten rodeados por llaves sus pares de elementos nombre-valor estaacutenseparados por una coma y el nombre y el valor de cada par estaacuten separados por dospuntos Los nombres en un objeto son de tipo String mientras que sus valores

9 httpsjcporgaboutJavacommunityprocessfinaljsr353indexhtml

Servicios Rest

141

pueden ser cualquiera de los siete tipos que ya hemos indicado incluyendo a otro objetou otro array

bull Los arrays estaacuten rodeados por corchetes [] y sus valores estaacuten separados por una coma Cada valor en un array puede ser de un tipo diferente incluyendo a otro objeto o array

bull Cuando los objetos y arrays contienen otros objetos yo arrays los datos adquieren unaestructura de aacuterbol

Los servicios web RESTful utilizan JSON habitualmente tanto en las peticiones como en lasrespuestas La cabecera HTTP utilizada para indicar que el contenido de una peticioacuten o unarespuesta es JSON es la siguiente

Content-Type applicationjson

La representacioacuten JSON es normalmente maacutes compacta que las representaciones XML debidoa que JSON no tiene etiquetas de cierre A diferencia de XML JSON no tiene un esquemade definicioacuten y validacioacuten de datos ampliamente aceptado

Actualmente las aplicaciones Java utilizan diferentes libreriacuteas para producirconsumir JSONque tienen que incluirse junto con el coacutedigo de la aplicacioacuten incrementando asiacute el tamantildeo delarchivo desplegado El API de Java para procesamiento JSON proporciona un API estaacutendarpara analizar y generar JSON de forma que las aplicaciones que utilicen dicho API sean maacutesligeras y portables

Para generar y parsear datos JSON hay dos modelos de programacioacuten que son similares alos usados para documentos XML

bull El modelo de objetos crea un aacuterbol en memoria que representa los datos JSON

bull El modelo basado en streaming utiliza un parser que lee los datos JSON elemento aelemento (uno cada vez)

Java EE incluye soporte para JSR 353 de forma que el API de java para procesamiento JSONse encuentra en los siguientes paquetes

bull El paquete javaxjson contiene interfaces para leer escribir y construir datos JSONseguacuten el modelo de objetos asiacute como otras utilidades

bull El paquete javaxjsonstream contiene una interfaz para parsear y generar datosJSON para el modelo streaming

Vamos a ver coacutemo producir y consumir datos JSON utilizando cada uno de los modelos

53 Modelo de procesamiento basado en el modelo de objetos

En este caso se crea un aacuterbol en memoria que representa los datos JSON (todos losdatos) Una vez construido el aacuterbol se puede navegar por eacutel analizarlo o modificarlo Estaaproximacioacuten es muy flexible y permite un procesamiento que requiera acceder al contenidocompleto del aacuterbol En contrapartida normalmente es maacutes lento que el modelo de streamingy requiere utilizar maacutes memoria El modelo de objetos genera una salida JSON navegandopor el aacuterbol entero de una vez

El siguiente coacutedigo muestra coacutemo crear un modelo de objetos a partir de datos JSON desdeun fichero de texto

Creacioacuten de un modelos de objetos a partir de datos JSON

Servicios Rest

142

import javaioFileReaderimport javaxjsonJsonimport javaxjsonJsonReaderimport javaxjsonJsonStructureJsonReader reader = JsoncreateReader(new FileReader(datosjsontxt))JsonStructure jsonst = readerread()

El objeto jsonst puede ser de tipo JsonObject o de tipo JsonArray dependiendo delos contenidos del fichero JsonObject y JsonArray son subtipos de JsonStructure Este objeto representa la raiacutez del aacuterbol y puede utilizarse para navegar por el aacuterbol o escribirloen un stream como datos JSON

Vamos a mostrar alguacuten ejemplo en el que utilicemos un StringReader

Objeto JSON con dos pares nombre-valor

jsonReader = JsoncreateReader(new StringReader( + manzanaroja + plaacutetanoamarillo + ))JsonObject json = jsonReaderreadObject()

jsongetString(manzana) jsongetString(plaacutetano)

El meacutetodo getString() devuelve el valor del string para la clave especificadacomo paraacutemetro Pueden utilizarse otros meacutetodos getXXX() para acceder al valorcorrespondiente de la clave en funcioacuten del tipo de dicho objeto

Un array con dos objetos cada uno de ellos con un par nombre-valor puede leerse como

Array con dos objetos

jsonReader = JsoncreateReader(new StringReader([ + manzanarojo + plaacutetanoamarillo + ]))

JsonArray jsonArray = jsonReaderreadArray()

La interfaz JsonArray tambieacuten tiene meacutetodos get para valores de tipo boolean integer y String en el iacutendice especificado (esta interfaz hereda de javautilList)

Creacioacuten de un modelos de objetos desde el coacutedigo de la aplicacioacuten

A continuacioacuten mostramos un ejemplo de coacutedigo para crear un modelo de objetos medianteprogramacioacuten

Ejemplo de creacioacuten de un modelo de objetos JSON desde programacioacuten

import javaxjsonJsonimport javaxjsonJsonObjectJsonObject modelo =

JsoncreateObjectBuilder() add(nombre Duke)

Servicios Rest

143

add(apellidos Java) add(edad 18) add(calle 100 Internet Dr) add(ciudad JavaTown) add(codPostal 12345) add(telefonos

JsoncreateArrayBuilder() add(JsoncreateObjectBuilder() add(tipo casa) add(numero 111-111-1111)) add(JsoncreateObjectBuilder() add(tipo movil) add(numero 222-222-2222))) build()

El tipo JsonObject representa un objeto JSON El meacutetodoJsoncreateObjectBuilder() crea un modelo de objetos en memoria antildeadiendoelementos desde el coacutedigo de nuestra aplicacioacutenEl meacutetodo JsoncreateArrayBuilder() crea un modelo de arrays en memoriaantildeadiendo elementos desde el coacutedigo de nuestra aplicacioacuten

El objeto modelo de tipo JsonObject representa la raiacutez del aacuterbol que es creado anidandollamadas a meacutetodos acuteadd()acute y construyendo el aacuterbol a traveacutes del meacutetodo build()

La estructura JSON generada es la siguiente

Ejemplo formato JSON generado mediante programacioacuten

nombre Duke apellidos Java edad 18 calle 100 Internet Dr ciudad JavaTown codPostal 12345 telefonos [ tipo casa numero 111-111-1111 tipo movil numero 222-222-2222 ]

Navegando por el modelo de objetos

A continuacioacuten mostramos un coacutedigo de ejemplo para navegar por el modelo de objetos

import javaxjsonJsonValueimport javaxjsonJsonObjectimport javaxjsonJsonArrayimport javaxjsonJsonNumberimport javaxjsonJsonStringpublic static void navegarPorElArbol(JsonValue arbol String clave)

Servicios Rest

144

if (clave = null) Systemoutprint(Clave + clave + ) switch(arbolgetValueType()) case OBJECT Systemoutprintln(OBJETO) JsonObject objeto = (JsonObject) arbol for (String nombre objectkeySet()) navegarPorElArbol(objectget(nombre) name) break case ARRAY Systemoutprintln(ARRAY) JsonArray array = (JsonArray) arbol for (JsonValue val array) navegarPorElArbol(val null) break case STRING JsonString st = (JsonString) arbol Systemoutprintln(STRING + stgetString()) break case NUMBER JsonNumber num = (JsonNumber) arbol Systemoutprintln(NUMBER + numtoString()) break case TRUE case FALSE case NULL Systemoutprintln(arbolgetValueType()toString()) break

El meacutetodo navegarPorElArbol() podemos usarlo con el ejemplo anterior de la siguienteforma

navegarPorElArbol(modelo OBJECT)

El meacutetodo navegarPorElArbol() tiene dos argumentos un elemento JSON y una claveLa clave se utiliza para imprimir los pares clave-valor dentro de los objetos Los elementos enel aacuterbol se representan por el tipo JsonValue Si el elemento es un objeto o un array serealiza una nueva llamada a este meacutetodo es invocada para cada elemento contenido en elobjeto o el array Si el elemento es un valor eacuteste se imprime en la salida estaacutendar

El meacutetodo JsonValuegetValueType() identifica el elemento como un objeto un arrayo un valor Para los objetos el meacutetodo JsonObjectkeySet() devuelve un conjunto deStrings que contienene las claves de los objetos y el meacutetodo JsonObjectget(Stringnombre) devuelve el valor del elemento cuya clave es nombre Para los arraysJsonArray implementa la interfaz ListltJsonValuegt Podemos utilizar bucles formejorados con el valor de SetltStringgt devuelto por JsonObjectkeySet() y coninstancias de JsonArray tal y como hemos mostrado en el ejemplo

Escritura de un modelo de objetos en un stream

Los modelos de objetos creados en los ejemplos anteriores pueden escribirse en un streamutilizando la clase JsonWriter de la siguiente forma

Servicios Rest

145

import javaioStringWriterimport javaxjsonJsonWriterStringWriter stWriter = new StringWriter()

JsonWriter jsonWriter = JsoncreateWriter(stWriter)

jsonWriterwriteObject(modelo)

jsonWriterclose()

String datosJson = stWritertoString()Systemoutprintln(datosJson)

El meacutetodo JsoncreateWriter() toma como paraacutemetro un OutputStreamEl meacutetodo JsonWriterwriteObject() escribe el objeto JsonObject en elstreamEl meacutetodo JsonWriterclose() cierra el stream de salida

Modelo de procesamiento basado en streaming

El modelo de streaming utiliza un parser basado en eventos que va leyendo los datos JSONde uno en uno El parser genera eventos y detiene el procesamiento cuando un objeto o arraycomienza o termina cuando encuentra una clave o encuentra un valor Cada elemento puedeser procesado o rechazado por el coacutedigo de la aplicacioacuten y a continuacioacuten el parser continuacuteacon el siguiente evento Esta aproximacioacuten es adecuada para un procesamiento local en elcual el procesamiento de un elemento no requiere informacioacuten del resto de los datos El modelode streaming genera una salida JSON para un determinado stream realizando una llamada auna funcioacuten con un elemento cada vez

A continuacioacuten veamos con ejemplos coacutemo utilizar el API para el modelo de streaming

bull Para leer datos JSON utilizando un parser (JsonParser)

bull Para escribir datos JSON utilizando un generador (JsonGenerator)

Lectura de datos JSON

El API para el modelo streaming es la aproximacioacuten maacutes eficiente para parsear datos JSONutilizando eventos

import javaxjsonJsonimport javaxjsonstreamJsonParserJsonParser parser = JsoncreateParser(new StringReader(datosJson))while (parserhasNext()) JsonParserEvent evento = parsernext() switch(evento) case START_ARRAY case END_ARRAY case START_OBJECT case END_OBJECT case VALUE_FALSE case VALUE_NULL case VALUE_TRUE Systemoutprintln(eventotoString())

Servicios Rest

146

break case KEY_NAME Systemoutprint(eventotoString() + + parsergetString() + - ) break case VALUE_STRING case VALUE_NUMBER Systemoutprintln(eventotoString() + + parsergetString()) break

El ejemplo consta de tres pasos

1 Obtener una instancia de un parser invocando el meacutetodo estaacuteticoJsoncreateParser()

2 Iterar sobre los eventos del parser utilizando los meacutetodos JsonParserhasNext() yJsonParsernext()

3 Realizar un procesamiento local para cada elemento

El ejemplo muestra los diez posibles tipos de eventos del parser El meacutetodoJsonParsernext() avanza al siguiente evento Para los tipos de eventos KEY_NAME VALUE_STRING y VALUE_NUMBER podemos obtener el contenido del elemento invocandoal meacutetodo JsonParsergetString() Para los eventos VALUE_NUMBER podemostambieacuten usar los siguientes meacutetodos

bull JsonParserisIntegralNumber

bull JsonParsergetInt

bull JsonParsergetLong

bull JsonParsergetBigDecimal

El parser genera los eventos START_OBJECT y END_OBJECT para un objeto JSON vaciacuteo

Para un objeto con dos pares nombre-valor

manzajaroja plaacutetanoamarillo

Mostramos los eventos generados

START_OBJECT manzajaKEY_NAMErojaVALUE_STRING plaacutetanoKEY_NAMEamarilloVALUE_STRINGEND_OBJECT

Los eventos generados para un array con dos objetos JSON seriacutean los siguientes

[START_ARRAY START_OBJECT manzajaKEY_NAMErojaVALUE_STRING END_OBJECT

Servicios Rest

147

START_OBJECT plaacutetanoKEY_NAMEamarilloVALUE_STRING END_OBJECT]END_ARRAY

Escritura de datos JSON

El siguiente coacutedigo muestra coacutemo escribir datos JSON en un fichero utilizando el API para elmodelo de streaming

Ejemplo de escritura de datos JSON con el modelo de streaming

FileWriter writer = new FileWriter(testtxt)JsonGenerator gen = JsoncreateGenerator(writer)genwriteStartObject() write(nombre Duke) write(apellidos Java) write(edad 18) write(calle 100 Internet Dr) write(ciudad JavaTown) write(codPostal 12345) writeStartArray(telefonos) writeStartObject() write(tipo casa) write(numero 111-111-1111) writeEnd() writeStartObject() write(tipo movil) write(numero 222-222-2222) writeEnd() writeEnd() writeEnd()genclose()

Este ejemplo obtiene un generador JSON invocando al meacutetodo estaacuteticoJsoncreateGenerator() que toma como paraacutemetro un output stream o un writerstream El ejemplo escribe los datos JSON en el fichero texttxt anidando llamadas a losmeacutetodos write() writeStartArray() writeStartObject() and writeEnd() El meacutetodo JsonGeneratorclose() cierra el output stream o writer stream subyacente

54 Pruebas de servicios REST

Hasta ahora hemos visto varias formas de probar nuestros servicios REST desde liacutenea decomandos con Curl desde IntelliJ con la herramienta Test RESTFul Web Service y desde elnavegador Chrome con Postman (siendo esta uacuteltima la que maacutes hemos utilizado)

Vamos a ver coacutemo implementar tests para nuestros servicios REST utilizando Maven y JUnitPara ello repasaremos algunas cuestiones baacutesicas sobre los ciclos de vida de Maven10

Ciclo de vida de Maven y tests JUnit

Un ciclo de vida en Maven es una secuencia de acciones determinada que defineel proceso de construccioacuten de un proyecto en concreto Como resultado del proceso deconstruccioacuten de un proyecto obtendremos un artefacto (fichero) de un cierto tipo (porejemplo jar war earhellip) Por lo tanto podriacuteamos decir que un ciclo de vida estaacute formado por

10 httpsmavenapacheorgref333maven-corelifecycleshtml

Servicios Rest

148

las acciones necesarias para convertir nuestros archivos fuente que constituyen el proyectoen por ejemplo un jar un warhellip

Maven propone 3 ciclos de vida es decir tres posibles secuencias de acciones que podemosutilizar (y modificar a nuestra conveniencia) para construir nuestro proyecto Dichos ciclos devida son clean site y el denominado default-lifecycle

Cada ciclo de vida estaacute formado por fases Una fase es un concepto abstracto y define el tipode acciones que se deberiacutean llevar a cabo Por ejemplo una fase del ciclo de vida por defectoes compile para referirse a las acciones que nos permiten convertir los ficheros java en losficheros class correspondientes

Cada fase estaacute formada por un conjunto de goals que son las acciones que se llevaraacuten a caboen cada una de las fases Las goals no viven de forma independiente sino que cualquiergoal siempre forma parte de un plugin Maven Podriacuteamos decir que un plugin por lo tantoes una agrupacioacuten loacutegica de una serie de goals relacionadas Por ejemplo el plugin wildflycontiene una serie de goals para desplegar re-desplegar deshacer-el-despliegue arrancarel servidor etc es decir agrupa las acciones que podemos realizar sobre el servidor wildflyUna goal se especifica siempre anteponiendo el nombre del plugin al que pertenece seguidode dos puntos por ejemplo wildflydeploy indica que se trata de la goal deploy que perteneceal plugin wildfly de maven

Pues bien por defecto Maven asocia ciertas goals a las fases de los tres ciclos de vidaCuando se ejecuta una fase de un ciclo de vida por ejemplo mvn package se ejecutan todaslas goals asociadas a todas las fases anteriores a la fase package en orden y finalmentelas goals asociadas a la fase package Por supuesto podemos alterar en cualquier momentoeste comportamiento por defecto incluyendo los plugins y goals correspondientes dentro dela etiqueta ltbuildgt en nuestro fichero de configuracioacuten pomxml

Vamos a implementar tests JUnit Los tests como ya habeacuteis visto en sesiones anterioresen el directorio srctest Algunas normas importantes son que los tests pertenezcan almismo paquete loacutegico al que pertencen las clases Java que estamos probando Por ejemplosi estamos haciendo pruebas sobre las clases del paquete orgexpertojavarest los testsdeberiacutean pertenecer al mismo paquete aunque fiacutesicamente el coacutedigo fuente y sus pruebasestaraacuten separados (el coacutedigo fuente estaraacute en srcmain y los tests en srctest)

Para realizar pruebas sobre nuestros servicios REST necesitamos que el servidor Wilfly esteacuteen marcha Tambieacuten necesitamos empaquetar el coacutedigo en un fichero war y desplegarlo enel servidor todo esto ANTES de ejecutar los tests

Las acciones para arrancar el servidor Wilfly y desplegar nuestra aplicacioacuten en eacutel NO formanparte de las acciones (o goals) incluidas por defecto en el ciclo de vida por defecto de Mavencuando nuestro proyecto tiene que empaquetarse como un war (etiqueta ltpackaginggt denuestro pomxml) Podeacuteis consultar aquiacute11 la lista de goals asociadas a las fases del ciclo devida por defecto de Maven

Por otro lado en el ciclo de vida por defecto se incluye una goal para ejecutar los testsasociada a la fase test Dicha goal es surefiretest El problema es que por defecto la fasetest se ejecuta ANTES de la fase package y por lo tanto antes de empaquetar y desplegarnuestra aplicacioacuten en Wildfly

Por lo tanto tendremos que alterar convenientemente este comportamiento por defectopara que se ejecuten las acciones de nuestro proceso de construccioacuten que necesitemos

11 httpsmavenapacheorgref333maven-coredefault-bindingshtmlPlugin_bindings_for_war_packaging

Servicios Rest

149

y en el orden en el que lo necesitemos Como ya hemos indicado antes esto lo haremosincluyendo dichas acciones en la etiqueta ltbuildgt de nuestro pomxml y configurandolasconvenientemente para asegurarnos que el orden en el que se ejecutan es el que queremos

La siguiente figura muestra parte de la secuencia de fases llevadas a cabo por Maven ensu ciclo de vida por defecto Para conseguir nuestros propoacutesitos simplemente antildeadiremosla goals wildflydeploy y la asociaremos a la fase pre-integration-test y cambiaremos lafase a la que estaacute asociada la goal surefiretest para que los tests se ejecuten DESPUEacuteS dehaber desplegado el war en Wildfly

A continuacioacuten mostramos los cambios que tenemos que realizar en el fichero de configuracioacutenpomxml

Adicioacuten de las goals wildflydeploy y surefiretest a las fases pre-integration-test ysurefiretest respectivamente

lt-- forzamos el despliegue del war generado durante la fase pre-integration-test justo despueacutes de obtener dicho war--gtltplugingt ltgroupIdgtorgwildflypluginsltgroupIdgt ltartifactIdgtwildfly-maven-pluginltartifactIdgt ltversiongt102Finalltversiongt ltconfigurationgt lthostnamegtlocalhostlthostnamegt ltportgt9990ltportgt ltconfigurationgt

Servicios Rest

150

ltexecutionsgt ltexecutiongt ltidgtwildfly-deployltidgt ltphasegtpre-integration-testltphasegt ltgoalsgt ltgoalgtdeployltgoalgt ltgoalsgt ltexecutiongt ltexecutionsgtltplugingt

lt--ejecutaremos los test JUnit en la fase integration-test inmediatamente despueacutes de la fase pre-integration-test y antes de la fase verify--gtltplugingt ltgroupIdgtorgapachemavenpluginsltgroupIdgt ltartifactIdgtmaven-surefire-pluginltartifactIdgt ltversiongt218ltversiongt ltconfigurationgt ltskipgttrueltskipgt ltconfigurationgt ltexecutionsgt ltexecutiongt ltidgtsurefire-itltidgt ltphasegtintegration-testltphasegt ltgoalsgt ltgoalgttestltgoalgt ltgoalsgt ltconfigurationgt ltskipgtfalseltskipgt ltconfigurationgt ltexecutiongt ltexecutionsgtltplugingt

Tambieacuten necesitamos incluir en el pomxml las libreriacuteas de las que depende el coacutedigo depruebas de nuestro proyecto (clases XXXXTest situadas en srctest) libreriacutea JUnit JAXB yel API cliente de JAX-RS Por lo que antildeadimos en el las dependencias correspondientes

Dependencias del coacutedigo srctest con JUnit y API cliente de JAX-RS

ltdependencygt ltgroupIdgtjunitltgroupIdgt ltartifactIdgtjunitltartifactIdgt ltversiongt412ltversiongt ltscopegttestltscopegtltdependencygtltdependencygt ltgroupIdgtorgjbossresteasyltgroupIdgt ltartifactIdgtresteasy-clientltartifactIdgt ltversiongt3013Finalltversiongt ltscopegttestltscopegtltdependencygtltdependencygt ltgroupIdgtorgjbossresteasyltgroupIdgt

Servicios Rest

151

ltartifactIdgtresteasy-jaxb-providerltartifactIdgt ltversiongt3013Finalltversiongtltdependencygt

Dado que vamos a trabajar con el API Json y dado que ejecutaremos los tests desde lamaacutequina virtual de Java y no dentro del servidor WildFly necesitamos antildeadir tambieacuten lassiguientes libreriacuteas

Dependencias del coacutedigo srctest con el API Json de jaxrs

lt--Libreriacuteas para serializardeserializar json --gtltdependencygt ltgroupIdgtorgjbossresteasyltgroupIdgt ltartifactIdgtresteasy-jackson-providerltartifactIdgt ltversiongt3013Finalltversiongtltdependencygt

lt--Jaxrs API json --gtltdependencygt ltgroupIdgtorgjbossresteasyltgroupIdgt ltartifactIdgtresteasy-json-p-providerltartifactIdgt ltversiongt3013Finalltversiongtltdependencygt

No hemos incluido en el pomxml la orden para arrancar Wildfly Vamosa hacer esto desde IntelliJ en un perfil de ejecucioacuten como ya habeacuteishecho en sesiones anteriores De esta forma podremos ver desde IntelliJla consola de logs del servidor En este caso podemos crear un perfilsolamente para arrancar el servidor Wildfly (no es necesario que se incluyael despliegue del war generado puesto que lo haremos desde la ventanaMaven Projects) Antes de iniciar el proceso de construccioacuten por lo tantotendremos que asegurarnos de que hemos arrancado Wildlfly

Con estos cambios en el pomxml y ejecutando el comando mvn verify se llevaraacuten a cabolas siguientes acciones en este orden

bull Despueacutes de compilar el proyecto obtenemos el war (fase package )

bull El war generado se despliega en el servidor de aplicaciones Wilfly (fase pre-integration-test )

bull Se ejecutan los test JUnit sobre la aplicacioacuten desplegada en el servidor (faseintegration-test )

Anotaciones JUnit y aserciones AssertThat

JUnit 4 proporciona anotaciones para forzar a que los meacutetodos anotados con Test seejecuten en el orden que nos interese (por defecto no estaacute garantizado que se ejecuten enel orden en el que se escriben)

En principio debemos programar los tests para que sean totalmente independientes unos deotros y por lo tanto el orden de ejecucioacuten no influya para nada en el resultado de su ejecucioacutentanto si se ejecuta el primero como a mitad o el uacuteltimo El no hacer los tests independientes

Servicios Rest

152

hace que el proceso de testing se alargue y complique innecesariamente ya que puede serque unos tests enmascaren en resultado de otros o que no podamos saber si ciertas partesdel coacutedigo estaacuten bien o mal implementadas hasta que los tests de los que dependemos sehayan superado con eacutexito

Auacuten asiacute y dado que muchas veces se obtienen errores por hacer asunciones en el ordende la ejecucioacuten de los tests JUnit nos permite fijar dicho orden Para ello utilizaremosla anotacioacuten FixMethodOrder indicando el tipo de ordenacioacuten como por ejemploMethodSortersNAME_ASCENDING de forma que se ejecutaraacuten los tests por ordenlexicograacutefico

Por ejemplo

Ejemplo para forzar el orden de ejecucioacuten de los test (orden lexicograacutefico)

FixMethodOrder(MethodSortersNAME_ASCENDING)public class TestMethodOrder

Test public void testB() Systemoutprintln(second)

Test public void testA() Systemoutprintln(first)

Test public void testC() Systemoutprintln(third)

En ese caso el orden de ejecucioacuten seraacute testA() a continuacioacuten testB() y finalmentetestC()

Otra aportacioacuten de JUnit 4 es la incorporacioacuten de aserciones de tipoassertThat En sesiones anteriores habeacuteis utilizado aserciones con meacutetodosAssertassertEquals(resultado_esperado resultado_real) Los nuevosmeacutetodos AssertassertThat() permiten una mayor flexibilidad a la hora de expresar lasaserciones realizadas en nuestros tests asiacute como una mayor legibilidad de los mismos Elprototipo general de las aserciones de este tipo es

assertThat([value] [matcher statement])

en donde [value] es el resultado real (valor sobre el que se quiere afirmar algo)y [matcher statement] es un Matcher u objeto que realiza operaciones deemparejamiento sobre una secuencia de caracteres seguacuten un determinado patroacuten

Por ejemplo

Ejemplos de sentencias assertThat

assertThat(x is(not(4)))

Servicios Rest

153

assertThat(responseStringJson

either(containsString(nombre))and(containsString(apellido)))

assertThat(myList hasItem(3))

Aquiacute utilizamos un matcher con el patroacuten 4 esta sentencia devuelve false si x = 4Podemos combinar varios matchers de forma que se tengan que satisfacer maacutes de unoEn este caso aplicamos el matcher sobre un conjunto de elementos

Hay varias libreriacuteas que implementan Matchers JUnit incluye parte de los matchers deHamcrest (Hamcrest es un framework para escribir objetos matcher permitiendo definir reglasde matching de forma declarativa) Otra libreriacutea interesante para realizar testing de serviciosrest que utilizan representaciones Json es la libreriacutea hamcrest-json que podemos utilizarpara realizar aserciones sobre dos objetos Json

Por ejemplo supongamos que nuestro objeto Json contiene una lista de enlaces Hateoas detipo Link Los objetos Link seraacuten serializadosdeserializados (Wildfly utiliza Jackson pararealizar estas tareas) convenientemente Cuando serializamos un objeto Link (obtenemossu representacioacuten Json) veremos ademaacutes de los objetos uri valor type valor y relvalor que son los que baacutesicamente utilizamos al crear los enlaces Hateoas otros comouriBuilder hellip paramshellip que puede que no nos interese consultar o incluso que no leshayamos asignado ninguacuten valor

Si en nuestro test queremos comprobar que el objeto Json que nos devuelve el servicio(resultado real) se corresponde con el valor esperado tendremos que comparar ambasrepresentaciones Ahora bien puede que solamente nos interese comparar ciertos valorescontenidos en el objeto Json no el objeto completo

Hacer esta comprobacioacuten elemento a elemento es bastante tediosoLa libreriacutea hamcrest-json nos proporciona lo que estamos buscandocon los meacutetodos sameJSONAs() allowingExtraUnexpectedFields() yallowingAnyArrayOrdering() de la siguiente forma

Meacutetodo para comparar dos representaciones Json ClaseukcodatumedgehamcrestjsonSameJSONAs

AssertassertThat(age43 friend_ids[16 52 23] sameJSONAs(friend_ids[52 23 16]) allowingExtraUnexpectedFields() allowingAnyArrayOrdering())

En este coacutedigo tenemos una representacioacuten formada por dos objetos uno de los cuales tienecomo valor un array de enteros Si el servicio rest devuelve un objeto Json con maacutes elementoso en otro orden en este caso el resultado de la sentencia assertThat es true Volviendo alejemplo anterior de un objeto Json que contiene enlaces Hatehoas podriacuteamos realizar lasiguiente comparacioacuten

Comparamos dos objetos Json que contienen hiperenlaces Hateoas (objetos Link)

JsonObject json_object = clienttarget(httplocalhost8080forousuarios) request(MediaTypeAPPLICATION_JSON)

get(JsonObjectclass)

String json_string = json_objecttoString()

Servicios Rest

154

JsonObject usuarios = JsoncreateObjectBuilder() add(usuarios JsoncreateArrayBuilder() add(JsoncreateObjectBuilder() add(nombre Pepe Lopez) add(links JsoncreateArrayBuilder() add(JsoncreateObjectBuilder() add(uri httplocalhost8080forousuariospepe) add(type applicationxmlapplicationjson) add(rel self)))) add(JsoncreateObjectBuilder() add(nombre Ana Garcia) add(links JsoncreateArrayBuilder() add(JsoncreateObjectBuilder() add(uri httplocalhost8080forousuariosana) add(type applicationxmlapplicationjson) add(rel self)))))

build()

AssertassertThat(json_string sameJSONAs(usuariostoString()) allowingExtraUnexpectedFields()

allowingAnyArrayOrdering())

Realizamos la llamada al servicio REST y recibimos como respuesta un objeto Json Eneste caso nuestro objeto Json estaacute formado por una lista de objetosObtenemos la representacioacuten de nuestro objeto Json (resultado real) en forma de cadenade caracteresCreamos un nuevo objeto Json con el resultado esperadoComparamos ambos objetos Si el resultado real incluye maacutes elementos que loscontenidos en json_string o en otro orden consideraremos que hemos obtenido larespuesta correcta

Para utilizar esta libreriacutea en nuestro proyecto simplemente tendremos que antildeadirla comodependencia en la configuracioacuten de nuestro pomxml

Libreriacutea para comparar objetos Json en los tests

lt--Hamcrest Json --gtltdependencygt ltgroupIdgtukcodatumedgeltgroupIdgt ltartifactIdgthamcrest-jsonltartifactIdgt ltversiongt02ltversiongtltdependencygt

Observaciones sobre los tests y algunos ejemplos de tests

Recuerda que para utilizar el API cliente necesitas utilizar instanciasjavaxwsrsclientClient que debemos cerrar siempre despueacutes de su uso paracerrar el socket asociado a la conexioacuten

Servicios Rest

155

Para ello podemos optar por Crear una uacutenica instancia Client antes de ejecutar cualquiertest (meacutetodo BeforeClass) y cerrar el socket despueacutes de ejecutar todos los tests (meacutetodoAfterClass) Crear una uacutenica instancia Client antes de ejecutar CADA test (meacutetodoBefore) y cerrar el socket despueacutes de ejecutar CADA tests (meacutetodo After)

Si el resultado de una invocacioacuten sobre la instancia Client es de tipojavaxwsrscoreResponse debemos liberar de forma expliacutecita la conexioacuten para quepueda ser usada de nuevo por dicha instancia Client

Por ejemplo supongamos que queremos realizar un test en el que realizamos una operacioacutenPOST y a continuacioacuten una operacioacuten GET para verificar que el nuevo recurso se ha antildeadididocorrectamente

Ejemplo de Test que utiliza una instancia Client para todos los tests

public class TestRESTServices private static final String BASE_URL = httplocalhost8080rest private static URI uri = UriBuilderfromUri(BASE_URL)build() private static Client client

BeforeClass public static void initClient()

client = ClientBuildernewClient()

AfterClass public static void closeClient()

clientclose()

Test public void createAndRetrieveACustomer()

Customer customer = Creamos un nuevo cliente Response response = clienttarget(uri) request() post(Entityentity(customer MediaTypeAPPLICATION_JSON)) assertEquals(ResponseStatusCREATED responsegetStatusInfo()) URI referenceURI = responsegetLocation()

responseclose()

Obtenemos el recurso que hemos antildeadido response = clienttarget(referenceURI)request()get()

Customer retrieved_customer = responsereadEntity(Customerclass) assertEquals(ResponseStatusOK responsegetStatusInfo()) assertEquals(retreivedRefgetName() rgetName())

responseclose()

Creamos una instancia Client ANTES de ejecutar cualquier testCerramos el socket asociado a la conexioacuten DESPUEacuteS de ejecutar TODOS los testsLiberamos la conexioacuten para poder reutilizarla

Servicios Rest

156

Liberamos la conexioacuten para poder reutilizarlaAhora veamos otro ejemplo en el que utilizamos una instancia Client para cada test

Ejemplo de Test que utiliza una instancia Client para CADA test

public class TestRESTServices

private Client client

Before public void setUp()

thisclient = ClientBuildernewClient()

After public void tearDown()

thisclientclose()

Test public void getAllCustomersAsJson() String uriString = httplocalhost8080restcustomers JsonArray json_array = client target(uriString) request(MediaTypeAPPLICATION_JSON) accept(MediaTypeAPPLICATION_JSON) get(JsonArrayclass)

AssertassertEquals(2 json_arraysize())

Test public void getAllCustomers() String uriString = httplocalhost8080restcustomers Consultamos los datos de todos los customers ListltCustomergt lista_usuarios = clienttarget(uriString) request(applicationjson) get(new GenericTypeltListltCustomergtgt() ) AssertassertEquals(2 lista_usuariossize())

Creamos una instancia Client ANTES de ejecutar CADA testCerramos el socket asociado a la conexioacuten DESPUEacuteS de ejecutar CADA los tests

Podemos ver en este uacuteltimo ejemplo que no es necesario liberar la conexioacuten entre usossucesivos de la instancia Client si no utilizamos la clase Response En este caso el procesose realiza de forma automaacutetica por el sistema

Finalmente comentaremos que debido a un bug en la especificacioacuten JAX-RS eldeserializado del de los objetos Link no se realiza por lo que obendremos una listade Links vaciacutea (ver httpkingsfleetblogspotcomes201405reading-and-writing-jax-rs-link-objectshtml) Podemos comprobar que si obtenemos la representacioacuten en formato texto dela entidad del mensaje dicha lista de objetos tendraacute el valor correcto

Si no utilizamos la solucioacuten propuesta en el enlace anterior deberemos usar la anotacioacutenJsonIgnoreProperties(ignoreUnknown = true) De esta forma ignoraremos el

Servicios Rest

157

deserializado de los objetos Link pero tendremos que utilizar la representacioacuten en formato decadena de caracteres del recurso json en lugar del objeto java Link asociado

Asiacute por ejemplo si nuestro recurso Customer tiene asociado una lista de objetos Link parapoder utilizar el API Cliente y acceder a la lista de enlaces usaremos la anotacioacuten anterior enla implementacioacuten de la clase Customer

JsonIgnoreProperties(ignoreUnknown = true)XmlRootElement(name=customer)public class Customer int id String name ListltLinkgt links

Servicios Rest

158

55 Ejercicios

Tests utilizando el API cliente y un mapeador de excepciones (1 punto)

Se proporciona como plantilla el MOacuteDULO IntelliJ s5-tienda con una implementacioacuten parcialde una tienda de clientes on-line Este proyecto ya contiene varios tests implementados amodo de ejemplo

Los recursos rest implementados lanzan excepciones de tipo RestException si porejemplo se intenta realizar una consulta sobre un producto yo usuario que no existe

Se ha implementado un mapeador de excepciones RestExceptionMapper quecaptura excepciones de tipo RuntimeException y devuelve una respuesta de tipoErrorMensajeBean que seraacute serializada a formato json yo formato xml (dependiendo delvalor de la cabecera Accept de la peticioacuten) con informacioacuten sobre el error producido

Implementa los siguientes dos tests test7recuperarTodosLosUsuarios() en el querealizamos una invocacioacuten GET sobre httplocalhost8080s5-tiendarestclientes Esta URIpodriacutea corresponderse con un meacutetodo anotado con GET y que devolviese una lista de todoslos clientes de la tienda Sin embargo no existe tal meacutetodo en nuestro recursos rest Verificaque dicha invocacioacuten devuelve el coacutedigo de estado 500 (Internal Server Error) y que en elcuerpo del mensaje se recibe Servicio no disponible

bull test8recuperarClienteQueNoExiste() en el que intentamos recuperar lainformacioacuten de un cliente que no exista en nuestra base de datos En este caso debemosverificar que el mensaje obtenido en formato json es el siguiente

status Not Found code 404 message El producto no se encuentra en la base de datos developerMessage error

Tests utilizando el API Json y JUnit (1 punto)

Vamos a seguir usando el proyecto s4-foroAvanzado con el que hemos trabajado en la sesioacutenanterior

Vamos a implementar algunos tests con JUnit en los que utilizaremos ademaacutes del API clienteel API Json que nos proporciona jaxrs

Para ejecutar los tests necesitamos modificar el pomxml antildeadiendo las dependenciascorrespondientes que hemos visto a lo largo de la sesioacuten y antildeadiendo las goals para que seejecuten los tests despueacutes de desplegar la aplicacioacuten en Wildfly

Proporcionamos el contenido del pomxml con las libreriacuteas y plugins que necesitaraacutes(aunque como ejercicio deberiacuteas intentar modificar la configuracioacuten tuacute mismo y luego puedescomprobar el resultado con el pomxml que se proporciona) El contenido del nuevo pomxmllo tienes en srctestresourcesnuevo-pommxl

Inicializacioacuten de los datos para los tests

Vamos a utilizar DBUnit para inicializar la BD para realizar los tests Para ello tendraacutes queantildeadir en el pomxml las dependencias necesarias (ya estaacuten antildeadidas en el fichero de

Servicios Rest

159

configuracioacuten proporcionado) En el fichero srctestresourcesforo-inicialxml encontrareacuteis elconjunto de datos con el que inicializaremos la base de datos para ejecutar nuestros tests

No es necesario (aunque es una muy buena praacutectica) que inicialicemos la BD para cada test

Implementacioacuten de los tests

Vamos a implementar los siguientes tests (que se ejecutaraacuten en en este mismo orden)

bull test1ConsultaTodosUsuarios() recuperamos los datos de todos los usuarios delforo Recuerda que previamente tienes que haber inicializado la BD con los datos del ficheroforo-inicialxml Recupera los datos en forma de JsonObject y comprueba que el nuacutemerode usuarios es el correcto Tambieacuten debes comprobar que tanto el login como los enlaceshatehoas para cada usuario estaacuten bien creados En concreto para cada usuario debesverificar que la uri (uri) el tipo mime (type) y el tipo de enlace (rel) son los correctos

bull test2CreamosMensajeDePepe() crearemos un nuevo mensaje del usuario con loginpepe Recuerda que este usuario tiene el rol registrado El mensaje tendraacute el asuntocena y el texto seraacute Mejor me voy al cine En este caso deberaacutes comprobar el valorde estado (debe ser 201) y debes recuperar (consultar con una peticioacuten REST) el mensajepara comprobar que la operacioacuten de insertar el mensaje ha tenido eacutexito

bull test3CreamosMensajeDeUsuarioNoAutorizado() creamos un nuevo mensaje deun usuario que no estaacute autorizado (por ejemplo de un usuario con login juan) En estecaso el mensaje tendraacute el asunto cena y el mensaje puede ser Pues yo tampoco voyEl resultado debe ser el coacutedigo de estado 401 ( Unauthorized)

bull test4ConsultaUsuario() Consultamos los datos del usuario pepe Recuperaremoslos datos como un JsonObject y comprobaremos que el valor de la uri para el tipo derelacioacuten self del enlace Link asociado es el correcto

  • Servicios Rest
  • Table of Contents
  • 1 Introduccioacuten a REST Disentildeo y creacioacuten de servicios RESTful
    • 11 iquestQueacute es un servicio Web
      • Servicios Web RESTful
        • 12 Fundamentos de REST
          • Recursos
          • Representacioacuten de los recursos
          • Direccionabilidad de los recursos URI
          • Uniformidad y restricciones de las interfaces
            • 13 Disentildeo de servicios Web RESTful
            • 14 Un primer servicio JAX-RS
              • Modelo de objetos
              • Modelado de URIs
              • Definicioacuten del formato de datos
                • Formato de datos para operaciones de lectura y modificacioacuten de los recursos
                • Formato de datos para operaciones de creacioacuten de los recursos
                  • Asignacioacuten de meacutetodos HTTP
                    • Visualizacioacuten de todos los Pedidos Clientes o Productos
                    • Obtencioacuten de Pedidos Clientes o Productos individuales
                    • Creacioacuten de un Pedido Cliente o Producto
                    • Actualizacioacuten de un Pedido Cliente o Producto
                    • Borrado de un Pedido Cliente o Producto
                    • Cancelacioacuten de un Pedido
                      • Implementacioacuten del servicio Creacioacuten del proyecto Maven
                      • Implementacioacuten del servicio Recursos JAX-RS
                        • Clases de nuestro dominio (entidades) Clientejava
                        • Clases de nuestro servicio RESTful ClienteResourcejava
                        • Creacioacuten de clientes
                        • Consulta de clientes
                        • Modificacioacuten de clientes
                          • Construccioacuten y despliegue del servicio
                          • Probando nuestro servicio
                            • 15 Ejercicios
                              • Servicio REST ejemplo (0 puntos)
                              • Servicio REST saludo (1 punto)
                              • Servicio REST foro (1 punto)
                                  • 2 Anotaciones baacutesicas JAX-RS El modelo de despliegue
                                    • 21 iquestCoacutemo funciona el enlazado de meacutetodos HTTP
                                    • 22 La anotacioacuten Path
                                      • Expresiones Path
                                        • Expresiones regulares
                                        • Reglas de precedencia
                                          • Paraacutemetros matrix (Matrix parameters)
                                          • Subrecursos (Subresource Locators)
                                            • Caraacutecter dinaacutemico del dispatching de peticiones
                                                • 23 Usos de las anotaciones Produces y Consumes
                                                  • Anotacioacuten Consumes
                                                  • Anotacioacuten Produces
                                                    • 24 Inyeccioacuten de paraacutemetros JAX-RS
                                                      • javaxwsrsPathParam
                                                      • Interfaz UriInfo
                                                      • javaxwsrsMatrixParam
                                                      • javaxwsrsQueryParam
                                                      • javaxwsrsFormParam
                                                      • javaxwsrsHeaderParam
                                                      • javaxwsrscoreContext
                                                      • javaxwsrsBeanParam
                                                      • Conversioacuten automaacutetica de tipos
                                                      • Valores por defecto (DefaultValue)
                                                        • 25 Configuracioacuten y despliegue de aplicaciones JAX-RS
                                                          • Configuracioacuten mediante la clase Application
                                                          • Configuracioacuten mediante un fichero webxml
                                                          • Configuracioacuten en un contenedor que no disponga de una implementacioacuten JAX-RS
                                                            • 26 Ejercicios
                                                              • Creacioacuten de un recurso creacioacuten y consulta de temas en el foro (05 puntos)
                                                              • Despliegue y pruebas del recurso (05 puntos)
                                                              • Muacuteltiples consultas de los temas del foro (05 puntos)
                                                              • Creacioacuten de subrecursos (05 puntos)
                                                                  • 3 Manejadores de contenidos Respuestas del servidor y manejo de excepciones
                                                                    • 31 Proveedores de entidades
                                                                      • Interfaz javaxwsrsextMessageBodyReader
                                                                      • Interfaz javaxwsrsextMessageBodyWriter
                                                                        • 32 Proveedores de entidad estaacutendar incluidos en JAX-RS
                                                                          • javaxwsrscoreStreamingOutput
                                                                          • javaioInputStream javaioReader
                                                                          • javaioFile
                                                                          • byte[]
                                                                          • String char[]
                                                                          • MultivaluedMapltString Stringgt y formularios de entrada
                                                                            • 33 Muacuteltiples representaciones de recursos
                                                                            • 34 Introduccioacuten a JAXB
                                                                              • Clase JAXBContext
                                                                              • Manejadores JAX-RS para JAXB
                                                                              • JAXB y JSON
                                                                                • 35 Respuestas del servidor
                                                                                  • Coacutedigos de respuesta por defecto
                                                                                    • Respuestas que indican eacutexito
                                                                                    • Respuestas que indican una situacioacuten de fallo
                                                                                      • Elaboracioacuten de respuestas con la clase Response
                                                                                        • Inclusioacuten de cookies en la respuesta
                                                                                        • El tipo enumerado de coacutedigos de estado
                                                                                        • La clase javaxwsrscoreGenericEntity
                                                                                            • 36 Manejadores de excepciones
                                                                                              • La clase javaxwsrsWebApplicationException
                                                                                              • Mapeado de excepciones
                                                                                              • Jerarquiacutea de excepciones
                                                                                                • 37 Ejercicios
                                                                                                  • Servicio REST ejemplo
                                                                                                  • Plantillas que se proporcionan
                                                                                                  • Uso de JAXB (05 puntos)
                                                                                                  • Uso de manejadores de contenidos y clase Response (075 puntos)
                                                                                                  • Manejo de excepciones (075 puntos)
                                                                                                      • 4 HATEOAS y Seguridad
                                                                                                        • 41 iquestQueacute es HATEOAS
                                                                                                        • 42 HATEOAS y Servicios Web
                                                                                                          • Enlaces Atom
                                                                                                          • Ventajas de utilizar HATEOAS con Servicios Web
                                                                                                            • Transparencia en la localizacioacuten
                                                                                                            • Desacoplamiento de los detalles de la interaccioacuten
                                                                                                            • Reduccioacuten de errores de transicioacuten de estados
                                                                                                              • Enlaces en cabeceras frente a enlaces Atom
                                                                                                                • 43 HATEOAS y JAX-RS
                                                                                                                  • Construccioacuten de URIs con UriBuilder
                                                                                                                  • URIs relativas mediante el uso de UriInfo
                                                                                                                  • Construccioacuten de enlaces (Links) en documentos XML y en cabeceras HTTP
                                                                                                                    • 44 Seguridad
                                                                                                                      • Autentificacioacuten en JAX-RS
                                                                                                                        • Creacioacuten de usuarios y roles
                                                                                                                          • Autorizacioacuten en JAX-RS
                                                                                                                          • Encriptacioacuten
                                                                                                                            • Anotaciones JAX-RS para autorizacioacuten
                                                                                                                              • Seguridad programada
                                                                                                                                • 45 Ejercicios
                                                                                                                                  • Uso de Hateoas (1 puntos)
                                                                                                                                  • Ejercicio seguridad (1 punto)
                                                                                                                                      • 5 Api cliente Procesamiento JSON y Pruebas
                                                                                                                                        • 51 API cliente Visioacuten general
                                                                                                                                          • Obtenemos una instancia Client
                                                                                                                                          • Configuramos el target del cliente (URI)
                                                                                                                                          • Construimos y Realizamos la peticioacuten
                                                                                                                                          • Manejo de excepciones
                                                                                                                                            • 52 Procesamiento JSON
                                                                                                                                            • 53 Modelo de procesamiento basado en el modelo de objetos
                                                                                                                                              • Creacioacuten de un modelos de objetos desde el coacutedigo de la aplicacioacuten
                                                                                                                                              • Navegando por el modelo de objetos
                                                                                                                                              • Escritura de un modelo de objetos en un stream
                                                                                                                                              • Modelo de procesamiento basado en streaming
                                                                                                                                                • Lectura de datos JSON
                                                                                                                                                • Escritura de datos JSON
                                                                                                                                                    • 54 Pruebas de servicios REST
                                                                                                                                                      • Ciclo de vida de Maven y tests JUnit
                                                                                                                                                      • Anotaciones JUnit y aserciones AssertThat
                                                                                                                                                      • Observaciones sobre los tests y algunos ejemplos de tests
                                                                                                                                                        • 55 Ejercicios
                                                                                                                                                          • Tests utilizando el API cliente y un mapeador de excepciones (1 punto)
                                                                                                                                                          • Tests utilizando el API Json y JUnit (1 punto)
                                                                                                                                                            • Inicializacioacuten de los datos para los tests
                                                                                                                                                              • Implementacioacuten de los tests
Page 7: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 8: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 9: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 10: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 11: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 12: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 13: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 14: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 15: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 16: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 17: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 18: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 19: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 20: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 21: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 22: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 23: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 24: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 25: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 26: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 27: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 28: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 29: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 30: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 31: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 32: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 33: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 34: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 35: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 36: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 37: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 38: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 39: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 40: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 41: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 42: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 43: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 44: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 45: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 46: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 47: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 48: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 49: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 50: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 51: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 52: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 53: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 54: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 55: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 56: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 57: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 58: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 59: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 60: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 61: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 62: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 63: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 64: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 65: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 66: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 67: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 68: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 69: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 70: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 71: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 72: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 73: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 74: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 75: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 76: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 77: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 78: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 79: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 80: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 81: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 82: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 83: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 84: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 85: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 86: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 87: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 88: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 89: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 90: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 91: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 92: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 93: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 94: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 95: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 96: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 97: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 98: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 99: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 100: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 101: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 102: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 103: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 104: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 105: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 106: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 107: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 108: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 109: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 110: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 111: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 112: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 113: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 114: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 115: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 116: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 117: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 118: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 119: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 120: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 121: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 122: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 123: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 124: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 125: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 126: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 127: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 128: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 129: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 130: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 131: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 132: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 133: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 134: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 135: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 136: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 137: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 138: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 139: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 140: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 141: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 142: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 143: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 144: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 145: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 146: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 147: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 148: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 149: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 150: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 151: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 152: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 153: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 154: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 155: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 156: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 157: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 158: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones
Page 159: Servicios Rest - Experto Javaexpertojava.ua.es/experto/restringido/2015-16/rest/rest.pdf · 2016. 5. 30. · Son un tipo de Servicios Web, que se adhieren a una serie de restricciones