Etiqueta

13 de febrero de 2020

Cómo mostrar un PDF desde un campo BLOB de Oracle sobre el navegador, sin descargarlo

En este artículo quiero mostrarles un "truco" ideado por Francesco Del Dotto de soporte Omnis en Italia, de modo que lo primero es agradecer su tiempo y darle mi enhorabuena ante la pericia demostrada.


En principio podría pensarse que se trata de algo sencillo de hacer desde Omnis Studio, ya conocemos el método Do $cinst.$clientcommand("assignpdf",row) donde el parámetro row se define como row(html-object-name,pdf-parameters,pdf-id,timeout,pdf-filename), pero el reto está precisamente en el parámetro pdf-filename, es decir que el uso de éste método requiere de la presencia física del fichero con el documento pdf, pero el reto que se plantea en el enunciado de este artículo, es enviar al navegador del cliente, un documento pdf que se encuentra en una variable binary y no en un fichero físico.

La parte digamos "sencilla" fue construir el método (denominado muestraPdf) javascript que se encargaría de mostrar el documento, pasándole el contenido del campo BLOB de oracle y que mostramos a continuación:

# El dato BLOB de Oracle es iDocumentos.DOCUMENTO
# pdfBuffer es una variable var de JavaScript
# el método ha sido marcado como "Execute On Client" 

Calculate pdfBuffer as iDocumentos.DOCUMENTO
JavaScript: var arrayBuffer = base64ToArrayBuffer(pdfBuffer);
JavaScript: function base64ToArrayBuffer(base64) {
JavaScript: var binaryString = window.atob(base64);
JavaScript: var binaryLen = binaryString.length;
JavaScript: var bytes = new Uint8Array(binaryLen);
JavaScript: for (var i = 0; i < binaryLen; i++) {
JavaScript: var ascii = binaryString.charCodeAt(i);
JavaScript: bytes[i] = ascii;
JavaScript: }
JavaScript: return bytes;
JavaScript: }
JavaScript: var blob = new Blob([arrayBuffer], {type: "application/pdf"});
JavaScript: var link = window.URL.createObjectURL(blob);
JavaScript: window.open(link);

El problema estriba en como invocar a éste método desde el lado del servidor, ya que sólo existe en el navegador del cliente y por tanto sólo puede ser invocado desde un método que (al igual que este) haya sido marcado como "Execute On Client", el problema es que la recuperación del campo BLOB con el contenido del PDF, debe hacerse desde un método creado y ejecutado en el entorno del servidor, es decir que no puede llevar la marca "Execute On Client", y es aquí donde llega el "truco de Francesco".

Para que la magia tenga su efecto, lo primero que hacemos es colocar un "Timer Control" sobre el formulario remoto...


...al que incluiremos en su método "$event" el código siguiente y que estratégicamente habremos marcado como "Execute On Client":

On evTimer
Do $cinst.$objs.timerObj.$running.$assign(kFalse)
Do $cinst.$muestraPdf()

El truco consiste en activar el timer alojado en el navegador del cliente desde el servidor, el cual se encargará a su vez de invocar al método "$muestraPdf()" ademas de auto-desactivarse.

A continuación mostramos la secuencia que obtiene el campo BLOB de oracle y activa el timer:

