Archivo
ADF tips: Control de cambios pendientes

El control de cambios pendientes cuando el usuario abandona una página es siempre un problema muy a tener en cuenta, sobretodo cuando el usuario utiliza formas de navegación no controladas por la aplicación (como el botón atrás, cerrar el navegador, refrescar la página…).
ADF ofrece una manera muy sencilla de alertar al usuario cuando tiene cambios pendientes, simplemente debemos añadir el atributo uncommittedDataWarning dentro del tag af:document; como se muestra en la imagen.
Esta nueva propiedad provoca que cuando el usuario intenta cerrar el navegador, refrescar la página, ir atrás o una acción similar, salte un popup genérico que le alerte de que si continua puede perder sus cambios.
Además es posible forzar a que este aviso sea lanzado cuando se haga uso de algún elemento de navegación dentro de la propia pantalla, por ejemplo al pulsar un botón o hacer un cambio de pestaña. Para ello debemos añadir el atributo af:checkUncommittedDataBehavior dentro del componente deseado.
Aunque es una solución válida para evitar salidas descontroladas del navegador, en algunos casos puede que necesitemos personalizar este popup de alerta. Por ejemplo para ofrecerle al usuario la opción de guardar sus cambios desde el propio popup, forzar a deshacer todos los cambios antes de permitir la navegación o tal vez darle un aspecto más amigable al aviso. Para ello podemos acceder a la comprobación que internamente el framework ejecuta por nosotros para saber cuándo debe mostrar el mensaje de aviso; teniendo la opción de por ejemplo abrir un popup de aviso propio.
En resumen, gracias a la opción de uncommittedDataWarning podemos alertar al usuario de una forma rápida y eficaz de que va a perder los cambios pendientes, y en caso de que el aviso genérico no cumpla con todos nuestros requisitos, existe una forma alternativa de hacer la misma comprobación y controlar manualmente todas las opciones que el usuario tendrá disponibles.
ADF tips: Versionado de aplicaciones ADF en WebLogic

WebLogic proporciona una utilidad muy interesante: el versionado de aplicaciones, que permite tener al mismo tiempo dos versiones de la misma aplicación desplegada, pudiendo elegir de una forma muy sencilla qué versión es la que dará servicio a las nuevas peticiones.
En entornos de desarrollo puede que esta utilidad no tenga demasiado sentido, pero una vez se llega a entornos en los que hay que garantizar una vuelta atrás rápida y controlada, se vuelve esencial.
Para que WebLogic reconozca automáticamente la versión de la aplicación que se está desplegando hay que crear un fichero MANIFEST.MF dentro de los descriptores del proyecto.
Dentro del fichero MANIFEST.MF hay que incluir el parámetro Weblogic-Application-Version que definirá la versión que se desplegará.
Una vez completado el fichero hay que incluirlo dentro del descriptor de despliegue de nuestra aplicación, desde las propiedades del proyecto.
Además, hay que incluir un filtro para que cuando se genere el EAR no se incluya el fichero manifest creado anteriormente; dado que ya se incluirá al estar definido en el descriptor de despliegue. Si no hacemos este filtro, puede que el despliegue falle. Desde las mismas propiedades de despliegue del EAR se define este filtro.
Así, cuando despleguemos cada nueva versión (modificando el parámetro del fichero manifest), WebLogic mantendrá la versión anterior en un segundo plano, lista para poder volver a la versión anterior si es necesario.

ADF tips: Refinando el “Failed to validate all rows in transaction”

En este tip explicaremos cómo ampliar de forma sencilla y útil la conocida excepción de “Failed to validate all rows in transaction“.
Todo proyecto una vez pasa la fase de desarrollo necesita buenas trazas de log, sobretodo en casos de error. En el momento que una entidad no cumple todas sus validaciones lanza una excepción en el método validateEntity del tipo ValidationException. En las trazas de log por defecto no veremos demasiada información acerca del error, lo que siempre nos complica la resolución del mismo.
Para tener algo más de información podemos crear una entidad genérica de la que hereden el resto de entidades de nuestro proyecto; esto se configura desde las propiedades del proyecto en la opción Business Components:
ADF tips: Integrando Web Services y Business Componets

