viernes, 16 de noviembre de 2012

Como enviar un archivo con Apache Tapestry

Apache Tapestry
Si en la entrada anterior explique un ejemplo de como devolver un archivo con Apache Tapestry desde el servidor al cliente en esta vamos a ver la operación contraria, enviar un archivo del cliente al servidor con Apache Tapestry a través de un formulario html.

En una aplicación web el enviar un archivo al servidor se realiza con un campo <input type="file" /> pero en el servidor se trata de forma especial para recibirlo. Dado que no es tan simple recibir un archivo como recibir un dato de un campo file como de otros campos (text, radio, select) que son únicamente una cadena lo habitual será usar alguna librería que nos facilite la tarea o a través de las facilidades que nos proporcione el framework que usemos.

Con Tapestry para subir un archivo al servidor tenemos que modificar el html para el formulario e indicar el atributo enctype="multipart/form-data" de la siguiente forma:

En Tapestry disponemos de un componente que generará el html de un <input type="file" />:

En la parte del servidor en el código java de la página o componente tendremos un atributo para recibir el archivo seleccionado por el usuario (de nombre igual al atributo value del componente t:upload):

Esto será suficiente para recibir el archivo, con la referencia al objeto UploadedFile podremos obtener el nombre del archivo, tamaño y el InputStream para obtener su contenido. Finalmente, para hacer la subida de archivos en Tapestry necesitaremos incluir la siguiente dependencia en la versión específica que usemos:

Si desarrollamos una aplicación para ser ejecutada en el Google App Engine (GAE) deberemos hacer otras modificaciones para que la subida de archivos funcione correctamente. Añadiremos al descriptor del GAE appengine-web.xml la siguiente propiedad y redefiniremos el servicio FileItemFactory de la forma en que se hace en los siguientes enlaces de ejemplo [1], [2]:

En el código fuente de la siguiente aplicación se puede ver un ejemplo completo y fucionando.

Referencia:
http://tapestry.apache.orghttp://tapestry.apache.org/uploading-files.html
Documentación sobre Apache Tapestry

sábado, 10 de noviembre de 2012

Como devolver un archivo con Apache Tapestry

Apache Tapestry
En esta entrada voy a explicar como enviar un archivo al cliente desde el servidor usando el framework Apache Tapestry. Esto es necesario si por ejemplo nuestra aplicación necesita generar un informe ya sea en formato txt, excel, pdf, un archivo comprimido o de cualquier otro tipo.

En tapestry el proceso no es muy complicado, tan solo deberemos devolver un objeto que implemente la interfaz StreamResponse en el manejador de evento («event handler») que procesa la petición.

Aquí el código de parte de un manejador de evento que construye y devuelve un objeto que implementa la interfaz StreamResponse:

Una posible implementación de la clase StreamResponse podría ser la siguiente que podríamos reutilizar siempre que tengamos que devolver un archivo:

Si estuviésemos devolviendo un archivo de texto y si el cliente acepta compresión gzip según las cabeceras que nos ha enviado en la petición (Accept-encoding: gzip) podríamos devolver el resultado comprimido con este formato ahorrado ancho de banda y muy posiblemente reduciendo el tiempo de descarga del archivo. Haciendo uso del servicio ResponseCompressionAnalyzer y del método isGZipSupported podemos averiguarlo de forma muy sencilla.

En la siguiente entrada de documentación sobre Apache Tapestry puedes encontrar más entradas, ejemplos y documentación.

Para finalizar, decir que la forma indicada en este ejemplo para convertir el OutputStream al InputStream que necesita el objeto StreamResponse puede ser mejorado con el uso de un PipedInputStream. La manera indicada en el ejemplo con el ByteArrayOuputStream obliga a mantener en memoria el contenido completo generado antes de ser enviado al cliente además de no empezar a enviarlo hasta que se genera por completo, si se trata de unos pocos MiB puede ser suficiente pero si son varias decenas la aplicación puede presentar algún problema en el uso de memoria.

Referencia:
http://tapestry.apache.org
http://betterexplained.com/articles/how-to-optimize-your-site-with-gzip-compression/

sábado, 3 de noviembre de 2012

Convertir imágenes entre formatos y cambiar tamaño con ImageMagick

