viernes, 5 de octubre de 2007

Forms 6 y Forms 10g: Generar archivo excel (XLS)

En Jakarta hay un proyecto que consiste en un API para manipular y generar documentos de Office sin necesidad de tener Office instalado, luego los documentos resultantes se pueden abrir con cualquier suite ofimática, como por ejemplo OpenOffice.

http://poi.apache.org/trans/es/index.html

En el momento de escribir esto la última versión era la 3.0.1 y las clases necesarias se encuentran en el archivo poi-3.0.1-FINAL-20070705.jar

Para poder usar estas clases en un programa de forms tenemos que hacer los siguientes pasos:

Forms 10g:

  • En esta ventana seleccionamos las clases:
    • org.apache.poi.hssf.usermodel.HSSFWorkbook
    • org.apache.poi.hssf.usermodel.HSSFRow
    • org.apache.poi.hssf.usermodel.HSSFSheet
    • org.apache.poi.hssf.usermodel.HSSFCell
    • java.io.FileOutputStream (posiblemente tengamos que añadir al CLASSPATH el archivo rt.jar
  • Una vez importadas las clases nos crea paquetes PL/SQL para poder interactuar con ellas, por lo que ahora simplemente hay que escribir el código PL/SQL normal.
  • Para poder ejecutar el programa hay que añadir el archivo poi-3.0.1-FINAL-20070705.jar al CLASSPATH en el servidor. Para ello miramos en el archivo formsweb.cfg que se encuentra en $ORACLE_HOME/forms/server la variable envFile, y al archivo que apunta es donde tenemos que modificar la variable CLASSPATH.
Forms 6i:

  • Hay que usar una máquina virtual de Java versión 1.4.2 o superior, lo he probado con la 1.4.2_06 y con 1.6.0_02.
  • Añadir a la variable de entorno PATH las carpetas: (Sustituir c:\program files\java\jdk1.6.0_02 por la carpeta en que tengamos instalado el jdk, si no lo configuramos correctamente nos dará el error: "PDE-UJI001 Fallo al crear JVM." al intentar importar las clases)
    • C:\Program Files\Java\jdk1.6.0_02\bin
    • C:\Program Files\Java\jdk1.6.0_02\jre\bin
    • C:\Program Files\Java\jdk1.6.0_02\jre\bin\client
  • Al igual que en Forms 10g hay que añadir a la variable CLASSPATH el archivo poi-3.0.1-FINAL-20070705.jar
  • Añadir también al CLASSPATH el archivo importer.jar que se encuentra en $ORACLE_HOME\tools\common60\java
  • Al igual que en Forms 10g, en el programa que se quiera usar hay que importar las mismas clases Java que se indican en el apartado para Forms 10g (esas son necesarias para los ejemplos, pero seguramente que en un caso real tengamos que importar más), para ello vamos al menú "Programa" y luego a "Importar Clases Java...".


Ejemplos:

Ejemplo New Workbook de Jakarta POI

HSSFWorkbook wb = new HSSFWorkbook();
FileOutputStream fileOut = new FileOutputStream("workbook.xls");
wb.write(fileOut);
fileOut.close();

Equivalente en Forms

DECLARE
v_wb      ORA_JAVA.JOBJECT;
v_fileout ORA_JAVA.JOBJECT;
BEGIN
v_wb := hssfworkbook.new();
v_fileout := fileoutputstream.new('workbook.xls');
hssfworkbook.write(v_wb, v_fileout);
fileoutputstream.close(v_fileout);
END;

Ejemplo New Shet de Jakarta POI


HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet1 = wb.createSheet("new sheet");
HSSFSheet sheet2 = wb.createSheet("second sheet");
FileOutputStream fileOut = new FileOutputStream("workbook.xls");
wb.write(fileOut);
fileOut.close();

Equivalente en Forms:

DECLARE
v_wb       ORA_JAVA.JOBJECT;
v_fileout  ORA_JAVA.JOBJECT;
v_sheet1   ORA_JAVA.JOBJECT;
v_sheet2   ORA_JAVA.JOBJECT;
BEGIN
v_wb := hssfworkbook.new();
v_sheet1 := hssfworkbook.createsheet(v_wb, 'new sheet');
v_sheet2 := hssfworkbook.createsheet(v_wb, 'second sheet');
v_fileout := fileoutputstream.new('workbook.xls');
hssfworkbook.write(v_wb, v_fileout);
fileoutputstream.close(v_fileout);
END;

Ejemplo Creating Cells de Jakarta POI


HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("new sheet");

// Create a row and put some cells in it. Rows are 0 based.
HSSFRow row = sheet.createRow((short)0);
// Create a cell and put a value in it.
HSSFCell cell = row.createCell((short)0);
cell.setCellValue(1);

// Or do it on one line.
row.createCell((short)1).setCellValue(1.2);
row.createCell((short)2).setCellValue("This is a string");
row.createCell((short)3).setCellValue(true);

// Write the output to a file
FileOutputStream fileOut = new FileOutputStream("workbook.xls");
wb.write(fileOut);
fileOut.close();

Equivalente en Forms:

DECLARE
v_wb         ORA_JAVA.JOBJECT;
v_fileout    ORA_JAVA.JOBJECT;
v_sheet      ORA_JAVA.JOBJECT;
v_row        ORA_JAVA.JOBJECT;
v_cell       ORA_JAVA.JOBJECT;
BEGIN
v_wb := hssfworkbook.new();
v_sheet := hssfworkbook.createsheet(v_wb, 'new sheet');
v_row := hssfsheet.createrow(v_sheet, 0);
v_cell := hssfrow.createcell(v_row, 0);
hssfcell.setcellvalue(v_cell, 1);
v_cell := hssfrow.createcell(v_row, 1);
hssfcell.setcellvalue(v_cell, 1.2);
v_cell := hssfrow.createcell(v_row, 2);
hssfcell.setcellvalue(v_cell, 'This is a string');
v_cell := hssfrow.createcell(v_row, 3);
hssfcell.setcellvalue(v_cell, TRUE);
v_fileout := fileoutputstream.new('workbook.xls');
hssfworkbook.write(v_wb, v_fileout);
fileoutputstream.close(v_fileout);
END;

Ejemplo para enviar el contenido de un bloque a una excel:

DECLARE
v_wb               ORA_JAVA.JOBJECT;
v_fileout          ORA_JAVA.JOBJECT;
v_sheet            ORA_JAVA.JOBJECT;
v_row              ORA_JAVA.JOBJECT;
v_cell             ORA_JAVA.JOBJECT;
v_fin              BOOLEAN := FALSE;
v_bloque           VARCHAR2(30) := :system.cursor_block;
v_record_anterior  NUMBER := :system.cursor_record;
v_item_anterior    VARCHAR2(61) := :system.cursor_item;
v_item             VARCHAR2(30);
v_num_celda        NUMBER;
v_num_fila         NUMBER;
BEGIN
v_wb := hssfworkbook.new();
v_sheet := hssfworkbook.createsheet(v_wb, 'new sheet');
v_num_fila := 0;

FIRST_RECORD;

WHILE NOT v_fin LOOP
v_row := hssfsheet.createrow(v_sheet, v_num_fila);
v_num_fila := v_num_fila + 1;
v_item := Get_Block_Property(v_bloque, FIRST_ITEM);
v_num_celda := 0;

WHILE v_item IS NOT NULL LOOP
v_cell := hssfrow.createcell(v_row, v_num_celda);
hssfcell.setcellvalue(v_cell, NAME_IN(v_bloque || '.' || v_item));
v_item := Get_Item_Property(v_bloque || '.' || v_item, NEXTITEM);
v_num_celda := v_num_celda + 1;
END LOOP;

IF :system.last_record = 'TRUE' THEN
v_fin := TRUE;
ELSE
NEXT_RECORD;
END IF;
END LOOP;

v_fileout := fileoutputstream.new('workbook.xls');
hssfworkbook.write(v_wb, v_fileout);
fileoutputstream.close(v_fileout);

GO_RECORD(v_record_anterior);
GO_ITEM(v_item_anterior);
END;

NOTA 15/12/2009: Las versiones recientes de las librerías de Jakarta POI están compiladas para usar una máquina virtual de java superior a la que tiene Forms 10g. Al importar en Forms10g sale este mensaje: "Se ha producido una excepción: java.lang.UnsupportedClassVersionError: org/apache/poi/hssf/usermodel/HSSFWorkbook (Unsupported major.minor version 49.0)". Con la versión 3.0.1 de las librerías funciona perfectamente. Esta versión se puede descargar de aqui: http://sites.google.com/site/elfeme/files/poi-3.0.2-FINAL-20080204.zip?attredirects=0&d=1

domingo, 30 de septiembre de 2007

Forms10g: Modificar apariencia

En el archivo formsweb.cfg se dispone del parámetro lookAndFeel para modificar la apariencia de la aplicación, los valores que puede tomar son:
  • generic: Apariencia típica de Windows
  • oracle: Apariencia por defecto definida por Oracle. Dentro de esta apariencia se puede usar el parámetro colorScheme para modificar el conjunto de colores usados. Los valores que puede tomar son:
    • teal
    • red
    • titanium
    • blue
    • khaki
    • olive
    • purple
    • blaf: Este en concreto no está soportado oficialmente por Oracle fuera de Oracle Applications.
Podemos modificar estos parámetros en el archivo formsweb.cfg o también a través de la url, por ejemplo: http://direccionequipo:8889/forms/frmservlet?config=prueba&lookandfeel=generic

domingo, 23 de septiembre de 2007

Forms10g: clientDPI

Se me ha dado el caso de una aplicación desarrollada en Forms10g sobre plataforma Windows, que al desplegarse en algún sistema Linux se veían las pantallas demasiado pequeñas.

Dependiendo de la aplicación, puede ser que parezca que es problema de las fuentes, en mi caso la aplicación al arrancar se maximizaba y ocupaba toda la pantalla y eso hacía que pareciese que el problema fuera con las fuentes.

El problema se encuentra en los puntos por pulgada que usa la máquina virtual de Java. La configuración de los puntos por pulgada se puede variar añadiendo el parámetro clientDPI en el archivo de configuración formsweb.cfg en la sección de nuestra aplicación, por ejemplo:

[prueba]
form=prueba.fmx
...
clientDPI=96

Para que el applet tenga en cuenta este parámetro tenemos que modificar los archivos webutiljpi.htm, webutiljini.htm, webutilbase.htm, basejpi.htm, basejini.htm y base.htm. En realidad solo sería necesario modificar los que terminan en jpi (Java plug-in) ya que en Linux no se puede instalar el Jinitiator por lo que nunca podríamos usar los que terminan en jini (Jinitiator), pero podría ser que en ordenadores Windows con Jinitiator quisiesemos alterar este valor, por lo que modificarlos todos no está más.

La modificación que hay que hacer es la siguiente (para el webutiljpi.htm):

Después de la línea: <param name="WebUtilMaxTransferSize" value="%WebUtilMaxTransferSize%">

Añadir:

<param name="clientDPI" value="%clientDPI%">

Después de la línea: WebUtilMaxTransferSize="%WebUtilMaxTransferSize%"

Añadir la línea:

clientDPI="%clientDPI%"

Ejemplo del formulario de prueba a 96 dpi:


Ejemplo del formulario de prueba a 50 dpi:



Para hacer pruebas para encontrar el valor que mejor se nos ajuste, no hace falta estar modificando el archivo de configuración formsweb.cfg, podemos alterar el valor añadiendo el parámetro clientDPI a la url, por ejemplo: http://equipo:8889/forms/frmservlet?config=pru&form=test&clientDPI=50

Para más detalles consultar la nota 1071248.6 en el metalink.

sábado, 22 de septiembre de 2007

Configurar varios entornos de desarrollo

Muchas veces se me ha dado el caso de tener que trabajar en varios proyectos, donde las librerías (plls) y librerías de objetos (olb) son distintas y se encuentran en varios directorios, la solución que solía hacer era modificar la variable FORMS60_PATH en el regedit, pero un día se me ocurrió una idea que consiste en lo siguiente:

Por cada entorno de ejecución me hice un script que inicializa las variables de entorno y luego arranca el Forms Builder, incluso ya arranca conectado con la base de datos que quiero.

Ejemplo: Nombre del script produccion.cmd

set FORMS60_PATH=L:\olb;L:\adaptaciones;L:\plls;L:\mmx;L:\fmxs
set UI_ICON=L:\iconos
set REPORTS60_PATH=L:\adaptaciones;L:\plls;L:\reps
set PATH=c:\oracle\dev6i\bin
set NLS_LANG=SPANISH_SPAIN.WE8ISO8859P15
set NLS_DATE_FORMAT=DD/MM/YYYY
set NLS_NUMERIC_CHARACTERS=,.
set NLS_SORT=BINARY
set REPORTS60_TMP=c:\temp
start ifbld60 userid=usuario/password@cadenaconexion

Ejecutando el script primero inicializa las variables y luego arranca el Forms Builder. Si tenemos otro proyecto y las librerías están en otros directorios hacemos otro script de forma similar.

En caso de Forms10g es muy similar solo hay que cambiar los nombres de las variables de entorno, por ejemplo no es FORMS60_PATH, sería FORMS_PATH y el ejecutable no es ifbld60, sería frmbld

jueves, 13 de septiembre de 2007

Forms 6: Enviar correo con adjunto a través de Outlook

Con este código nos permite añadir un mensaje en la bandeja de salida y luego enviarlo o mostrar el mensaje para ser enviado luego por el usuario.

DECLARE
v_outlookapp ole2.obj_type;
v_namespace ole2.obj_type;
v_mailitem ole2.obj_type;
v_oleparam ole2.list_type;
v_attachments ole2.obj_type;
BEGIN
v_outlookapp := ole2.create_obj('Outlook.Application');
v_oleparam := ole2.create_arglist;
ole2.add_arg(v_oleparam, 'MAPI');
v_namespace := ole2.invoke_obj(v_outlookapp, 'GetNameSpace', v_oleparam);
ole2.destroy_arglist(v_oleparam);
v_oleparam := ole2.create_arglist;
ole2.add_arg(v_oleparam, 0);
v_mailitem := ole2.invoke_obj(v_outlookapp, 'CreateItem', v_oleparam);
ole2.destroy_arglist(v_oleparam);
ole2.set_property(v_mailitem, 'To', 'xxxx@xxxmail.com');
ole2.set_property(v_mailitem, 'Subject', 'Asunto');
ole2.set_property(v_mailitem, 'Body', 'Texto del mensaje');

-- Añadimos el adjunto
v_attachments := ole2.get_obj_property(v_mailitem, 'Attachments');
v_oleparam := ole2.create_arglist;
ole2.add_arg(v_oleparam, 'C:\Temp\prueba.pdf');
ole2.invoke(v_attachments, 'add', v_oleparam);
ole2.destroy_arglist(v_oleparam);

-- Con esta línea invocamos el envío, si queremos
-- simplemente abrir el Outlook para que pueda revisar
-- el correo cambiamos 'Send' por 'Display'
ole2.invoke(v_mailitem, 'Send');

ole2.RELEASE_OBJ(v_mailitem);
ole2.RELEASE_OBJ(v_namespace);
ole2.RELEASE_OBJ(v_outlookapp);
END;

Sin adjunto sería parecido pero con menos código, ahí va el ejemplo:

DECLARE
v_outlookapp ole2.obj_type;
v_namespace ole2.obj_type;
v_mailitem ole2.obj_type;
v_oleparam ole2.list_type;
BEGIN
v_outlookapp := ole2.create_obj('Outlook.Application');
v_oleparam := ole2.create_arglist;
ole2.add_arg(v_oleparam, 'MAPI');
v_namespace := ole2.invoke_obj(v_outlookapp, 'GetNameSpace', v_oleparam);
ole2.destroy_arglist(v_oleparam);
v_oleparam := ole2.create_arglist;
ole2.add_arg(v_oleparam, 0);
v_mailitem := ole2.invoke_obj(v_outlookapp, 'CreateItem', v_oleparam);
ole2.destroy_arglist(v_oleparam);
ole2.set_property(v_mailitem, 'To', 'xxxx@xxxmail.com');
ole2.set_property(v_mailitem, 'Subject', 'Asunto');
ole2.set_property(v_mailitem, 'Body', 'Texto del mensaje sin adjunto');

-- Con esta línea invocamos el envío, si queremos
-- simplemente abrir el Outlook para que pueda revisar
-- el correo cambiamos 'Send' por 'Display'
ole2.invoke(v_mailitem, 'Send');

ole2.RELEASE_OBJ(v_mailitem);
ole2.RELEASE_OBJ(v_namespace);
ole2.RELEASE_OBJ(v_outlookapp);
END;

miércoles, 12 de septiembre de 2007

Tetris en Forms


Hace tiempo me encontré el juego del Tetris programado en Forms, en concreto en la versión 6. Me llamó la atención la forma en que captura las pulsaciones de las teclas, ya que Oracle Forms no tiene forma de manejar las pulsaciones de teclado de forma nativa (se podría hacer usando Java), por lo que hay que usar trucos.

En concreto el truco que han usado aqui es, para la tecla arriba y abajo capturan los eventos KEY-UP y KEY-DOWN respectivamente y para capturar las pulsaciones de las techas derecha e izquierda usan un campo de tipo radio-button oculto a la vista, es decir alto y ancho cero, entonces cuando se pulsa la techa derecha se lanza el evento WHEN-RADIO-CHANGED y a la izquierda lo mismo.

tetris.fmb en content-type.com
tetris.fmb en www.oraclehome.co.uk

Conclusión: No es matar moscas a cañonazos, es lo contrario, es como ir a la guerra usando alfileres como arma, pero es muy interesante lo que se puede llegar a hacer con un entorno de desarrollo 4gl que no está pensado precisamente para juegos.

viernes, 31 de agosto de 2007

Qué es Oracle Forms

Como bien se entiende en el título es una herramienta de Oracle y su principal cometido es el desarrollo rápido de aplicaciones, sobre todo de gestión. También se puede considerar como un lenguaje 4gl, es decir, de cuarta generación.

Llevo trabajando más de 10 años con Oracle Forms, desde la versión 4.5, 6i y ahora con la 10g, mé he saltado la 9.

Las versiones 4.5 y la 6.0 funcionan en el modelo Cliente/Servidor. La 6i funciona en web aparte de en Cliente/Servidor, y a partir de la versión 9 (se han saltado la 7 y 8) solo funciona en entorno web.

Para el funcionamiento en web usa un applet, por lo que es necesario tener instalado el plug-in de Java en el ordenador que abra la aplicación.

La versión 6i en web no tengo experiencia de como va, lo que he visto de 10g no tiene mala pinta.