jueves, 22 de noviembre de 2012

I'm at war

Aunque de facto llevo un tiempo en lucha conmigo mismo nunca está de más el hacer publico y asumir como propios planteamientos como el que hace ya algún tiempo lanzó David Bonilla.

Que coño, tambien yo estoy en esta guerra. Hasta donde pueda y lo que pueda. Porque tengo ideas que quiero llevar a cabo y ya he estado demasiado tiempo con el cerebro al ralentí. Porque tengo clientes que se empeñan en seguir apretando las clavijas como si nuestro trabajo fuera físico en lugar de intelectual. Como si no les diéramos un valor añadido a sus negocios. Me he cansado de que algunos clientes me paguen con cacahuetes. Tal vez lo haya merecido pero esto no se puede mantener. Hay muchos mundos ahi fuera, y no tengo por qué renunciar a que mi empresa, yo mismo y mi gente accedamos a ellos.







Estamos en guerra contra una clase política que nos defrauda día sí y día también, que no gobierna para nosotros sino para los intereses de otros, y unos medios de comunicación cómplices y sumisos que generan una sensación de alarma general que empeora la situación.
Estamos en guerra contra una falsa Industria de la informática dirigida por botarates que siguen intentando competir con precio y comidas de tres platos, copa, café y puro en vez de con originalidad e innovación.
Estamos en guerra contra esa inmensa masa de técnicos mansos y adormecidos, que se quejan de la situación y no hacen nada por remediarlo.
Estamos en guerra contra esa comunidad de voceros de medio pelo, falsos gurús, ninjas del social media, aceleradoras que desaceleran, venture capitalists que no saben que significa venture y mucho menos pronunciarlo,entrepeneurs de salón y parásitos subvencionados y de subvenciones.
Pero, por encima de todo, estamos en guerra con nosotros mismos. Por distraernos todos los días con lo que otros dicen, pero no hacen.  Por tener miedo de hacer lo que queremos hacer y por haber tardado un par de años en reunir el valor para hacerlo.
Por primera vez, uno de nuestros referentes, uno de nuestros ejemplos a seguir, alguien que lleva generando riqueza desde este país y para este país desde hace muchos años, nos ha confesado que se plantea dejar una España estropeada por unos y empeorada por otros. Esto nos ha abierto los ojos: estamos en guerra.
hemos decidido luchar.
Luchar de la única forma que sabemos: trabajando. Y no trabajando en cualquier cosa, sino en proyectos que nos permitan ganar terreno al enemigo, aumentando la productividad de nuestra gente o exportando fuera.
Nos da igual lo que digan las noticias, los periódicos, los telediarios, Standard & Poors y los blogs de expertos y agoreros. No leemos ninguno.
Programamos. Diseñamos. Trabajamos.

domingo, 4 de noviembre de 2012

Trampas y trucos de GORM - Parte 2

En la primera parte hemos visto una introducción a algunas de las particularidades relacionadas con la persistencia de instancias con GORM. Ahora vamos a atacar a la forma en que se manejan las relaciones, con especial énfasis en hasMany y belongsTo

Gorm tiene, de base, muy pocos elementos para definir las relaciones entre clases de dominio, pero son suficientes para satisfacer la mayoria de las necesidades. Cuando doy (N.del.T: Peter Ledbrook, obviamente :-)) cursos sobre Grails siempre me sorprende el pequeño número de diapositivas (slides) dedicado a las relaciones. Como puedes imaginar, esta aparente simplicidad esconde algunas trampas que pueden pillar desprevenidos a los mas inocentes.

Comencemos con la relación mas básica, la de muchos-a-uno.

Relación de muchos-a-uno


Imagina que tenemos las siguientes clases de dominio:

class Lugar {
  String ciudad
}

class Autor {
  String nombre
  Lugar lugar
}

Si vemos la clase Autor , ya podemos ir suponiendo que la clase Libro no puede estar muy lejos. Es cierto, tendremos una clase Libro mas adelante, pero de momento vamos a concentrarnos solo en las dos clases de dominio que tenemos arriba y la relacion muchos-a-uno de Lugar

Parece sencillo, ¿verdad?. Y lo es. Simplemente asigna la propiedad lugar a una instancia de la clase Lugar y ya tienes enlazado un autor a un lugar. Pero veamos lo que pasa cuando ejecutamos el siguiente código en la consola Grails:

def a = new Autor(nombre: "Haruki Murakami", lugar: new Lugar(ciudad: "Tokio"))
a.save()


Nos salta una excepcion. Si buscas la causa última del porqué ha saltado esta excepcion, veras el mensaje: "not-null property references a null or transient value: Autor.lugar". ¿Que ha pasado?

Supongo que unas palabras sobre "transient value" (valor transitorio) serán bienvenidas en este punto. Una instancia transitoria es aquella que no esta asociada a una sesion Hibernate. Como puedes ver en el código de arriba, estamos asignando  Autor.lugar un valor recien creado, no uno recuperado desde la base de datos. Por eso, esa instancia es transitoria. El arreglo mas evidente es hacer la instancia de Lugar persistente guardándola:



def l = new Lugar(ciudad: "Tokio")
l.save()

def a = new Autor(nombre: "Haruki Murakami", lugar: l)
a.save()


Pero, si tenemos que persistir las instancias antes de poder usarlas de esta forma, ¿como puede ser que hayas visto tantos ejemplos muy parecidos a nuestro primer código, donde hemos creado sobre la marcha una nueva instancia de Lugar?. La respuesta es que en este tipo de situaciones, normalmente encontrarás una propiedad belongsTo en alguna de las clases de dominio:

Actualizaciones en cascada con belongsTo

Cuando vayas a tratar con relaciones en Hibernate, necesitas tener un buen conocimiento de lo que significan las operaciones en cascada. Y esto se aplica tambien a GORM.  Estas operaciones determinan que tipo de acciones, cuando se aplican a una instancia de clase de dominio, tambien se propagan a las relaciones de esa instancia. Por ejemplo, en el modelo anterior, ¿se guarda el Lugar del autor cuando se guarda el autor?. ¿Se borra el lugar cuando borramos al autor?. ¿Que pasa si borramos el lugar? ¿Se borra tambien el autor relacionado?

El guardado y borrado son las dos acciones conectadas en cascada mas comunes, y en realidad son las únicas que necesitas comprender. Si vuelves atras a la seccion anterior te darás cuenta de que la instancia de Lugar no se guarda con autor porque la operación en cascada no esta funcionando en la relacion de Autor hacia Lugar.  Si cambiamos la clase Lugar a:

class Lugar {
    String ciudad
 
    static belongsTo = Autor 
}

encontramos que la excepción desaparece y que la instancia de Lugar es guardada junto con la de autor. La linea belongsTo asegura este guardado en cascada de Autor a Lugar. Tal como indica la documentación, esta propagación en cascada funciona para los borrados tambien, así que si borras un autor, su lugar asociado tambien se borra. Pero no a la inversa: guardar o borrar un lugar no guarda o borra al autor.

¿Cual belongsTo?


Un tema que a menudo confunde a la gente inicialmente es que belongsTo admite dos sintaxis diferentes. La forma que hemos usado arriba solamente define la cascada entre dos clases, mientras que la alternativa tambien añade la correspondecia de vuelta (backreference), convirtiendo la relacion en bidirecional:

class Lugar {
    String ciudad
 
    static belongsTo = [ autor: Autor ]
}



En este caso, la propiedad autor se añade a Lugar al mismo tiempo que se define la relacion de cascada. Una ventaja añadida es que con esta notación puedes tambien definir multples relaciones de cascada. Un detalle que puedes notar si usas esta última sintaxis es que cuando guardas un nuevo Autor con su Lugar, Grails automaticamente asigna la propiedad de Lugar del Autor a la instacia de Autor. En otras palabras, la referencia (backreference) es inicializada sin que tengas que hacerlo explicitamente.

Antes de seguir adelante con colecciones, me gustaria comentar una última cosa sobre la relacion de muchos-a-uno. A veces se piensa que añadiendo la referencia inversa (backreference) tal como hemos hecho convierte la relacion en uno-a-uno. Lo cierto es que técnicamente no lo es a no ser que añadamos una  constraint  de unicidad en uno de los extremos de la relación. Por ejemplo:

class Autor {
    String nombre
    Lugar lugar
 
    static constraints = {
        lugar(unique: true)
    }
}


Por supuesto, la relación de arriba no tiene ningun sentido en el caso de la relación entre un Autor y un Lugar en este caso concreto, pero creo que sirve para entender como como se define una relación de uno-a-uno.