Calculate lCompara as con("where ROWID = '";iListaDctos.ROWID;"'")
Do iDocumentos.$select(lComparaReturns #F
If flag true
Do iDocumentos.$fetch()
If not(isclear(iDocumentos.DOCUMENTO))
Do $cinst.$objs.timerObj.$running.$assign(kTrue)
End If
End If

El código encargado de recuperar el documento desde la base de datos, es también el encargado de activar el timer, que fue previamente alojado en el navegador del cliente.

Gracias de nuevo a Francesco de soporte Omnis, por este maravilloso truco.
 

22 de enero de 2020

La librería de conversión de DML a SQLite

Recientemente Omnis Studio proporciona la utilidad denominada "Convert Datafile to RDBMS", la cual está soportada por la librería "omsqlconv.lbs" bajo el directorio "startup", pero no debemos pasar por alto, que ésta, no sólo se encarga del proceso de conversión, sino que también funciona en segundo plano, realizando el trabajo de emular los antiguos comandos DML, traduciéndolos y ejecutándolos contra SQLite, así como del comportamiento de semáforos (semaphores) y bloques reversibles (reversible blocks) cuando son usados.


La librería "omsqlconv.lbs", se apoya también en tablas creadas sobre la base de datos resultante de la conversión, las cuales no deberán ser modificadas o eliminadas, a continuación mostramos una lista de los comandos DML que son emulados:

Close datafile, Close lookup file, Create datafile, Floating default datafile, Open datafile, Open lookup file, Prompt for datafile, Set current datafile, Set default datafile, lookup(), Build indexes, Delete data, Drop indexes, Open runtime datafile browser, Rename data, Cancel prepare for update, Delete, Delete with confirmation, Do not flush data, Do not wait for semaphores, Flush data, Flush data now, Prepare for edit, Prepare for insert, Prepare for insert with current values, Test for only one user, Update files, Update files if flag set, Wait for semaphores, Clear all files, Clear main & connected, Clear main file, Clear range of fields, Clear selected files, Set main file, Clear find table, Disable relational finds, Enable relational finds, Find, Find first, Find last, Load connected records, Next, Previous, Prompted find, Single file find, Test for a current record, Test for a unique index value, Clear search class, Reinitialize search class, Set search as calculation, Set search name, Test data with search class, Clear sort fields, Set sort field, Build list from file, Begin reversible block, End reversible block, Quit all methods, $root.$getodbfilelist(), y alguna funciones de sys(), como: sys(11), sys(139), sys(3000) & sys(3001)

Para habilitar la emulación de comandos DML, es necesario establecer la preferencia de librería "$mapdmltodam" para SQLite. No obstante aún habrá que hacer un pequeño cambio en el código de nuestra librería, que tiene que ver con el modo en que se abrimos o accedemos a la base de datos, antes un fichero con extensión .df1 y ahora con extensión .db. El comando para SQLite sería:

Open datafile sdb://192.168.0.10:5743/SQLiteTest

Pero si queremos también podemos optar por cambiar éste comando y hacer uso de la sintaxis de notación del modo siguiente:

Do omsqlSess.$logon(‘sdb://192.168.0.10:5743/SQLiteTest’,’’,’’) Returns #F

15 de octubre de 2019

¿Cómo evitar el diálogo "Install the legacy Java SE 6 runtime" en Omnis Studio 10.1 y Catalina?

Es muy probable que tras instalar de nuevo el soporte Java para SOAP en Omnis Studio 10.1, nos encontremos con que Omnis nos muestre un cuadro de diálogo Java que tenga el siguiente texto;
Este cuadro de diálogo puede aparecer aún al intentar iniciar una JVM de Oracle en una versión de Studio de 64 bits, es decir, Studio 8.0 y posterior. Las instrucciones predeterminadas establecidas en una versión de Java de 64 bits no permiten que sean iniciadas desde una aplicación incluida en JNI (como es el caso de Omnis). Lo cual causará un fallo el intentar iniciar la JVM mostrándose el mensaje anterior.

Para corregirlo, simplemente debemos editar el fichero "Info.plist" de nuestro Oracle JDK en uso, para permitir que JVM sea iniciado desde aplicaciones incluidas en JNI.

Podrá localizar el fichero a editar en, "/Library/Java/JavaVirtualMachines/jdkX.X.X_XXX.jdk/Contents/Info.plist" (donde X.X.X_XXX es la versión, por ejemplo, 1.8.0_101).

Ahora simplemente cambie la entrada:

≤key≥JVMCapabilities≤/key≥
≤array≥
   ≤string≥CommandLine≤/string≥
≤/array≥
por:

   ≤key≥JVMCapabilities≤/key≥
   ≤array≥
      ≤string≥CommandLine≤/string≥
      ≤string≥JNI≤/string≥
      ≤string≥BundledApp≤/string≥
   ≤/array> 

..y eso habrá sido todo.