Proyecto Java EE
 

Integración con Servicios Web

Introducción

En esta sesión de integración vamos a ofrecer como servicio web algunas de las operaciones implementadas mediante EJBs en sesiones anteriores. También utilizaremos un servicio web externo para obtener información extendida sobre los libros de nuestra biblioteca. Por último, crearemos un cliente Java para nuestro servicio.

Nota
Para realizar el trabajo de esta sesión tomaremos como punto de partida el resultado de la sesión de integración con JMS (aunque realmente no vamos a utilizar JMS, por lo que se podría partir del resultado de la integración con EJBs).

Crearemos todos los proyectos de esta sesión dentro de la carpeta proyint-ent-expertojava/jbib-ent

Creación de servicios web

Vamos a exponer como un servicio web SOAP la funcionalidad que obtiene información de los libros de nuestra biblioteca, implementada actualmente por un EJB. Dicho servicio, por lo tanto, podrá ser utilizado por otras aplicaciones (de nuestra empresa o fuera de ella).

CREAMOS EL PROYECTO

Comenzaremos creando un nuevo proyecto web Maven (dentro de la carpeta jbib-ent, que contiene el proyecto multi-módulo de nuestra biblioteca) con los siguientes datos:

  • nombre: jbib-ent-ws
  • groupID: es.ua.jtech.proyint
  • version: 1.0-SNAPSHOT
  • package: es.ua.jtech.jbib.ws
  • server: Server jbib

MODIFICAMOS LAS DEPENDENCIAS DE LOS PROYECTOS

Nuestro proyecto (jbib-ent-ws) necesita acceder al proyecto (va a depender de) jbib-ent-ejb por lo que añadiremos las dependencias correspondientes en nuestro pom. Pondremos el scope a provided. Con esto estamos indicando que el jar correspondiente se necesitará para compilar el proyecto jbib-web-services pero no se incluirá en el empaquetado final del mismo. Para añadir la dependencia tenemos dos opciones: (1) Modificamos directamente el fichero pom.xml e incluimos la nueva dependencia con la etiqueta <dependency>; (2) En el nodo Dependencies de nuestro proyecto, pinchamos con botón derecho y seleccionamos: Add Dependency...

Podemos visualizar gráficamente las dependencias de nuestro proyecto, situándonos sobre él y pinchando con botón derecho sobre Open POM. Deberemos cambiar a la vista "Graph" en lugar de "Source", que es la opción por defecto. En la figura mostrada, podemos pinchar sobre jbib-ent-ws y veremos lo siguiente:

Dependencias del proyecto jbib-ent-ws

También tenemos que añadir, en el proyecto jbib-ent-ear la dependencia con nuestro proyecto jbib-ent-ws, de la misma forma que hemos comentado (con la diferencia de que el scope debe ser el de por defecto, es decir, compile). Después de hacer las modificaciones, las dependencias de la aplicación enterprise serán:

Dependencias del proyecto jbib-ent-ear

Además, nuestro proyecto tiene que incluirse como un módulo de la aplicación jbib-ent. Añadimos el módulo jbib-ent-ws en el proyecto jbib-ent. De nuevo podemos optar por editar directamente el pom.xml. O, de forma alternativa, podemos añadir el módulo desde el nodo Modules del proyecto jbib-ent, seleccionando con botón derecho la opción Add Existing Module...

El contenido del fichero pom.xml de nuestro proyecto multimódulo (jbib-ent) debe contener la siguiente lista de módulos (en el mismo orden en que se muestran).

<project>
   ...
   <modules>
      <module>jbib-ent-modelo</module>
      <module>jbib-ent-ws</module>
      <module>jbib-ent-ejb</module>
      <module>jbib-ent-web</module>
      <module>jbib-ent-ear</module>
      <module>jbib-ent-client</module>
      <module>jbib-ent-remotelibrary</module>
  </modules>
   ...
</project>   

CREAMOS EL SERVICIO WEB

El proyecto jbib-ent-ws contendrá nuestro servicio web que va a ofrecer dos operaciones, utilizando la funcionalidad de la implementación del ejb que hemos creado en sesiones anteriores. Los datos con los que crearemos nuestro servicio web serán:

  • Name: LibroWS
  • package: es.ua.jtech.jbib.ws
  • Marcaremos Create WS from Existing Session Bean
  • Elegiremos el elemento LibroService

Concretamente, las operaciones que deberá ofrecer este servicio serán (únicamente):

  • buscaLibroPorIsbn
  • listaLibros

La operación buscaLibroPorIsbn nos devuelve los datos del libro solicitado a partir de su isbn. La seguna operación (listaLibros) permitirá a un cliente externo obtener los datos de todos los libros de nuestra biblioteca.