La relación de muchos-a-uno es bastante directa una vez comprendes como funciona belongsTo. Por otro lado, las relaciones que involucran colecciones, pueden darte algunas sorpresas desagradables si no estas acostumbrado a Hibernate.

Colecciones (uno-a-muchos / muchos-a-muchos)

Las colecciones son la forma natural de modelar las relaciones de uno-a-muchos en un lenguaje orientado a objetos, y GORM hace que su uso sea realmente sencillo teniendo en cuenta lo que pasa detras de las cámaras. En cualquier caso, no cabe duda de que es una de las areas donde las diferencias entre un lenguaje orientado a objetos y las bases de datos relaciones muestras su peor cara. Para empezar, tienes que recordar que los datos que ves en la memoria pueden ser diferentes de los que hay fisicamente en la base de datos.

Colecciones de objetos de dominio contra registros en Base de Datos


Cuando tienes una coleccion de objetos de una clase de dominio estas tratando con objetos en memoria.  Y esto quiere decir que puedes manejarlos de la misma forma que cualquier otra coleccion de objetos. Puedes iterarlos y puedes modificarlos. En algún momento probablemente querras persistirlos a base de datos, lo que puedes hacer salvando los objetos de la coleccion. Volveremos a esto pronto, pero antes déjame mostrarte alguna de las sutilezas asociadas con esta desconexión que existe entre tu coleccion de objetos y los datos reales. Para hacerlo, vamos a introducir la clase Libro:


class Libro {
    String titulo
 
    static constraints = {
        titulo(blank: false)
    }
}
 
class Autor {
    String nombre
    Lugar lugar
 
    static hasMany = [ libros: Libro ]
}

Estamos creando una relación unidireccional (Libro no tiene una referecia (backreference) a Autor) de uno-a-muchos, donde un autor tiene cero o mas libros. Ahora supongamos que ejecutamos el siguiente código en la consola Grails (maravillosa herrerramienta para experimentar con GORM):


def a = new Autor(nombre: "Haruki Murakami", lugar: new Lugar(ciudad: "Tokio"))
a.save(flush: true)
 
a.addToLibros(titulo: "Tokio Blues")
a.addToLibros(titulo: "Sputnik mi amor")
 
println a.libros*.titulo
println Libro.list()*.titulo


La salida probablemente será algo parecido a:

[Sputnik mi amor, Tokio Blues]
[]

Vaya. Parece que podemos pintar la coleccion de libros pero no están aún en la base de datos. Incluso podemos incluir a.save() despues del segundo a.addToLibros() sin que tenga efecto aparentemente. ¿Recuerdas en el artículo anterior que decíamos que llamar a save()no garantizaba la persistencia inmediata de los datos?. Aquí tenemos un ejemplo de ello. Si quieres ver los libros en tu consulta tendras que forzar la escritura con un flush explícito:


[...]
 
a.addToLibros(titulo: "Tokio Blues")
a.addToLibros(titulo: "Sputnik mi amor")
a.save(flush: true)          // <---- Añadimos esta linea

println a.libros*.titulo
println Libro.list()*.titulo




Ahora si que podemos ver los dos libros, aunque no necesariamente en el mismo orden. Otro síntoma mas de la discrepancia entre los datos en memoria y los datos de la base de datos se puede ver si cambias las sentencias println con:

println a.books*.id

Incluso despues de hacer save() (sin un flush explicito), esto pintará nulls. Sólamente cuando se haga la escritura (flush) de la sesión los hijos en esta relación tendrán los IDs asignados. Este comportamiento es bastante diferente del caso muchos-a-uno que habíamos visto antes, donde no necesitábamos un flush explícito del objeto de la clase Lugar para que persistiera en la base de datos. Es importante que veas la diferencia o te encontrarás con problemas.

Por cierto, como comentario en el caso de que estes siguiendo los ejemplos con la console Grails: date cuenta de que cualquier cosa que salves cuando ejecutas un script en la consola estará aún ahi cuando ejecutes el siguiente script. Los datos solamente se limpiarán cuando reinicies la consola. Tambien, debes saber que la sesion siempre se escribe (flush) cuando finaliza el script.

Vale, volvemos a las colecciones. Los ejemplos anteriores han mostrado un comportamiento interesante del que quiero hablar a continuación. ¿Porque las instancias de Libro han persistido en la base de datos aun cuando no hemos definido un belongsTo en Libro?

Cascada

Como con las otras relaciones, dominar las colecciones significa dominar su comportamiento en cascada. La primera cosa a notar es que la forma de guardar es siempre de padres a hijos, incluso si no se ha especificado la cláusula belongsTo. Asi pues, ¿hay motivo para usar belongsTo?.  Si

Mira los que sucede si ejecutamos este código en la consola despues de que hayamos añadido al autor y sus libros:

def a = Autor.get(1)
a.delete(flush: true)
 
println Autor.list()*.nombre
println Libro.list()*.titulo


La salida será algo como:

[]
[Sputnik mi amor, Tokio Blues]

Asi que tenemos que el Autor ha sido borrado, pero sus libros no. Aqui es donde entra en juego belongsTo: asegurando que los borrados en cascada tambien se reflejen. Simplemente añadiendo una linea  static belongsTo = Autor el código de arriba pintará dos listas vacias para Autor y Libro. Simple, ¿no?.  Vale, en este caso si, pero la diversion no ha hecho mas que comenzar.  Por cierto, ¿te has dado cuenta de como hemos forzado el flush de la sesion en el ejemplo anterior?. Si no lo hubieramos hecho, la orden Autor.list() probablemente habria mostrado el autor que acabábamos de borrar, porque el cambio aún no habia sido persistido en ese punto.

Borrando a los hijos

Borrar algo como la instancia de Autor y dejar a GORM que borre a los hijos automáticamente hemos visto que es directo e inmediato. Pero ¿que pasa si queremos borrar un o mas libros pero no al propio autor?. Podríamos hacer algo como lo siguiente:

def a = Autor.get(1)
a.libros*.delete()


pensando que de esta forma borraremos todos los libros. Pero si lo haces veras que ese código genera una excepción:

org.springframework.dao.InvalidDataAccessApiUsageException: deleted object would be re-saved by cascade (remove deleted object from associations): [Libro#1]; ...
 at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:657)
 at org.springframework.orm.hibernate3.HibernateAccessor.convertHibernateAccessException(HibernateAccessor.java:412)
 at org.springframework.orm.hibernate3.HibernateTemplate.doExecute(HibernateTemplate.java:411)
 at org.springframework.orm.hibernate3.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:374)
 at org.springframework.orm.hibernate3.HibernateTemplate.flush(HibernateTemplate.java:881)
 at ConsoleScript7.run(ConsoleScript7:3)
Caused by: org.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations): [Libro#1]



¡Hey!. ¡Mira que traza tan util!. Efectivamente: el problema es que los libros están aún en memoria en la coleccion del autor, asi que cuando finalice la sesion y los cambios se guarden, volverá a ser creada en base de datos. Recuerda: no solo hay guardados en cascada, sino que tambien las instancias de las clases de dominio se guardan automáticamente a causa del dirty-checking que veíamos en la anotación anterior.

La solución, tal como indica el mensade la excepción, pasa por borrar los libros de la colección:


def a = Autor.get(1)
a.libros.clear()


... aunque en realidad esto tampoco es la solución, porque los libros siguen en la base de datos y simplemente los hemos disociado del autor. Vale, parece que tendremos que borrarlos explicitamente también:


def a = Autor.get(1)
a.libros.each { libro ->
    a.removeFromLibros(libro)
    libro.delete()
}


Oops, ahora tenemos un error de tipo ConcurrentModificationException porque estamos borrando libros de la coleccion del autor mientras estamos iterando sobre esa misma coleccion. Una trampa del propio Java estándar. Podemos solucionarlo creando una copia de la coleccion:


def a = Autor.get(1)
def l = []
l += a.libros
 
l.each { libro ->
    a.removeFromLibros(libro)
    libro.delete()
}


Esto funciona, pero requiere un poco mas de esfuerzo por nuestra parte.

Tambien tenemos que tener cuidado si tenemos una relacion bidireccional. Por ejemplo, si nuestro belongsTo usa la sintáxis: static belongsTo = [ autor: Autor ]. Si eliminamos los libros de la coleccion sin borrarlos realmente, como por ejemplo:

def a = Autor.get(1)
def l = []
l += a.libros
 
l.each { libro ->
    a.removeFromLibros(libro)
}


tendremos un error parecido a esto: "not-null property references a null or transient value: Libro.autor" .  Como veremos ahora, esto sucede porque el libro se ha quedado con la propiedad autor asignada a null, y como resulta que esa propiedad no puede ser nullable salta ese error. Esto es como para volverse loco.  :-)

