Facilitando el consumo de nuestros Web Services basados en PL/SQL
23/09/2010 -
Una de las cosas más importantes a la hora de desarrollar web services es proporcionar un contrato detallado describiendo las operaciones y parámetros de nuestro servicio. Este contrato se describe mediante lenguaje XML en un fichero de tipo WSDL (Web Service Description Language). Cuanto más precisa sea la definición del servicio en su contrato, menos problemas tendremos de interpretación entre lo que entienden los que desarrollan el servicio y los que lo consumen. Además, un contrato preciso permite a los consumidores utilizar herramientas de generación automáticas que les facilitan la tarea de llamar al servicio, pudiéndose concentrar en la lógica de la interacción y no en la tecnología.
JDeveloper, desde su versión 9, proporciona herramientas para publicar web services basados en packages PL/SQL. De este modo, es fácil reaprovechar la lógica de negocio embebida en nuestras bases de datos Oracle. El único problema que tiene el wizard de JDeveloper, es cuando se trata de generar el contrato para un servicio que devuelve directamente una estructura XML, es decir, un objeto de tipo XmlType. En este caso, y ante la incapacidad de averiguar cuál es la estructura que devuelve el PL/SQL, JDeveloper pone en el contrato que el método devuelve cualquier XML (un elemento de tipo any o anyType de XSD).
Si queremos aplicar buenas prácticas, deberemos modificar el contrato generado para que contenga la descripción de la estructura que realmente devolvemos (y que nosotros, en calidad de proveedores del servicio, conocemos). En este post, explicamos cuáles serían los pasos a realizar.
1) Programar la lógica del servicio
En primer lugar hay que escribir el código PL/SQL que se corresponderá a nuestro servicio. Para cada servicio crearemos un package PL/SQL, donde las operaciones públicas serán los métodos del Web Service. En nuestro ejemplo, hemos creado un package con una única operación, que devuelve los datos de un país en formato XML:
En el código vemos cómo la función devuelve un parámetro de tipo XmlType. También tenemos un ejemplo de cómo construir elementos pertenecientes a este tipo. La lógica de obtener los datos de un país a partir de la base de dados se ha eliminado para simplificar el caso, pero debería ser la lógica de negocio que requiere nuestro servicio.
2) Generar un Web Service a partir del PL/SQL
Como ya hemos comentado, JDeveloper nos ofrece la funcionalidad de convertir un package PL/SQL a Web Service. En la web de Oracle podéis encontrar un tutorial para convertir un package PL/SQL a Web Service en JDeveloper 10gR3. Sólo tened en cuenta que deberías escoger el tipo de mensaje Document/Literal al tratarse de una respuesta de tipo XmlType.
Qué versión de JDeveloper deberíamos utilizar?
Lo más lógico sería utilizar la correspondiente a la versión de servidor Oracle donde despleguemos el Web Service: si tenéis un IAS 10.1.3.4, utilizaremos la versión 10.1.3.4 de JDeveloper. Desplegar el servicio web en un servidor que no se corresponda con la versión de JDeveloper puede darnos problemas de librerías que es mejor evitar (aunque se pueden soslayar en algún caso mediante una configuración adecuada).
3) Crear una nueva versión del WSDL del Web Service
Cualquiera que sea la versión de JDeveloper que hayamos utilizado para obtener nuestro Web Service, dentro de la carpeta WEB-INF de nuestro proyecto encontraremos una carpeta wsdl que contiene el contrato del servicio:
Observad como el campo de salida está formado por un elemento de tipo <any/>. Esto significa que este Web Service puede devolver cualquier dato de salida en formato XML. Nosotros, como proveedores del servicio, sabemos que no es así, y por tanto, queremos proporcionar a los consumidores un contrato más preciso de los datos de entrada/salida de nuestro servicio web.
Duplicamos la carpeta wsdl dentro de WEB-INF con el nombre wsdlCliente. Dentro de la nueva carpeta, creamos mediante JDeveloper un nuevo esquema XSD (Xml Schema Definition). Estos ficheros sirven para definir los datos que forman una estructura XML. JDeveloper ofrece una herramienta gráfica para escribir este tipo de ficheros.
No entraremos aquí en detalle en los elementos que conforman el lenguaje XSD, pero en nuestro caso, tal y como hemos visto devolvemos un nodo de un tipo complejo DatosPais con cuatro campos en él: Codigo, Nombre, Continente, Poblacion. Si miramos el esquema queda de la manera siguiente:
Vemos que hemos definido el tipo de datos conteniendo los cuatro campos deseados y hemos declarado los tipos de estos campos. Además, hay que destacar que debemos establecer un targetNamespace para nuestro esquema. Lo más normal es coger el namespace que se define en la declaración de tipos de nuestro fichero WSDL (en nuestro caso http://ws/GetDatosPais.wsdl/types/) y añadirle algún sufijo que identifique a nuestro esquema (en nuestro caso, como sólo contiene los datos de respuesta hemos escogido http://ws/GetDatosPais.wsdl/types/respuesta).
Finalmente, nos queda ligar este esquema con el contrato. Editamos el fichero WSDL de nuestra carpeta wsdlCliente de la siguiente manera:
Hemos introducido tres modificaciones (marcadas en rojo):
1) En la cabecera, hemos declarado el espacio de nombres con el prefijo res, que se corresponde al namespace de nuestro esquema XSD (http://ws/GetDatosPais.wsdl/types/respuesta).
2) Dentro de la declaración de tipos, hemos realizado la importación de nuestro namespace. Con la propiedad schemaLocation indicamos la ruta relativa al fichero que contiene el esquema que queremos importar.
3) Finalmente, sustituimos la declaración <any/> para el contenido de la respuesta del WSDL original por un elemento de nombre Pais que es del tipo DatosPais de nuestro nuevo esquema.
A partir de ahora, cada vez que necesitemos facilitar el contrato de nuestro servicio a un consumidor del mismo, les proporcionaremos el contenido de nuestra carpeta wsdlCliente. De este modo, les estaremos ofreciendo toda la información que necesitan para consumir con éxito los datos de nuestro Web Service (y a la vez, dejaremos documentada de una manera clara cuáles son los datos que devuelve el servicio).