A veces nos encontramos con la necesidad de mostrar en la misma fila de una tabla campos procedentes de diferentes fuentes de datos (‘data sources’). Puede darse el caso, por ejemplo, de que necesitemos cruzar mediante una operación ‘JOIN’ dos tablas que se encuentran en distintas Bases de Datos. También es posible que algunos atributos se encuentren en la B.D. y el resto deban obtenerse mediante un Web Service. Este post muestra los pasos a seguir para implementar una solución para este segundo escenario.
Vamos a describir de forma concisa el problema:
- Disponemos de una tabla en la B.D. donde se registran a modo de log todas las conexiones a nuestro sistema. Se guardan dos atributos, la fecha de conexión y la dirección IP de la máquina que se conectó.
- Tenemos que diseñar una pantalla que presente de forma tabular el log de conexiones con las siguientes columnas: fecha, IP y una tercera columna con el nombre del país al que pertenece la dirección IP.
Los dos primeros campos, los tenemos, pero la información correspondiente al tercero, ¿de dónde la obtenemos? Muy sencillo: existen varios Servicios Web que nos proporcionan este dato, uno de los más conocidos es GeoIP. Trabajar con él será fácil: nosotros le damos una dirección IP y el WS nos responde con el nombre del país.
¿Y cómo pegamos lo uno con lo otro, y en que lugar? El punto de encuentro está claro: en la capa del modelo, exactamente en el mismo ViewObject donde se realiza la consulta sobre la tabla de Log; añadimos un nuevo atributo llamado Country y guardamos los cambios. Una vez hecho esto, editamos la clase que implementa el registro del ViewObject y sobrescribimos el método getCountry() para que se comunique con el Web Service, consiga el país y lo devuelva. No lo hará directamente, usaremos un JavaBean para ocultar y envolver las llamadas al servicio.
Hay que hacer notar, que estamos planteando un caso muy sencillo, basta con utilizar un ViewObject de sólo lectura y no basado en entidad. Si los campos de la B.D. tuvieran que ser modificables, entonces diseñaríamos una vista basada en entidad y todo lo dicho para la columna externa (Country) seguiría siendo válido. Otro caso de uso un poco mas complicado contemplaría la posibilidad de trabajar con WS que proporcionan operaciones CRUD (create, read, update, delete). Entonces crearíamos entidades basadas en Web Services (y tendríamos que trabajar a nivel de entidad ocultando el origen de datos a los ViewObjects), pero eso lo dejamos para un futuro post.
Pongámonos manos a la obra:
1. Creamos un proyecto nuevo en nuestra aplicación y a partir del WSDL del servicio construimos un proxyService (de tipo JAX-WS, con el ‘endpoint‘ de la versión 1.2)
2. Creamos un JavaBean, para envolver la llamada al servicio. A partir de este Bean tendremos la posibilidad de crear un DataControl que nos permita trabajar de forma declarativa con los datos las estructuras del WS. El método getGeoIP() se encarga de hacer la llamada a la operación del WS que nos interesa.
3. Diseñamos el ViewObject (LogView1) a partir de la tabla Log y le añadimos un atributo nuevo, Country.
4. Sobrescribimos el método getCountry() en la implementación del registro de LogView1, para que obtenga a través d nuestro JavaBean el país correspondiente a la dirección IP.
5. Diseñamos la página con la tabla de log, arrastrando y soltando el DataControl correspondiente a la vista LogView1 sobre la página, ejecutamos y vemos el resultado final.
De esta forma en cinco sencillos pasos hemos conseguido resolver el problema. Seguramente no es muy eficiente llamar al servicio para cada fila, para mejorar esta situación usaremos nuestro JavaBean. Teniendo en cuenta que este tipo de servicios web ofrece operaciones que permiten pasar como parámetro listas de elementos en vez de elementos individuales y de igual manera devuelven listas de resultados, podremos invocar la operación del WS una vez y mantener en un HashMap la lista de paises, evitando así una invocación por cada registro.
ADF tips: Validadores personalizados

En este tip explicaremos cómo se puede crear un validador personalizado en la capa de vista utilizando una clase java propia, siendo reutilizable en tantas páginas como queramos.
ADF permite validaciones desde nivel de entity hasta nivel de vista; en este caso nos centraremos en la parte de vista, heredada de JSF. El primer paso es crear la clase Java que implemente la interfaz de javax.faces.validator.Validator; lo que nos obliga a implementar su método abstracto validate:
Una vez creada la clase, debemos registrarla en el fichero faces-config.xml de nuestro proyecto utilizando el wizard:
Ahora sólo queda hacer uso de este validador en una de nuestras páginas. Para ello hay que añadir el tag de <af:validator> dentro de uno de nuestros inputText:
ADF tips: Forzar orden de commit en maestro-detalle