Pero no temas, que hay solución. Si añadimos esta cláusula mapping en Autor:

static mapping = {
    libros cascade: "all-delete-orphan"
}


entoces cualquier libro que se separe de su autor será automáticamente borrado por GORM. El trozo de código anterior, donde quitábamos todos los libros de la coleccion, ahora funcionará. Y en realidad, si la relacion es unidireccion,  puedes reducir tu código sustancialmente:

def a = Autor.get(1)
a.libros.clear()


Esto quitará todos los libros y los borrará de un plumazo.

La moraleja de esta historia es simple: si usas belongsTo en una coleccion, asigna explicitamente el tipo de cascada a "all-delete-orphan" en el bloque de mappings del padre. De hecho, bien podria ser este el comportamiento por defecto para belongsTo y las relaciones de uno-a-mucho en GORM.

Esto nos lleva a una pregunta interesante. ¿porqué el método clear() funciona en una relación bidireccional?. No estoy 100% seguro, pero creo que es porque los libros mantienen una referencia (backreference) al autor. Para comprender porque esto afectaria al comportamiento de clear(), primero debes darte cuenta de que GORM crea en base de datos estructuras diferentes para relaciones unidireccionales o bidireccionales de uno-a-muchos. Para las relaciones unidireccionales, GORM crea una tabla de unión intermedia, asi que cueando limpias la coleccion de libros, simplemente se borran de esta tabla de unión. Las relaciones bidireccionales se enlazan usando directamente la clave externa (la foreign key) en la tabla hija, como la tabla de libros en nuestro ejemplo. Tal vez con un diagrama quede mas claro:




Cuando limpias la coleccion de libros, la clave externa (foreign keu) estará todavia ahí porque GORM no hay borrado el valor de la propiedad autor. Como si la colecion nunca se hubiera vaciado.

Y esto es práticamente todo sobre colecciones. Para terminar de atar completamente esta seccion me gustaria echarle un vistazo rápido a los métodos addTo*() y removeFrom*()

addTo*() contra <<


En los ejemplos anteriores, hemos usado los métodos dinámicos addTo*() y removeFrom*() que provée GORM. ¿Porqué?. Despues de todo, si hemos dicho que son colecciones estándar de Java, podríamos haber usado un código como este:


def a = Autor.get(1)
a.libros << new Libro(title: "Tokio Blues")


Probablemente si, pero hay algunos beneficios si usamos los métodos de GORM. Echa un vistazo a este código:


def a = new Autor(nombre: "Haruki Murakami", lugar: new Lugar(ciudad: "Tokio"))
a.libros << new Libro(title: "Tokio Blues")
a.save()


No parece haber nada erróneo, ¿verdad?. Pero si ejecutas el código veras que tienes una NullPointerException porque la coleccion de libros no ha sido inicializada. Este comportamiento es diferente al que ves cuando recuperas al autor desde la base de datos, por ejemplo usando get(). En ese caso podemos añadir alegremente libros a la coleccion. Sólo tenemos este problemas porque estamos creando el autor con new(). Si utilizas el método addTo*() no tienes que preocuparte de esto porque esta forma es null-safe  (N. del T.: no se muy bien como traducir null-safe, pero supongo que se entiende que quiere decir que no tienes porqué tener cuidado de que los valores sean nulos ya que el propio método realiza la validación)

Ahora miremos al ejemplo en el que recuperamos el autor usando get() antes de añadir un nuevo libro a la coleccion. Si la relación es bidireccional, nos encontraremos un error "property not-null or transient" porque la propiedad autor en la instancia de libro no ha sido asignada. Si usas los métodos estándar de una coleccion tendras que inicializar las referencias manualmente, pero con el método addTo*() GORM lo hace por ti.

La última característica del método addTo*() es la creación implícita de la clase de dominio correcta. ¿Te has dado cuenta de que en nuestros ejemplos simplemente hemos pasado los valores de inicialización de libro al método en lugar de instanciar explícitamente la clase Libro? Esto es porque el método es capaz de inferir desde la propiedad hasMany que tipo de coleccion contiene. Mola, ¿verdad?

El método removeFrom*() es menos util, pero al menos borra las back references. Por supuesto, esto funciona mejor con la opcion de cascada "all-delete-orphan" tal como hemos visto antes.

El último tipo de relación que veremos es la de muchos-a-muchos.

Relación de muchos-a-muchos

Si quieres, GORM puede manejar tambien las relaciones de muchos-a-muchos, pero hay unas cuantas cosas con la que tienes que tener cuidado:


  • Los borrados no se hacen en cascada. Punto. 
  • Un lado de la relación tiene que tener un belongsTo , aunque normalmente no importa en que lado esté
  • el belongsTo solo afecta afecta la direccion en la que se hace la propagación en cascada al salvar los datos -puesto que no lo hace con los borrados-
  • Siempre se usa una tabla intermedia para la unión, y no puedes añadir mas información en ella. 
Siento ser tan firme sobre los borrados, pero es importante comprender que el comportamiento es bastante diferente de las relaciones muchos-a-uno o uno-a-muchos. También es importante comprender el último punto: un gran número de relaciones muchos-a-muchos tienen mas información asociada. Por ejemplo, un usuario puede tener muchos roles y un rol puede tener muchos usuarios. Pero el usuario puede tener diferentes roles dependiendo de un proyecto, asi que el proyecto está asociada con la propia relación. En estos casos es mejor que manejes la relación tu mismo. 

Resumen


Bueno, probablemente este es el artículo mas largo que he escrito hasta ahora (N.del T.: tambien yo :-)), pero hemos llegado al final. Felicidades. No te preocupes si no has podido digerir todo de una sentada, siempre puedes volver aqui como referencia.

Creo que GORM hace un gran trabajo de abstraccion tratando las relaciones de bases de datos de una forma orientada a objetos, pero como hemos visto no puedes olvidar que al final (normalmente) estas tratando con una base de datos relacional. Armado con la información de esta anotación, pienso que no deberias tener excesivos problemas con lo básico para manejarte con las colecciones.

Puede que no me creas, pero aun no hemos cubierto todo lo que necesitas saber sobre colecciones. Todavia hay algunos puntos interesantes sobre la carga-perezosa, pero eso lo dejamos para el siguiente artículo.




lunes, 29 de octubre de 2012

Los certificados de menos de 1024 bits e Internet Explorer


Si, otra entrada mas dedicada a microsoft. Hace unas semanas, una de nuestras aplicaciones, que está corriendo en un servidor https empezó a dar problemas.

No a todo el mundo, solo a unos cuantos. Ni siquiera a todos los que tenian Explorer 9, tambien algunos con Explorer 8. Y otra gente funcionaba bien.

El problema tal como lo veian los clientes era de lo mas curioso: iban a su página de login (http) desde donde se les redirigía a la página de login de verdad, ya por https, y en el camino el navegador se quedaba colgado. Ningun mensaje de error, ni en cliente ni en servidor.

Hoy, tras unos ratos intentando localizar el problema, finalmente lo hemos encontrado: Hace unos meses Microsoft lanzó un aviso sobre la inseguridad de los certificados de menos de 1024 bits y aparentemente en alguna de las actualizaciones de Windows Update liberaron el parche KB2661254.

Efectivamente, el certificado que tenia el servidor (autofirmado) era de 512 bits, aparentemente el valor por defecto cuando generé las claves, pero... hombre!, ¡no es para tomárselo asi!. Y al menos, podían dar un aviso de error indicando lo que estaba pasando. Lo de dejar frito el navegador sin pantallazo azul ni nada no está bien.

Asi que ya lo sabes: si te esta dando un problema de este tipo es probable que tu error sea el mismo que el nuestro, y no parece que este documentado por muchos sitios. Será que éramos los unicos que teníamos este tipo de certificado...

 


lunes, 22 de octubre de 2012

Trampas y trucos de GORM - Parte 1


¿Eres nuevo en Grails?. O ¿has tenido tu primera pesadilla con GORM?. Si es asi, tal vez te interese leer estas anotaciones de Peter Ledbrook traducidas.  En ellas se intentará explicar no solo las peculiaridades que a veces nos hacen tener errores sino tambien porqué GORM se comporta de esa forma.

