Y tu... le presentas al usuario mensajes de error claros y contextualizados?

Reglas de Integridad

Cuando los datos se modifican con sentencias INSERT, DELETE o UPDATE, su integridad puede perderse de muchas maneras diferentes. Pueden añadirse datos no válidos a la base de datos, tales como un pedido que especifica un producto no existente.

Pueden modificarse datos existentes tomando un valor incorrecto, como por ejemplo si se reasigna un vendedor a una oficina no existente. Los cambios pueden ser aplicados parcialmente, como por ejemplo si se añade un pedido de un producto sin ajustar la cantidad disponible para vender.

Para evitar que la integridad de los datos se pierda, se pueden definir reglas que se encargaran de protegerla. Estas reglas pueden definirse en distintos niveles del código de la aplicación:

  • Presentación
  • Fachada de Negocios
  • Reglas de Negocios
  • Persistencia
  • Base de Datos

En general, es mejor si las reglas pueden en los niveles más bajos de la aplicación, porque de ese modo protegerán a los datos de cualquier acción efectuada por los niveles superiores. Por otro lado, entre más abajo en las capas este la regla de validación, más tiempo tardara el sistema en determinar si el cambio es o no valido, lo que podría afectar negativamente la usabilidad de la aplicación.

Asi que lo tipico, en estos tiempos, es hacer las validaciones, simples, y rapidas, en el cliente y las validaciones complejas (que requieren de consultas) tan abajo en las capas de la aplicacion como se pueda, sin embargo, es importante recordar que toda validacion que se ejecute en el equipo cliente puede ser fácilmente violada por el usuario, por lo que un sistema verdaderamente seguro deberia validar redundantemente los datos en el servidor (y ofrecer validacion en el cliente solo como una herramienta para dara una sencacion de mayor rapides para el usuario, y no como algo que verdaderamente garantice la integridad de los datos)

Integridad al nivel de la base de datos Oracle

Oracle valida la integridad de la base de datos y presenta los siguientes mensajes de error en caso de que la integridad haya sido violada:

  • ORA-00001:unique constraint (owner.constraintname) violated
  • ORA-02292: violated integrity constraint (owner.constraintname)- child record found
  • ORA-02290: check constraint (owner.constraintname) violated
  • ORA-02291: integrity constraint (owner.constraintname) violated - parent key not found

Todos estos mensajes aparecen en ingles… y aunque estuvieran en español no están redactados para ser entendidos por personal sin entrenamiento técnico en diseño y/o mantenimiento de bases de datos:

  • ORA-00001:restriccion única (owner.constraintname) violada
  • ORA-02292: restricción de integridad violada (owner.constraintname)- se encontro un registro hijo
  • ORA-02290: restricción de comprobación (owner.constraintname) violada
  • ORA-02291: restricción de integridad (owner.constraintname) violada – llave padre no encontrada

Generalmente estos mensajes son presentados al usuario como:

  • El valor esta repetido
  • Hay registros que dependen de este
  • El valor es invalido
  • El valor es invalido

Lo cual suele dejar al usuario preguntándose: Que valor esta repetido? Que valores dependen de cuáles? Porque este valor esta invalido? No será que el sistema está equivocado?

Mensajes claros para los usuarios

Al usuario, los mensajes muy técnicos, o demasiado cortos y genéricos no le ofrecen una solución aceptable. Lo que el usuario debería ver en realidad, es algo como:

  • ORA-00001
    • Error: El /La (nombre de negocios del campo o campos) “(valor o valores)” esta repetido
  • ORA-02292
    • Error: El /La (nombre de entidad de negocios dependiente) con (valor de llave identificadora) que dependen de (nombre de entidad de negocios padre) (valor de llave identificadora de entidad de negocios padre)
  • ORA-02290
    • Error: El valor (valor) para (nombre de negocios del campo) de la/el (nombre de negocios de la entidad afectada) no es válido, debería (descripción de regla en lenguaje natural)
  • ORA-02291
    • Error: No es posible asociar al (nombre de negocios de la entidad afectada) (identificador de negocios de registro afectado) con la (entidad de negocios padre) (identificador de negocios de entidad padre) por que dicha dependencia no existe

Para darles una idea más clara, a continuación unos ejemplos con información ficticia:

  • ORA-00001
    • Error: El nombreJuan” esta repetido.
  • ORA-02292
    • Error: El pedido con folio 12345 depende del producto con numero de inventario 54321
  • ORA-02290
    • Error: El valor “2009/13+13” para fecha de la factura no es válido, debería tener el formato: “AAAA/MM/DD
  • ORA-02291
    • Error: No es posible asociar al empleado con nombreJuan Pérez” con la dependenciaIngeniería Aeroespacial” por que dicha dependencia no existe.

Además de presentar a los mensajes en un mensaje claro que el usuario pueda entender, es importante poder darle al usuario una interfaz que le permita llegar al dato que está causando el error de forma fácil y directa:

Ejemplo en una forma:

Ejemplo en una tabla editable:

El poder presentar los mensajes de error de esta forma, implica que contenga información estructurada que pueda hacerlos corresponder con elementos de la interfaz del usuario