ImageMagick
Algunas personas que trabajan con muchas imágenes seguramente se han encontrado en la situación de querer convertir imágenes de un formato a otros o cambiarles de tamaño de forma masiva. Hacerlo de forma manual es un trabajo aburrido y repetitivo además de consumir mucho tiempo que será mayor o menor dependiendo de la cantidad de imágenes a tratar. En esta situación ImageMagick y unos pocos comandos en la terminal de Linux nos pueden ahorrar una buena cantidad de tiempo además de evitar posibles errores al hacer las cosas manualmente. Para convertir imágenes de un formato a otro podremos hacerlo con:

Esto convierte todas las imágenes de extensión png del directorio actual a imágenes en formato jpg cambiándoles además a los nombres de los archivos la extensión de «.png» a «*.jpg». También podríamos cambiar el tamaño de las imagenes a una determinada anchura máxima o a una altura máxima de forma masiva con:

El primer comando reescala la anchura de las imagenes a 400 píxeles y el segundo a 300 píxeles de altura en ambos casos manteniendo la proporción de las imágenes originales y en ambos casos sobrescribiendo la imagen original.
También podemos hacer uso del comando específico para realizar escalados:

Para instalar ImageMagick en Arch Linux basta con instalar su correspondiente paquete después de lo cual dispondremos de los comandos convert y mogrify:

Otra cosa que a veces es necesaria es añadir marcas de agua a las imágenes, para esto ImageMagick también es muy útil y se puede automatizar posiblemente ahorrando mucho tiempo, evitando errores al hacerlo manualmente y hacerlo de forma exactamente igual para todas las imágenes.

Referencia:
Documentación comando convert
Añadir marcas de agua a imágenes con ImageMagick

jueves, 25 de octubre de 2012

Crear un archivo comprimido Zip con Java

Java
Comprimir cierto tipo de archivos como los archivos de texto puede suponer un considerable reducción del tamaño del archivo original, un xml del 20 MiB se puede quedar en 1.5 MiB, es decir, una tasa de compresión de 13 o lo que es lo mismo el fichero comprimido ocupa 13 veces menos. Esto es muy interesante en las aplicaciones web ya que puede ayudar a ahorrar ancho de banda y reducir los tiempos de descarga del archivo. La compresión se hace a costa de cargar la CPU con trabajo pero normalmente el tiempo global que se tarda en descargar el archivo suele ser menor comprimiendo ya que el mayor limitante suele ser el ancho de banda de la red y no la carga de la CPU. Otra ventaja para las aplicaciones web es que algunos formatos además de comprimir pueden contener varios archivos y por tanto haciendo uso de ellos se pueden devolver un archivo pero que contiene a varios en una sola petición.

Java proporciona una serie de clases para trabajar con archivos comprimidos en formato zip y su uso no es muy complicado. Aquí el código para generar un archivo zip con la Api de Java y guardando en él dos archivos.

También hay disponibles librerías en Java para trabajar con otros algoritmos de compresión aparte del zip, como gzip, LZMA o XZ que producen archivos más pequeños y con mayor ratio de compresión aunque no de forma más rápida.

Referencia:
http://java.sun.com/developer/technicalArticles/Programming/compression/
http://www.7-zip.org/
http://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Markov_chain_algorithm

sábado, 20 de octubre de 2012

Reglas de negocio con Drools y Activiti

Drools
Activiti
Los procesos de negocio pueden tener varios caminos de ejecución distintos. En algunos casos la decisión que determina el camino de ejecución del proceso de negocio puede estar determinado por una decisión de negocio. Por ejemplo, si se determina que un cliente es VIP se puede querer dirigir el flujo del proceso de negocio por un camino alternativo para tratarle de forma especial por su condición de cliente VIP, esta decisión se puede realizar mediante una regla de negocio («business rule»). También, el propio hecho de determinar si el cliente es VIP puede ser realizado por otra regla de negocio. Con lo que podemos aplicar las reglas de negocio al menos a dos escenarios:
  • Para dirigir el flujo de un proceso de negocio. A este tipo de reglas de negocio se les denomina reglas con lógica de decisión («Decision logic rules»).
  • Para tratar información en base a definiciones, operaciones y restricciones. A este tipo de reglas se les denomina reglas con lógica de negocio («Business logic rules»).
Una regla de negocio con lógica de decisión que podría dirigir el flujo de un proceso de negocio podría ser:
  • Si el cliente es VIP enviar paquete por transporte urgente.
La regla de negocio que determina si el cliente es VIP y una regla con lógica de negocio podrían ser:
  • Si el cliente ha realizado una compra por importe superior a 100€ el cliente es VIP.
  • Si el cliente es VIP aplicar un descuento de 10€.