Supongo que al menos ya sabrás que GORM es la libreria de acceso a base de datos que se utiliza con Grails. Está basada en el que probablemente es el ORM más popular, Hibernate. Como ya puedes suponer, Hibernate es una herramiente muy potente y flexible, pero usarla tiene su coste, y muchos de los problemas que suelen tener los usuarios de GORM tienen su origen en la forma en que funciona Hibernate. GORM intenta ocultar los detalles de la implementación de la mejor forma posible, pero a veces hay cosas que no funcionan tan bien como uno desearía.

En esta anotación describiremos las bases de persistencia de objetos en una base de datos. Suena sencillo, pero incluso en cosas tan aparentemente simples GORM tal vez no funcione de la forma en que tu lo esperas.

Cuando llamo a save(), quiero decir save!: guárdalo!


Los problemas guardando instancias de clases de dominio son con seguridad los primeros con los que se encontrará un desarrollador. Puede suceder que te encuentres en el caso de "lo he guardado, asi que ¿porqué no está en la base de datos?". Si te sirve de consuelo, a todos nos ha pasado. Pero ¿por qué sucede?. Puede haber un par de posibilidades.

No te olvides de la validación!

Cada vez que llamas al método save() para guardar una instacia de una clase de dominio, Grails la valida usando las constraints que has definido. Si algun valor viola estas constraints, grails no guardará en registro en la base de datos y se adjuntarán los errores correspondientes en tu instancia. El problema es que este proceso es silencioso: no te darás cuenta de lo que ha pasado a no ser que verifiques el valor de vuelta de save() o hagas una llamada al método hasErrors().

Cuando estas asociando tu instancia de la clase de dominio con los datos que ha introducido un usuario por pantalla, normalmente este es el comportamiento deseado: no es una sorpresa que los usuarios se equivoquen al introducir los datos o que estos no sigan el comportamiento que has definido, asi que hacer saltar una Exception en un caso asi, simplemente no parece apropiado. Es mejor verificar lo que ha pasado en la llamada al método save() para informar al usuario y que pueda actuar en consecuencia. El método save() devolverá null si ha habido algun problema, o la instancia de la clase en caso de que todo haya salido bien.

 def book = new Book(params)  
 if (!book.save()) {  
   // Fallo! Mostrar errores al usuario  
   ...  
 }  

Por otro lado, cuando estas introduciendo datos de prueba en la clase de BootStrap o en la consola Grails, normalmente esperaras que los datos sean correctos (¡los estas metiendo tu!), asi que si hay algún fallo de validación es porque has metido la pata. En este caso, seguramente preferirías no complicarte verificando el valor de cada llamada a save() y que si tienes un error Grails te avise con una Exception. No es el comportamiento por defecto, pero puedes usarlo de forma sencilla con el argumento failOnError de save():



book.save(failOnError: true)


Si insistes, incluso puedes hacer que este sea el comportamiento por defecto: simplemente asigna la propiedad grails.gorm.failOnError a true en grails-app/conf/Config.groovy. Y por cierto, no te olvides de que todas las propiedades de una clase de dominio tienen por defecto una constraint de nullable: false!

La sesión de Hibernate


En alguna ocasión extraña, te encontrarás el caso de salvar una instancia de tu clase sólo para descubrir que la siguiente consulta a base de datos no la encuentra, incluso aunque haya pasado todas las validaciones.

Esto puede ser a causa del funcionamiento de Hibernate por debajo, ya que Hibernate es un ORM basado en sesiones. Este es un punto importante y para sentirte cómodo con GORM deberías comprender perfectamente el impacto que esto puede tener en tus aplicaciones.

¿Que es una sesion?. Basicamente es un cache en memoria de objetos que van y vienen de la base de datos. Cuando guardas una instancia de una clase de dominio, está implícitamente unida a la sesión: esto es: se añado al caché y se convierte en un objeto manejado por hibernate. Pero se persiste a la base de datos en este punto! . El siguiente diagrama muestra este comportamiento:





Cuando guardas una instancia, está inmediatamente disponible para esa sesión, pero Hibernate puede decidir que prefiere persistir a base de datos mas tarde para optimizar el acceso a la base de datos. Normalmente no te darás cuenta de estas cosas porque Hibernate y Grails se encargan de ello, pero a veces sucederán estas cosas.

Como puedes imaginar, Grails te permite un cierto grado de control sobre estos casos. ¿Has visto  código como este en algun ejemplo? :



book.save(flush: true)


Ese flush: true fuerza a Hibernate a persistir los cambios en la base de datos ahora mismo. Es lo que se llama hacer flush de la sesion.

Ahora. claro, estarás pensando que porqué no poner flush: true por todo todos lados. No lo hagas. Deja que Hibernate haga su trabajo y solo usa este recurso cuando necesites hacerlo, por ejemplo al final de un proceso de actualizacion batch o por lotes. Sólo deberias usarlo cuando no veas datos en la base de datos que deberían estar ahí. Ya se que parece un poco sistema de ensayo y error,  pero el como funcione en tu caso dependerá de la implementacion de tu base de datos y de otros factores. Un caso donde probablemente sea muy util forzar la escritura a disco es cuando este interactuando con otra aplicación que acceda a la misma base de datos.

Y ahora que no quiero que guardes, ¿lo haces?


En el apartado anterior hemos visto que a veces una llamada al método save() no guarda los datos, al menos no instantaneamente. Pero considera ahora el caso inverso: objetos que se persisten en la base de datos sin su llamada a save() correspondiente. Si no te ha pasado nunca, te garantizo que antes o despues te pasará. ¿Porqué sucede?

Hibernate tiene el concepto de dirty-checking (N.del.T: podriamos traducirlo como verificación de datos que han cambiado desde que se leyeron o algo asi. Mejor lo dejo en inglés).  Este comportamiento verifica que datos salieron de la base de datos, y si han cambiado en memoria, intenta escribirlos de vuelta. Como puede parecer -y lo es- un poco lioso, mejor lo vemos con un ejemplo: Supongamos la clase de domino Libro con un par de propiedades titulo y autor y el siguiente código en su clase controller:



def b = Book.findByAuthor(params.author)
b.title = b.title.reverse()


Fíjate en que no hay ninguna llamada a save(), pero cuando la petición haya finalizado te encontrarás con que el titulo está al reves en la base de datos. El cambio se ha persistido en la base de datos sin una llamada explicita a save(). Esto ha sucedido porque:

  1. El libro esta unido a la sesion (por haber sido recuperado por una query)
  2. La propiedad titulo es persistente (todas las propiedades lo son a no ser que las configures como transient)
  3. El valor de la propiedad ha cambiado cuando la sesion se cierra
Hemos mencionado que los objetos estan unidos a la sesion, pero ¿como se unen?.  Si usas GORM para recuperar cualquier objeto, por ejemplo con el método get() o cualquier tipo de query, los objetos quedan automáticamente unidos a la sesion. Si creas un objeto usando new(), el objeto no está unido a la sesion hasta que no llames al método save()

Segundo: las clases de dominio son persistentes por defecto, asi que tienen una columna en la base de datos relacionada con cada propiedad que hayas definido para guardar sus valores. Pero puedes hacer que la propiedades sean transitorias añadiendolas la lista estatica transients , que se usa precisamente para no guardar esos valores en la base de datos. 

Para terminar, hemos mencionado que los cambios se persisten si existe cuando se cierra la sesion. ¿Que quiere decir esto?. Que para hacer cualquier operacion con Hibernate tienes que tener abierta una sesion. Una vez que se cierra la sesion no puedes usarla para acceder a la base de datos. La sesion es flushed (forzada a escribirse en base de datos) al final de la accion de nuestro controlador (Grails automaticamente abre una sesion al principio de la peticion y la cierra al final)

¿Podemos evitar este comportamiento?. Claro que si. Una opcion es llamar nosotros a save() en nuestra instancia de clase de dominio: si alguna de las propiedades falla las validaciones, los cambios no persistirán.  Por supuesto, aunque los datos cumplan con las validaciones puede que no querramos que persistan, y podemos llamar al método discard() en nuestra instancia de clase de dominio. Esta operación no restaura los valores originales, pero asegura que los nuevos no se guardan en la base de datos.

Hasta aqui tenemos una buena cantidad de información para ir digiriendo hasta la próxima entrada. La clave es comprender como la sesion de Hibernate puede afectar al comportamiento de tus clases de dominio. En las próximas anotaciones habrá mas información y ejemplos.