Aplicaciones AJAX

En una aplicacion AJAX la interfaz (Capa de Presentacion) se ejecuta dentro del browser en el equipo cliente, por lo que la información estructurada con los errores tendría que viajar del WebServer al WebBrowser en un formato que sea fácil de estructurar en el servidor y fácil de procesar en el cliente, y que además proporcione la estructura suficiente para poder indicar que elementos necesitan estar marcados como erróneos:

Ejemplo de estructuras:

Si todo salió bien y no se requiere de otra información del servidor, recibiríamos una respuesta del tipo:

{
    exito:{

    }

}

Si la operación fue exitosa, y además incluye cierta información que ser requiere conocer en el servidor como resultado de la operación (por ejemplo la cve_persona asignado a una nueva persona):

    exito:{
persona:{

cve_persona:1

            }             }

Por otro lado si algo salió mal, entonces la estructura podria ser, por ejemplo, para el caso en que se tienen 2 personas con el mismo nombre:

    fallo:{
table:’persona’,

            llave:{cve_persona:1},

            errores:[

                {

                    nombre:'nombre',

                    campo:'NOM',

                    value:'Juan',

                    mensaje:'El nombre Juan esta repetido'

                }

            ]             }

De esta forma, indicamos que la orden de campo, con un campo “id”, con valor “1” tiene un error, cuyo mensaje es “El nombre Juan esta repetido”. Utilizando esta información, podemos buscar al elemento de la interfaz que esta presentando esta información, y ofrecer un cambio de apariencia enfocado a ayudar al usuario a saber exactamente que elementos de la interfaz es necesario utilizar para ajustar los datos y poder realizar la operación con éxito.

Pero que pasa si se tiene mas de un campo con error (por ejemplo, la fecha de nacimiento es invalida, y además, el nombre esta repetido?) Una posibilidad es detenerse al primer error, forzando al usuario a corregir 1 error, volver a enviar los datos, y luego enviar los datos otra ves al servidor, la otra posibilidad es indicar que varios campos son incorrectos en una misma respuesta:

    fallo:{
table:’persona’,

            llave:{cve_persona:1},

            errores:[

                {

                    nombre:'nombre',

                    campo:'NOM',

                    value:'Juan',

                    mensaje:'El nombre Juan esta repetido'

                },

                {
                    nombre:'fecha de nacimiento',

                    campo:'FECHA_NAC',

                    value:'2009/13+13',

                   formato:'AAAA/MM/DD',

                    mensaje:''El valor “2009/13+13” para fecha de nacimiento no es válido, debería tener el formato: “AAAA/MM/DD”'

                }

            ]             }

Un problema es que definir estas cadenas en código PL/SQL y además hacerlas legibles resulta en cadenas demasiado grandes, por ejemplo en el caso del ejemplo anterior, son 659 caracteres debido a los espacios, y un mensaje de raise_application_error tiene un limite de 2048 bytes (si en UTF-8 son 2048 caracteres, 1 byte por caracter, pero si llegamos a tener algún caracteres especifico del español, como acentos, esos ocuparían 2 bytes).

Una posible solución para este problema, seria remover todos los espacios en blanco superfluos:

fallo:{table:’persona’,llave:{cve_persona:1}, errores:[{nombre:'nombre',campo:'NOM',value:'Juan',mensaje:'El nombre Juan esta repetido'},{nombre:'fecha de nacimiento',campo:'FECHA_NAC',value:'2009/13+13',formato:'AAAA/MM/DD',mensaje:''El valor “2009/13+13” para fecha de nacimiento no es válido, debería tener el formato: “AAAA/MM/DD”'},]}

Lo cual lo reduce a 339 caracteres teóricamente deberíamos poder poner un mensaje 8 veces mas grande en una excepción sin problemas.

Ademas, el mensaje sigue una forma definida, y puede ser generado por parte de la aplicación cliente a partir de plantilla predefinidas, utilizando como base los elementos devueltos por el JSON, de esta forma, puede reducirse aun mas la longitud de la cadena JSON (a solo 188 caracteres):

fallo:{table:’persona’,llave:{cve_persona:1}, errores:[{nombre:'nombre',campo:'NOM',value:'Juan'},{nombre:'fecha de nacimiento',campo:'FECHA_NAC',value:'2009/13+13',formato:'AAAA/MM/DD'}]}

Por otro lado, si nos preocupa llegar a alcanzar el limite, tal vez sería más sano no utilizar procedimientos para las acciones de inserción, actualización o borrado, si no utilizar funciones, que siempre devolverían una cadena JSON indicando su estatus eso podría de permitirnos devolver cadenas de la longitud que nos convenga para cada caso.

Ejemplo en PL/SQL:

raise_application_error(-20001, 'fallo:{table:’persona’,llave:{cve_persona:1}, errores:[{nombre:'nombre',campo:'NOM',value:'Juan' },{nombre:'fecha de nacimiento',campo:'FECHA_NAC',value:'2009/13+13',formato:'AAAA/MM/DD'},]}');

Cuando se lanza un error en PL, debe ir acompañado de un código de error, este código de error podría permitirnos detectar la forma general del error, evitando asi tener que incluir la descripción en lenguaje natural del problema en el JSON. Lo que haríamos seria ir a buscar la forma general del error ORA-20001 que seria El /La (nombre) “(valor)” esta repetido y utilizaríamos la llave para determinar cual de los que esta desplegado en pantalla es el que tiene el problema (por otro lado si no tenemos llave, no podemos hacer eso, lo cual nos limita para el caso de nuevos registros).

Una ves recibida la cadena de error JSON (a través de la excepción lanzada desde Oracle), podemos procesarla fácilmente en Java,  utilizando,por ejemplo un SQLExceptionTranslator que cargue de un archivo xml las “plantillas generales” para cada tipo de error, y las combine usando Freemarker. Por supuesto que, para que Freemarker haga eso, es necesario tener cargarda la informacion de error en una coleccion de objetos java, es trabajo lo puede hacer la las  la biblioteca json-simple, que hace muy facil el convertir el JSON a colección de Java (List o Map). Veamos un ejemplo de como podrian quedar las plantilla de Freemarker:

  • · ORA-00001
    • Error: El ${nombre} ${valor} esta repetido
  • ORA-02292
    • Error: El /La ${nombre} con "${valor} que dependen de ${nombrePadre}${valorPadre}
  • ORA-02290
    • Error: El valor ${valor} para ${nombre} de la/el ${tabla} no es válido, debería ${regla}
  • ORA-02291
    • Error: No es posible asociar al ${nombre}${valor} con la ${nombrePadre}${valorPadre} por que dicha dependencia no existe

Finalmente, ya en el navegador, podemos utilizar alguna biblioteca como JQuery, para modificar la apariencia de los elementos de la interfaz que representen a los datos que no pasaron la validación.

Me pregunto… por que las bases de datos no proporcionan ya de por si mensajes con información estructurada?… seria mas fácil si así fuera… y no conozco ninguna, que proporcione mensajes de error con el nivel de detalle suficiente… ustedes si? es casi como si quisieran complicarnos la vida a propósito :’(

Siguen Uds. una metodología  similar a esta al manejar sus mensajes de error? Siguen otra? Que ventajas y desventajas le ven a esta?

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de Nopalin

mas o menos

Pues no sigo una metodologia tan estricta, tal vez por que estoy traumado por el performance o no se, pero en el cliente solo hago validaciones simples.
En el bean de negocios es donde tengo las validaciones mas comunes, como que un usuario no este repetido, si se relaciona una entidad a otra no existente, si se quiere actualizar el importe de una deuda ya saldada, etc. Además que estan los constrainst en la bd para no perder la integridad.

Esta manera me ha dado muy buenos resultados, si el usuario ve un mensaje de error de error no entendible después de realizar una acción, significa que se me esta pasando una verificación en mi bean de negocios por lo tanto la agrego. Fácil, entendible y simple.

PD. ¿por que todos los ejemplos van relacionados con aplicaciones web?

Imagen de luxspes

Por que web?

Pues... por que para mi desdicha... es en lo que estoy programando ahorita... y en lo que parece que a la mayoria de los clientes le gustan ahorita las aplicaciones (solo hay que ver las capacidades para programacion web, y "no web" en Java, para darse cuenta de cual fue la prioridad de Sun durante los ultimos 10 años... (una pista: Web) ).

P.D. Pero por ejemplo, la imagen de indicadores de error en tabla (y de hecho la otra también, pero de esa si hay ejemplos web),no pude encontrar un ejemplo de algo asi en una aplicacion web (deberia existir, pero encontre a nadie capas de hacerlo, asi que esa es una foto de una aplicacion "no web")

Imagen de luxspes

Error Databinding: (Manual o Automatico)

Lo que mas me gusta de esta metodologia, es que en el JSON viene la informacion de que tabla y que campo tuvo que error, y dicha informacion puede utilizarse para marcar de forma precisa el campo que tuvo el error inclusive se pueden usar las muy rudimentarias facilidades de control de foco del navegador para enviar el foco al (primer) campo con error, algo que mas de un usuario me ha pedido. ¿Como consiguen ustedes esa funcionalidad?

Me encantaria si alguien me dijera que conoce algun lugar de donde se pueda descargar un componete opensource para swing que haga lo mismo que el ErrorProvider de .NET

Imagen de luxspes

No es lo mismo decir...

No es lo mismo decir "en el en el cliente solo hago validaciones simples", a decir "a las validaciones simples solo las hago en el cliente". Lo primero es correcto (siempre que también hagas dichas validaciones en el servidor)... lo segundo (que implica que no las haces en el servidor)... abre la puerta a que te hackeen. A cual de las 2 te referias exactamente?

Imagen de ezamudio

Tapestry 5

Parezco disco rayado. Pero esta página muestra cómo validar entradas del usuario en Tapestry 5. La validación se hace de inmediato en el cliente, con AJAX (T5 incluye Prototype) y nuevamente en el servidor al recibir la petición. Puedes poner validaciones simples como que un campo no se puede quedar en blanco, o que debe ser numérico dentro de un rango, o que sea un email válido, o una expresión regular, una fecha en el pasado (o en el futuro), etc.
Para validaciones de integridad y cosas así, pues no se va a saber qué pasa hasta que se intente guardar datos, en ese caso es cosa de que el error (por ejemplo una excepción) traiga un mensaje útil al usuario para que la página de Tapestry agregue el mensaje a los errores de la forma y se le presente en la página, con el mismo formato que los errores que ya maneja de manera automática.

Actualmente la versión 5.1 trae sus propias anotaciones para validación, lo cual es un rollo si por ejemplo usas la validación de Hibernate; tuve que hacer para una aplicación un convertidor dinámico que detecta anotaciones de Hibernate Validation y le agrega a esos métodos (los getters de beans de dominio) las anotaciones equivalentes de validación de Tapestry.

Pero para la versión 5.2 ya anunciaron que van a usar el estándar nuevo de Java (que no usaban antes porque no existía), y Hibernate ya dijo lo mismo, de modo que ahora en Hibernate podrás usar las anotaciones JSR-303 y se van a validar en Tapestry cuando edites directamente un bean de dominio en una forma.

Imagen de luxspes

Y el foco... y la interactividad Javascript?

Mmmm, cada ves me convenzo mas de que tal ves deberia usar Tapestry... por otro lado, yo también parezco disco rayado: Que hay del foco? Tapestry ya maneja lo de colocar el foco en el primer campo con error automáticamente? o si, por ejemplo, quiero integrar el manejo de error con mensajes tipo growl, que tan facil o dificil es integrar algo como jGrowl con Tapestry?

Del JSR-303.... no estoy seguro de estar convencido de que deberas vaya a ayudar... los ejemplos que he visto en internet todos se quedan en el nivel de validar a nivel booleano si el campo es "valido o invalido" y por lo tanto generan mensajes del tipo "El valor esta repetido" o "El valor es erroneo" y no llegan a ser capaces de presentar mensajes del tipo "El nombre Juan esta repetido" o "El valor “2009/13+13” para fecha de nacimiento no es válido, por debería tener el formato: “AAAA/MM/DD” '". Igualmente, los ejemplos que me he encontrado no cubren casos en los que se necesita consultar a la base de datos para verificar que las cosas esten correctas (como registros repetidos, restricicones derivdas del estado de otras tablas (como no aprobar pedido si no se ha hecho el deposito, o no permitir 2 juntas a la misma hora en el mismo salon). Inclusive tiene problemas para representar validaciones relativamente simples como que la "fecha de matrimonio" no puede ser anterior a la "fecha de nacimiento" (presentando por supuesto, los nombres de lo 2 campos y valores en el mensaje)

Imagen de ezamudio

Foco etc

No recuerdo si Tapestry te pone en el primer campo con error, casi podría apostar que lo hace. No he visto jGrowl, pero tendrías que ver una demo del manejo de errores de Tapestry; digamos que estás en un campo que espera un valor entre 0 y 100 y le pones 500, al dar tab (como es el estándar jajaj) para cambiar de campo, aparece una burbuja sobre el campo que abandonaste, indicando algo como "debes teclear un valor entre 0 y 100" (Puedes personalizar los mensajes de error). Si intentas submit a la forma no te deja por ese error. Si desactivas JavaScript y le das submit de todas formas, te regresa a la misma página y te aparece al principio de la forma un recuadro con todos los errores que contiene.

JSR-303 me parece que trae algo adicional para validación cross-field (cuando los valores de un campo dependen de otro campo, como lo de fecha de matrimonio y de nacimiento), pero evidentemente no manejan reglas de integridad, eso lo seguirás haciendo tú en tu aplicación, pero ya es menos chamba, no? No pretende ser una panacea de validaciones, pero pues para validaciones simples creo que va a simplificar la chamba porque en conjunto con un validador en Swing o JSF o Tapestry o Struts o lo que uses, ya te dejas de preocupar de ese manejo de errores.

Lo de formato de fechas no sé si lo contempla JSR-303 pero T5 actualmente sí valida el formato de fechas y me parece que le puedes dar un rango de fechas válidas (para que en fecha de nacimiento nadie ponga algo antes de 1910 por ejemplo).

Aquí puedes bajar algunos demos para que lo veas en acción.

Imagen de luxspes

Que tan facil es de extender?

No he visto jGrowl, pero tendrías que ver una demo del manejo de errores de Tapestry;

No se ve mal el manejo de errores de Tapestry (aunque siendo sincero, con JSF/Richfaces/Seam se pueden conseguir aproximadamente el mismo efecto). La cuestion es que cuando te piden hacer algo que JSF/Richfaces/Seam no hace "de fabrica" el esfuerzo es demasiado grande... Hay que: Implementar tu annotation en Java, ponersela a tu POJO, ajustar tus componentes JSF (la parte mas dolorosa), implementar una logica espejo en javascript para dar sensacion de velocidad (si es que aplica), etc, etc.

¿Que tan facil (o dificil) es implementar este tipo de extensiones a la validacion con Tapestry?

Imagen de luxspes

Jaxer: Validacion web sin dolor?

Mientras tanto, en el mundo de los programadores Jaxer, se puede implementar la validacion sin violar DRY. Como me da tristeza que no lo hayan implementado sobre Rhino... Ese seria un proyecto de tesis interesante, migrar Jaxer a Rhino y poder hacer esto:

<script type="text/javascript" runat="both">
            function funcionEnJavaScript() {

            };
</script>

Como el "runat" esta en both, esta funcion javascript se invocara de forma transparente tanto en el cliente como en el servidor, asi que si la usas para validar datos, puedes estar seguro de que el usuario no podra "darle la vuelta"

Imagen de ezamudio

sección "Tracking Validation Errors"

En la liga que mandé de input validation en Tapestry, lee esa sección. No sé si es a lo que te refieres con extenderla. Puedes extender la clase AbstractValidator de Tapestry para implementar validaciones que no se incluyan, que son Required, Max, Min, MaxLength, MinLength, Email y Regexp.

La sección "customizing validation messages" también es útil. Ya viste que son validaciones en campos; las puedes hacer muy sofisticadas usando expresiones regulares. Aparte puedes convertir entre cadenas y otros tipos (como fecha por ejemplo) usando FieldTranslators (sección "overriding the translator with events").

Imagen de luxspes

Extender: Nuevos tipos de validaciones, y otra presentacion

En la liga que mandé de input validation en Tapestry, lee esa sección. No sé si es a lo que te refieres con extenderla.

Por lo que me dices, esta el AbstractValidator de Tapestry para implementar validaciones que no se incluyan, pero, lo que me pregunto, es de que modo puedes amarrar las validaciones ya existentes(u otras nuevas) con algún modo de presentación especifico. En otras palabras, lo que preocupa es la integracion de comportamiento nuevo no solo por la parte de Java, si no tambien por la parte de JavaScript

Ejemplo: Mi usuario quiere que los mensajes de error le aparezcan con jGrowl o con jQuery Dialog o alguna otra apariencia personalizada... que habria que hacer? que tan latoso resulta? que facilitades te da Tapestry para poderte comunicar con javascript? Maneja Tapestry un API Javascript para facilitar la vida a la hora de crear componentes con uso intensivo de javascript? o supon que quiero "disparar" un comportamiento en el cliente, personalizado, solo necesario para una pantalla... me provee Tapestry con algún API javascript que me permita "reaccionar" y saber que hubo un error de validación y llamara a una funcion javascript en consecuencia?

Imagen de luxspes

Tapestry + Custom Components in JavaScript: Facil o dificil?

Por ejemplo, veo aqui, que Tapestry usa Blackbird para ayudarte a depurar interacciones javascript, no conocia Blackbird, pero se ve excelente! Se puede decir que Tapestry se integra bien por el lado de logging con Javascript, pero me gustaría saber si también lo hace en otros sentidos (por ejemplo, si yo quisiera integrar un mecanismo de logging diferente de Blackbird... seria muy dificil?)... Se que puedo averiguar al respecto googleando, pero me gustaria conocer la opinon al respecto de alguien que ya lo haya intentado ...

Imagen de ezamudio

AJAX

El soporte de AJAX en Tapestry, hasta donde yo he visto, consiste en cosas como lo siguiente:

  • Puedes preguntarle al objeto Request si es parcial o completa (parcial es una petición de XmlHttpRequest, completa pues es normal del navegador). Esto te sirve dentro de métodos de tu página que pueden ser invocados en ambos casos.
  • Puedes implementar métodos que devuelven estructuras JSON (Tapestry trae clases para manejar estructuras JSON aunque no es necesario que las manejes directamente) e invocarlos desde Javascript (T5 trae incluido Prototype pero por supuesto puedes usar jQuery si lo prefieres o alguna otra cosa).
  • La manera en que Tapestry recibe parámetros en sus peticiones se presta bien para invocaciones parciales desde Javascript. No me lo sé de memoria bien pero por ejemplo si quieres invocar el método buscar en la página Persona y pasarle el nombre "juan", el URL es algo como http://host/app/persona.buscar/juan ; el método buscar lo puedes implementar como public List<String> buscar(String nombre) y dentro del método puedes invocar request.isXHR() para saber si estás respondiendo a una petición AJAX o a una normal (si es que te interesa hacer cosas distintas en uno u otro caso). Tapestry codifica la lista como JSON y la devuelve para que la puedas parsear en el Javascript que hizo la invocación.

Ah y cómo obtienes la petición? En tu clase con IoC:

public class Persona {

@Inject
private Request request;

}

Tapestry trae una cosa que se llaman mixins, uno que viene incluido es para autocompletar cosas, ya sabes el típico que tecleas algo en un campo de texto y te aparecen opciones; lo puedes tener en una forma sin teclear una sola línea de Javascript.

Otra cosa que usa AJAX y que puedes usar sin teclear nada de JS es lo de actualizar regiones de una página. En mi blog puse algo de eso.

Imagen de ezamudio

Blackbird

No sé si sea fácil sustituir Blackbird por otra cosa. Antes no había nada y ahora trae Blackbird, que es una maravilla porque te aparece la ventana con un como stacktrace de Javascript, y trae incluso algo de info de lo que pasó en el server, etc. así que no he visto cómo quitarlo o sustituirlo porque para empezar no sé si haya otra cosa similar y segundo, está muy bueno el blackbird pero tampoco creas que lo he usado a fondo; me ha servido mucho para ver los errores y me ayuda a resolverlos por la info que me despliega, pero pues trae como hasta una línea de comando la cual no tengo idea cómo usar (no he tenido la necesidad).

Imagen de luxspes

Blackbird: El chiste no es lo que hace, si no la integracion

La pregunta no iba orientada tanto al uso de Blackbird, mas bien a los "integration hook" que posiblemente tenga Tapestry para poder hacer callbacks hacia JavaScript y reportar su estado (lo que permitiria integrarlo con Javascript de poderosas e interesantes maneras)

Imagen de luxspes

Tapestry AJAX

Puedes implementar métodos que devuelven estructuras JSON (Tapestry trae clases para manejar estructuras JSON aunque no es necesario que las manejes directamente) e invocarlos desde Javascript (T5 trae incluido Prototype pero por supuesto puedes usar jQuery si lo prefieres o alguna otra cosa).

Se parecen muchismo a las de json-simple, excepto por una (en mi opinon importante) carencia por parte de Tapestry, las clases equivalente de de json-simple implementan las interfaces de java Collections. Es decir, en mi opinión, org.apache.tapestry5.json.JSONObject deberia implementar java.util.Map y org.apache.tapestry5.json.JSONArray deberia implementar java.util.List, el que en las clases equivalentes (y practicamente con el mismo nombre y API) en json-simple si implementen esas interfaces, las hace mucho mas facil de manipular (por ejemplo si lo requiero, puedo pasarle un JSONObject a NamedParameterJdbcTemplate.queryForList

La manera en que Tapestry recibe parámetros en sus peticiones se presta bien para invocaciones parciales desde Javascript. No me lo sé de memoria bien pero por ejemplo si quieres invocar el método buscar en la página Persona y pasarle el nombre "juan", el URL es algo como http://host/app/persona.buscar/juan ;

Eso suena muy muy interesante, eso significa que puedo acceder al API de mis componentes mediante URLs pero al mismo tiempo puedo binder esa API con elementos de formas? como una fusion entre DWR y JSF... suena muy poderoso, tendre que hecharle una mirada a fondo :-)

Otra cosa que usa AJAX y que puedes usar sin teclear nada de JS es lo de actualizar regiones de una página. En mi blog puse algo de eso.

Si, eso es comodo, tambien existe en JSF+RichFaces, el problema es que con interfaces complejas llegue rapidamente al limite de lo que se puede hacer con regiones de refrescado (al menos en Richfaces, este es un caso mas de "fundido de foco", las regiones de refrescado construyen nodos del DOM de la pagina, y si te toca la mala suerte de que el nodo reconstruido es el que iba a recibir el foco, pues el foco se pierde (RF-7706)... los refrescados de Tapestry si conservan el foco? otro problema era cuando tenia que refrescar una región dentro de un componente que aparecía mas de 1 ves en una pagina, Richfaces simplemente se confundia y refrescaba siempre a la instancia del componente que encontraba primero (RF-7706)... Tapestry no sufre de eso?).

Debido a ese y otras limitaciones con el refrescado por region para interfaces con interacciones complejas, prefiero hacer yo mismo mis refrescados con javascript. pidiendo JSON del servidor... definitivamente tengo que averiguar mas de ese aspecto de Tapestry...

Imagen de ezamudio

Refresh parcial

La primera vez que hice un refrescado parcial de una página, dentro de un ciclo, me topé con lo que dices; se refrescaba el primer elemento en vez del que debía ser. Pero le di la vuelta, no me acuerdo ahorita ni cómo... pero puedes revisar la página de detalle de pregunta en el proyecto de JavaMexico 2.0, ahí es donde le di la vuelta a ese rollo. Bueno realmente lo importante creo que está en un componente reutilizable, que es una liga para poner contenido, condicional a que estés registrado en la página. Es decir si un visitante ve la página, la liga dice "Regístrese para contestar" por ejemplo, mientras que a un usuario ya registrado le aparece "Contestar esta pregunta"; en el primer caso, al activar la liga, aparece un pequeño panel de login pidiendo usuario y password y si pones bien tus datos, se sustituye por el campo de texto para agregar respuesta. En el segundo caso, se sustituye la liga directamente por el campo de texto para contestar. El componente se puede reutilizar en otras páginas, se le indica el texto que debe aparecer al usuario ya registrado, el texto al visitante, y el componente de hecho rodea el contenido que debe aparecer cuando un usuario registrado activa la liga.

Imagen de Nopalin

Aplicaciones swing

Me referia a eso, a que en el cliente solo hago validaciones simples del tipo: que el email escrito en un campo sea realmente un email, que la longitud de una cadena tenga una cantidad de caracteres como minimo, a que se obligue a capturar un campo.. etc.

Y para aplicaciones swing, existen unas librerias para validaciones llamadas jgoodies (en realiad son todo un conjunto de librerias para facilitar la programcion en swing, que en lo personal encuentro bastante utiles, al menos la de forms y looks). Y aparte existe el framework basado en spring llamado spring rich client platform, que esta muy bien pensando y tiene todo un enfoque para realizar validaciones en las forms. No hace validaciones verificando la info en la base de datos, sin embargo ofrece una muy buena manera de retroalimentación al usuario para cosas simples, como las que mencione arriba.

Realmente la única diferencia entre tener un sistema basado en web y otro en el escritorio, es que para el segundo se necesita instalar la java virtual machine, y que para estos tiempos ya casi todo mundo tiene, es como una obligacion junto con flash.

saludos

Imagen de ezamudio

Swing vs. Web

Realmente la única diferencia entre tener un sistema basado en web y otro en el escritorio, es que para el segundo se necesita instalar la java virtual machine

No opino lo mismo (y algunos otros tampoco)... aquí una discusión al respecto.

En cuanto a validaciones para swing, jGoodies está muy bonito pero no es software libre, ni es un estándar tampoco. Lo bonito del JSR-303 es que permitirá usar exactamente las mismas anotaciones. Si programas un objeto de dominio (digamos la entidad Usuario), le pones anotaciones de JSR303 y ya será cosa de los frameworks procesarlas: Tapestry en web (o ICEFaces o quien sea; la idea es que un framework de web decente deberá procesar esas anotaciones de alguna forma), Hibernate al momento de persistir, y en Swing pues ya será jGoodies o algún otro framework quien procese estas anotaciones durante la captura.

Imagen de luxspes

Y las vuelves a validar en el servidor?

Me referia a eso, a que en el cliente solo hago validaciones simples del tipo: que el email escrito en un campo sea realmente un email, que la longitud de una cadena tenga una cantidad de caracteres como minimo, a que se obligue a capturar un campo.. etc.

Y todas ellas, las vuelves a validar en el servidor?

Imagen de luxspes

Refresh funde foco

Y el caso del fundido de foco? RF-6039. Si, por ejemplo, tengo 2 controles, digamos un input textbox y un select combobox y:
en "onblur" del textbox tengo que resfrecar al combobox, pero ademas el combobox es el elemento que recibe el foco cuando este abandona al textbox... en richfaces, lo que ocurre es que como el refrescado destruye al combobox y lo crea otra ves, el foco se pierde... que ocurre en Tapestry?

Imagen de ezamudio

NPI

Desconozco lo que pase en ese caso con Tapestry. Pero pues probablemente lo mismo; hay manera de darle la vuelta el whateverFaces? Se puede solucionar por ejemplo en una app con JSP solito aunque sea mucha talacha? o es bronca del navegador o de la spec de HTML/Javascript/loquesea?

Imagen de Jvan

Jajaja

excelente respuesta, para mi que es problema de la computacion, por no hacer los focos multifuncionales y perfectos jajaj

Imagen de luxspes

mas bien de la computacion WEB

mas bien de la computacion web... si no fuera por el html y los browsers todo seria perfeccion y armonia con los focos ;-) (Ciertamente no dan problemas en Swing, o en SWT, o en WPF, o en GTK+ o en Motif o en Win32 o en...)

Imagen de luxspes

AJAX por Refrescado = Ajax por recreacion de elementos UI

Al parecer, este problema de fundido de foco, es derivado de hacer AJAX con frameworks que funcionan "por recreacion de fragmentos the interfaz".

Si te pones a pensarlo, en una aplicacion tradicional, de escritorio, cuando quieres cambiar el texto de un inputtext, o de combobox, no quitas al inputtext y pones otro. Lo que haces es asignarle el valor que quieres ver al input text. Pero en AJAX por refrescado, quitas al elemento del DOM y pones uno nuevo. Seria como si en Swing en vez de hacer esto:

/* Esto ocurrio al crear el Panel
JPanel panel = new JPanel(....
JTextField nombreField= new JTextField();
nombreField.setName("nombre");
panel.add(nombreField);
Ahora, pongamosle un texto: */

nombreField.setText("Juan Perez");

Hicieramos esto:

/* Esto ocurrio al crear el Panel
JPanel panel = new JPanel(....
JTextField nombreField= new JTextField();
nombreField.setName("nombre");
panel.add(nombreField);
Ahora, pongamosle un texto:*/

for(Component component : panel.getComponents()){
if("nombre".equals(component.getName())){
   panel.remove(component);  
    break;
}
}
JTextField nombreField  = new JTextField();
nombreField.setName("nombre");
nombreField.setText("Juan Perez");
panel.add(nombreField);

Parece de locos no? Pues en Web con AJAX por refrescado... es lo normal!

En Richfaces, el modo de darle la vuelta es mantener una variable global en el cliente (en javascript) que recuerde el "Id" del elemento que tenia el foco antes del ultimo proceso de recreacion de UI, y luego, cuando el proceso AJAX a terminado, asignarle el foco al nuevo elemento (que ahora tiene el Id del destruido).

Yo diria que es una bronca del modo como se hacen los refrescamientos AJAX... son demasiado burdos (e ineficientes), tumban todo un inputtext cuando podrian hacer un diff y solo cambiar el value (que es lo que esta diferente) en ves de cambiar todo el control (e imaginate si ademas ese control ya tiene conectados diferentes eventos, o personalizada su apariencia, todo eso hay que volverlo a hacer en cada recreacion)... eso hace que las aplicaciones AJAX programadas a base de javascript siempre sean mas eficientes que las basadas en refrescado (imaginate cuando lidias con una tabla editable, hay que reconstruir toda la tabla, en ves de solo "bindearla" con el nuevo conjunto de datos).

Imagen de ezamudio

No siempre reemplazas

Todos los frameworks de AJAX quitan el elemento y lo sustituyen por otro? Yo recuerdo haber hecho alguna vez a patín una función en javascript que refrescaba las opciones de un combo según lo que seleccionas en otro combo, y lo que hacía era quitar todos los elementos del combo y volverlo a llenar, pero no lo quitaba y ponía otro... no creo que ningún framework AJAX haga lo mismo...

Imagen de luxspes

Richfaces lo hace, no se si otros, pero sospecho que si...

Bueno, es un hecho que RichFaces lo hace... la pregunta es... lo hace Tapestry? No lo se, lo que se, es que los framewoks basados en Javascript (ExtJs, YUI, Spry, jQuery, etc) no lo hacen (bueno, en realidad no es que no lo hagan, es que como se hace de un modo mas "manual" es inmediatamente obvio para el desarrollador que tumbar todo no seria lo mas conveniente) y que Richfaces me hizo pasar un trago amargo por esta peculiaridad... por eso te preguntaba sobre la influencia del refresh de Tapestry en el foco quiero saber si es un problema unico de Richfaces, o si es mas bien un error comun a las frameworks que implementan Ajax por refrescado (como Tapestry, JSF 2.0, Richfaces, ICEFaces, ASP.NET, etc, etc).

Imagen de Nopalin

En realidad queria dar a

En realidad queria dar a entender de que es mucho mejor hacer un sistema de escritorio que uno web. Creo que no se notó la ironía del asunto.

Imagen de Nopalin

y el dbms

así es, y luego las valida la base de datos (bueno no todas, como por ejemplo la del email no se podria).
El asunto es mantener la integridad de la información, no? ahora la pregunta mas bien es: en que momento informarle al usuario de que está violando una regla. Yo prefiero que la validación la haga mi bean de negocio, por si existen varios clientes, no estar preocupandome si le agregue tal cosa o no.

sobres

Imagen de luxspes

Bean de negocio: como reportar a interfaz

Estoy de acuerdo con lo de hacer la validacion con el bean de negocio (claro que no siempre se puede, por que a veces en donde trabajas a algunos les gustan mas los stored procedures) lo que si es que es mejor validar en una capa baja, precisamente por por si existen varios clientes, para no estar preocupándonos si le agregue tal cosa o no. Pero ahi es donde entra la pregunta.... ¿Como reportas tu tus errores desde el Bean? Lanzas una excepcion? Manejas un objeto "contexto de error"? manejas un Map con los errores por Bean? En que momento validas? Cuando cambia la propiedad? con insertas al objeto en el UnitOfWork? Cuando haces commit de la transaccion? Como preparas a tus widgets the Swing para que reciban informacion de tus beans?

Imagen de Nopalin

excepciones

lo de manejar un contexto de errores se me afigura a que tu bean de negocios primero hará los checks, por cada fallido lo agregas a tu contexto y antes de iniciar con el update checas, si hay errores pues no inicias y retornas tu contexto de error.

Bueno, sinceramente no creo que esto tenga mucho impacto, el chiste es notificar al usuario con un error entendible no? Yo acostumbro en mi beans de negocios tener 2 procesos en un mismo metodo, primero realizo todas las verificaciones, si falla alguna lanza una excepción. En mi cliente cacho esa excepción y muestro un dialogo de error con el mensaje de la excepción. Si la llamada en el cliente se hizo desde un dialogo, la excepcion se cacha antes de cerrar el dialogo para que el usuario pueda modificar solo los errores. Y la segunda es el procedimiento, que en este punto no deben existir errores así que cualquier commit deberia funcionar. Claro que no todo es perfecto, tal vez se me estan pasando checks en el bean que la base de datos si tiene, y el usuario veria un mensaje no entendible, pero bueno nadie es perfecto, solamente agrego el check al bean cuando se necesite.

Y otro caso mas extremo que se da en ocaciones muy aisladas es cuando por ejemplo, tu objeto de dominio debe tener por ejemplo una relacion de pagos que en total sumen un importe y no se debe pasar. Si dos usuarios dan clic al mismo tiempo en pagar, el sistema no deberia crear los dos movimientos. Eso yo lo soluciono conel versioning, pero ese tipo de errores ni como detectarlos y a fuerzas el usuario verá un mensaje no muy claro. Pero bueno, todo sea por mantener la integridad de los datos.

sobres