Estos son unas pocas reglas de negocio y bastante sencillas, en un sistema real podría haber muchas más y más complicadas. Por ejemplo, se podría querer aplicar descuentos a ciertos tipos de productos como libros o DVDs o descuentos cuando se compren varios productos relacionados a la vez. Esto se puede complicar suficientemente para que si se realizan las reglas de negocio mediante código de un lenguaje de programación tradicional como Java o C#, el código se convierta en poco mantenible y que pondría dificultades para adaptar una aplicación suficientemente rápido a los cambios que se producen en negocio.

Algunas ventajas de las reglas de negocio son:
  • Están documentadas y puede ser gestionadas, no es un conocimiento que posean las personas en sus cabezas.
  • Las reglas de negocio puede estar escritas en un lenguaje entendible por las personas de negocio. La gente de negocio puede verificar la lógica y cambiarla.
  • No se utiliza código de programa en algún lenguaje de programación que probablemente con el tiempo se convierta en código spaghetti y que no es entendible por las personas de negocio.
  • Es más fácil, flexible y rápido modificar las reglas de negocio que el código de programa equivalente en Java o C#. Se pueden modificar independientemente del código ya que las reglas no se encuentran en la aplicación.
En el siguiente enlace puedes encontrar otros motivos y ventajas, ¿Por qué usar reaglas de negocio?. Las reglas en definitiva contienen parte del conocimiento del negocio, los sistemas que se encargan de aplicarlas son denominados BRMS («Business Rule Management Systems»). Un ejemplo de BRMS es Drools y este puede ser usado en combinación con Activiti encargándose de esta forma cada unos de ellos del área especifica para el que están destinados, Drools para las reglas y Activiti para los procesos.

Veamos ahora un ejemplo similar al realizado en Usar variables en un proceso de negocio con Activiti pero usando reglas de negocio. El diagrama del proceso de negocio es el siguiente:


Mediante la tarea «Determinar Tipo Cliente» se determina el tipo de cliente en función del importe de la compra de un cliente valor que se toma de una variable del proceso de negocio del mismo nombre. En la tarea «Determinar Descuento» se determina el descuento a aplicar en función del tipo de cliente asignado en la tarea anterior. Estas dos tareas son ejemplos de reglas con lógica de negocio. Finalmente, el envío se realiza también en función del tipo de cliente dirigiendo el flujo del proceso mediante un gateway exclusivo, «Envío Normal» si el cliente es normal o «Envío Urgente» si el cliente es VIP. Este es un ejemplo de regla de negocio con lógica de decisión.

Veamos ahora el proceso de negocio en notación BPMN:

En esta definición la etiqueta «businessRuleTask» se encarga de ejecutar las reglas de negocio indicadas en el atributo «activiti:rules» con los siguientes hechos (denominados facts en la jerga de los sistemas de reglas) o variables «${cliente},${importe}» indicados en «activiti:ruleVariablesInput». Tambien se ve como es un gateway exclusivo en el xml, de él parten dos «sequenceFlow», se tomará el camino para el que se cumpla su «conditionExpression».

Ahora el código de las reglas de negocio para determinar el tipo de cliente, las reglas para determinar el descuento y el tipo de envío son muy parecidas y pueden verse en el código fuente del ejemplo:

Una regla de negocio es básicamente una condición que se evalúa para los hechos insertados en la base de conocimiento y en caso de cumplirse se ejecuta su parte then. Veamos ahora los teses unitarios para probar este proceso de negocio.

Las reglas de negocio para los teses se añaden a activiti como deployments de la misma forma que se hace para los procesos de negocio. Para que los deployments de las reglas de negocio de drools sean detectados correctamente hay que asignar un «customPostDeployer» al bean «processEngineConfiguration». También hay que añadir las siguientes dependencias al proyecto «org.drools:drools-core:5.4.0.Final», «org.drools:drools-compiler:5.4.0.Final» tal y como se puede ver en el archivo build.gradle del código fuente del proyecto.

Finalmente, el programa para ejecutar este proceso de negocio y su salida:



Referencia:
Conceptos sobre procesos de negocio (BP, BPM, BPMS, ...)
Procesos de negocio con Activiti
Usar variables en un proceso de negocio con Activiti
Tratamiento de errores en procesos de negocio con Activiti
Código fuente del ejemplo Reglas de Negocio con Activiti

viernes, 12 de octubre de 2012

Tratamiento de errores en procesos de negocio con Activiti