En general, se recomienda que siempre utilices save() para guardar tus objetos en lugar de fiarte del dirty-checking. Hace tu código mas claro tiene la ventaja de que tus cambios son primero validados y luego guardados. Tambien es muy recomendable verificar el valor de vuelta del método save() y utilizar el parámetro failOnError: true cuando estés dando de alta datos de prueba o en el bootstrap.

Si lo que has leido hasta aqui de GORM te ha intimidado, recupérate, no es para tanto. Hace que jugar con la base de datos se mas sencillo y divertido, y confío en que esta serie de artículos te sean de ayuda para solucionar los problemas que puedas ir encontrando.



Anotación original, de Peter Ledbrook: Gorm Gotchas - Part 1

Anotaciones relacionadas: Trampas y Trucos de GORM - Parte 0

Trampas y trucos de GORM - Parte 0

En las siguientes anotaciones voy a intentar traducir de forma mas o menos libre las excelentes entradas sobre GORM, el módulo de acceso a datos de Grails, escritas por Peter Ledbrook en el blog de Springsource Gorm Gotchas - Part 1, Gorm Gotchas - Part 2 y Gorm Gotchas - Part 3.

Mi motivación es por una parte puramente egoista: Estoy aprendiendo Groovy y Grails sin tener ningún conocimiento previo de Spring ni Hibernate, llegando directamente desde el Java del Siglo XX y con conocimientos básicos de base de datos ya que siempre me he dedicado a código Java (bueno, no siempre). Pero las aplicaciones que solemos hacer en mi empresa suelen tener un gran componente de base de datos, asi que es necesario manejar ciertas estructuras con soltura.

Por otra parte, hay bastante poca literatura clara en castellano (o no he sido capaz de encontrarla) sobre GORM. Esta mañana, siguiendo unos enlaces de la lista de Grails en castellano he llegado a estas 3 páginas de Peter Ledbrook y me están pareciendo tan claras en la forma de exponer los conceptos que voy a ver si soy capaz de traducirlas de forma un poco decente aunque no sea de forma literal. De esta forma, además de interiorizar yo mismo los conceptos, si resulta que sirven para alguien mas... pues mejor que mejor.

Pasen y lean.

Trampas y trucos de GORM - Parte 1 
Trampas y trucos de GORM - Parte 2 (en preparación)
Trampas y trucos de GORM - Parte 3 (en preparación)







On a new road



On a new road era el nombre del blog que James Gosling, autor de Java entre otras cosas, tenia durante el tiempo que paso en Google tras abandonar Oracle.

Y es el nombre de esta anotación porque es exactamente como me siento desde hace algunos meses: En un nuevo camino. No porque haya cambiado de empresa, cosa que (toco madera) espero que no suceda en mucho tiempo. Pero si porque despues de mucho tiempo, tanto mi empresa como yo necesitamos un cambio.

Yo empecé con este cambio hace un par de años. Tras mucho tiempo de mantener mi sistema nervioso al ralentí de repente vi la luz. Si, al estilo de los Blues Brothers. O algo asi. En realidad podríamos decir que fue mas parecido a lo que contaba una vez Motorhead Lenny de que un dia recuperó la conciencia desnudo en una playa comiendo una lata de alubias con una navaja y se dió cuenta de que asi no podia seguir. Lo mio no llegó a tanto pero de repente tome conciencia de que habia cosas que cambiar. Y, entre otras cosas, empecé a correr. Nunca hubiera pensado que yo era capaz de correr 10Km, menos aún un 31 de Diciembre, pero el año pasado fui capaz de terminar la San Silvestre. Y ni siquiera fuí el ultimo.

Y los cambios no sólo eran necesarios en mi forma de hacer las cosas: tambien en mi empresa habia que hacer muchos cambios. Y, Dios, que fuerte es la primera ley de Newton, ya sabes:


Todo cuerpo permanecerá en su estado de reposo o movimiento uniforme y rectilíneo a no ser que sea obligado por fuerzas externas a cambiar su estado".


Que complicado es hacer cambios incluso en una empresa de poco mas de 10 personas que llevan trabajando muchos años. La inercia lo es todo, pero cuando las cosas no marchan todo lo bien que a uno le gustaría y se intentan hacer cambios la tarea se vuelve imposible: a los humanos nos gusta la seguridad de lo conocido aunque racionalmente nos demos cuenta de que no es el camino adecuado.

Intenté aplicar pequeños cambios en el funcionamiento de la empresa y aunque sobre el papel con mis socios parecían cosas razonables (en especial muchas de las que Berto Pena cuenta en su blog y su libro), en el dia a dia cuesta infinito llevarlas a la práctica.

Y entonces, en Febrero de 2012, llegó el Spring IO. Y fúe como meter mi cabeza en una maquina centrifugadora. O algo asi. Yo iba a ver de que iba Spring, si merecia la pena darle una oportunidad en lugar de usar GWT, que es lo que estaba mirando en aquel momento para sustituir a nuestro viejo -mas de 13 años- y propietario sistema de programacion, LWAS.

Y dos días despues sali con la cabeza sin Spring pero llena de JSON, Groovy & Grails, Hibernate, Scrum, RabbitMQ, herramientas de Atlassian, MongoDB, Git... Habia conocido -sin saber quienes eran- a Peter Ledbrook, Graeme Rocher, Matt Raible y Adrian Colyer. Asistí a las magníficas conferencias (entre otras) de Thomas Lin y de Domingo Suarez.  Y todo ese mundo que se abría ante mi, como programador, no podia dejarlo escapar.

Asi que aqui estoy. Empezando de nuevo. Como un pardillo en programación a mis cuarenta y tantos, tras 10 años dedicado sobre todo a sistemas y con la programación, lo que siempre me ha gustado,  dejada bastante -nunca del todo- de lado durante estos años. Sin tener apenas ni idea de javascript, ni de patrones de diseño mas allá de donde la intuición me ha llevado. La de cosas que han pasado en programación a mi lado sin que apenas me enterara dentro de mi caja estanca...

Intentando cambiar la metodologia de trabajo de la empresa hacia algo parecido a Agilismo, usando herramientas como Jira o Confluence sin las cuales no se como hemos podido vivir hasta ahora. Intentando entender si Bamboo y la integración continua nos sirve realmente para algo. Intentando dar la vuelta al cerebro para saber si la programación orientada a test es la panacea. O hasta que punto merece la pena. Pegándonos con Git.

En este camino nos esta echando una mano la (magnifica) gente de Salenda con Alvaro a la cabeza. Y abriendo mucho las orejas en las reuniones en petit-comité del grupo de usuarios de Grails Madrid junto con mi compañero Borja, sin el que no podría estar avanzando a la velocidad (sea la que sea, todo es relativo) que estamos haciéndolo.

Que coño. Hacia años que no me sentía tan vivo. A por ello.




domingo, 21 de octubre de 2012

Caramba, que coincidencia


El viernes pasado tuve uno de los mayores problemas a los que me he enfrentado en los ultimos tiempos: toda la infraestructura de servidores de mi empresa se vino abajo por culpa de un fallo de conectividad entre los servidores y el NAS. Los de produccion, los de desarrollo y los de uso interno.

Contado así el origen del fallo parece que no deberia haber sido para tanto, pero déjame contarte que hace alrededor de año y medio cambié todos (en su momento unos 14) los servidores físicos que tenia por sólo 3, utilizando la virtualización de VMWare ESXi. La forma en que lo instalé tal vez no sea muy convencional, pero lo hice montando los servidores sin discos duros ahorrandome una pasta en discos, controladoras RAID y demas, e invirtiendo ese dinero en meter mas memoria (tampoco una pasada: 32 GB de RAM, nada comparable a los 29 Tb de RAM que Larry Ellison dice que hacen falta para que Oracle vaya fino fino) y arrancando los servidores con un pincho USB.

¿Sin discos?. ¿Donde están los ficheros de las maquinas virtuales, los datos y todo lo demás?: En un único NAS, un Thecus 7700 con 7 discos en RAID6 (para ser exactos, 6 discos + 1 en espera) al que acceden los 3 servidores por iSCSI.

El sistema me ha estado funcionando mejor que fantástico durante este año y pico. Ni un solo fallo, mejora de velocidad en todos los procesos, flexibilidad infinita a la hora de 'ampliar el hardware' de las maquinas virtuales (« venga, un par de Gb mas de RAM para este servidor que ahora le hace falta », o « como vamos a hacer procesos de calculo intensivo, le meto 4 cores mas a este servidor »).  Tambien, en lugar de apretar los programas en los servidores existentes me puedo permitir el lujazo de meter un solo servidor para nuevos servicios (y algun dia contaré lo mucho que estamos cambiando en mi empresa en este sentido) y tener todo mucho mas controlado y separados por funcionalidades.

