lunes, 5 de diciembre de 2011

Servicios REST con Jersey y Ajax

Esta semana estuve de cursos en Indra Ciudad Real, por cierto, un grupo majísimo/panísima.

Hicimos un ejercicio de servicios REST con Java, Jersey, e invocación de estos con Ajax (una parte Prototype y otra jQuery) que no podía dejar pasar por alto. Utilizamos Eclipse como IDE, Derby como RDBMS y Glassfish como Servidor de Aplicaciones.

Les dejo los pasos en este artículo. El código completo lo pueden conseguir aquí.

Implementamos un CRUD para una entidad llamada Persona. Aquí les dejo la estructura de la BD.

El código incluye tres partes y de esa forma espero abordar este artículo, a saber: DAO, Servicio REST e Interfaz Gráfica de Usuario (GUI).

El DAO

Realmente aquí no hubo demasiada sofisticación, como el curso iba de Web 2.0, hicimos el menor énfasis en acceso a datos. Les dejo el pequeño DAO que hicimos para trabajar con la BD.

La interfaz, pensando en el uso futuro de IoC, la pueden conseguir aquí.

La clase concreta, aquí.

Si se fijan, la interfaz la implementamos siguiendo el estándar de nombrado de C# que me parece más fácil de seguir, es decir, agregamos una "I" al inicio de la interfaz. El resto es la implementación de la clase concreta. En caso de utilizar IoC, por ejemplo con Spring, la inicialización y destrucción de la conexión en el constructor y método finalize debería desaparecer. De hecho, como está ahora no me gusta mucho, pero lo hice de esta forma para mantener el código lo más simple posible.

La clase Persona es simplemente un POJO con todos los campos de la entidad: id, nombre y apellido. El código aquí.

El Servicio REST

El servicio REST lo implementamos con Jersey, que facilita bastante el código.

Para configurar Jersey se debe agregar como filtro en el Descriptor de Despliegue (web.xml), por supuesto después de haber descargado los JAR (en el proyecto dentro de WEB-INF/lib). Clic aquí para ver el web.xml.

Una vez configurado Jersey, implementamos la API, el código aquí. Cosas importantes que destacar:
  • Los métodos de consulta (GET) producen JSON.
  • Los métodos de modificación (PUT, POST y DELETE) producen HTML, success y error.
  • El método POST está implementado para hacer POST y DELETE por restricciones impuestas por la librería Prototype (ver aquí), para peticiones diferentes de GET y POST; ésta hace peticiones POST especificando el método en la petición a través del parámetro _method. Se puede modificar la librería para no hacer esto, pero me parece más robusto y escalable hacerlo del lado del servicio REST.
  • Para que pudiesen "convivir" Prototype y jQuery se tuvo que activar el modo de no conflictos de jQuery, ver aquí.

La API se puede probar utilizando cURL. A continuación los comandos de prueba que utilizamos, que además ayudan a describir el funcionamiento de los servicios:
$ echo "GET todos"; curl -X GET -H "Content-type:text/json" -v http://localhost:8080/TiendaVirtual/rest/persona
$ echo "GET uno"; curl -X GET -H "Content-type:text/json" -v http://localhost:8080/TiendaVirtual/rest/persona/1
$ echo "PUT"; curl -X PUT -H "Content-type:application/x-www-form-urlencoded" -d "nombre=Cambiar" -d 
"apellido=Apellido" -v http://localhost:8080/TiendaVirtual/rest/persona
$ echo "POST"; curl -X POST -H "Content-type:application/x-www-form-urlencoded" -d "id=1" -d "nombre=Cambiar" -d "apellido=Apellido" -v http://localhost:8080/TiendaVirtual/rest/persona
$ echo "DELETE"; curl -X DELETE -H "Content-type:application/x-www-form-urlencoded" -d "id=1" -v http://localhost:8080/TiendaVirtual/rest/persona

La GUI

La GUI la hicimos en HTML puro y utiliza una librería JS que separa las llamadas Ajax de la lógica de presentación.

