Iteración 2. API REST para la aplicación de gestión de reservas

FJRP - CCIA-2011


Date: Noviembre-2011



Índice General

Pasos previos: configuración del proyecto Netbeans

 
IMPORTANTE: Problemas con GlassFish v3.1
  • La versión de GlassFish que se distribuye con Netbeans 7.0 tiene una implementación del API CDI (Java Contexts and Dependency Injection) con varios bugs que complican el despligue de algunas aplicaciones (más información).

  • En la versión de GlassFish 3.1.1 (que se distribuye con Netbeans 7.0.1) se emplea la versión 1.1.1-Final del proyecto Weld que resuelve el problema.

SOLUCIONES:

  1. Trabajar con Netbeans 7.0.1 y GlassFish 3.1.1
  2. Deshabilitar el soporte CDI en el proyecto GestionReservas-rest

    Bastará con eliminar el fichero de configuración de CDI (beans.xml)

    1. En el proyecto GestionReservas-rest, dentro de Configurarion Files, seleccionar beans.xml, [botón derecho] + Delete

    2. Reiniciar el servidor GlassFish (adicionalmente se puede eliminar [undeploy] la aplicación GestionReservas desde la pestaña de servidores de Netbeans), limpiar y reconstruir el proyecto principal y volver a desplegarlo (Deploy)

 

Se trabajará sobre el subproyecto Java Web GestionReservas-rest, incialmente vacío.

  1. Incluir el JAR del proyecto GestionReservas-ejb para tener disponibles los .class de las entidades JPA y de los interfaces de los EJBs
         
    Sobre el proyecto ''GestionReservas-rest'' -> [botón derecho] -> seleccionar ''Properties''
    En ''Libraries'', pulsar botón ''AddProject'' y seleccionar ''GestionReservas-ejb'' [Add Project JAR files] + [OK]
    

  2. Crear el ApplicationPath (raíz de los URIs del API REST)

  3. Crear la clase de implementación de los recursos REST.

    Será un EJB de sesión local sin interfaz. Se hace así para poder usar el soporte de inyección de dependencias para acceder a los EJBs del proyecto GestionReservas-ejb