¡Que bonito todo!, ¿verdad?.  Si: hasta que, como comentaba al principio, un error de conectividad en uno de los switches hace que los servidores físicos pierdan la conexión con el NAS... y todo se venga abajo.  Para mas coña (aún estoy investigando el porqué) despues de la pérdida de conexion el Thecus decidió que ya que habia dejado de trabajar, se echaba una siestecita, y se quedo frito. Resultado: unas 7 horas de reconstruccion de RAID.

Asi que ahí andaba yo, el viernes por la noche levantando los sistemas. ¿Y que me encontré?:



¡Si!. ¡Oh casualidad!.  29 servidores Linux (Ubuntus v8, v10, v12. Fedoras 6, CentOS), dos de ellos con cosas tan delicadas a priori como puede ser Oracle. Incluso un Windows 2003 Server.  Y 3 malditos Windows 2008.  Cuando intentaba arrancarlos me aparecia la pantalla de aviso de que algo iba mal y que entrara en modo a prueba de fallos. En uno de ellos directamente pantallazo azul y reboot.

Entrando en modo de recuperación me pedia el DVD de instalación y al dárselo me decia que no encontraba disco de sistema y que le dijera donde estaban los drivers (pues hombre, si no los encuentras tu... ).  Resultado: tuve que restaurar la copia de seguridad de las maquinas virtuales de la semana pasada. Afortunadamente no he sufrido perdida de datos importantes en estos servidores pero el tema ha llamado mucho mi atención.

¿Como puede ser que de 33 servidores, repartidos por distintos volúmenes iSCSI, en un mismo NAS sólo haya problemas graves con 3 que corren el mismo sistema operativo?. ¿Alguien de ciencias me puede decir las probabilidades de que suceda esto?

En los Linux, que cuando sucedió el problema estaban echando humo de gente trabajando con ellos, sólo en 5 de ellos hubo que ejecutar el comando fsck para comprobar el disco. En 2 ni siquiera fué a causa del problema sino porque llevaban mas de 400 dias sin apagarse, y cuando en el arranque de un Linux se detecta que un sistema de ficheros lleva mas de X dias sin verificarse, se fuerza el chequeo.

Tengo algunos clientes que usan SQLServer en sus servidores de producción asi que me veo obligado a tener acceso a esta base de datos (que, dicho sea de paso, funciona bastante bien). Afortunadamente estoy migrando esta infraestructura a Amazon AWS para no tener que mantenerla, y despues de los problemas de este fin de semana, mas aún.

Desde luego, si mi falta de confianza en Windows andaba por los suelos, esta experiencia ha sido la puntilla que me faltaba.





 

jueves, 27 de septiembre de 2012

Una foto que merece un premio



Lo comentaba él mismo hace dia y medio. Darán que hablar. Y tanto.

Como mucha otra gente, sigo el blog Pixel y Dixel desde hace unos cuantos años. Y ademas, tuve la suerte de conocer a Jonan Basterra hace cosa de un año, cuando nos citaron a unos cuantos blogger en la T4 de Barajas para conocer algunas de sus interioridades. Para mas cachondeo, tuvimos que ir juntitos al pasar todos los controles porque se equivocaron al imprimir nuestras tarjetas de acredicación: la mia tenia su foto y su acreditacion, mi careto.

Hoy Jonan ha tenido su minuto (laaargo minuto) de fama fotográfica: Una foto suya ha sido portada en la version impresa de El Pais y tambien ha sido usada en El Mundo. Los dos periódicos de mayor tirada en España usando una foto suya.

No es la primera vez que Jonan consigue una portada. Como buen pamplonica, no se pierde unos San Fermines de hace años y aprovecha para tirar fotos. Tan buenas que hasta el El Diario de Navarra, donde ha publicado unas cuantas, le dio este año un premio.

Pero hablando de las fotos que me han motivado a escribir esta entrada, la que publica El Pais no es, para mi gusto, la mejor de la serie que hizo. Ni mucho menos. La mejor segun mi opinion es la que él mismo publicó en su blog. Esta:



¿Porque me parece impresionante la foto?. Porque fue tomada al mismo tiempo que en Madrid habia una manifestación contra el gobierno, contra una clase política alejada de la realidad de los ciudadanos y que vive en su propio mundo de ilusiones políticas. Una manifestación que se saldó con mas de 60 heridos. Y mientras, ¿Mariano que?. Dando cuenta de un veguero por la 5ª avenida de Nueva York, con aspecto de hombre no ya del siglo XX, sino del XIX. Rodeado de un séquito vestido igual que su lider, con aspecto de gangsters de la época de Lucky Luciano.

Para rematar la faena, tras fumarse su puraco, Rajoy declaro que agradecía su colaboracion a todos los españoles que no se habian manifestado.

Y asi nos va. Como la frase (para enmarcar) que leí el otro dia en twitter:

En este país sólo hay una cosa más débil que la palabra de un político: La memoria de sus votantes.






martes, 11 de septiembre de 2012

Auriculares Microsoft Lifechat LX-3000




Me recomendaron estos auriculares con micro para el ordenador y no las tenia todas conmigo, entre otras cosas porque son de Microsoft y yo uso en todos los equipos Linux.

Y el asunto tampoco era trivial porque los auriculares son USB (los primeros que tengo de este tipo, siempre he usado mini-jack) asi que hacia falta que el equipo los reconociera, funcionara el volumen y esas cosas.

Total,  que al final los compre y la verdad es que (para ser de Microsoft ;-)) van bastante bien. Ligeros y con buen sonido (no excepcional, pero hasta ahora solo he oido Spotify con ellos), buena respuesta de graves (sin abusar) y buen sonido de micro segun los que me han escuchado por el usando Skype y Google Hangout.

Si tengo que ponerles un pero es sólamente el poco aislamiento que tienen a pesar de ser cerrados. Cubren completamente la oreja pero el aislamiento del exterior no es como, por ejemplo, los AKG que uso en casa y si hay ruido exterior se filtra claramente.

En cualquier caso, una buena compra por los alrededor de 20€ que me han costado.


 

viernes, 17 de agosto de 2012

Compartiendo la conexion 3G / UMTS via WiFi (Ubuntu Linux)



Tal como era de suponer, Vodafone no consiguió a tiempo cambiar mi tarifa de datos, asi que llevo semana y media sufriendo una velocidad del siglo pasado en el movil. Y tambien en el iPad, puesto que el movil por medio de tethering era lo que estaba usando para actualizar el tableto.

La excusa (ojo, que no dudo que el problema fuera realmente ese) es que la aplicación de gestión no les dejaba hacer el cambio de tarifa. Que les daba un error en la aplicación. Pero aún asi seguian manteniendo la fecha para solucionar el problema. Me imagino a todo el departamento corporativo de programación de vodafone buscando el bug y desplegando una nueva version de su aplicación interna en todos los servidores. O, quien sabe, en todos los terminales. Si el problema hubiera sido en mi empresa, probablemente nos hubieramos dejado los cuernos buscando lo que pasaba. Si en vodafone ha sido asi... lo siento chicos: ha sido inutil el esfuerzo. Demasiado tarde, como avisé desde el principio.

El caso es que en el portatil, que lleva una tarjeta SIM de no demasiado facil acceso si que tenía aún conexión. No me valia para las funciones móviles habituales pero si para conectarme al trabajo. De hecho, era la única conexión que permitía establecer la conexion VPN. Pero aunque habia buscado algun programa para que el iPad accediera a la conexion, no habia tenido éxito. Hasta ayer, que encontre de pura casualidad la forma mas simple que habia podido imaginar para hacerlo. Y sin instalar ningun programa.

Así que al grano: ¿como compartir la conexion 3G / UMTS o lo que sea desde un Linux via wifi?. Facil, facil al menos con mi configuración actual: Ubuntu 10.10 Maverick con NetworkManager para gestionar la conexiones de red e iptables instalado.

Primero: creamos una nueva red Wifi, de tipo Ad-Hoc (nunca lo habia usado hasta ahora), con el tipo de cifrado que queramos y el nombre de red que mas te guste.

Segundo: habilitamos en el kernel para que admita packet-forwarding:

sysctl -w net.ipv4.conf.all.forwarding=1