El HTML lo pueden conseguir aqúi y la librería de utilidades (donde se manejan los eventos Ajax, con Prototype y jQuery) aquí. Cosas importantes que destacar:
  • Las llamadas de listar son síncronas, por lo que su manejo es diferente al del resto.
  • Las llamadas de modificación (agregar, modificar, eliminar) son asíncronas y funcionan con callback sobre listar.
  • Debido a que el código es de en un ejemplo para un curso, donde Javascript era uno de los temas, el listado se genera con DOM también (lamentablemente sólo funciona en Firefox, o al menos con Gecko como motor). Está deshabilitado por defecto, pero se puede activar con la variable TRABAJAR_DOM

Finalmente para ejecutar: http://localhost:8080/TiendaVirtual/persona.html

Espero este artículo sea de utilidad, sobre todo por el uso de Jersey y la invocación de la API vía Ajax. No conseguí demasiada información precisa y ejemplos que funcionaran desde el inicio. Inicialmente me apoyé bastante en el blog de Lars Vogel, aquí el ejemplo.


martes, 23 de agosto de 2011

Configurando mod_proxy para evitar CORS (por Same Origin Policy)

Otra solución que conseguimos para evitar CORS (por Same Origin Policy) fue instalar/configurar mod_proxy como "proxy en reverso" en nuestro Apache HTTP Server. Sugiero leer este artículo también: CORS, XDM, Same Origin Policy, Prototype y JQuery.

Les dejo el pequeño HOWTO que hice:

0.- Configuración del sistema

$ cat /etc/issue
Ubuntu 10.10 \n \l

$ dpkg -l \*apache\* | grep ^i
ii  apache2-mpm-prefork                  2.2.16-1ubuntu3.1                                 Apache HTTP Server - traditional non-threaded model
ii  apache2-utils                        2.2.16-1ubuntu3.1                                 utility programs for webservers
ii  apache2.2-bin                        2.2.16-1ubuntu3.1                                 Apache HTTP Server common binary files
ii  apache2.2-common                     2.2.16-1ubuntu3.1                                 Apache HTTP Server common files
ii  libapache2-mod-php5                  5.3.3-1ubuntu9.5                                  server-side, HTML-embedded scripting language (Apache 2 module)
ii  libapache2-mod-proxy-html            3.0.1-1                                           Apache2 filter module for HTML links rewriting

1.- Pasos para la instalación del mod_proxy

1.1.- Instalación de paquetes

$ sudo aptitude install libapache2-mod-proxy-html
$ sudo a2enmod proxy
$ sudo a2enmod proxy_http

1.2.- Configuración del VirtualHost

Agregar al virtual host (Ej. /etc/apache2/sites-available/default) correspondiente lo siguiente:

ProxyRequests Off


Order deny,allow
Allow from all


ProxyPass /mio http://mio.com
ProxyPassReverse /mio http://mio.com

2.- Pruebas

Simplemente bastaría con hacer una petición a: http://localhost/mio/recurso



CORS, XDM, Same Origin Policy, Prototype y JQuery

Tiempo sin escribir nada por estos lados...

Me trae de vuelta un problema que me quitó mucho tiempo y me tuvo varias horas atascado, y como este blog va de compartir problemas y soluciones en la red, pues ahí lo dejo...

Los navegadores suelen seguir una política/restricción de seguridad conocida, llamada Same Origin Policy, que restringe la modificación de páginas/recursos -generalmente con Javascript- con contenidos obtenidos de otro lugar diferente al "origen". Esta tabla me parece perfecta para explicar qué se considera diferente del "origen".

Bueno, pues resulta que esta política se puede saltar valiéndose del concepto de CORS (Cross Origin Resource Sharing) o directamente utilizando una librería de XDM (Cross Direct Messaging), como: easyXDM.

Resulta que XDM nos parecía un hack y no queríamos valernos de este tipo de librerías; mucho más frecuentes en sitemashups. Entonces la solución era CORS. Cumplíamos con todos los parámetros, simplemente "creíamos" enviar los parámetros básicos en nuestras peticiones HTTP, a saber: Accept, Accept-Language, Content-Language, Content-Type o Media-Type (sólo dentro del rango: application/x-www-form-urlencoded, multipart/form-data y text/plain).

Y pasó lo que tenía que pasar, no funcionaba!! Estábamos utilizando Prototype porque JQuery no nos funcionaba en el navegador. Resulta que nuestro código si funcionaba en local, utilizando Firefox, con JQuery y seguía sin funcionar con Prototype.