Tendremos que editar el código generado, comentado el resto de métodos, ya que por defecto Netbeans ha expuesto como operaciones del servicio, todos los métodos del ejb que hemos seleccionado. Además, vamos a modificar el tipo del elemento que retornan las operaciones de nuestro servicio, para que devuelvan objetos de tipo LibroTO, y list<LibroTO>, respectivamente.

  • public LibroTO buscaLibroPorIsbn(String isbn)
  • List<LibroTO> listaLibros()

El objeto LibroTO es un TransferObject (una clase java plana con atributos y sus correspondientes getters y setters). Esto nos permitirá tener control sobre las estructuras de datos que se serializan durante la llamada a los servicios y así evitar enviar información no necesaria o con un formato que no resulte adecuado para la conversión a XML. Utilizando entidades (como por ejemplo LibroDomain) tendremos problemas con los campos que relacionan dichas entidades con otras (relaciones uno a muchos). Por ejemplo, si devolvemos un LibroDomain, el servicio web generado intentará devolver también los ejemplares y reservas, lo cual causará un problema al intentar recuperar esta información de forma lazy.

La clase LibroTO, en la que volcaremos los datos de nuestros objetos LibroDomain, se ubicará en el paquete es.ua.jtech.jbib.ws del proyecto jbib-ent-ejb, y tendrá la siguiente información:

public class LibroTO implements Serializable {
    
    private String isbn;
    private String titulo;
    private String autor;
    private Integer numPaginas;
    
    //constructor vacío, necesario para que desde el 
    //servidor de aplicaciones se pueda crear una instancia
    //de LibroTO para devolver la respuesta en el caso de que tengamos definido
    //algún constructor CON parámetros, como en este ejemplo   
    public LibroTO() {
    }

    public LibroTO(LibroDomain libro) {
    ...
    }
    
    //getters y setters

Podemos ver que esta clase tiene un constructor a partir de un LibroDomain, que nos permitirá volcar de forma sencilla los datos de la entidad JPA a nuestro Transfer Object. No olvides crear también un constructor sin parámetros. Sin él, el servidor de aplicaciones no será capaz de crear la instancia de nuestro servicio Web.

Podemos consultar el wsdl generado por Glassfish al desplegar nuestro servicio accediendo a:

 http://localhost:8080/jbib-ent-ws/LibroWS?wsdl
 

Podemos probar el servicio creado con el cliente de pruebas que nos genera Glassfish al desplegar el servicio accediendo a:

 http://localhost:8080/jbib-ent-ws/LibroWS?tester
 

Acceso a servicios web externos

Vamos a añadir a la aplicación de la biblioteca la funcionalidad de obtener los detalles de un libro (portada, precio, editorial y fecha de publicación). Para hacer esto recurriremos a un servicio web (InfoLibroSWService) que hemos creado en el servidor de jtech para el curso de especialista, y al que denominaremos servicio web de Jtech.

Opcional
Si queréis probar el servicio web de Jtech lo podéis hacer accediendo a: http://server.jtech.ua.es:3700/servcweb-infoLibro-jtech/InfoLibroSWService?tester. Puedes utilizar el siguiente valor como isbn: 0321127420

Primero vamos a añadir un nuevo Transfer Object LibroInfoTO, en el paquete es.ua.jtech.jbib.ws del proyecto jbib-ent-ejb:

public class LibroInfoTO implements Serializable{
    
    private String isbn;
    private float precio;
    private String imagen;
    private int imagenAncho;
    private int imagenAlto;
    private String editorial;
    private String anyoEdicion;
    
