jueves, junio 28, 2007

Publicitando SOA

En el sitio de Tibco, en la imagen central, hay una pestaña que dice "SOA". Al seleccionarla, aparece un recuadro con una serie de declaraciones graciosas sobre SOA: primero, una serie de personas anónimas pelean por la atención del usuario, y en el portafolio de uno de ellos se lee: "Muchos vendors ofrecen una solución SOA". Luego, explican: SOA --> Same Old Approach. Aparece a continuación una persona y a su lado pasan rápidamente gabinetes que dicen SOA, al estilo de Matrix. "Hay mucho SOA allá afuera". "Necesitas alguien que tenga blood, sweat and years".
Hay tres o cuatro pequeños episodios similares en corriendo uno tras de otro, en el que ofrecen entre otras cosas varias acepciones de SOA: Sell Old Applications, Sad Opportunistic Attempt, Seriously Over-Advertised, Shovel On Adjectives,.. Cuando muestran imágenes que representan las soluciones SOA de la competencia, le ponen siempre un asterisco; el asterisco lleva a una letra pequeña que empieza corriendo lentamente y acaba a toda velocidad, y que dice algo como "Aplican restricciones. No se garantiza la interoperabilidad con productos que no cumplen con las especificaciones de ..."

Me pareció muy buena propaganda, y conociendo a Tibco, seguramente tienen un producto bueno para respaldar su marketing.

Sin embargo, no creo que el quid del SOA esté en
la interoperabilidad. Desde siempre me ha parecido que SOA es una gran idea pero que no puede ser aplicada como panacea: hay muchos puntos todavía "asterisqueables" en las especificaciones de web services, como transacciones, orquestación, autenticación, incluso attachments. Creo que el verdadero problema es crear fácilmente clientes genéricos que consuman los servicios, y que sepan descubrirlos y utilizarlos de una forma sencilla. Creo que antes de comprar una solución SOA se debe averiguar si es SOA-Server o SOA-Client o ambas, y qué tipo de generación de código utiliza. En mi mente, el SOA-Server siempre tiene que venir acompañado de un Application Server o de un motor de BPM (Business Process Management), de lo contrario básicamente te están dejando a ti la tarea de hacer deploy manual de los servicios generados. Supongo que existirán herramientas que sepan hacer este deploy en multitud de servidores, pero me parece que, a menos que siempre utilices el servidor preferido por tu vendor, corres el riesgo de que, cuando las cosas no funcionen o se caigan, te encuentres con dos compañías apuntándose con el dedo entre sí y tú enmedio sintiéndote miserable.

He visto muchas soluciones que convierten en web service una clase cualquiera de Java (un POJO) y me parece que es suficientemente sencillo hacer esto, pero en cuanto a la generación de clientes de web services, es decir, de programas que sepan consumir esos web services, tienen demasiadas deficiencias: generan código repetitivo e ininteligible, crean clases "helpers" al por mayor, no utilizan soluciones genéricas y se parecen demasiado a un EJB (en el mal sentido). En un proyecto en particular en que estuve, dos equipos tuvimos que hacer la integración con una nueva interfase (web services, por supuesto) hacia el sistema central. El otro equipo utilizó la generación de código del WebSphere Developer Studio, y yo me limité a programar un cliente genérico basado en Axis. La interfase consistía en ocho o nueve servicios, y mi solución quedó implementada con tres clases y un archivo de configuración (además de las librerías necesarias); la otra solución la vi en su forma final y utilizaba unas 100 clases, más o menos 10 por cada servicio. Cada vez que creaban un cliente para algún servicio en particular, echaban a andar el wizard y ¡voilá! quedaba mágicamente funcionando el código. Claro que todavía hacía falta integrarlo con sus propias clases, pero eso es irrelevante; lo interesante es que conforme se fueron agregando servicios al sistema central, me llamaban para preguntarme "¿Cuándo puedes venir para que nos crees el código que hace falta? Es que ya hay tres nuevos servicios..." Yo les decía que no hacía falta, que simplemente abrieran el archivo de configuración, agregaran una línea que apuntara al WSDL, otra en la que especificaran el nombre del método a utilizar, y cambiaran el JSP que enviaba los datos (lo hice como JSP porque no tenían idea de qué era Ant y no estaba dispuesto a "migrar" mi proyecto al Developer Studio aquél). Para el otro equipo, siempre hacía falta que fuera un consultor un par de días a hacer los cambios en las clases y echar a andar nuevamente el wizard. Al cabo de un par de iteraciones, el cliente les pidió que cambiaran su código para utilizar clases parametrizables, porque ya estaba harto de que le cobraran cada vez que hubiera cambiecitos. Y eso, a mi entender, es el valor principal de una solución SOA: poder generar fácilmente la infraestructura para consumir servicios con nada o poco de código, sin tener que ensuciarse las manos con el parser de XML ni la especificación de SOAP, o preocuparse por las versiones de las librerías.