Estuve al menos 1 hora depurando/debuggeando con Firebug y la paranoia y desesperación me llevaron hasta a probar con Wireshark. Fue cuando nos dimos cuenta de que Prototype estaba incluyendo algunos parámetros adicionales en la petición y JQuery no.

Ya estábamos más cerca de solucionar el problema. Este artículo abrió nuestros ojos defintivamente y agradezco de nuevo a su autor. Incluimos el código comentado allí y resolvimos el problema. Simplemente borra las cabeceras cuando detecta peticiones a recursos diferentes del origen. ¿Por qué JQuery si funcionaba? Porque cuando detecta peticiones a otro origen remueve los parámetros.

lunes, 30 de mayo de 2011

Clonando máquinas virtuales con VirtualBox

Estuve dándome varios golpes con VirtualBox para clonar una máquina virtual que había configurado con Ubuntu.

Hasta que por fin! Lo que hice fue:
  1. Instalar la máquina virtual fuente (vbox1). Para ello me descargué la ISO de Ubuntu Server de la Web, la configuré en el VirtualBox como CD, reinicié e instalé.
  2. Configurar la máquina virtual con dos interfaces de red. Una primera con vista "sólo anfitrión" y otra con NAT. La idea de la primera es mantener conexión con la máquina anfitrión (la no virtual) y la segunda tener acceso a Internet.
  3. Instalar todos los paquetes necesarios. En mi caso instalé: apache2, php5, php5-mysql, mysql-server y openssh-sever.
  4. Clonar la máquina. Esto me dio varios problemas de configuración de red... Al final lo que hice fue:
    1. Seleccionar la opción de "Exportar servicio virtualizado" (CRTL+E). Para lo que generé un archivo .OVA con un nombre diferente (vbox1_1)
    2. Seleccionar la opción de "Importar servicio virtualizado" (CRTL+I). Seleccionando el archivo .OVA recientemente generado.
    3. Modificar, en opciones avanzadas, las dos direcciones MAC de las 2 interfaces de red de la nueva máquina (vbox2). Para hacer esto basta con hacer clic en el botón de refrescar. Si no se hace este paso, VirtualBox asignará las mismas direcciones de red a ambas máquinas y habrán conflictos de red.
    4. Modificar el nombre de la nueva máquina. Para esto inicié la vbox2 y cambié el nombre del host en /etc/hosts y /etc/hostname.
    5. Borrar los parámetros heredados de direcciones MAC. Esto fue realmente problemático, como esta nueva máquina era un clon de otra, se traía las direcciones MAC de su padre y Ubuntu fallaba al reconocer las nuevas direcciones MAC. Para esto simplemente se debe eliminar el contenido del archivo: /etc/udev/rules.d/70-persistent-net.rules
    6. Reiniciar. Aunque creo que bastaría con "/etc/init.d/udev restart" y "/etc/init.d/networking restart".

Cogiendo N líneas aleatorías sin repetición de un archivo

Se me presentó el siguiente problema: tenía un archivo con N líneas y debía tomar/coger de éste M líneas no repetidas y de forma aleatoria. Siendo M < N. Como me "da nota"/mola bash, decidí hacerlo con un script. Antes explico el algoritmo, inspirado en un método para barajar cartas:

  1. Creando el mazo de cartas. Creo un arreglo con tantas posiciones como líneas tiene el archivo de entrada (el de N líneas).

  2. Barajando el mazo de cartas. Cojo el primer y último elemento del arreglo origen (source) y los coloco de últimos en el arreglo destino (tmp). Esto lo hago hasta vaciar el arreglo de origen y repito el procedimiento un número "aleatorio" de veces, entre 20 y 120 veces en este ejemplo. Siempre reemplazando al final de cada bucle el arreglo de origen por el temporal.

  3. Intercambiando última y primera carta. Esto para garantizar que la primera carta no permanezca siempre igual

  4. Sacando M cartas del mazo. Cojo las primeras M cartas del mazo.


#!/bin/bash

# Variables
fileName=$1
inputLines=`wc -l $fileName | cut -d' ' -f1`
outputLines=$2 # 596 

