VIRTUAL DATAPORT 4.0 GUÍA DEL DESARROLLADOR NOTA Este documento es confidencial y propiedad de denodo technologies (en adelante denodo). Ninguna de las partes del documento puede ser copiada, fotografiada, fotocopiada, transmitida electrónicamente, almacenada en un sistema de gestión documental o reproducida mediante cualquier otro mecanismo sin la autorización previa o por escrito de denodo. copyright © 2007 Queda prohibida la reproducción total o parcial de este documento sin la autorización por escrito de denodo technologies Virtual DataPort 4.0 Guía del Desarrollador ÍNDICE PREFACIO...........................................................................................................................................................................I ALCANCE .....................................................................................................................................................................I QUIÉN DEBERÍA USAR ESTE DOCUMENTO..........................................................................................................I RESUMEN DE CONTENIDOS....................................................................................................................................I 1 INTRODUCCIÓN ..................................................................................................................................... 1 EJEMPLOS Y FICHEROS DE AYUDA................................................................................................... 1 1.1 2 ACCESO A TRAVÉS DE JDBC............................................................................................................... 2 3 3.1 3.2 ACCESO A TRAVÉS DE ODBC .............................................................................................................. 4 ACCESO MEDIANTE EASYSOFT ODBC-JDBC GATEWAY.............................................................. 4 ACCESO MEDIANTE OPENLINK ODBC-JDBC BRIDGE ................................................................... 5 4.1 4.2 4.3 4.4 4.5 ACCESO A TRAVÉS DE INTERFAZ WEB SERVICE............................................................................ 8 ACCESO A VISTAS PUBLICADAS COMO WEB SERVICES ............................................................. 8 INTERFAZ WEB SERVICE GENÉRICA ................................................................................................. 8 EJECUTANDO CONSULTAS EN CLIENTES SIN ESTADO................................................................ 9 EJECUTANDO CONSULTAS EN CLIENTES CON ESTADO .............................................................. 9 PROCESANDO LOS RESULTADOS DE UNA SENTENCIA.............................................................. 10 4 5 5.1 5.2 5.3 5.4 5.4.1 5.4.2 5.5 5.6 5.6.1 5.7 5.7.1 5.7.2 5.7.3 5.7.4 API DE DESARROLLO DATAPORT..................................................................................................... 12 CONFIGURACIÓN DE LAS APLICACIONES CLIENTE ..................................................................... 13 CASO PRÁCTICO .................................................................................................................................. 13 ESTRUCTURA GENERAL DE LA API DE DESARROLLO DATAPORT............................................. 15 DATASOURCES VIRTUAL DATAPORT.............................................................................................. 16 DataSources............................................................................................................................................ 16 Conexiones Virtual DataPort................................................................................................................... 17 PROTOCOLO DE COMUNICACIONES CON EL SERVIDOR............................................................. 18 PROCESAMIENTO DE RESULTADOS DE UNA CONSULTA........................................................... 21 Implementación de Nuevos Ensambladores de Resultados................................................................... 22 MODOS DE FUNCIONAMIENTO GENERAL DE UN CLIENTE ........................................................ 25 Clientes Síncronos – Requieren control sobre el flujo de datos ............................................................ 26 Clientes Asíncronos – No requieren control sobre el flujo de datos ..................................................... 26 Cliente Iterador Asíncrono ...................................................................................................................... 28 Información sobre la Traza de Ejecución de Sentencias ........................................................................ 28 BIBLIOGRAFÍA ............................................................................................................................................................... 30 Virtual DataPort 4.0 Guía del Desarrollador ÍNDICE DE FIGURAS Figura 1 Figura 2 Figura 3 Figura 4 Figura 5 Figura 6 Figura 7 Figura 8 Figura 9 Figura 10 Figura 11 Figura 12 Configuración de EasySoft ODBC-JDBC Gateway.................................................................................... 5 Configuración de OpenLink ODBC-JDBC Lite (1) ...................................................................................... 6 Configuración de OpenLink ODBC-JDBC Lite (2) ...................................................................................... 7 Código de un cliente iterador asíncrono................................................................................................. 15 Estructura general de la API nativa ........................................................................................................ 16 Configuración de un VDBDataSource ..................................................................................................... 17 Superclase de todos los resultados parciales ........................................................................................ 19 Jerarquía de resultados parciales .......................................................................................................... 20 Estructura general de un ensamblador de resultados............................................................................ 21 Código de ConsolePrinter........................................................................................................................ 25 Código de un cliente síncrono................................................................................................................. 26 Código de un cliente asíncrono............................................................................................................... 27 Virtual DataPort 4.0 ÍNDICE DE TABLAS Guía del Desarrollador Virtual DataPort 4.0 Guía del Desarrollador PREFACIO ALCANCE Denodo Virtual DataPort permite que las aplicaciones puedan acceder a vistas integradas de distintas fuentes de información heterogéneas y distribuidas. Este documento introduce al usuario del producto los mecanismos disponibles para que aplicaciones cliente utilicen Denodo Virtual DataPort. También se proporciona la información necesaria para desarrollar una aplicación cliente propia. QUIÉN DEBERÍA USAR ESTE DOCUMENTO Este documento está dirigido a usuarios que deseen que una aplicación cliente saque partido de las funcionalidades avanzadas de integración, extracción, inserción, actualización y borrado de datos de Virtual DataPort. También está dirigido a desarrolladores que quieran conocer con detalle cómo desarrollar sus propias aplicaciones cliente. La información detallada precisa para instalar el sistema y administrarlo se proporciona en otros manuales, que serán referenciados a medida que sea necesario. RESUMEN DE CONTENIDOS Más concretamente, en este documento: • Se presentan las tareas fundamentales que son necesarias para que una aplicación cliente acceda a las vistas unificadas creadas mediante Virtual DataPort. • Se describe cómo acceder a un servidor DataPort utilizando la API Java estándar para acceso a Bases de Datos, JDBC. • Se describe cómo acceder a un servidor DataPort utilizando la interfaz para acceso a Bases de Datos, ODBC. • Se describe cómo acceder a las vistas de DataPort publicadas como Servicios Web desde aplicaciones cliente. • Se describe la API genérica basada en Servicios Web para la realización de cualquier sentencia VQL sobre un servidor DataPort. • Se detallan todas las peculiaridades de la API nativa de desarrollo de Denodo Virtual DataPort. • Se incluyen o se referencian diversos ejemplos de desarrollo de clientes que usan Virtual DataPort tanto de forma síncrona (en la cual el cliente se queda bloqueado hasta recibir la respuesta completa del servidor) como asíncrona (en el cual el cliente controla el flujo de la comunicación con el servidor y puede tratar los resultados de las consultas a medida que estos se van obteniendo de las fuentes). Prefacio i Virtual DataPort 4.0 1 Guía del Desarrollador INTRODUCCIÓN Denodo Virtual DataPort es una solución global para la integración de fuentes de información heterogéneas y dispersas, utilizando el enfoque EII (Enterprise Information Integration). Virtual DataPort integra aquella información que es relevante para la empresa, sin importar su procedencia, formato o nivel de estructuración, incorpora esa información en su sistema de información, en tiempo real o con precargas configurables, y facilita la construcción de servicios telemáticos de alto valor estratégico y funcional, tanto corporativos como de negocio, imposibles de concebir sin su aplicación. DataPort está basado en una arquitectura cliente-servidor en la cual los clientes emiten sentencias al servidor escritas en VQL (Virtual Query Language), lenguaje utilizado para el manejo y definición de datos (ver la Guía del Administrador [1] y la Guía Avanzada de VQL [2]) Este documento introduce al usuario del producto los mecanismos disponibles para que aplicaciones cliente utilicen Denodo Virtual DataPort, sacando partido de sus facilidades de integración, extracción, inserción, actualización y borrado de datos. Véase la Guía del Administrador [1] para obtener información sobre cómo instalar, configurar y administrar el servidor DataPort, así como para utilizarlo en la creación de vistas unificadas de datos sobre fuentes heterogéneas y dispersas. Existen varias formas que las aplicaciones cliente pueden utilizar para acceder a Virtual DataPort 3.5: • • • • • Acceso a través de JDBC (Java DataBase Connectivity) [5]. DataPort incluye un driver JDBC que las aplicaciones cliente pueden utilizar para este propósito. Acceso a través de ODBC (Open DataBase Connectivity) [10]. El driver JDBC de DataPort puede ser utilizado conjuntamente con un puente ODBC-JDBC para proporcionar acceso desde clientes ODBC. Acceso a vistas publicadas como Web Services. La herramienta de administración de Virtual DataPort (ver la Guía del Administrador [1]) permite publicar como un Web Service cualquier vista de datos definida en DataPort. Acceso a través de una interfaz Web Service genérica. DataPort incluye una interfaz genérica Web Service para ejecutar sentencias VQL sobre un servidor DataPort. Se proporciona el fichero WSDL [7] que puede ser utilizado para generar los “stubs” precisos para acceder a esta interfaz. Acceso a través de la API nativa. DataPort incluye también una API JAVA para la interacción con el servidor. En la ruta DENODO_HOME\docs\vdp se encuentra el fichero README_VDPClient con información útil sobre las diferentes interfaces de acceso de Virtual DataPort. En esta guía se exponen las directrices básicas para el uso de cada uno de estos modos de acceso y se indican algunos ejemplos de uso. Se recomienda la lectura de la documentación Javadoc [3] para mayor detalle sobre clases, atributos y operaciones. 1.1 EJEMPLOS Y FICHEROS DE AYUDA En la ruta DENODO_HOME\samples\vdp\vdp-clients se encuentran diversos ejemplos de implementación de aplicaciones cliente para VDP, utilizando los diferentes métodos de acceso comentados en este documento. Para cada ejemplo se incluye su código fuente y scripts para su compilación y ejecución. Véase el fichero DENODO_HOME\samples\vdp\vdp-clients\README para más información sobre los ejemplos. Introducción 1 Virtual DataPort 4.0 2 Guía del Desarrollador ACCESO A TRAVÉS DE JDBC JDBC (Java DataBase Connectivity) es una API Java que permite realizar consultas contra una Base de datos relacional, independientemente del Sistema de Base de Datos utilizado. Virtual DataPort proporciona un driver que implementa las principales características de JDBC 3.0 [5]. El driver JDBC de Virtual DataPort soporta, entre otros, los siguientes aspectos de la especificación JDBC: Los tipos de datos soportados son los definidos por el gestor en la Guía avanzada de VQL [2] (incluye soporte para todos los tipos básicos así como para campos de tipo array y registro). Permite ejecutar las sentencias de creación de vistas unificadas, consulta, inserción, actualización y borrado de las mismas soportadas en VQL [2]. Soporta también sentencias de descripción de relaciones y listado de elementos del catálogo del gestor. Soporta el uso de PreparedStatement en Virtual DataPort, permitiendo delegar en el driver el formateo de los parámetros de una sentencia VQL. Soporta la consulta sobre wrappers mediante la sentencia QUERY WRAPPER [2]. Por último, también soporta la invocación de procedimientos almacenados mediante la sentencia CALL [2]. El driver JDBC para Virtual DataPort es un driver tipo 2, por lo que requiere disponer de la librería con la API DataPort nativa en la máquina que ejecuta la aplicación cliente. Para realizar una conexión JDBC contra un servidor se necesitan los siguientes elementos: Clase que implementa el driver JDBC para el gestor contra el que realizar las consultas (com.denodo.vdp.jdbc.Driver). Cadena de conexión, que indica la máquina, puerto y nombre de la base de datos del gestor (jdbc:vdb://<hostName>:<port>/<databaseName>) Nombre del usuario a utilizar en la conexión contra el gestor. Clave de acceso del usuario que accederá al gestor. Opcionalmente también es posible añadir una lista de parámetros de configuración específicos de Virtual DataPort que se muestran a continuación junto con sus valores por defecto (véase la sección 5.4 para una descripción de cada uno de estos parámetros): i18n=es_euro queryTimeout=80000 chunkTimeout=90000 chunkSize=100 initSize=0 maxActive=30 maxIdle=20 poolEnabled=false Por lo tanto la sintaxis completa para el URL de conexión JDBC es la siguiente: jdbc:vdb://<hostName>:<port>/<databaseName>?paramName1=paramValue1&... &paramNameN=paramValueN Por ejemplo: jdbc:vdb://localhost:9999/admin?queryTimeout=100000&chunkTimeout=95000 Acceso a través de JDBC 2 Virtual DataPort 4.0 Guía del Desarrollador Para que el acceso a DataPort utilizando el driver JDBC sea exitoso, el CLASSPATH de la aplicación cliente debe incluir el fichero denodo-vdp-jdbcdriver.jar, que tras la instalación puede encontrarse en la ruta DENODO_HOME\lib\vdp-jdbcdriver-core\ NOTA: El driver JDBC de Virtual DataPort utiliza las siguientes librerías externas: Jakarta commonscollections, Jakarta commons-pool, Jakarta commons-logging, Jakarta commons-lang y Apache log4j). Se proporciona también en la misma ruta una versión del driver que no incluye estas librerías (denodo-vdp-jdbcdriver-basic.jar). Esta versión puede utilizarse en aplicaciones que ya empleen para otros propósitos implementaciones compatibles de las mismas. Es importante tener en cuenta, sin embargo, que si no se utilizan versiones compatibles con las librerías incluídas con el producto, el driver JDBC podría no funcionar correctamente. En la ruta DENODO_HOME\samples\vdp\vdp-clients pueden encontrarse ejemplos de programas cliente que acceden a DataPort a través de JDBC (ver fichero README en la citada ruta). Acceso a través de JDBC 3 Virtual DataPort 4.0 3 Guía del Desarrollador ACCESO A TRAVÉS DE ODBC El driver JDBC de DataPort puede ser utilizado conjuntamente con un puente ODBC-JDBC para proporcionar acceso a DataPort desde clientes ODBC. En esta sección se indica cómo configurar para su uso con DataPort dos de los puentes ODBC-JDBC más utilizados del mercado: EasySoft ODBC-JDBC Gateway [11] (ha sido probado con éxito con la versión 1.3.1) y OpenLink ODBCJDBC bridge [12] (ha sido probado con éxito con la versión 5.2). 3.1 ACCESO MEDIANTE EASYSOFT ODBC-JDBC GATEWAY Los pasos para acceder a Virtual DataPort utilizando EasySoft ODBC-JDBC Gateway son: 1. Instalar EasySoft ODBC-JDBC Gateway (ver instrucciones en [11]). 2. Crear un nuevo origen de datos ODBC utilizando EasySoft ODBC-JDBC Gateway. a. b. c. d. 3. En el Panel de Control de Windows, ir a Herramientas Administrativas. Seleccionar ‘Orígenes de datos ODBC’. Hacer clic en el panel ‘DSN de Sistema’ y pulsar ‘Agregar’. Seleccionar EasySoft ODBC-JDBC Gateway y pulsar finalizar. Rellenar la siguiente pantalla como se indica a continuación (ver Figura 1): a. En los campos ‘User Name’ y ‘Password’ introducir el nombre de usuario y contraseña a utilizar en el servidor DataPort. b. Los campos ‘Driver Class’, ‘Classpath’ y ‘URL’ deben rellenarse con los valores correspondientes del driver JDBC de Virtual DataPort (ver sección 2): clase del driver JDBC y URL de acceso a un servidor DataPort. 4. Pulsar ‘Test’ para comprobar que la conexión al servidor DataPort puede ya realizarse. 5. Finalmente, pulsar ‘Ok’ para finalizar el proceso. Acceso a través de ODBC 4 Virtual DataPort 4.0 Guía del Desarrollador Figura 1 Configuración de EasySoft ODBC-JDBC Gateway 3.2 ACCESO MEDIANTE OPENLINK ODBC-JDBC BRIDGE Los pasos para acceder a Virtual DataPort utilizando OpenLink ODBC-JDBC Bridge (llamado ODBC-JDBC-Lite) son: 1. Instalar OpenLink ODBC-JDBC-Lite (ver instrucciones en [12]). a. b. 2. El proceso de instalación puede modificar el CLASSPATH actual, por lo que es conveniente comprobarlo tras la instalación. Asegurese de que el fichero jvm.dll (que habitualmente se encuentra en la ruta %JAVA_HOME%\jre\bin\server) se encuentra en el PATH del sistema. Crear un nuevo origen de datos ODBC utilizando OpenLink ODBC-JDBC-Lite. a. b. c. d. En el Panel de Control ir a Herramientas Administrativas. Seleccionar ‘Orígenes de datos ODBC’. Hacer clic en el panel ‘DSN de Sistema’ y pulsar ‘Agregar’. Seleccionar OpenLink ODBC-JDBC Lite y pulsar finalizar. 3. En la siguiente pantalla indicar un nombre y una descripción para el nuevo origen de datos y pulsar ‘Next’. 4. Rellenar la siguiente pantalla como sigue (ver Figura 2): Acceso a través de ODBC 5 Virtual DataPort 4.0 5. Guía del Desarrollador a. En los campos ‘Login ID’ y ‘Password’ introducir el nombre de usuario y contraseña a utilizar en el servidor DataPort. b. Los campos ‘JDBC Driver’ y ‘URL String’ deben rellenarse con los valores correspondientes del driver JDBC de Virtual DataPort (ver sección 2): clase del driver JDBC y URL de acceso a un servidor DataPort. Las siguientes pantallas del wizard permiten configurar diversas opciones de uso del origen de datos. En la primera pantalla es necesario marcar las siguientes opciones (ver Figura 3): a. b. ‘Drop Catalog Name from Database Metadata Calls’. ‘Disable support of quoted identifier’. 6. Una vez finalizada la configuración, se accederá a una pantalla que permite probar el nuevo origen de datos mediante el botón ‘Test Data Source’. 7. Si la comprobación es exitosa, pulsar ‘Finish’ para finalizar el proceso. Figura 2 Configuración de OpenLink ODBC-JDBC Lite (1) Acceso a través de ODBC 6 Virtual DataPort 4.0 Guía del Desarrollador Figura 3 Configuración de OpenLink ODBC-JDBC Lite (2) Acceso a través de ODBC 7 Virtual DataPort 4.0 4 Guía del Desarrollador ACCESO A TRAVÉS DE INTERFAZ WEB SERVICE 4.1 ACCESO A VISTAS PUBLICADAS COMO WEB SERVICES La herramienta de administración de Virtual DataPort permite publicar como un Web Service cualquier vista de datos definida en DataPort. Véase la Guía del Administrador [1] para obtener información sobre este proceso. Estas vistas publicadas sólo permiten realizar consultas sobre las vistas. Si se desea realizar operaciones de inserción, actualización y borrado de datos desde un entorno Web Service, es necesario utilizar el Web Service genérico descrito en el siguiente apartado. El proceso de publicación de la vista genera un fichero .wsdl y un fichero .war en la ruta especificada durante el proceso de publicación. El fichero .war contiene la implementación del Servicio Web y puede ser instalado en un contenedor web J2EE (como, por ejemplo, Apache Tomcat [8]). El fichero .wsdl [7] puede ser utilizado junto a una utilidad para la programación de Web Services (como, por ejemplo, las incluidas con Apache Axis [9]) para generar los stubs necesarios para implementar un programa cliente que acceda al Servicio Web. En la ruta DENODO_HOME\samples\vdp\vdp-clients pueden encontrarse ejemplos de programas cliente que acceden a una vista publicada como Servicio Web (en el fichero README de la citada ruta se explica cómo generar y publicar las vistas accedidas por los clientes de ejemplo). 4.2 INTERFAZ WEB SERVICE GENÉRICA Virtual DataPort también puede ser accedido a través de una interfaz Web Service genérica que permite ejecutar cualquier sentencia escrita en VQL. Dicho servicio web se encuentra empaquetado en el fichero denodo-vdpwsclient.war situado en la ruta DENODO_HOME\webapps\vdp-wsclient. En esta ruta se encuentra también el fichero WSDL [7] que define la interfaz de acceso al mismo. Pueden construirse dos tipos de aplicaciones cliente que utilizan la interfaz Web Service: • • Cliente sin estado. En este caso, el cliente establece una sesión con el servidor cada vez que emite una sentencia, cerrándola una vez terminada la ejecución. Cliente con estado. En este modo el cliente establece una sesión con el servidor, utiliza dicha sesión para emitir sentencias sobre el mismo y la cierra una vez que ha terminado. Esto permite evitar el coste de establecimiento de sesión para cada sentencia. Las siguientes subsecciones describen la manera en que las aplicaciones cliente de cada uno de estos tipos pueden ejecutar sentencias sobre un servidor DataPort. Posteriormente se describe el formato en el que se obtiene la respuesta a dichas sentencias. En la ruta DENODO_HOME\samples\vdp\vdp-clients pueden encontrarse ejemplos de ambos tipos de clientes (ver fichero README en la citada ruta). Acceso a través de Interfaz Web Service 8 Virtual DataPort 4.0 4.3 Guía del Desarrollador EJECUTANDO CONSULTAS EN CLIENTES SIN ESTADO Este tipo de clientes pueden utilizar directamente las operaciones executeQuery y executeQueryByConfig para ejecutar sentencias sobre un servidor DataPort. executeQuery recibe los siguientes parámetros de entrada: • • • • • • • Sentencia VQL a ejecutar sobre el servidor (elemento de tipo String). URI de conexión al servidor DataPort (String). Identificador de usuario con el que se realizará la conexión al servidor DataPort (String). Contraseña asociada al usuario especificado (String). Máximo tiempo (en milisegundos) que está dispuesto a esperar el cliente hasta que finalice la sentencia (elemento de tipo int). Este parámetro es opcional. Si no se indica (o recibe el valor 0), entonces se espera hasta que la ejecución finalice. Máximo tiempo (en milisegundos) que está dispuesto a esperar el cliente hasta que llegue un resultado parcial (int). Si se sobrepasa este tiempo, Virtual DataPort devuelve un resultado parcial vacío. Es opcional. Si no se especifica (o recibe el valor 0), el gestor devuelve todos los resultados conjuntamente al finalizar la ejecución de la sentencia. Número de resultados que conforman un resultado parcial (int). Si Virtual DataPort recibe este número de resultados, los reenviará al cliente aunque no se haya cumplido el tiempo máximo de resultado parcial. Los dos últimos parámetros permiten controlar cómo el servidor y el cliente intercambian los resultados devueltos por la sentencia. Permitir que el servidor devuelva al cliente los resultados a medida que los tiene disponibles puede influir en el tiempo de ejecución. Por su parte, la operación executeQueryByConfig permite la misma funcionalidad pero agrupando los parámetros de entrada de la siguiente forma: • • Un elemento de tipo String conteniendo la sentencia VQL a ejecutar sobre el servidor. Un elemento de tipo WSConfigVO que contiene la información necesaria para localizar el servidor y configurar la conexión con el mismo. Los elementos de tipo WSConfigVO están compuestos por los siguientes sub-elementos: • • • • • • 4.4 vdpURL. URI de conexión al servidor DataPort. vdpUser. Identificador de usuario con el que se realizará la conexión al servidor DataPort. vdpPassword. Contraseña asociada al usuario especificado. vdpQueryTimeout. Máximo tiempo (en milisegundos) que está dispuesto a esperar el cliente hasta que finalice la sentencia. Este parámetro es opcional. Si no se indica (o recibe el valor 0), entonces se espera hasta que la ejecución finalice. vdpChunkTimeout. Máximo tiempo (en milisegundos) que está dispuesto a esperar el cliente hasta que llegue un resultado parcial. Si se sobrepasa este tiempo, Virtual DataPort devuelve un resultado parcial vacío. Es opcional. Si no se especifica (o recibe el valor 0), el gestor devuelve todos los resultados conjuntamente al finalizar la ejecución de la sentencia. vdpChunkSize. Número de resultados que conforman un resultado parcial. Si Virtual DataPort recibe este número de resultados, los reenviará al cliente aunque no se haya cumplido el tiempo máximo de resultado parcial. EJECUTANDO CONSULTAS EN CLIENTES CON ESTADO En este modo el cliente establece una sesión con el servidor, utiliza dicha sesión para emitir sentencias sobre el mismo y la cierra una vez que ha terminado. Acceso a través de Interfaz Web Service 9 Virtual DataPort 4.0 Guía del Desarrollador • La operación openConnection permite establecer una sesión en el servidor DataPort. Recibe como parámetro un objeto WSConfigVO que contiene la información necesaria para localizar el servidor y configurar la conexión (ver sección anterior). Devuelve un identificador de sesión de tipo String. • Una vez establecida la sesión, puede utilizarse la operación executeQueryBySession para emitir sentencias sobre el servidor DataPort. Esta operación recibe dos parámetros de tipo String: o o • 4.5 La sentencia VQL a ejecutar. El identificador de sesión devuelto por openConnection. El cliente puede cerrar la sesión invocando la operación closeConnection, que recibe como parámetro el identificador de la sesión que se desea cerrar. PROCESANDO LOS RESULTADOS DE UNA SENTENCIA El método executeQuery devuelve un elemento de tipo WSTableVO, que encapsula toda la información devuelta por el proceso de ejecución de una sentencia. Consta de los siguientes sub-elementos: • error. Si no se produce un error toma el valor NULL. En caso de producirse contendrá un elemento de tipo WSErrorVO con dos sub-elementos: o errorCode. Contiene el código del error producido. Los códigos de error son los mismos utilizados por la API JAVA nativa de DataPort (Véase la documentación Javadoc [3] de la clase com.denodo.vdb.vdbinterface.common.clientResult.ErrorCodes, para el detalle de los códigos de error posibles)). o errorMessage. Contiene un mensaje explicativo del error producido. • schema. Elemento que contiene la información sobre el esquema de los resultados devueltos por la sentencia. Es un array de elementos de tipo WSColumnMetadataVO dónde cada elemento representa el nombre y el tipo de un atributo del resultado. • data. Contiene las tuplas obtenidas como respuesta a la sentencia. Es un array de elementos WSRowVO donde cada elemento representa una tupla del resultado de la sentencia. Cada elemento WSRowVO está compuesto a su vez por un array de elementos WSValueVO. Cada elemento WSValueVO representa el valor de un atributo de la tupla. Es importante recordar que DataPort utiliza un modelo de datos que soporta tipos compuestos de tipo registro y de tipo array. Los atributos compuestos de tipo array contienen siempre a nivel interno un registro que define el esquema de cada uno de los elementos del array (véase la Guía Avanzada de VQL [2] para más información). De esta forma los objetos WSValueVO, empleados para representar el valor de un atributo en una tupla del resultado, pueden ser de tres subtipos: • WSSimpleValueVO. Representa un valor de un tipo de dato simple. Existe un subtipo para cada uno de los tipos simples disponibles (WSIntValueVO, WSLongValueVO, WSFloatValueVO, WSDoubleValueVO, WSTextValueVO, WSBlobValueVO, WSBooleanValueVO, WSDateValueVO, WSTimeValueVO, WSMoneyValueVO, WSEnumeratedValueVO). Todos los subtipos definen un subelemento llamado value del tipo básico de dato XML correspondiente (int en el caso de WSIntValueVO, string en el caso de WSTextValueVO, etc.). • WSRegisterValueVO. Representa un valor de tipo de dato registro. Contiene un elemento fieldValues que es un array de elementos WSRegisterFieldValueVO. Cada elemento Acceso a través de Interfaz Web Service 10 Virtual DataPort 4.0 Guía del Desarrollador WSRegisterFieldValueVO representa el valor de uno de los campos del registro de datos y tiene dos subelementos: o name. String que contiene el nombre del campo. o value. elemento de tipo WSValueVO que contiene el valor del campo. • WSArrayValueVO. Representa un valor de tipo de dato array. Contiene un elemento values que es un array de elementos WSRegisterValueVO. Cada uno de ellos representa un valor del array de datos. Por su parte los elementos WSColumnMetadataVO, que representan la metainformación de esquema de un atributo devuelto en el resultado de la consulta, contienen dos subelementos: • columnName. Es un String que contiene el nombre del atributo. • columnType. Contiene un elemento de tipo WSTypeVO que describe el tipo del atributo. Los elementos WSTypeVO contienen un subelemento typeName que indica el nombre del tipo de dato. Los elementos WSTypeVO pueden ser también de varios subtipos: • WSSimpleTypeVO. Representa un atributo de un tipo simple (e.g. int, float, String,…) y no tiene subelementos adicionales. • WSRegisterTypeVO. Representa un atributo de tipo registro. Contiene un subelemento fields que es un array de elementos WSRegisterFieldMetadataVO. Cada uno de los elementos de este array contiene la metainformación de un campo del registro y está formado por dos subelementos: • name. Es un String que contiene el nombre del campo. • type. Contiene un elemento de tipo WSTypeVO que describe el tipo del campo. • WSArrayTypeVO. Representa un atributo de tipo array. Contiene un subelemento basetype de tipo WSRegisterTypeVO que describe el tipo de los elementos del array. • WSEnumeratedTypeVO. Representa un atributo de tipo enumerado. Contiene un subelemento values de tipo array de strings con un elemento para cada valor posible del tipo enumerado. Acceso a través de Interfaz Web Service 11 Virtual DataPort 4.0 5 Guía del Desarrollador API DE DESARROLLO DATAPORT Denodo Virtual DataPort también ofrece una API Java que permite ejecutar cualquier sentencia VQL sobre el servidor: sentencias de consulta, inserción, actualización y borrado, sentencias de creación de relaciones, de enumeración de elementos del catálogo, descripción de los mismos, etc. Existen dos etapas fundamentales en la realización de una sentencia sobre Virtual DataPort. En la primera se efectuará el envío de la sentencia al servidor y en la segunda se realizará la solicitud de los resultados y la recepción e interpretación de los mismos. Cuando el servidor recibe una petición de los resultados de una sentencia, éste devolverá todas las tuplas de dicho resultado que haya obtenido hasta ese momento (debe recordarse que en el contexto en el que funciona DataPort puede ser necesario acceder a la información de las fuentes en tiempo real a través de la red, lo cual puede causar que cuando se soliciten los resultados de una consulta, no todos estén aún disponibles). Opcionalmente se puede especificar el número máximo de elementos que el cliente desea recibir en cada resultado parcial (“chunk”). Esta última característica es aconsejable sólo en el caso de que la cantidad de resultados que tenga disponibles el servidor sea muy elevada, y la probabilidad de que el cliente llegue a utilizar todos estos resultados sea baja1. Por otro lado, existen varios tipos de mensajes que el cliente puede recibir como respuesta a una sentencia: • Mensajes conteniendo tuplas. Contienen parte o todas las tuplas que componen el resultado a la consulta. • Mensajes vacíos. Indican que el gestor no tiene más respuestas para la sentencia en ese momento. • Mensajes de fin. Indican que ha finalizado la ejecución de la sentencia en el servidor. • Mensajes de error. Se envía si se produce algún tipo de error durante la ejecución de la sentencia. Existen fundamentalmente cuatro clases relevantes a la hora de desarrollar un cliente para el gestor Virtual DataPort: • com.denodo.vdb.vdbinterface.common.datasource.VDBDataSource. Define la información necesaria para realizar la conexión contra el gestor • com.denodo.vdb.vdbinterface.common.connection.VDBConnection. Frontal contra el que los clientes realizan sus consultas. Representa una conexión con el servidor. • com.denodo.vdb.vdbinterface.common.clientResult.PartialResult. Engloba todos los tipos de mensaje que puede recibir el cliente. • com.denodo.vdb.vdbinterface.client.printer.Printer. Encargada de convertir los resultados emitidos por el servidor al formato deseado por un determinado tipo de cliente. 1 Usar esta opción puede incrementar las comunicaciones precisas entre cliente-servidor, por lo que se aconseja utilizarla sólo cuando sea necesaria API de Desarrollo DataPort 12 Virtual DataPort 4.0 5.1 Guía del Desarrollador CONFIGURACIÓN DE LAS APLICACIONES CLIENTE Los ficheros .jar necesarios en el CLASSPATH de las aplicaciones que deseen utilizar el API Cliente de Virtual DataPort son los siguientes: 1. En sistemas Windows: a. %DENODO_HOME%\lib\contrib\commons-collections.jar b. %DENODO_HOME%\lib\contrib\commons-logging.jar c. %DENODO_HOME%\lib\contrib\commons-pool.jar d. %DENODO_HOME%\lib\contrib\denodo-util.jar e. %DENODO_HOME%\lib\contrib\log4j.jar f. %DENODO_HOME%\lib\vdp-client-core\denodo-vdp-clientbase.jar g. %DENODO_HOME%\lib\vdp-client-core\denodo-vdp-clientext.jar 2. En sistemas Unix: a. $DENODO_HOME/lib/contrib/commons-collections.jar b. $DENODO_HOME/lib/contrib/commons-logging.jar c. $DENODO_HOME/lib/contrib/commons-pool.jar d. $DENODO_HOME/lib/contrib/denodo-util.jar e. $DENODO_HOME/lib/contrib/log4j.jar f. $DENODO_HOME/lib/vdp-client-core/denodo-vdp-clientbase.jar g. $DENODO_HOME/lib/vdp-client-core/denodo-vdp-client-ext.jar El paquete Apache Log4j API es el utilizado para gestión de trazas. Un fichero de configuración tendría que incluirse en el CLASSPATH para configurar los niveles de traza. Un fichero de ejemplo puede encontrarse en la ruta %DENODO_HOME%\conf\vdp-admin\log4j.xml. Los valores de algunos parámetros de configuración pueden modificarse mediante la utilización de un fichero de configuración de propiedades que debe incluirse en el CLASSPATH. Un fichero de ejemplo se encuentra en %DENODO_HOME%\conf\vdp-admin\VDBAdminConfiguration.properties. El siguiente apartado proporciona un ejemplo de un caso típico de ejecución de una consulta utilizando estas clases. Posteriormente, los siguientes apartados se ocupan, respectivamente, de la estructura general de la API y de cada una de las clases principales en detalle. Finalmente, se ofrecen varios ejemplos adicionales de utilización para realizar consultas sobre el servidor, tanto utilizando un enfoque síncrono (en el cual el cliente se queda bloqueado hasta recibir la respuesta completa del servidor) como utilizando un enfoque asíncrono (en el cual el cliente controla el flujo de la comunicación con el servidor y puede tratar los resultados de las consultas a medida que estos se van obteniendo, sin tener que esperar a que todos estén disponibles). 5.2 CASO PRÁCTICO Antes de comentar cada uno de los elementos de la API propietaria, se muestra un ejemplo de implementación de un cliente típico para la ejecución de sentencias de consulta contra Virtual DataPort. En este ejemplo se utilizará un enfoque asíncrono para el acceso a los datos del gestor (ver apartados 5.7.2 y 5.7.3 para más detalles sobre la creación de clientes asíncronos). import import import import import import import com.denodo.vdb.vdbinterface.client.printer.standard.StandardRowVO; com.denodo.vdb.vdbinterface.client.util.VQLStatement; com.denodo.vdb.vdbinterface.client.util.datasource.DataSourceLocator; com.denodo.vdb.vdbinterface.client.util.iterator.VDBResultSetIterator; com.denodo.vdb.vdbinterface.common.clientResult.MetadataField; com.denodo.vdb.vdbinterface.common.clientResult.vo.descriptions.type.TypeVO; com.denodo.vdb.vdbinterface.common.clientResult.vo.sentences.ValueVO; API de Desarrollo DataPort 13 Virtual DataPort 4.0 import import import import Guía del Desarrollador com.denodo.vdb.vdbinterface.common.connection.VDBConnection; com.denodo.vdb.vdbinterface.common.datasource.VDBDataSource; com.denodo.vdb.vdbinterface.common.connection.VDBConnectionException; java.util.*; public class IteratorDemoClient { public static void main(String args[]) { (1) (2) (3) (4) (5) String dsName = "clientDs"; String sentence = "SELECT * from view"; String url = args[0]; String login = args[1]; String passwd = args[2]; try { VDBDataSource tempds = new VDBDataSource(dsName, "testDS", url, login, passwd); DataSourceLocator.addDataSource(tempds); VDBDataSource ds = DataSourceLocator.getDataSource(dsName); VDBConnection vdpConnection = ds.getConnection(); VQLStatement vqlStatement = vdpConnection.createVQLStatement(); vqlStatement.setPreparedStatement(sentence); VDBResultSetIterator resultSetIterator= vqlStatement.executeQuery(); (6) // Iterate through results int numOfTuples = 0; while (resultSetIterator.hasNext()) { numOfTuples++; StandardRowVO tuple = (StandardRowVO)resultSetIterator.next(); System.out.print(numOfTuples + ". "); Map map = tuple.getValues(); Iterator it = map.keySet().iterator(); while(it.hasNext()) { ValueVO valueVO = (ValueVO)map.get(it.next()); System.out.print(valueVO + "\t"); } } (7) // Shows messages returned from the server System.out.println("Messages:"); System.out.println(resultSetIterator.getMessages()); (8) (9) // Shows the schema of the obtained data System.out.println("Schema:"); MetadataField[] schema = resultSetIterator.getSchema(); if (schema != null) { for (int i=0; i<schema.length;i++) { TypeVO type = schema[i].getType(); System.out.println(schema[i].getName() + “: “ + type); } System.out.println(""); } } catch(Exception e) { System.err.println("Error trying to access the server ... "); } finally { // The connection must be closed if( vdpConnection != null) { try { vdpConnection.close(); } catch (VDBConnectionException e) { System.err.println("Error closing connection ... "); e.printStackTrace(); } } API de Desarrollo DataPort 14 Virtual DataPort 4.0 Guía del Desarrollador } } } Figura 4 Código de un cliente iterador asíncrono A continuación se muestran los diferentes pasos realizados para la ejecución de la sentencia: (1) El primer paso es localizar la información del servidor contra el que realizar la conexión. Para esta tarea es posible o bien definir un objeto VDBDataSource a partir de los parámetros de conexión requeridos, o bien utilizar la clase DataSourceLocator para localizar esa información a partir de un nombre (ver 5.4.1). (2) Una vez localizada la metainformación necesaria para realizar una comunicación con el servidor, se obtiene una conexión para dar soporte a esa comunicación (ver 5.4.2). (3) A partir de la conexión se obtiene un objeto VQLStatement, que se prepara con la sentencia que se desea ejecutar. (4) En el caso de que la sentencia presentase algún parámetro variable, VQLStatement define un método para asociar valores concretos a los parámetros. (5) Se ejecuta la sentencia, obteniendo un iterador asíncrono con los resultados. El iterador asíncrono proporciona también información adicional relativa a la sentencia ejecutada (ver 5.7.3) (6) Una vez iniciada la ejecución de una consulta, hay que iterar sobre el objeto devuelto para obtener los resultados del gestor. Por defecto, utilizando este tipo de clientes, cada tupla se representa como un objeto StandardRowVO. (7) Es posible también obtener los mensajes informativos o de error enviados por el gestor. (8) El gestor también proporciona información relativa al esquema de los datos devueltos. (9) La conexión ha de cerrarse para que sea devuelta al pool de conexiones. 5.3 ESTRUCTURA GENERAL DE LA API DE DESARROLLO DATAPORT En la Figura 5 se muestra la estructura general de la API DataPort. El elemento esencial en las comunicaciones cliente-servidor es la clase VDBConnection. Para establecer una conexión contra el gestor es necesario obtener un objeto VDBDataSource a partir de la metainformación de conexión con el gestor. Utilizando la clase utilidad DataSourceLocator se simplifica la obtención del VDBDataSource para un gestor determinado a partir de un nombre. DataSourceLocator LocateDataSourceException DataSourceException VDBDataSource VDBConnectionException VDBConnection Printer <<use>> VQLStatement PartialResult <<use>> VDBResultSetIterator <<use>> StandardPrinter API de Desarrollo DataPort 15 Virtual DataPort 4.0 Guía del Desarrollador Figura 5 Estructura general de la API nativa La clase VDBConnection proporciona mecanismos directos de ejecución de sentencias contra el gestor, indicando además una clase (que extenderá Printer) para componer los resultados obtenidos. Para simplificar el proceso de ejecución de consultas puede utilizarse la clase VQLStatement, que permite: a) Utilizar una sentencia VQL conteniendo parámetros que serán posteriormente reescritos por unos valores concretos (de forma similar a un PreparedStatement de JDBC) b) Obtener un objeto que permite iterar, de forma asíncrona, sobre los resultados devueltos por el gestor. c) Utilizar una implementación de objeto componedor de resultados por defecto (llamado StandardPrinter, que devolverá cada tupla como un objeto de tipo StandardRowVO). Véase la sección 5.7.3 para más detalle. 5.4 5.4.1 DATASOURCES VIRTUAL DATAPORT DataSources Un DataSource identifica un origen de datos del que se puede extraer información. En la API de Virtual DataPort se representa mediante un objeto de la clase VDBDataSource. Para definir un VDBDataSource es necesario especificar: dataSourceName: Un nombre de DataSource. description: Una descripción del DataSource. dbURI: Una cadena de conexión contra el servidor (//host:port/databaseName). userName: Un nombre de usuario para realizar la conexión userPwd: Contraseña para el usuario. queryTimeout: Máximo tiempo (en milisegundos) que está dispuesto a esperar la aplicación usuaria hasta que finalice una sentencia. Este parámetro es opcional. Si no se indica (o recibe el valor 0), entonces se espera el tiempo necesario hasta que finalice la ejecución de la sentencia. chunkTimeout: Máximo tiempo (en milisegundos) que está dispuesto a esperar la aplicación usuaria hasta que llegue un resultado parcial. Si se sobrepasa este tiempo, Virtual DataPort devuelve un resultado parcial vacío. Es opcional. Si no se especifica (o recibe el valor 0), el gestor devuelve todos los resultados conjuntamente al finalizar la ejecución de la sentencia. chunkSize: Número de resultados que conforman un resultado parcial. Este factor, junto con el anterior, definen el comportamiento de devolución de resultados parciales del gestor. Si Virtual DataPort recibe este número de resultados, los reenviará al cliente aunque no se haya cumplido el tiempo máximo de resultado parcial. Si no se especifica (o recibe el valor 0), entonces todos los resultados de la consulta se devolverán en un único resultado parcial. A partir de un VDBDataSource pueden obtenerse los objetos conexión para el gestor asociado a ese origen de datos (método getConnection). Para reducir los tiempos en la creación de conexiones ante la ejecución de sentencias contra el gestor Virtual DataPort, la clase VDBDataSource permite especificar el uso de un pool de conexiones reutilizables. En el caso de que se desee utilizar el pool, puede indicarse su configuración con la clase PoolConfig, indicando: initSize: Número de conexiones con las que se desea inicializar el pool. Se establecen y crean un número de conexiones en estado "idle", listas para ser usadas. maxActive: Número máximo de conexiones activas que puede gestionar el pool al mismo tiempo. (cero implica sin límite) maxIdle: Número máximo de conexiones activas que pueden permanecer ociosas en el pool sin necesidad de que se desocupen conexiones adicionales. (cero implica sin límite) Para más información, ver documentación Javadoc de la API del desarrollador [3]. API de Desarrollo DataPort 16 Virtual DataPort 4.0 5.4.1.1 Guía del Desarrollador Definición y Localización de DataSources Para simplificar la creación de clientes, Virtual DataPort proporciona una clase utilidad, com.denodo.vdb.vdbinterface.client.util.datasource.DataSourceLocator, que permite obtener un VDBDataSource para un servidor DataPort a partir de su nombre. VDBDataSource ds = DataSourceLocator.getDataSource(DATASOURCE_NAME); Para que sea posible la localización del VDBDataSource, es necesario haberlo registrado previamente. Hay tres formas de hacer esto: • La clase DataSourceLocator define un método addDataSource que permite registrar objetos VDBDataSource creados directamente por la aplicación. • El sistema registrará automáticamente aquellos DataSources cuyas propiedades estén accesibles en un fichero .properties de nombre ConfigurationParameters.properties (o cualquier otro si dicho nombre se especifica con la propiedad de la máquina virtual –DconfFile). • El sistema registrará automáticamente aquellos DataSources cuyas propiedades estén accesibles a través de JNDI [6] en el formato vdbDataSource.<dataSourceName>.<propertyName>. En particular las propiedades deben de estar exportadas en un servidor JNDI colgando de la ruta java:comp/env/. Esto permite, por ejemplo, que los DataSources puedan ser definidos en el fichero web.xml de una aplicación web J2EE. En la Figura 6 se muestra el nombre de todas las propiedades junto con algunos valores típicos. # Datasource configuration used by provided clients # to connect to Virtual DataPort. vdbDataSource.clientDs.dbURI=//localhost:9999/vdb vdbDataSource.clientDs.userName=demo vdbDataSource.clientDs.userPwd=demo vdbDataSource.clientDs.queryTimeout=80000 vdbDataSource.clientDs.chunkTimeout=90000 vdbDataSource.clientDs.chunkSize=100 # Pool parameters. Non Mandatory. vdbDataSource.clientDs.initSize=0 vdbDataSource.clientDs.maxActive=30 vdbDataSource.clientDs.maxIdle=20 # Uncoment to disable pool. #vdbDataSource.clientDs.poolEnabled=false Figura 6 Configuración de un VDBDataSource 5.4.2 Conexiones Virtual DataPort La clase VDBConnection encapsula una conexión contra un servidor Virtual DataPort. Representa un proxy a “Virtual DataPort” (véase para más información el patrón de diseño Proxy [4]). Permite que los clientes dispongan de un frontal sencillo para acceder al gestor, delegando en él las tareas de comunicación, y ocultando la tecnología utilizada para la comunicación (Java RMI). Los principales métodos de esta clase son los siguientes: • exec(query,printer): método que ejecuta una sentencia o query sobre el servidor. El segundo argumento es una clase Printer. Esta clase, que se comentará con detalle más adelante, es la que formateará los resultados recibidos desde el servidor de acuerdo con las necesidades particulares de cada cliente. API de Desarrollo DataPort 17 Virtual DataPort 4.0 Guía del Desarrollador • execDeferred(query,printer): este método también ejecuta una sentencia sobre Virtual DataPort. La diferencia con el método exec(query,printer) está en que este último ejecuta la sentencia e internamente solicita los resultados al gestor hasta que la consulta está completamente finalizada, mientras que execDeferred(query,printer), simplemente ejecuta la sentencia y retorna el control al cliente sin haber solicitado ninguno de los resultados. Para solicitar los resultados al servidor, este método se debe combinar con getNextResult(printer) ó getNextResult(printer,n). • getNextResult(printer): Este método obtiene el siguiente “chunk” de resultados (o ‘resultado parcial’) del servidor. De nuevo la clase Printer será la encargada de ensamblar o componer el resultado que espera recibir el cliente a partir de los datos que se reciben del servidor. • getNextResult(printer,n): Método similar al anterior excepto en que además se especifica el número máximo de resultados que espera recibir el cliente. • openClientConnection(user,pwd,url): Método que abre una conexión con Virtual DataPort. Si alguno de los parámetros es incorrecto, (e.g. la contraseña es incorrecta, el usuario no existe o no se puede conectar con el servidor), este método devolverá una excepción. La utilidad de está función es para clientes que deseen validar de forma rápida y sencilla las credenciales que introduce el usuario por teclado (e.g. para comprobar un proceso de login). • cancel(): Método que detiene la ejecución de una consulta. • getConnectionTime(): Permite obtener el tiempo de establecimiento de conexión. • isCached(): Permite saber si la conexión actual ha sido obtenida de un pool de conexiones (está siendo reutilizada) o si se ha creado una nueva. • close(): Método que finaliza una conexión contra un gestor (o la devuelve al pool, si se ha definido). • createVQLStatement(): Método que permite crear un objeto VQLStatement asociado a este objeto conexión. Esta clase permite ejecutar sentencias parametrizadas y obtener los resultados utilizando un iterador asíncrono (ver apartado 5.7.1) Existen otros métodos en esta clase que son útiles para obtener y establecer los parámetros de configuración del gestor Virtual DataPort, es decir, métodos get() y set() para el nombre del usuario, la contraseña, el dbURI, el queryTimeout, el chunkTimeout y el chunkSize. 5.5 PROTOCOLO DE COMUNICACIONES CON EL SERVIDOR Otro elemento relevante para la realización de una comunicación con el servidor es la jerarquía de resultados parciales (‘chunks’ recibidos desde el servidor como respuesta a la consulta) que encapsulan los datos intercambiados. La superclase de todos estos resultados es la clase PartialResult. API de Desarrollo DataPort 18 Virtual DataPort 4.0 Guía del Desarrollador PartialResult <<setter>>+setDecorated( result : PartialResult )-decorated : void <<getter>>+getDecorated() : PartialResult +accept( visitor : PartialResultVisitor ) : void Figura 7 Superclase de todos los resultados parciales Los tipos de resultados parciales que puede recibir un cliente son los siguientes: • EmptyResult: resultado parcial sin ningún tipo de datos. Lo emite el servidor cuando el cliente solicita los resultados antes de que se disponga de algún resultado todavía no entregado. • EndResult: resultado parcial que emite el servidor cuando una consulta ha finalizado. Después de recibir un mensaje de este tipo, el cliente no debe realizar otra solicitud de datos al servidor. • ErrorResult: resultado parcial emitido por el servidor cuando se produce un error en la ejecución de una consulta. Se trata también de un mensaje de finalización. Después de recibir un mensaje de este tipo, el cliente no debe de realizar otra solicitud de datos al servidor. • MessageResult: mensaje informativo emitido por el servidor. No es un mensaje de finalización y en su interior contiene información sobre la ejecución de la consulta. Desde la versión 4.0, se incluye un nuevo subtipo de mensaje llamado WarningResult, que notifica incidencias ocurridas durante la ejecución de una sentencia. • MetadataResult: mensaje con los metadatos de una sentencia de tipo SELECT, QUERY WRAPPER o CALL emitida sobre Virtual DataPort. Entre la metainformación de un conjunto de resultados se encuentra el nombre de cada columna con su tipo (Objeto MetadataField). • ChunkResult: mensaje que contiene tuplas resultado de una sentencia de tipo SELECT, QUERY WRAPPER o CALL emitida sobre el gestor. Por otra parte, el protocolo establecido entre el cliente y el servidor para el envío y recepción de los mensajes sigue las siguientes reglas: • El número de fragmentos o resultados parciales en los que puede llegar el resultado de una consulta no es fijo. • La consulta se termina cuando llega un mensaje de finalización o un mensaje de error. • Un mensaje puede encapsular a otro mensaje (según el patrón de diseño decorador [4]). Esto permite minimizar el número de comunicaciones entre el cliente y el servidor. Esta regla no es válida para los mensajes de error y mensajes vacíos, que nunca pueden tener ningún otro resultado parcial en su interior. API de Desarrollo DataPort 19 Virtual DataPort 4.0 Guía del Desarrollador • Si se trata de una sentencia de consulta sobre una vista (SELECT), consulta sobre un wrapper (QUERY WRAPPER) o invocación de procedimiento almacenado (CALL), el primer mensaje que no sea vacío será el mensaje con los metadatos referentes a la consulta efectuada (fundamentalmente, se indicarán las columnas que componen el resultado junto con sus tipos de datos asociados). “Decorando” el mensaje de metainformación se envía otro mensaje informativo (MessageResult) que encapsula el mapa de propiedades de internacionalización (I18nVO) de la consulta. En la Guía avanzada de VQL [2] puede encontrarse el listado de nombres de propiedades de internacionalización que encapsula. • Un mensaje de error puede contener un mensaje explicativo y un código que indique el tipo de error que se ha producido (e.g. un error interno dentro del gestor, un error del usuario en la introducción de los datos, etc. Véase la documentación Javadoc [3] de la clase com.denodo.vdb.vdbinterface.common.clientResult.ErrorCodes, para el detalle de los códigos de error posibles). En el siguiente diagrama se muestra de forma más detallada la jerarquía de los resultados parciales, con todos los tipos de mensajes que puede enviar el servidor como resultado de una consulta. PartialResult ChunkResult EmptyResult EndResult ErrorResult <<use>> MessageResult MetadataResult WarningResult 1..* ErrorCodes MetadataField -_name : String = null +NO_ERROR : int = 0{readOnly} -_type : TypeVO = null +SYNTAX_ERROR : int = 1{readOnly} ... +OBJECT_NOT_FOUND : int = 2{readOnly} +SEMANTIC_ERROR : int = 3{readOnly} <<setter>>+setName( value : String ) : void +INTERNAL_ERROR : int = 4{readOnly} <<getter>>+getName() : String +STORE_ERROR : int = 5{readOnly} <<setter>>+setType( value : TypeVO ) : void +UNABLE_TO_CONNECT : int = 6{readOnly} : TypeVO +ERROR_GETTING_RESULTS : int = 7{readOnly} <<getter>>+getType() ... +DEPENDS_ON_DELETE : int = 8{readOnly} +NOTSUPPORTED_OPERATION : int = 9{readOnly} +DUPLICATE_OBJECT : int = 10{readOnly} +INSUFFICIENT_PRIVILEGES : int = 11{readOnly} +LICENSE_ERROR : int = 12{readOnly} +SYNCHRONIZE_ERROR : int = 13{readOnly} +UNKNOWN : int = 99{readOnly} ... Figura 8 Jerarquía de resultados parciales Como ya se ha comentado, cuando se emite una sentencia de consulta (SELECT) sobre Virtual DataPort, el cliente obtiene un ChunkResult. Dentro de un objeto ChunkResult, hay un conjunto de tuplas (ChunkRow), y cada tupla contiene un conjunto de valores (ValueVO). Un valor puede ser simple (SimpleVO), de tipo array (ArrayVO) o de tipo registro (RegisterVO). Los valores simples (SimpleVO) pueden ser de cada uno de los tipos de dato soportados (ver [2]). Por ejemplo: enteros (IntVO), fecha (DateVO), etc. A su vez, un MessageResult, puede tener encapsulado además de un mensaje informativo, un objeto de tipo MessageVO que puede ser de tipo descriptivo (VDBObject) o de tipo listado (VDBObjectList). Desde la versión 4.0, se añade un subtipo WarningResult que contiene notificaciones sobre eventos producidos durante la ejecución. API de Desarrollo DataPort 20 Virtual DataPort 4.0 Guía del Desarrollador Existe un objeto VDBObject por cada elemento del catálogo de Virtual DataPort (wrapper, vista base o derivada, procedimiento almacenado, operador, tipo, etc. Véase [1] para más detalle sobre estos elementos) cuya descripción se puede solicitar al gestor. También existe un objeto VDBObject para representar la traza de ejecución de una sentencia (ver sección 5.7.4). Para conocer en detalle la estructura de los resultados parciales que puede recibir un cliente, se puede consultar la documentación Javadoc [3]. 5.6 PROCESAMIENTO DE RESULTADOS DE UNA CONSULTA Las necesidades de las distintas aplicaciones cliente en cuanto al formato deseado para recibir los resultados de una consulta sobre DataPort pueden diferir mucho. Por ello la solución adoptada en DataPort separa la lógica de comunicación, de la cual se encarga el objeto conexión, de la lógica de transformación y ensamblado de los resultados parciales que van siendo devueltos desde el servidor, de la cual se encargará una nueva jerarquía de clases que tienen como superclase a la clase Printer. De este modo, con la aparición de un nuevo tipo de cliente, sólo se tendrá que proporcionar una nueva subclase de Printer, que transforme los resultados que llegan desde el servidor. PartialResultVisitor +format( result : EmptyResult ) : void +format( result : EndResult ) : void +format( result : ChunkResult ) : void +format( result : MetadataResult ) : void +format( result : MessageResult ) : void +format( result : WarningResult ) : void +format( result : ErrorResult ) : void Printer <<getter>>+isFinished() : boolean <<setter>>+setFinished( flag : boolean ) : v +format( result : EmptyResult ) : void +format( result : EndResult ) : void +format( result : ChunkResult ) : void +format( result : MetadataResult ) : void +format( result : MessageResult ) : void +format( result : WarningResult ) : void +format( result : ErrorResult ) : void +format( exception : Exception ) : Object <<getter>>+getClientResult() : Object +log( message : String ) : void <<getter>>+getErrorCode() : int +reset() : void Figura 9 Estructura general de un ensamblador de resultados Un desarrollador puede crear sus propias clases Printer o puede utilizar algunas de las implementaciones que Virtual DataPort incluye y que son adecuadas para la mayoría de casos (paquete com.denodo.vdb.vdbinterface.client.printer): API de Desarrollo DataPort 21 Virtual DataPort 4.0 Guía del Desarrollador • ConsolePrinter. Formatea textualmente los resultados y los envía a un stream de salida (como, por ejemplo a la salida estándar). • standard.StandardPrinter: Clase Printer que ensambla los resultados de cualquier tipo de sentencia emitida sobre Virtual DataPort. Los resultados, así como el plan de ejecución asociado, son devueltos encapsulados en el objeto standard.StandardTableVO. Un objeto StandardTableVO define métodos para obtener las tuplas de resultados del gestor (getRows()), el esquema de los datos devueltos (getSchema()), los mensajes incluidos en la respuesta como un String (getMessage()) y la traza de eventos asociada a la consulta (getTrace()). Cada tupla del resultado será devuelta como un objeto StandardRowVO que contendrá un campo por cada atributo presente en la respuesta. Consúltese la documentación Javadoc [3] para obtener información detallada sobre la estructura de las clases Printer predefinidas. 5.6.1 Implementación de Nuevos Ensambladores de Resultados Para aquellos casos en los que se desee añadir una nueva implementación para Printer, en la Figura 9 se muestran los métodos que deben implementar todas las instancias de la misma. En primer lugar debe definirse cómo formatear todos los tipos de mensajes que se pueden generar: esto se realiza implementando una serie de métodos de la interfaz PartialResultVisitor. También deben implementarse o redefinirse los siguientes métodos de la clase Printer: • isFinished(): método que devuelve verdadero (true) cuando el printer determina que la consulta ha finalizado. Como el printer es quien procesa los mensajes recibidos por el cliente, es éste quien debe decidir cuándo una consulta ha finalizado. Típicamente, esto sucede cuando llega un mensaje de error o un mensaje de finalización; aunque nada impide que un printer, por las características intrínsecas de un tipo de cliente, finalice prematuramente una consulta. • setFinished(boolean): método que permite finalizar manualmente una consulta. Un ejemplo de uso de esta característica podría ser cuando se pulsa un botón de cancelación en una ventana de una herramienta gráfica. • getClientResult(): método que devuelve el resultado de la consulta adaptado al formato que espera recibir el cliente. • log(String): método que permite hacer un log de las operaciones que va realizando el cliente. Cada subclase debe redefinir este método para redirigir los mensajes a la salida adecuada según el cliente. Por ejemplo, un cliente de línea de comandos debe redirigir estos mensajes a la salida estándar y un cliente gráfico debe redirigir los mensajes a una ventana gráfica. • getErrorCode(): si se produce un error, con este método se podrá extraer el tipo de error que se ha producido durante la ejecución de la consulta. Véase la documentación Javadoc [3] para obtener una lista de los códigos de error posibles, con su correspondiente descripción. • reset(): método que restaura el estado de la clase para finalizar una consulta e iniciar una nueva. API de Desarrollo DataPort 22 Virtual DataPort 4.0 • Guía del Desarrollador format(Exception): método para formatear los errores no controlados y por lo tanto aquellos que generan una excepción. Las excepciones también pueden necesitar un procesado diferente según el tipo de cliente. Es el printer quien debe encargarse de su procesado. A modo de ejemplo, la Figura 10 muestra la implementación del Printer predefinido ConsolePrinter, que formatea textualmente los resultados obtenidos y los escribe en un stream de salida (por defecto, la salida estándar). // Import needed for the log import java.io.PrintStream; import java.util.Iterator; import com.denodo.vdb.vdbinterface.client.printer.Printer; import com.denodo.vdb.vdbinterface.common.clientResult.ChunkResult; import com.denodo.vdb.vdbinterface.common.clientResult.EmptyResult; import com.denodo.vdb.vdbinterface.common.clientResult.EndResult; import com.denodo.vdb.vdbinterface.common.clientResult.ErrorResult; import com.denodo.vdb.vdbinterface.common.clientResult.MessageResult; import com.denodo.vdb.vdbinterface.common.clientResult.MetadataField; import com.denodo.vdb.vdbinterface.common.clientResult.MetadataResult; import com.denodo.vdb.vdbinterface.common.clientResult.VQLException; import com.denodo.vdb.vdbinterface.common.clientResult.vo.sentences.ChunkRow; import com.denodo.vdb.vdbinterface.common.clientResult.vo.sentences.ValueVO; /** * Asynchronous printer, receives a query result and forward it to a * PrintStream like the standar output or a file. */ public class ConsolePrinter extends Printer{ /** * The Stream the output will be forwarded to. Standard output by * default. */ private PrintStream _out=System.out; /** * Temporal metadata container. */ private String _metadata=null; /** * Empty Constructor. */ public ConsolePrinter() {} /** * Constructor that replaces the output stream the printer will use. * @param out New output stream. */ public ConsolePrinter(PrintStream out){_out=out;} /** * Defins the behavior when an empty result arrives. * @param result Empty message. */ public void format(EmptyResult result) throws VQLException { log("Info: received new empty result"); } /** * Defines the behavior when a finalization message arrives from * the server. API de Desarrollo DataPort 23 Virtual DataPort 4.0 Guía del Desarrollador * @param result Finalization message. */ public void format(EndResult result) throws VQLException { _out.println("_________________________"); log("Info: received end result"); setFinished(true); if (result.getDecorated()!=null) { result.getDecorated().accept(this); } } /** * Defines the behavior when a chunk arrives from the server. * @param result Partial result message. */ public void format(ChunkResult result) throws VQLException { log("Info: received new chunk result"); Iterator chunkRows=result.getResults().iterator(); while (chunkRows.hasNext()) { _out.print("| "); ChunkRow singleRow=(ChunkRow)chunkRows.next(); Iterator values=singleRow.getValues().iterator(); while (values.hasNext()) { _out.print((ValueVO)values.next() + " | "); } _out.println(); } if (result.getDecorated()!=null) { result.getDecorated().accept(this); } } /** * Defines the behavior when a message with the metadata from a query * arrives from the server. * @param result Message with the metadata. */ public void format(MetadataResult result) throws VQLException { log("Info: received metadata result"); _metadata=new String("[ "); Iterator fields=result.getFields().iterator(); while (fields.hasNext()) { MetadataField field=(MetadataField)fields.next(); _metadata=_metadata+(field.getName()) + " "; } _metadata=_metadata+"]\n"; log(_metadata); if (result.getDecorated()!=null) { result.getDecorated().accept(this); } } /** * Defines the behavior when an informative * message arrives from the server. * @param result Informative message. */ public void format(MessageResult result) throws VQLException { log("Info: received message result"); if(result.getMessage()!=null)log(result.getMessage()); if(result.getObject()!=null)log(result.getObject().toString()); if (result.getDecorated()!=null){ result.getDecorated().accept(this); API de Desarrollo DataPort 24 Virtual DataPort 4.0 Guía del Desarrollador } } /** * Defines the behavior when an error message arrives. * @param result Error message. */ public void format(ErrorResult result) throws VQLException { setFinished(true); log(result.getMessage()); if (result.getDecorated()!=null) { result.getDecorated().accept(this); } } /** * Defines the behavior when an exception is generated during a query. * @param exception gemerated exception. */ public Object format(Exception exception) throws VQLException{ log("error: "+ exception.getMessage()); return null; } /** * Forwards the message to the output stream. */ public void log(String message){ _out.println(message); } } Figura 10 Código de ConsolePrinter 5.7 MODOS DE FUNCIONAMIENTO GENERAL DE UN CLIENTE En este apartado se va a mostrar el método general de creación de un nuevo cliente para el gestor Virtual DataPort utilizando la API JAVA nativa. Existen tres formas típicas de crear un cliente, dependiendo del nivel de control que se desee tener sobre el flujo de datos con el servidor tras el envío de la consulta: • • • Cliente síncrono. En este caso, el cliente, tras emitir una consulta, queda bloqueado hasta que se obtienen todos los resultados de la misma. Cliente asíncrono. Este modo permite un mayor control de los intercambios de datos con el servidor. Está especialmente indicado para poder procesar los resultados de una consulta a medida que están disponibles, sin necesidad de esperar a que la consulta haya terminado. Cliente iterador asíncrono. Es un caso particular de cliente asíncrono que se ofrece ya implementado por conveniencia para el desarrollador. Es la opción recomendada para la mayor parte de aplicaciones cliente. Los detalles de cada uno de estos tipos de cliente se describen en los siguientes subapartados. API de Desarrollo DataPort 25 Virtual DataPort 4.0 5.7.1 Guía del Desarrollador Clientes Síncronos – Requieren control sobre el flujo de datos En este caso, el cliente, tras emitir una consulta, queda bloqueado hasta que se obtienen todos los resultados de la misma en un único bloque. La creación de este tipo de clientes es muy sencilla y sólo se deben realizar las dos acciones siguientes: a) Implementar el Printer que ensamblará los resultados o utilizar alguno de los ya proporcionados con Virtual DataPort. Ver sección 5.6. b) Invocar el método exec(query,printer) de la clase VDBConnection. Este método se encargará de solicitar todos los datos al gestor hasta que la transmisión haya concluido. El resultado que devuelve esta función es el objeto que ha compuesto la clase Printer a medida que los resultados parciales han ido llegando desde el servidor. Un aspecto importante es que la aplicación no retorna el control hasta que el proceso de comunicación ha finalizado completamente. Un ejemplo de los pasos a seguir para ejecutar una sentencia sobre Virtual DataPort siguiendo este método se muestra en la Figura 11 (se utiliza el Printer predefinido ConsolePrinter): // Sentence to execute against the server String sentence = ”SELECT * FROM @ecb”; // The printer for assembling query results must be chosen Printer assembler = new ConsolePrinter(System.out); try { // We get a connection to Virtual DataPort VDBDataSource ds = DataSourceLocator.getDataSource("clientDs"); VDBConnection connection = ds.getConnection(); Object result = null; // Query is executed. The format of the final result is determined // by the printer. result = connection.exec(sentence, assembler) ; } catch(Exception ex){ ex.printStackTrace(); } finally { // The connection must be closed. if( vdpConnection != null) { try { vdpConnection.close(); } catch (VDBConnectionException e) { System.err.println("Error closing connection ... "); e.printStackTrace(); } } } Figura 11 Código de un cliente síncrono 5.7.2 Clientes Asíncronos – No requieren control sobre el flujo de datos El modo asíncrono permite un control mayor sobre el intercambio de datos con el servidor. Una de sus principales ventajas es que permite a la aplicación cliente obtener los resultados de una consulta a medida que estos van estando disponibles, sin necesidad de esperar a que la consulta haya terminado. API de Desarrollo DataPort 26 Virtual DataPort 4.0 Guía del Desarrollador En el modo asíncrono, la aplicación cliente solicitará al servidor nuevos datos cuando lo crea conveniente, no quedando bloqueado tras la emisión de la consulta. También podrá especificar el número máximo de resultados de la consulta que espera recibir en cada una de las transmisiones que el servidor realice. Los pasos para la construcción de este tipo de clientes son los siguientes: a) Realizar la implementación de la clase o clases Printer que ensamblarán los resultados (ahora se realizarán varias llamadas a métodos de la clase VDBConnection y podría darse el caso de que en cada llamada se usara un Printer diferente). También puede utilizarse alguno de los Printers ya incluidos con DataPort para evitar crear uno nuevo. b) Invocar el método de la clase VDBConnection execDeferred(query,printer). Este método sólo envía la consulta al gestor, sin solicitarle el envío de ningún resultado y retornando el control al cliente. En este caso la clase Printer que se pasa como argumento es necesaria sólo por si se produce un error en el envío de la consulta. c) Realizar la invocación de los métodos getNextResult(printer) o getNextResult(printer,n) de la clase VDBConnection para realizar las peticiones de datos que se necesiten. En este caso, el cliente va a trabajar con mayor control sobre el intercambio de datos, por lo que debe conocer las reglas de envío de resultados parciales que usa el servidor, y que están comentadas en apartados anteriores de este documento. En la Figura 12 se muestra un ejemplo de implementación de este tipo de clientes. // The sentence we want to execute String sentence = ”SELECT * FROM view”; Printer printer = new StandardPrinter(); try { VDBDataSource ds = DataSourceLocator.getDataSource("clientDs"); VDBConnection connection = ds.getConnection(); connection.execDeferred(sentence, printer); while (!printer.isFinished()) { // Each call returns the same result object but // it contains more elements each time StandardTableVO standardVO = (StandardTableVO) vdpConnection .getNextResult(printer, 2); } } catch(Exception ex){ ex.printStackTrace(); } finally { // The connection must be closed. if( vdpConnection != null) { try { vdpConnection.close(); } catch (VDBConnectionException e) { System.err.println("Error closing connection ... "); e.printStackTrace(); } } } Figura 12 Código de un cliente asíncrono API de Desarrollo DataPort 27 Virtual DataPort 4.0 5.7.3 Guía del Desarrollador Cliente Iterador Asíncrono Este tipo de cliente es una implementación particular de cliente asíncrono que se proporciona por conveniencia para el desarrollador. Es implementado por la clase com.denodo.vdb.vdbinterface.client.util.iterator.VDBResultSetIterator. Este cliente obtiene los resultados de forma asíncrona y los compone a través del ensamblador StandardPrinter. Internamente genera un objeto StandardTableVO con los resultados. La ejecución de una consulta con este cliente se puede realizar a través del objeto VQLStatement creado a partir de una conexión a un gestor Virtual DataPort. Un objeto VQLStatement permite la ejecución de sentencias VQL parametrizadas, de forma análoga a la clase PreparedStatement de la API JDBC [5] de Java. Las sentencias VQL deben incluir los parámetros con la sintaxis ?<parameterIdentifier> y se asocian al VQLStatement con el método setPreparedStatement(String). Si los parámetros se sustituyen indicando una lista (método setParameters(List)), entonces en la sentencia deben de aparecer de forma numerada, empezando en 1. Por ejemplo: SELECT * FROM xyz WHERE a > ?1 and b < ?2. Si se utiliza la opción de enviar los parámetros en un mapa (pares nombre,valor, método setParameters(Map)), entonces se sustituirán por las ocurrencias ?<parameterIdentifier> en la sentencia. Para ejecutar la sentencia se utiliza el método executeQuery() obteniendo un objeto VDBResultSetIterator que implementa la interfaz Java java.util.Iterator. Cada tupla devuelta se representa mediante un objeto StandardRowVO con un campo por cada atributo del esquema de salida de la consulta. Es necesario utilizar el método hasNext() del objeto VDBResultSetIterator cuando se desee iterar sobre los resultados. Esto es debido a que la espera asíncrona se encuentra implementada en este método, de manera que espera hasta que haya datos disponibles. El método next() de la misma clase lanza una excepción NoSuchElementException si no hay datos disponibles, aunque estén llegando; por ello es necesario utilizar antes el método hasNext(). En la Figura 4 se puede ver un ejemplo de implementación completa de este tipo de clientes. 5.7.4 Información sobre la Traza de Ejecución de Sentencias La traza de una sentencia permite un examen detallado de su plan de ejecución. Dicho plan se presenta al administrador en la forma de un árbol, donde cada nodo representa una vista intermedia involucrada en la ejecución de la consulta o un acceso a una fuente a través de un wrapper. Para cada nodo en el árbol de ejecución de la consulta, se muestran sus parámetros más relevantes. Entre estos parámetros se encuentran: • Tipo de nodo. Si el nodo es una vista, indica el tipo de vista de que se trata (vista base, unión, join, proyección,…). Si se trata de un acceso a una fuente (wrapper), se indica el tipo de fuente (JDBC, Web Service, Web, …). • Tiempo de ejecución. Tiempo invertido en la ejecución completa del nodo y de todos sus hijos. • Momento de inicio. Momento exacto en el que se inicia el procesamiento del nodo en el plan de ejecución. • Momento de fin de la consulta. Momento exacto en el que se finaliza el procesamiento del nodo (y de todos sus hijos) en el plan de ejecución. • Tiempo de obtención de primera tupla de resultados. Tiempo transcurrido hasta que el nodo recibió la primera tupla a procesar. API de Desarrollo DataPort 28 Virtual DataPort 4.0 • • • Guía del Desarrollador Número de tuplas procesadas. Número de tuplas procesadas por el nodo. Estado. Indica si el nodo se ejecutó correctamente o se produjo algún error. Parámetros avanzados. Proporcionan más detalle sobre cada tipo de nodo. Por ejemplo: o En el caso de nodos de tipo wrapper, se indican las subconsultas exactas ejecutadas sobre cada fuente de datos, así como los datos de conexión utilizados para acceder a cada una de ellas. o Para cada nodo de tipo vista, se indica si se ha utilizado o no la cache, si ha sido necesario realizar swapping, si existen reglas de reescritura asociadas a la misma, etc. Una de las principales utilidades de la funcionalidad de traza es la depuración en el caso de que se produzcan condiciones de error. Desde la herramienta de administración de Virtual DataPort se puede acceder a estos datos de manera gráfica (ver la Guía de Administración de Virtual DataPort [1] para más información). El API de Virtual DataPort permite acceder a la información de la traza del plan de ejecución. Para ello, es necesario obtener una instancia de la clase com.denodo.vdb.vdbinterface.common.clientResult.vo.descriptions.queryp lan.ExecutionPlanVO mediante la utilización del método public ExecutionPlanVO getExecutionPlan() de la clase com.denodo.vdb.vdbinterface.client.util.iterator.VDBResultSetIterator. Esta clase dispone de una serie de métodos de acceso a información del plan de ejecución, como el nombre de la vista a la que se accede, el número de tuplas que esa vista concreta ha devuelto, si la vista ha completado su proceso de extracción de datos, etc., tal y como se ha comentado anteriormente (para más información, véase la documentación JavaDoc [3]). El método List getSubExecutionPlans() devuelve los nodos hijo del nodo padre correspondiente, lo cuál permite ir navegando por la estructura arbórea del plan de ejecución hasta los propios dataSources (que ya no tienen hijos, devolviendo este método el valor null). Es importante recordar que para que el método getExecutionPlan() devuelva una instancia de la clase que encapsula el plan de ejecución, la consulta VQL ha de realizarse añadiendo la cláusula “TRACE” (p.e. “select * from view trace”). Dependiendo del tipo de nodo (vista, wrapper, procedimiento almacenado, …), existen diferentes subclases de ExecutionPlanVO, con acceso a parámetros de información adicionales (para más información, ver la descripción de las clases en la documentación JavaDoc [3]): com.denodo.vdb.vdbinterface.common.clientResult.vo.descriptions. queryplan.vdb.IDUAccessPlanVO para planes de inserción, actualización y/o borrado, com.denodo.vdb.vdbinterface.common.clientResult.vo.descriptions. queryplan.StoredProcedurePlanVO para procedimientos almacenados, com.denodo.vdb.vdbinterface.common.clientResult.vo.descriptions. queryplan.vdb.VDBAccessPlanVO para vistas. com.denodo.vdb.vdbinterface.common.clientResult.vo.descriptions. queryplan.wrapper.WrapperPlanVO para wrappers. API de Desarrollo DataPort 29 Virtual DataPort 4.0 Guía del Desarrollador BIBLIOGRAFÍA [1] Guía del Administrador de Virtual DataPort, versión 4.0. Denodo Technologies, 2007. [2] Guía Avanzada de VQL de Virtual DataPort, versión 4.0. Denodo Technologies, 2007. [3] Documentación Javadoc del API del Desarrollador. DENODO_HOME/docs/vdp/api/index.html [4] Design Patterns. Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides. Addison-Wesley. 1994. ISBN: 0201633612 [5] http://java.sun.com/products/jdbc/ [6] Java Naming and Directory Interface (JNDI). http://java.sun.com/products/jndi/ [7] Web Services Description Language (WSDL). http://www.w3.org/TR/wsdl [8] Apache Tomcat. http://tomcat.apache.org/ [9] Apache Axis. http://ws.apache.org/axis/ [10] Microsoft Open DataBase Connectivity (ODBC). http://www.microsoft.com/odbc [11] EasySoft Corporation. http://www.easysoft.com [12] OpenLink Corporation. http://www.openlinksw.com Bibliografía 30