(Nota: hasta donde se, este parametro pasa a ser definitivo despues de ejecutarlo. Quiero decir, que sobrevive al reinicio del equipo. Esto tiene como parte buena que no tendras que ponerlo las siguientes veces que quieras activar este tipo de conexion. Como malo, que tu equipo puede reenviar paquetes entre interfaces de red y es posible que esto no sea nada bueno en según que circunstancias)

Tercero: le decimos a iptables que queremos hacer NAT de todo lo que vaya a salir por la conexion 3G/UMTS (en tu equipo puede ser otro dispositivo diferente a ppp0, verifícalo con un simple /sbin/ifconfig)

iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE


Y .. voila!. Ya tienes tu conexión wifi con la que salir desde otros terminales a traves de tu caja Linux con tarjeta de conexion telefónica.

No se si esta solución es universal o no, pero a mi al menos me está salvando del apuro temporalmente. O al menos en parte, porque al Kindle no parecen gustarle mucho este tipo de conexiones. Que le vamos a hacer. Afortunadamente en el Kindle el volumen de datos es mucho menor y es una cuestion de paciencia el seguir usando la conexion capada del otro móvil.

lunes, 13 de agosto de 2012

Yo mismo

«¿Quién soy?». Si se tratara de una simple cantidad de información, no habría nadie en este mundo que pudiera aportar más datos que yo. No obstante, al hablar sobre mí, ese yo de quien estoy hablando queda automáticamente limitado, condicionado y empobrecido en manos de otro que soy yo mismo en tanto que narrador —víctima de mi sistema de valores, de mi sensibilidad, de mi capacidad de observación y de otros muchos condicionamientos reales—. En consecuencia, ¿hasta qué punto se ajusta a la verdad el «yo» que retrato? Es algo que me inquieta terriblemente. Es más, me ha preocupado siempre.

Sin embargo, la mayoría de las personas de este mundo no parece sentir ese temor, esa incertidumbre. En cuanto tienen oportunidad hablan de sí mismos con una sinceridad pasmosa. Suelen decir frases del tipo: «Yo parezco tonto de tan franco y sincero como soy», o «Soy muy sensible y me manejo muy mal en este mundo», o «Yo le leo el pensamiento a la gente». Pero he visto innumerables veces cómo personas «sensibles» herían sin más los sentimientos ajenos. He visto a personas «francas y sinceras» esgrimir sin darse cuenta las excusas que más les convenían. He visto cómo personas que «le leían el pensamiento a la gente» eran engañadas por los halagos más burdos. Todo ello me lleva a pensar: «¿Qué sabemos, en realidad, de nosotros mismos?».

Haruki Murakami, en Sputnik, mi amor




 

 

domingo, 12 de agosto de 2012

Casi sin conexion. Gracias vodafone



Alucinante. Simplemente alucinante es lo que me esta pasando con la conexion de datos (vodafone) desde hace unos dias.

Estoy pasando unos dias de vacaciones en el pueblo de Cabo de Gata, un pequeño pueblo cerca del punto geográfico del mismo nombre. En el pueblo, hasta donde yo se, sólo hay una antena de telefonía (lo que hace, entre otras cosas, que la geolocalizacion sin GPS sea inutil al no poder triangular) y eso hace que en determinados momentos del dia, y sobre todo en fines de semana, la calidad de las conexiones de datos y por ende su velocidad, se reduzca de forma notable. 

Asi que desde mediados de la semana pasada empecé a notar que la velocidad iba anormalmente lenta. Y no es que me esté bajando películas precisamente, pero es que el simple hecho de actualizar una aplicación del teléfono (4 ó 5 Mb) tardaba una eternidad.  Suponiendo lo que pasaba, el jueves llamé a mi oficina -el movil es de empresa- y efectivamente, el límite a partir del cual se ralentiza la velocidad es de 300Mb. Suficiente en Madrid con WiFi por todos lados pero insuficiente para mantener la conexion del portatil al trabajo, el iPad y toda la lista de programas habituales en el movil, desde twitter o facebook a instagram o foursquare. Por no no hablar de los mapas del GPS que necesité ayer mismo para buscar el camino mas rápido para llegar a un hospital con mi hijo. 

Vale, habrá que cambiar la tarifa de datos, pensé. Si en una semana he gastado unos 300Mb, con 1Gb tendré suficiente hasta el 18 que llego a Madrid.  Primer problema: no puedo subir la conexion hasta  1Gb. Tiene que ser a 2. Y cuesta 25€ mas al mes. Vale.  Me parece un precio mas que excesivo, pero puesto que lo necesito para monitorizar los servidores de la oficina y subir código, lo podemos pagar por un mes y luego darlo de baja y volver a la situacion anterior.  

Segundo problema: no se puede cambiar porque según parece, tengo dado de alta un servicio llamado Find&Go, que ni sabia que tenia ni por supuesto he usado nunca y no pueden desenganchar para activarme la nueva tarifa de datos. 

¿Por qué tengo ese servicio activado?. Nadie lo sabe. Era una oferta gratuita durante un año que no se sabe por qué sigue activa. ¿Por qué no se puede desenganchar?. Nadie lo sabe. ¿Por qué no se puede activar la nueva tarifa independientemente de si mantengo o no ese servicio?. Tampoco nadie sabe dar respuesta.  ¿Para cuando se podrá resolver el problema?. Para eso si que hay respuesta. Para el dia 16.  7 dias despues de solicitarlo. Y dos dias antes de llegar a Madrid. 

¿De verdad, Vodafone?. ¿No me estais tomando el pelo? ¿Para un cambio de servicio que debería tardarse en hacer menos 1 minuto, y por el que estoy dispuesto a pagar 25€ más vais a tardar una semana?

Yo necesito conectividad y la quiero pagar, vivo en un país (teóricamente) avanzado, trabajo en un sector hipercomunicado en el año 2012.. ¿En que año vivís vosotros?






viernes, 10 de agosto de 2012

Saber gastar en lo necesario

Si necesitas una máquina para hacer algo y no la compras al final te darás cuenta de que has pagado lo mismo y no tienes la máquina.

– Henry Ford

Via microsiervos

 

miércoles, 8 de agosto de 2012

Kiva




Hace ya algun tiempo que me di de alta en Kiva. Se trata de una organización que gestiona micro-créditos para que personas o grupos, normalmente de paises poco favorecidos, puedan llevar adelante proyectos que les permitan vivir. La vieja teoría de enseñar a pescar en lugar de regalar los peces.



Desde el punto de vista del colaborador (o sea, supongo tu que estas leyendo esto) la forma de hacer las cosas es simple: te das de alta en su sitio web, eliges a quien quieres prestar (si: prestar), que cantidad (mínimo 25€) y das tu numero de tarjeta. Listo. Acabas de participar con tus 25€ en que una familia de Togo, que necesitaba 2000$ para poner una pescaderia, pueda tenerlo mas facil. O para que un grupo heterogéneo de Mali pueda comprar aperos de labranza por valor de 1500$ para cultivar unas tierras. O para que ....


No solo se trata de que ves la cara de a quien estás haciendo una donación, también tienes la ventaja de que puedes elegir el destino de tu dinero en función de lo que te parezca mas importante, menos superfluo, o del pais que peor lo esté pasando segun tus propios criterios. También, como decia arriba, no es una limosna a fondo perdido. Es un prestamo. Y se devuelve. Leí en detalle en su momento la forma en la que los receptores devuelven el dinero, como kiva selecciona a quien presta y como son los plazos de devolución, lo puedes leer aquí. A mi me parecieron garantías suficientes y ya he hecho varias donaciones, en parte reinvirtiendo las devoluciones (y restando una parte que opcionalmente, puedes donar tambien a Kiva como organización)


Donaciones de Kiva por paises


Bueno, y ¿porqué escribo esta anotación justo ahora?. Porque me ha llegado un email de Kiva en el que por cada nuevo socio añaden a mi cuenta 25$ para hacer nuevas donaciones (sin que ese dinero me sea luego devuelto, claro). O lo que es lo mismo: si te das de alta usando este enlace aparte de lo que dones tu, alguien más recibirá 25$ .

Anímate. Con muy poco esfuerzo puedes ayudar a alguien que esta mucho mas jodido que tu.




domingo, 22 de julio de 2012

Nueva etapa

No se muy bien lo que voy a escribir por aquí. Despues de mas de cinco años escribiendo en coridio y aerotrastornados de repente se me pasaron las ganas de escribir. En Aerotrastornados, aun de vez en cuando me apetece. Pero Coridio, por donde empezaron mis andanzas bloggeras (que poco me gustan estas palabras derivadas), cada vez menos. Y cada vez menos ganas.