Implementar el API REST de acceso a los clientes

  1. Crear las clases JAXB que soportarán la serialización de datos en XML/JSON (por ejemplo ClienteREST)
    1. Sobre ''sources packages'' crear un nuevo paquete Java de nombre ''jaxb''
    2. Sobre el paquete ''rest'' -> [botón derecho] -> New -> Other -> Java Class
      • Anotarla con @XmlRootElement e incluir los atributos a manejar en los documentos XML/JSON a intercambiar.

        Se declararán como private y se anotarán con @XmlElement para que JABX los incluya en los XML generados

      • Se usará la convención de serializar los atributos id de las entidades en como atributos XML (anotación @XmlAttribute) en lugar de como elementos XML.
      • Debe incluirse un constructor vacío y crear métodos get() y set() para todos los atributos [importante]

      • Se ha incluido un constructor ''de conveniencia'' que toma la entidad Cliente original y un String con la URI base donde está desplegado el recurso y a aprtir de ellos se copian/construyen los atributos de ClienteREST
      • Nota: Otra alternativa podría hacer sido utilizar directamente las entidades JPA del paquete entidades, añadiéndoles las correspondientes anotaciones JAXB y evitando en el mapeo las referencias circulares entre entidades.

      package jaxb;
      
      import javax.xml.bind.annotation.XmlAttribute;
      import javax.xml.bind.annotation.XmlRootElement;
      
      
      @XmlRootElement
      public class ClienteREST {
          private Long id;
          private String nombre;
          private String apellidos;
          private String nif;
          private String domicilio;
          private String localidad;
          private String codPostal;
          private String provincia;
          private String telefono;
      
          private String URI;  // Alamcena el URI del recurso
      
          public ClienteREST() {
          }
      
          public ClienteREST(entidades.Cliente cliente, String URIbase) {
              this.id = cliente.getId();
              this.nombre = cliente.getNombre();
              this.apellidos = cliente.getApellidos();
              this.nif = cliente.getNif();
              this.domicilio = cliente.getDomicilio();
              this.localidad = cliente.getLocalidad();
              this.codPostal = cliente.getCodPostal();
              this.provincia = cliente.getProvincia();
              this.telefono = cliente.getTelefono();
              
              this.URI = URIbase + this.id; // Construye el URI
          }
      
          @XmlAttribute  // Para serializar el ID como atributo XML y no como elemento
          public String getId(){
      	return this.id;
          }
      ...
      }
      

    3. Crear los métodos que atenderán las peticiones REST y mapear las URIs y métodos HTTP
      1. Inyectar las referencias a los EJBs a utilizar.
        @Stateless
        @LocalBean
        @Path("/clientes")
        @Produces({"application/xml", "application/json"})
        @Consumes({"application/xml", "application/json"})
        public class EjemploREST {
             @EJB
             ClienteDAOLocal clienteDAO;
        ...
        }
        

      2. Inyectar una referencia al objeto gestor de URIs del API JAX-RS (mediante la anotación @Context aplicada a un objeto javax.ws.rs.core.UriInfo)
        @Stateless
        @LocalBean
        @Path("/clientes")
        @Produces({"application/xml", "application/json"})
        @Consumes({"application/xml", "application/json"})
        public class EjemploREST {
            @EJB
            ClienteDAOLocal clienteDAO;
        
            @Context
            private UriInfo uriInfo;  // Info. sobre la URI donde servidor despliega el recurso 
        ...
        }
        

      3. Implementar los puntos finales de las URIs y métodos REST.
        • Consulta al EJB clienteDAO y convierte las entidades Cliente en una lista de objetos ClienteREST que serán enviados al cliente.
              @GET
              public List<ClienteREST> listaClientes() {
                  List<ClienteREST> lista = new ArrayList<ClienteREST>();
          
                  String uriBase = uriInfo.getBaseUri().toString();
          
                  for (Cliente c : clienteDAO.buscarTodos()) {
                      lista.add(new ClienteREST(c, uriBase));
                  }
          
                  return lista;
              }
          

        • Obtiene los datos del Cliente cuyo ID se indica en el path del URI
              @Path("/{id}")
              @GET
              public ClienteREST clientePorId(@PathParam("id") Long id) {
                  Cliente c = clienteDAO.buscarPorId(id);
                  return (new ClienteREST(c, uriInfo.getBaseUri().toString()));
              }
          
        • Borra el Cliente cuyo ID se indica en el path del URI
              @Path("/{id}")
              @DELETE
              public void borrarCliente(@PathParam("id") Long id) {
                  Cliente c = clienteDAO.buscarPorId(id);
                  clienteDAO.eliminar(c);
              }
          

        • Crea un nuevo cliente con los datos recibidos y devuelve la URI del recurso creado
              @POST
              @TransactionAttribute(TransactionAttributeType.NEVER) // Para que el clienteDAO
                                                                    // gestione (y termine) 
                                                                    // su propia transacción
              public Response crearCliente(JAXBElement<ClienteREST> clienteJAXB) {
                  ClienteREST clienteREST = clienteJAXB.getValue();
          
                  Cliente nuevo = new Cliente();
          
                  nuevo.setNombre(clienteREST.getNombre());
                  nuevo.setApellidos(clienteREST.getApellidos());
                  nuevo.setNif(clienteREST.getNif());
                  nuevo.setDomicilio(clienteREST.getDomicilio());
                  nuevo.setCodPostal(clienteREST.getCodPostal());
                  nuevo.setProvincia(clienteREST.getProvincia());
                  nuevo.setTelefono(clienteREST.getTelefono());
                  
                  nuevo = clienteDAO.crear(nuevo);
                  
                  URI nuevoURI = uriInfo.getAbsolutePathBuilder().path(nuevo.getId().
                                                                     toString()).build();
                  return Response.created(nuevoURI).build();
              }
          

Desplegar y probar el ejemplo

  1. Sobre el proyecto principal GestionReservas, desplegar la aplicación (con lo que se incluye el despliege del módulo GestionReservas-rest)

  2. Probar el acceso a las URIs definidas desde un navegador o con la herramienta de linea de comandos curl
    curl -v http://localhost:8080/GestionReservas-rest/rest/clientes
    curl -v -X GET -H"Accept: application/json" http://localhost:8080/GestionReservas-rest/rest/clientes
    
    curl -v http://localhost:8080/GestionReservas-rest/rest/clientes/4
    curl -v -X GET -H"Accept: application/json" http://localhost:8080/GestionReservas-rest/rest/clientes/4
    
    curl  -v -X POST http://localhost:8080/GestionReservas-rest/rest/clientes \
          -HContent-type:application/xml \
          --data "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?> \
                 <clienteREST>  \
                   <nombre>Lucas</nombre> \
                   <apellidos>Grijander Grijander</apellidos> \
                   <nif>33333333C</nif>  \
                   <domicilio>Su casa</domicilio> <localidad>Santiago</localidad> \
                   <codPostal>15333</codPostal><provincia>A Coruña</provincia> \
                   <telefono>981333333</telefono>  \
                 </clienteREST>"
    

TAREAS A REALIZAR

API REST para acceso a los Hoteles

Crear una nueva clase para gestionar los recursos REST dentro del ''path'' @Path("hoteles")

(URI: http://localhost:8080/GestionReservas-rest/rest/hoteles)

Métodos REST a soportar

API REST para acceso a las Reservas

Crear una nueva clase para gestionar los recursos REST dentro del ''path'' @Path("reservas")

(http://localhost:8080/GestionReservas-rest/rest/reservas)

Métodos REST a soportar

Documentación a entregar

La documentación y el código a entregar para esta ''iteración 2'' se integrarán con los de la ''iteración 1''.



ribadas 2011-11-23