# Filling array
for i in `seq 0 $((inputLines-1))`; do
  source[$i]=$((i+1))
done

# Shuffling array
ceil=$((RANDOM%10*10+20)) # from 20 to 120, increment 10

for rand in `seq 1 $ceil`; do

  tmpIndex=0
  for srcIndex in `seq 0 $(($inputLines/2))`; do
    let srcTopIndex=$inputLines-$srcIndex

    tmp[$tmpIndex]=${source[$srcIndex]}
    tmp[$((tmpIndex+1))]=${source[$srcTopIndex]}

    let tmpIndex+=2

  done

  # for arrays with odd length
  if [ $(($inputLines%2)) -eq 1 ]; then
    tmp[$tmpIndex]=${source[$((srcIndex+1))]}
  fi

  source=( ${tmp[@]} ) # copying tmp array
done

# Changing first element for last one
tmp=source[0]
source[0]=${source[$((inputLines-1))]}
source[$((inputLines-1))]=$tmp

# Printing random lines of input file
for i in `seq 0 $(($outputLines-1))`; do
  sed -n "${source[$i]}p" $fileName
done

Cosas interesantes en este ejemplo:
  • Manejo de arreglos: ver iteraciones, copia, extracción y asignación de valores.
  • Extracción de una línea específica de un archivo, basado en el número de línea con sed.


sábado, 14 de mayo de 2011

Apagando y encendiendo el mousepad del laptop

Hoy estuve peleando con mi laptop tratando de encender y apagar el mousepad, básicamente porque no me siento cómodo utilizándolo, para lo que conecto un mouse externo, y además porque lo tropiezo al teclear, lo que se vuelve algo desesperante.

Intenté "apagar" el mousepad desde el menú de configuración del ratón, pero no conseguí ninguna opción. Entonces tuve que descubrir qué nombre tenía mi ratón, para lo que utilicé el siguiente comando:

rodolfo@tiuna:~$ xinput list --short | grep -i 'touchpad'
⎜   ↳ SynPS/2 Synaptics TouchPad               id=12 [slave  pointer  (2)]

Y luego para encender y apagar el ratón fácilmente creé el siguiente script:

#!/bin/bash

if [ $# -gt 0 ] && [ $1 == "on" ]; then
 mode=1
elif [ $# -gt 0 ] && [ $1 == "off" ]; then
 mode=0
else
 echo "You must specify an option, on or off"
 echo "Usage: script.sh on"
 exit
fi

format=8

# xinput list --short | grep -i 'touchpad'
DISPLAY=:0 xinput set-int-prop 'SynPS/2 Synaptics TouchPad' 'Device Enabled' $format $mode

Entonces, por ejemplo para apagar el mousepad bastaría con hacer:
rodolfo@tiuna:~$ ./mousepad.sh off

Por cierto, tengo instalado Ubuntu 10.10, por lo que supongo que esto funcionaría como mínimo en todas las distribuciones hijas de Debian.



lunes, 9 de mayo de 2011

Particionando el disco de mi nueva máquina

Compré una máquina este fin de semana y para instalar Linux tuve que enfrentarme al típico problema de particionamiento del disco.

Siempre había hecho esto con Partition Magic, ahora de Symantec, pero esta vez tuvo que ser un poco diferente.

La cuestión es que la máquina vino con Windows 7 de fábrica y quería conservarlo. La última versión pirata que conseguí del programa, siento aceptar que instalé la pirata, me estaba dando varios problemas con este Sistema Operativo.

Pues me puse a buscar y descubrí una herramienta, seguro recontra conocida, pero totalmente desconocida por mi, Parted y su equivalente gráfico Gparted.

Fue tan fácil particionar el disco que dudé hasta el final... Inicié la máquina por DVD (tengo un Ubuntu 10.10 en DVD), inicialmente ejecuté "sudo gparted", pero luego me di cuenta de que estaba en el menú de Administración.

Borré las particiones de HP UTILS y RECOVER, para lo que previamente compré a través de la página de HP el DVD de Recovery, mero romanticismo de conservar la máquina tal cual la compré en algún futuro improbable. Y luego, instalar Ubuntu! Fáchil perolito!

Enjoy! Recuerden, Gparted es la herramienta.