En este tip explicaré cómo evitar problemas a la hora de hacer commit de una transacción en la que se han creado registros dependientes por “foreign key” en base de datos.
Algunas veces por motivos de negocio nos vemos forzados a crear primero los registros dependientes y después el registro padre. Para evitar que a la hora de enviar los datos a base de datos tengamos problemas de que el registro hijo no encuentra su “parent key” tenemos dos opciones.
La primera opción, la más elegante: hacer uso de las asociaciones en modo “composition” entre las entities que forman el maestro-detalle.
Aún así, algunas veces nos vemos obligados a forzar programáticamente el envío de los registros “padre” para evitar problemas. En este caso la mejor opción es sobreescribir el método post-changes de la o las entities “hijas” y forzar el envío del padre antes que el suyo propio.
De esta forma obligamos a enviar a base de datos en el orden correcto de dependencias, con lo que nunca tendremos problemas del tipo “parent key no found”.
ADF tips: Gráficos interactivos utilizando Oracle ADF DVT

ADF Faces incorpora un conjunto de componentes visuales (‘Data Visualization Tools’ o DVT) con más de 50 tipos de gráficos (barras, tarta, líneas, dispersión, ajuste lineal curvo, etc.), permitiendo un diseño sencillo y una integración rápida de los mismos en nuestras aplicaciones.
Una librería de componentes gráficos que forme parte de un ‘Framework’ de desarrollo deberá ofrecer los mecanismos necesarios para conseguir un alto grado de dinamismo e interacción. DVT brilla especialmente en estos dos aspectos. Podemos encontrar una sencilla introducción sobre DVT en el siguiente documento:
http://www.oracle.com/technetwork/developer-tools/jdev/adf-dvt-graph-howto-082848.html
Ahora vamos a ver con un sencillo ejemplo como podemos interactuar con un gráfico de tarta ‘PIEGraph’, de tal forma que al clicar sobre una porción (‘slice’), se muestre un segundo gráfico con información más detallada para la porción seleccionada. Veámoslo paso a paso:
Los datos de partida están en una tabla donde cada línea representa un presupuesto y el gráfico de primer nivel se basa en el conteo de presupuestos agrupados por ESTADO (iniciado, aprobado, finalizado). El de segundo nivel desglosa para cada ESTADO la cantidad de presupuestos, esta vez agrupados por ZONA (norte, sur, este,…) .
Creamos un ‘ViewObject’ resumenEstadosVO a partir de la consulta:
SELECT estado, count(*) as conteo FROM presupuestos GROUP BY estado
Creamos el ‘ViewObject’ resumenEstadoZonasVO para el gráfico de segundo nivel:
SELECT zona, count(*) as conteo FROM presupuestos GROUP BY estado WHERE estado=:p_estado
Si hemos incluido las dos vistas anteriores en un módulo de aplicación, dispondremos de un Data Control para poder diseñar los gráficos.
Arrastramos y soltamos el Data Control resumenEstadosView1 sobre la página principal. En el diálogo que aparece a continuación, indicamos qué atributo (conteo) se corresponde con la tarta y qué atributo (estado) contiene las porciones o ‘slices’. El primer gráfico está casi terminado. Más tarde añadiremos el ‘listener’ y el manejador del evento clic.
El segundo gráfico se mostrará en un diálogo emergente, cada vez que hagamos clic sobre un estado.
La forma correcta de diseñar esto es:
1. Crear un task-flow que contenga una actividad y un fragmento de página con el segundo ‘PIEGraph’ dentro. También creamos un parámetro (tfp_estado). La actividad se obtiene arrastrando la operación ExecuteWithParams que cuelga del Data Control (resumenEstadoZonasView1), será la actividad por defecto y transitará incondicionalmente al fragmento de página, y como estamos trabajando con una vista parametrizada, al soltar el icono de la operación sobre el task-flow se nos pide que indiquemos como rellenar el parámetro p_estado y utilizamos para la ocasión #{pageFlowScope.tfp_estado} (el argumento que hemos definido en el task-flow).
2. Crear una región dentro del diálogo. Arrastramos el task-flow del punto anterior y lo soltamos sobre el diálogo. Es hora de decirle a la región de dónde tiene que tomar el parámetro del task-flow. Usaremos el nombre de una variable de sesión creada en el manejador del evento del siguiente punto (#{pageFlowScope.graphEstado})
3. Sólo queda definir el ‘listener’ en el gráfico.
Y por último codificar el método que maneja el evento en el ‘Backing Bean’ que da soporte a la página.
ADF tips: Redirección dinámica de task-flows

En este tip explicaremos cómo definir el destino de una acción en un task-flow en tiempo de ejecución.
En la capa controller, ADF permite definir una serie de acciones estáticas en tiempo de diseño. En algunos casos esto puede limitar el diseño y obliga al programador a hacer uso de elementos como routers o regiones dinámicas.
Si decidimos utilizar un router para definir todas las posibilidades de navegación puede elevar la complejidad del task-flow, lo que hace difícil su lectura; además de la pérdida de rendimiento que puede ocasionar.
Si por el contrario mantenemos un task-flow sencillo con una única navegación y hacemos uso de regiones dinámicas solventamos el problema de complejidad en el task-flow, pero cargaremos demasiado la aplicación. Además de hacer menos mantenible esta parte del código.
Sin embargo, desde hace varias versiones de ADF (11.1.1.2) podemos definir la navegación en tiempo de ejecución. Esto quiere decir que sólo debemos indicarle al task-flow que la navegación será de tipo dinámica y introducir una expresión EL con la que obtendrá el objeto TaskFlowId que será interpretado para obtener el task-flow destino.
La expresión EL debe retornar un objeto TaskFlowId creado previamente, por ejemplo:
ADF tips: Método rápido y sencillo para incorporar Google Maps a nuestra aplicación

Todos sabemos que la mejor forma de localizar una dirección es acceder mediante nuestro navegador a https://maps.google.es/ y teclear el nombre de la vía y de la localidad.
Google proporciona un servicio gratuito (disponible para cualquier sitio web que sea a su vez gratuito para el consumidor) para acceder a sus mapas, haciendo uso de la versión 3 de su API JavaScript.
Os voy a mostrar como podéis usar esta API desde una página JSF. En unos pocos pasos conseguiremos una aplicación que nos permitirá, a partir de los atributos dirección y población de una tabla en pantalla y mediante la pulsación de un botón de comando, lanzar un pop-up, donde ver la localización exacta del lugar.
ADF tips: Transacciones pequeñas y controladas

En una aplicación ADF hay que analizar muy detenidamente la estructura del modelo de nuestra aplicación. Una estructura mal organizada puede llevar a ocasionar problemas muy graves de rendimiento.
Antes de empezar a diseñar una aplicación ADF debemos pensar las transacciones básicas que se van a llevar a cabo dentro de ella, organizando en función de la misma diferentes Application Modules (AM) / View Objects (VO). La práctica de diseñar funcionalidades muy grandes sin tener en cuenta lo que ello conlleva a nivel de rendimiento y tiempo de respuesta suele ocasionar graves problemas, aunque probablemente no los detectaremos hasta realizar pruebas de estrés o directamente en el entorno productivo. Por ello es más que necesario el que los diseños funcionales, técnicos y de modelo de datos estén alineados y sean coherentes entre sí. Esto nos ayudará a controlar el número de elementos en memoria, lo que se traduce en buenos tiempos de respuesta.
Por otra parte, es recomendable controlar mucho el peso de los AM. Debemos tener muy en cuenta que un AM con exceso de VOs será muy pesado a la hora de pasivar; esto ocasiona problemas de rendimiento (tanto en tiempos de respuesta como en consumo de memoria). Y aunque sea muy apetecible el reaprovechar componentes hay que hacerlo con cabeza.
Una práctica muy extendida es el anidar AM como “nested” sin estudiar demasiado las consecuencias. Si tenemos una aplicación modular lo recomendable es que para interconectar módulos se utilicen AM de lógica de negocio. Estos AM deben tener la mínima cantidad de elementos posible para funcionar de forma atómica y en la medida de lo posible no deben tener otros AM anidados. Siguiendo esta práctica conseguiremos controlar las transacciones y el consumo de memoria no se disparará cuando nuestra aplicación sea utilizada por varios usuarios concurrentes.
Por otro lado, no debemos olvidarnos de adaptar el apartado de Tuning de todos y cada uno de los componentes de nuestra aplicación. Con el ajuste apropiado una misma aplicación puede reducir los tiempos y consumos de memoria en gran parte. Una aplicación grande sin tuneo puede ocasionar cuelgues y tiempos de espera prolongados y puede solucionarse con un análisis y configuración de estas opciones.





