Claro que siempre es útil saber someramente cómo funciona la solución, porque siempre sale un pelo en la sopa: la librería que utiliza tu solución es incompatible con tu app server, la versión de Java es distinta, o la versión de SOAP es la que te ocasiona problemas, el WSDL no es visible desde internet (o sí es visible pero el servicio no), etc...
(Y por cierto, para hacer debugging de web services no hay como el TCPMon de Axis - org.apache.axis.utils.tcpmon- ).

Así que ya quedamos en que no hay sustituto para estar informado y saber más o menos cómo "trabaja" la solución, pero el ideal es por supuesto llegar a un nivel de abstracción y estabilidad que no tengamos que preocuparnos por detalles, así como ocurre mientras escribo esto, y no estoy pensando en si mi Centrino Dual Core 2 estará dividiendo correctamente las tareas en procesos simultáneos virtuales, o si el Firefox no tendrá algún problema con cajas te texto de más de 1,500 caracteres...

Y por cierto, ya le corto aquí, para no hacer corajes si en efecto alguna de estas cosas está funcionando mal. Uno nunca sabe.

miércoles, junio 27, 2007

Ajax en ASP (sin ASP.net)

Leí ayer en un blog (http://warneronstine.com/blog) una buena idea: escribir siempre en el blog los problemas encontrados y las lecciones aprendidas en el desarrollo de un proyecto. Acabo de terminar un proyecto y tengo varias cosas en la mente, así que ahí van.

Este es un proyecto en ASP, era la modificación a un sitio existente, de modo que migrarlo a ASP.net o algo aún mejor no era una opción. El problema es sencillo de enunciar: para subir cierto contenido, existen actualmente unas determinadas categorías, lo que quería el usuario era tener subcategorías (sólo un nivel) y que ahora el contenido se subiera a ellas. Sencillo, típico. Latoso, también, por la plataforma, y por tener que hacer las consabidas pantallitas de alta, baja, modificación de subcategorías, etc.

De modo que, intentando divertirme mientras lo hacía, busqué algún widget que me dejara hacer todo eso pero con Ajax. Al principio no fue fácil encontrar algo que fuera puramente para ASP, pero me topé con un artículo y una demo que me hicieron las cosas fáciles: http://aspwebsolution.com/articles/xmlrep/ajax_treeview1/index.htm. Es un "tree view" que carga las "ramas" vía Ajax, y me pareció atractivo por varias razones:
  • La pantalla existente traía la lista de categorías de la base de datos, de modo que no tendría que cambiar el query inicial
  • Para cada "rama", sólo habíra que modificar el query que se hacía vía Ajax, de modo que la nueva funcionalidad entraría más o menos fácilmente, el famoso unobtrusive que tanto hay que perseguir, según nos dicen los gurús.
  • De otro modo, si hubiera querido pintar de un sólo jalón tanto categorías como subcategorías, el query habría estado complejo, al menos con los cambios que planeé a la base de datos.
Y por cierto, estos cambios eran sólo agregar una columna a la tabla de categorias, id_padre, que apuntaría obviamente al ID del padre del nodo en cuestión, y que tendría un valor de cero cuando el nodo fuera nodo raíz.

Realmente fue sencillo adaptar el arbolito para que desplegara categorías y subcategorías, quedando de esta forma:



Pero todavía hacía falta el CRUD, o en español, el ABC para las categorías. Es decir, permitir que se modificara el nombre de una categoría o subcategoría, borrar, agregar, etc. Decidí utilizar también Ajax para esto, intentando adaptar el script para el arbolito. Mi idea era que cuando alguien seleccionara "agregar subcategoría" apareciera una cajita de texto en la que se pudiera capturar el nombre, se le diera "Enviar", y el arbolito se actualizara automáticamente, todo sin cambiar de página.

Bueno, esto fue lo interesante, en realidad. El autor del código del arbolito había hecho algunas cosas muy lindas y genéricas, pero no estaba pensado para hacer cajitas voladoras ni actualizaciones mágicas. La solución original del treeview era como sigue:

  • La página principal crea el árbol vía javascript puro, y utiliza un div para señalizar el lugar donde se dibujará.
  • Todo el javascript está en el archivo scripts.js, que a su vez llama a ajax.js. Este último tiene el código consabido del XMLHttpRequest, pero está amarrado para que siempre, al llegar la respuesta, sea llamada la función processReqChange(). Me dije "aquí es donde voy a tener que poner mis ifs", pero me esperaba una sorpresa:
  • El autor tuvo el buen gusto de devolver todo el procesamiento en un XML, y después aplicarle un XSL para darle formato. Esto hizo maravillas para la sacrosanta Separación de Código: los archivos que modifican base de datos quedaron por un lado, el "controlador" del flujo en otro, y el archivo que controla el desplegado en un XSL. Por fortuna, mi mamá ya me había dicho que saber XSL me iba a llevar lejos, así que no fue un problema.
  • Y algo mejor: el XML que devuelve el "modelo" tiene dos partes principales:

    1 <response>
    2 <method>getUpdateResponse</method>
    3 <result>1</result>
    4 </response>

    La segunda parte, result, trae el resultado de la operación, y ahí es donde está el XML que es convertido desde el XSL; la primera parte, method, es la que me pareció muy ingeniosa: trae el nombre del método con el que debe procesarse el resultado. Esto me hizo un click inmediato: ¡sólo cambiando el nombre del método podría hacer que el mini-framework sirviera para hacer nuevos trucos!
De modo que mi solución fue simplemente poner los consabidos ifs en las partes del código que mandaban llamar al generador de XML, que cada uno de ellos devolviera el nombre del método que debía procesar la respuesta, y agregar ifs adicionales en el método que procesa las respuestas vía Ajax. De modo que el código asp que hace inserciones en la base de datos quedó como sigue:

1 <response>
2 <method>getUpdateResponse</method>
3 <result><% Call insert(id, value) %></result>
4 </response>

...y algo similar hice para el borrado y modificado de categorías (la función insert(id, value) simplemente ejecuta un insert en la base de datos). Por supuesto, la función insert devuelve un 1 cuando la operación fue exitosa y el número de error cuando no. En el caso de las funciones que necesito que generen HTML, devuelvo directamente el XML con el resultado.

El resto del trabajo fue simplemente agregar un poco de javascript para aparecer y desaparecer layers, y lograr el cómodo efecto de no tener que refrescar la página para tareas tan simples como agregar una rama o modificar su nombre:



El último detalle digno de mencionarse de cómo quedó la solución es que me pidieron que las categorías principales no pudieran tener contenido, sólo las ramas. Inicialmente pensé que esto tendría que lograrlo con alguna serie de validaciones, sin embargo simplemente quité en el XSL la liga que se mostraba en categorías "raíz", y ya: sólo las subcategorías tienen ahora link hacia la página de detalle. Claro, un usuario listillo podría ver que estoy enviando el ID de la categoría y cambiarla en el URL, pero estamos hablando de un sistema que da servicio sólo a dos usuarios, de modo que no me preocupa.