Creo que en parte hubo una temporada en la que me sentia obligado a publicar cosas para que gustaran. Para generar buzz. Para que alguien lo leyera. No es que tuviera necesidad de reconocimiento o algo parecido, mas bien creo que era una especie de carrera a ver si conseguia mas lectores.

El caso es que aun hoy de vez en cuando me apetece escribir algunas cosas, probablemente mas personales y casi seguro que sobre temas muy diferentes entre si. Y no se porqué, pero siento que Coridio no es el lugar apropiado para ello. Tuvo unos cientos de lectores fieles, con unas expectativas, y me da la sensación de que cambiar de repente el ritmo no seria bueno para nadie.

Y aqui estoy. En una piscina el primer dia que habia decidido no hacer nada relacionado con el trabajo en los ultimos 9 meses, pensando si hablaré de mi empresa en la que llevo 20 años, de mi otra empresa a medio fracasar, de los cambios que he experimentado (seria cruel decir sufrido cuando creo que han sido para mejor) en el ultimo año y medio. De programacion. De Linux. Ni idea.

Solo tengo una cosa clara: no quiero hacer un Coridio 2. No quiero hacer 'repost' de otros sitios, ni colgar los videos de moda. Esto sera otra cosa aunque aún no sepa el qué.


sábado, 21 de julio de 2012

¿Mueren los blogs?


Supongo que los blogs no mueren: hay que matarlos. Y en mi caso, me parecería una crueldad hacerlo con este despues de 735 entradas y 6 años. No lo haré. Que al menos queden para mi historia personal las horas dedicadas

Pero lo cierto es que en los últimos tiempos no tengo ni mucho tiempo ni muchas ganas de escribir. Tal vez algún dia vuelvan ambas cosas, pero por el momento creo justo que los 72 valientes suscriptores que aún permaneceis ahí segun feedburner sepais a que ateneros. No quiero tener sobre mi cabeza la presión ocasional de tengo que escribir algo, que tengo Coridio abandonado. Rompo el contrato tácito que me unia a vosotros. Si seguís ahí, no espereis nada nuevo. Y si aparece, será sorpresa.


En cualquier caso, y aprovechando que mañana es el ultimo dia del año, no puedo por menos que echar la vista atrás para recordar algunas cosas. Por ejemplo, que aunque mi sensación es que el blog ha ido a menos, en realidad he publicado el mismo numero de entradas que durante el 2010. No se si habrán estado mas o menos trabajadas, mas o menos personales, mas o menos interesantes.

Tambien mi blog aeronáutico, Aerotrastornados ha tenido un descenso en la atención que le prestaba. E incluso tras la ilusión que me hizo el que Iberia contactara conmigo para colaborar puntualmente en su blog Me Gusta Volar, al final no he escrito todo lo que me habria gustado. Espero solucionarlo en año que comienza ya mismo.

En otros temas, una de las cosas mas curiosas que he hecho durante este año ha sido empezar a correr. Nunca, jamas habia hecho nada de ejercicio y de repente a principios de año me dió por salir a correr un dia. Casi echo los pulmones por la boca tras correr 40 segundos seguidos, pero vaya usted a saber porqué seguí haciendolo dia si, dia no. Y hasta hoy, 614 Km corridos segun RunKeeper. Y mañana corro (es un decir: con llegar a la meta el penúltimo a gatas me doy por muy satisfecho) la San Silvestre Vallecana. Impensable hace un año.

Otra de las cosas que tampoco habria dicho un año antes que haría fue comprar un iPad. Yo, tan linuxero y androidero de repente me pasé al reverso tenebroso. Cierto es que estuve esperando a ver si aparecía algun tablet con Android que pudiera hacer sombra al iPad original. Salió el Motorola Xoom, mucho mas caro, y entonces Apple dió una patada hacia delante al mercado sacando el iPad2. Sinceramente, una de las mejores compras que he hecho nunca. Tanto para consumir información como para sustituto ocasional del portatil, como para ayuda en vuelo.

En lo profesional he tenido dos cambios destacables: por una parte el cambio de infraestructura que hice en los servidores de mi empresa gracias a la virtualizacion con VMWare, pasando de unos 14 servidores a solo 3. Una maravilla. Y por otro lado la decisión firme de volver a programar tras muchos años dedicándome sobre todo a labores de sistemas y poca programación. Quiero volver a crear. Yo era bueno en eso. Espero conseguirlo.

El que escriba menos por aqui no quiere decir que esté desaparecido de la vida social en red: sigo en Twitter, en Google+ y en todos los sitios donde estaba antes y que están por la columnna derecha del blog. Si, tambien en Facebook, pero cada vez me interesa menos. También en mi intento de página-personal-recopilatoria-con-dominio-propio: jmiguel.eu


¡Feliz año nuevo a todos!


Banda sonora para esta anotación: All things must pass, de George Harrison:

martes, 13 de marzo de 2012

martes, 6 de marzo de 2012

Los tres cerditos, según The Guardian

Los tres cerditos, según The Guardian:

Excepcional el anuncio del diario británico The Guardian. La traducción al castellano es de www.opeuribor.es (pincha en el botón de “cc” para activar los subtítulos).





Via Escolar



 



lunes, 20 de febrero de 2012

Spring I/O 2012


Comienzo ahora a escribir esta anotación que iré actualizando según vaya poniendo en orden mis notas, asi que no estará completa hasta dentro de unos dias. Intentaré publicar por aquí tanto mis notas (tomadas a mano con el iPad) como enlazar las presentaciones que estén disponibles. Aunque sea para mi propia referencia futura.

El jueves y viernes pasados asistí junto con un compañero de trabajo a las conferencias Spring I/O 2012 organizadas por JavaHispano. Una de las mejor organizadas de las que he asistido ultimamente, lo que dobla el mérito de una gente que no se dedica profesionalmente a organizar eventos. Sin ir mas lejos, la organización fue (bajo mi punto de vista) mucho mejor que la mostrada por los difuntos Sun microsystems hace dos años en el mismo campus.

Las conferencias fueron todas de carácter exclusivamente técnico, sin orientación comercial. Justo lo que quería en un momento en que estoy pensando en mi empresa que nueva orientación podemos tomar para mejorar nuestra productividad y la forma de programar.

Aparte de las notas que pueda comentar sobre cada una de las conferencias, algunas ideas generales que me llamaron la atención en un evento de este tipo:

  • Agnosticismo en las herramientas. En otras conferencias a las que he asistido, por ejemplo de Sun u Oracle, habia mucha mas imagen corporativa: todos el mismo sistema de presentaciones, los mismos editores, los mismos compiladores.. Aqui, aun siendo praticamente todas las herramientas de vmware cada uno usaba lo que mejor le parecia y me parece que es algo muy positivo y muestra apertura.
  • Reconocimiento de errores y problemas. No tanto en el sentido de que mal hemos hecho esto como en una conversacion de programador a programador: mejor no hagas esto por este camino porque que puede dar problemas.
  • Macs. Ubuntu. Un solo windows. Entre los ponentes solo vi un Windows. Entre los asistentes pocos mas. Cuando menos llamativo. O tal vez ya no tanto en los tiempos que corren. Aunque si resultaba curioso ver a un tipo con un super MacBook editando código con vi.


Habia tambien unos cuantos puestos informativos: Atlassian (con el irrepetible @david_bonilla a la cabeza, Paradigma, Escuela de Groovy y los chicos de JRebel. Atlassian fueron también los organizadores de la fiesta del primer día (que me perdí) y sorteaban una camiseta que a un pervertido de las friki-camisetas le encantaría tener pero no tuve la suerte de que me tocara. Aunque... bueno, aún tengo alguna esperanza ;-)

En las presentaciones conocí un enorme número de tecnologías que desconocía, acostumbrado a mis queridos EJBs, Oracle (retiro lo de querido en este caso), Jboss y nuestro propio framework interno. Solo por citar algunos de los que mas me llamaron la atención (y que ya iré enlazando): RabbitMQ , Thymeleaf (desarrollo español mas que interesante), o MongoDB


A continuación (y a falta de organizar), algunas de las presentaciones que se pudieron ver:




La mítica presentacion de Matt Raible comparando los distintos frameworks de desarrollo:



Thymeleaf, presentacion en PDF


Fallando con Grails



Y que no falte el resumen en video de David Bonilla:




Continuara....