    //getters y setters

Creamos dentro del módulo EJB (jbib-ent-ejb) un cliente para acceder al servicio web de Jtech, cuyo documento WSDL se puede encontrar en la siguiente dirección:

http://server.jtech.ua.es:3700/servcweb-infoLibro-jtech/InfoLibroSWService?wsdl

Indicaremos que el paquete en el que deben generar las clases al ejecutar wsimport será: es.ua.jtech.jbib.ws

Después de crear el cliente (y por lo tanto generar los stubs necesarios para acceder al servicio), tenemos que asegurarnos de que en el pom del proyecto jbib-ent-ejb, se encuentra el directorio src/main/resources como un directorio de recursos:

<build>
   <resources>
      ...
      <resource>
          <directory>src/main/resources</directory>
      </resource>
   </resources>
   ...
</build>   

Importante. Ficheros de recursos
Por defecto, todos los ficheros incluidos en el directorio src/main/resources se copian en el directorio raíz del jar/war generado. Así, por ejemplo, podemos ver que en dicho directorio (podemos ir a la pestaña "Files", o bien desde la pestaña "Projects", pinchamos sobre el nodo "Other Sources") tenemos el fichero "META-INF/persistence.xml", que se copiará en el jar generado (ver el contenido del fichero "jbib-ent-ejb-1.0-SNAPSHOT.jar" en el directorio tarjet). Ahora bien, cuando creamos el cliente del servicio Web, lo que hace NetBeans de forma automática es modificar el pom añadiendo el plugin wsimport y ejecutar dicho pom, generando así los stubs. Además de incluir el plugin, de forma automática se modifica el directorio de recursos por defecto, de forma que, además del directorio /src/main/resources, se incluyan los nuevos recursos a copiar en el jar, que serán: el directorio src/wsdl, más el fichero src/jax-ws-catalog.xml.

Añadiremos una nueva operación recuperaLibroInfo en la interfaz local del ejb (ILibroService):

es.ua.jtech.jbib.ws.LibroInfoTO recuperaLibroInfo (String isbn);

Y a continuación implementaremos dicho método en LibroService.java. Aquí es donde incluiremos el código para llamar al servicio web de Jtech, concretamente tendremos que utilizar la operación buscaLibro, del servicio InfoLibroWSService. Dicha operación devuelve información detallada de un libro a partir de su isbn.

Importante. Bug de Glassfish
Debido a un bug de Glassfish, la inyección de la referencia al servicio web (anotación @WebServiceReference) no funciona correctamente, de forma que, al ejecutar el servicio web, el servidor no encuentra el wsdl asociado a dicha referencia. En consecuencia, las llamadas al objeto Service provocará una excepción. Para solucionar este problema tendremos que quitar la anotación @WebServiceReference, y en su lugar, crearemos "a mano" nosotros dicha instancia en lugar de dejar que Glassfish lo haga. Por lo tanto añadiremos la línea service = new InfoLibroSWService(); antes de realizar la llamada service.getInfoLibroSWPort().

Probamos el acceso al servicio Web externo

Para probar la llamada al servicio Web de Jtech, lo haremos desde un nuevo servlet, al que llamaremos InfoLibrosJtech, que crearemos en la aplicación web jbib-ent-web. Para crear el nuevo servlet utilizaremos los siguientes parámetros:

  • Name: InfoLibrosJtech
  • package: es.ua.jtech.jbib.servlet
  • servletName: InfoLibrosJtech
  • URL: /InfoLibrosJtech

El código del servlet hará una llamada a una instancia de LibroService, accediendo al método recuperaLibroInfo utilizando, por ejemplo, el valor de isbn "0321180860". Se mostrarán por pantalla los datos: isb, editorial, año de edición, precio, dirección de descarga de la portada del libro. A continuación mostramos un ejemplo de ejecución del servlet:

Resultado de la ejecución del servlet InfoLibrosJtech

Para mostrar la imagen con la portada, podéis utilizar un código similar a éste:

out.println("<img src= \""+imagen+"\" alt=\"Portada\">")

Siendo imagen la variable (de tipo String) que contiene la dirección url del fichero de imagen

Cliente para el servcio Web

Crear un cliente Java con Netbeans para el servicio web de la biblioteca (LibroWS), en un nuevo proyecto Maven Java al que llamaremos jbib-ent-ws-client.

El programa deberá invocar la operación listaLibros y mostrar en la consola el listado de libros.

De cada libro se mostrará su ISBN, su título, y su autor.

Resumen

Proyecto jbib-ent-ws

Se deberá añadir un nuevo proyecto Maven Web jbib-ent-ws con el siguiente servicio web:

  • LibroWS: Servicio web que ofrecerá las operaciones:
    • buscaLibroPorIsbn, y
    • listaLibros

Proyecto jbib-ent-ejb

Se deberán añadir los siguientes componente al módulo jbib-ent-ejb:

  • LibroTO: Transfer Object con los datos detallados de un libro proporcionados por Jtech.
  • LibroInfoTO: Transfer Object con los datos detallados de un libro proporcionados por Jtech.
  • Cliente del servicio de Jtech: Stub para acceder a los servicios de Jtech.
  • Operación recuperaInfoLibro, en LibroService, que obtiene los datos detallados de un libro mediante el servicio de JTech

Proyecto jbib-ent-web

Se deberá añadir al proyecto Maven Web jbib-ent-web:

  • Servlet InfoLibrosJtech: para probar el cliente del servicio Web de Jtech desde el ejb.

Proyecto jbib-ent-ws-client

Se deberá añadir un nuevo proyecto Maven Java jbib-ent-ws-client, que contendrá:

  • Un cliente Java que acceda a nuestro servicio. Obtendrá la lista de todos los libros de nuestra biblioteca, y la mostrará por la consola