Activiti
Al igual que en todo programa durante la ejecución de los procesos de negocio se pueden producir condiciones de error que deberemos tratar. Por ejemplo, supongamos un proceso de negocio que trata con la venta de diferentes artículos, probablemente una comprobación y condición de error será comprobar las existencias del artículo que se va a vender en el proceso. Este es un error lógico que se podría dar pero también se pueden dar errores técnicos como por ejemplo un error al actualizar las existencias en la base de datos o al acceder a un sistema externo como podría ser un web service o servicio REST.

Los errores lógicos se pueden tratar mediante ciertas construcciones de la notación BPMN, los errores técnicos podemos tratarlos dirigiendo el flujo del proceso. Estas dos formas de tratamiento de errores no son exclusivas sino que se pueden aplicar ambas en un mismo proceso. Veamos un ejemplo de ambos tipos de tratamientos en un proceso de negocio que comprueba las existencias de un producto.

Primero la definición del proceso de negocio en notación BPMN y el diagrama:




La tarea «comprobarExistenciasServiceTask» contiene la lógica para comprobar las existencias de un producto y en función de si hay existencias o no toma la decisión de dirigir el flujo a través de la transición adecuada, este será el tratamiento técnico que aunque en este caso no se produce un excepción por un error con un sistema externo para este proceso es una condición de error. En caso de que haya existencias el subproceso acabarán en el evento «end» del subproceso y en caso de que no haya existencias el subproceso se terminará con el evento «error» del subproceso, este sería el tratamiento lógico del error.

Veamos la clase ComprobarExistenciasServiceTask:


En función de si hay existencias toma un transición u otra. Ahora la clase Producto.


Los teses unitarios para probar el proceso de negocio:


El test testHayExistencias comprueba la ejecución correcta para un producto con existencias, también que se resta una unidad a las existencias. El test testNoHayExistencias comprueba la ejecución en el caso de un producto sin existencias.

Finalmente, el programa que ejecuta el proceso de negocio:



En el apartado referencia puedes consultar el código fuente de este y otros ejemplos de procesos de negocio.

Referencia:
Conceptos sobre procesos de negocio (BP, BPM, BPMS, ...)
Procesos de negocio con Activiti
Usar variables en un proceso de negocio con Activiti
Reglas de negocio con Drools y Activiti
Código fuente del ejemplo tratamiento de errores con Activiti

sábado, 6 de octubre de 2012

Usar variables en un proceso de negocio con Activiti

Activiti
Un proceso de negocio en Activiti o en cualquier otro BPMS para ejecutarse es muy probable que necesite información de entrada que usarán las tareas y que también podrán modificar, además es posible que el proceso genere datos de salida como resultado de la ejecución de las tareas.

Vamos a ver un ejemplo de como es esto en Activiti. Para obtener el valor de una variable mientras el proceso aún está en ejecución se puede conseguir con:

Sin embargo, hay que tener en cuenta que una vez finalizado el proceso las variables son descartadas no pudiéndose recuperar de la forma anterior ya que la instancia del proceso ya no existirá con lo que si id no nos servirá. Una vez finalizado el proceso únicamente permanecen en los datos históricos del proceso siempre y cuando el nivel de histórico de datos está indicado como «full» en la configuración de Activiti. La siguiente sentencia nos dará las modificaciones de la variables ordenadas por tiempo de modificación en una lista de elementos HistoricVariableUpdate:

Pero hay que tener en cuenta que activar el nivel de histórico a nivel full puede dar como resultado que en las tablas de histórico se inserten muchos datos lo cual puede afectar al rendimiento. Una solución posible es pasar al proceso una variable que el proceso modificará y en el que recuperaremos los datos una vez haya finalizado. Veamos el ejemplo. Primero el proceso que tomará una decisión en base a un dato de entrada:

En una tarea scriptTask las variables del proceso pueden ser usadas como variables del script, en este script groovy de la tarea si el cliente es VIP se asigna a una variable un descuento de 10. Lo siguiente es el caso de prueba:

El proceso se inicia con rs.startProcessInstanceByKey pasándole al proceso un mapa con la variable cliente que usará el proceso para calcular el descuento y también un mapa, output, donde recogeremos el descuento que calculará el proceso. Finalmente, lo que sería el programa independiente:




Referencia:
Conceptos sobre procesos de negocio (BP, BPM, BPMS, ...)
Procesos de negocio con Activiti
Tratamiento de errores en procesos de negocio con Activiti
Reglas de negocio con Drools y Activiti
Código fuente del ejemplo Usando variables con Activiti