Gracias a mi amiga inglesa Vicky ya tengo mi proyecto de la web Amigo Invisible en inglés. Sin embargo me lo ha traducido como Invisible Friend… Me suena un poco raro, estoy seguro que los ingleses llaman a esto de otra manera, tendré que enterame…

Thanks Vicks!

Ya está disponible la versión del Amigo Invisible en Euskera!!!! Lagun Ezezagunaren Zozketa Online!

Mile esker Gorka eta Ainhoari!!!!

Desde esta noche está disponible un nuevo lenguaje, el francés, en la web del Amigo Invisible(Ami Secret Online!).

Gracias a Guillem Vidal que me ha ayudado a traducirlo. Vamos, más que ayudarme, lo ha traducido todo él!

Por cierto, una de las cosas útiles que aprendí ayer en el GDD, fue hablando con Ricardo Galli en su sessión de pasarela Jabber, y fue sobre la licencia de Meneame.
Utiliza la licencia Affero, que es compatible con GPL versión x, pero se aplica a productos web.

Resumidamente dice que puedes hacer lo que quieras con el código, pero cuando publiques una web que haya utilizado dicho código debes añadir un link e informar de cómo descargarlo!

Lo aplicaré al Amigo Invisible… cuando tenga tiempo!

A veces escribir www.amigoinvisibleonline.com es un poco farragoso y difícil, por lo que he usado Fon Gets Simple! y he creado los siguientes shortcuts:

Por probar…

Ya anunciaba hace unos días que necesitaba añadir persistencia en base de datos al amigo invisible, porque no tenía permisos para escribir en mi log4j, así que no sabía la actividad real de la aplicación.

Bueno, pues ya lo tiene. En el post anterior explico los detalles técnicos de la implementación.

Simplemente subrayar que lo único que almaceno son los nombres de los participantes, así como el amigo que le haya tocado (por si algún día hay que recuperar la información de un sorteo!), y además guardo un referencia del sorteo, la fecha y hora, y el mensaje enviado.

Es decir, NO almaceno emails! No me interesan, no quiero tener emails, no quiero que nadie se piense que utilizo esta aplicación para vender emails o hacer spam!

Y el que no me crea puede mirar el código fuente, aunque ya lo explicaba en el post anterior sobre hibernate annotations!

Por fin he añadido el soporte en base de datos de la información que creo interesante del proyecto amigo invisible online. Para ello he utilizado la JPA (java persistence API) con hibernate (basadas en el estándar JSR-220) y por supuesto Spring.

Las annotations te ahorran mucho código, más incluso que los xdoclets, y además te permite obviar completamente los incómodos archivos hbm.xml. Cómo utilizamos las annotations de hibernate?

Primero una introducción al contexto de mi proyecto. Quería persistir todo un grupo de participantes (clase Amigo) y su relación (clase Grupo), pero no quería guardar su email ni algún que otro dato, porque no quiero tener una base de datos de emails, no los quiero utilizar, y no quiero que por accidente alguna vez alguien los robe…

1.- Lo primero que hay que hacer es declarar estas clases como @Entity. De esta manera se informa que hay que persistirla y se creará automáticamente una tabla, utilizando como columnas de la tabla todos las propiedades (métodos get) que encuentre en la clase.

2.- Hay que definir una primary key a la tabla, el típico id secuencial autoincremental:

@Id @GeneratedValue(strategy=GenerationType.AUTO)
public Integer getId()...

3.- Si quieres que alguna propiedad no se añada a la tabla, puedes utilizar la anotación @Transient:

@Transient
public String getEmail()...

4.- En mi caso he tenido que crear esta relación 1..N entre Grupo y Amigo:

//En la clase Amigo se define el grupo...
@ManyToOne
@JoinColumn(name="grupo")
public Grupo getGrupo()

//En la clase Grupo se define el conjunto de Amigos...
@OneToMany(targetEntity=Amigo.class,cascade=CascadeType.ALL, mappedBy="grupo")
public Collection getAmigos()

5.- Los otros campos String, Integer… puedes utilizar @Basic, pero realemente es opecional

Y hasta aquí la programación java. La configuración es muy sencilla también. Me he creado este applicationContext-db.xml, donde podéis encontrar toda la configuración necesaria. Puedes hacer un copy-paste a tu proyecto y editar solo los siguientes puntos:

<property name="annotatedClasses">
<list>
<value>com.bcurtu.amigo.pojo.Amigo</value> <value>com.bcurtu.amigo.pojo.Grupo</value>
</list>
</property>

Aquí debes enumerar las clases que quieres que se persistan.

En la propiedad hbm2ddl:

<prop key="hibernate.hbm2ddl.auto">update</prop>

Puedes utilizar los valores update (solo modifica las tablas cuando hayan cambios), create (crea el schema si no existe), create-drop (borra lo que haya y después lo creas). Configurando esta propiedad te olvidas de la necesidad de crear el schema!

Por último (aparte de configurar correctamente los parámetros de acceso a la bbdd), debés crear los beans de tu DAO. En mi caso solo tengo uno:

<bean id="grupoDao" class="com.bcurtu.amigo.db.GrupoDaoHibernate">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>

Puedes bajarte el código completo del proyecto desde aquí para ver cómo encajan todos los otros componentes!

Finalmente me he decidido a añadir una base de datos al Amigo Invisible Online. En las FAQ afirmo que no tengo BDD, ya que quiero garantizar que los emails que se introducen no se guardan en ningún sitio y no voy a hacer mal uso de ellos.

Sin embargo tengo que contradecirme, ya que el hosting en el que estoy no me permite escribir en el log4j, por lo que no tengo control de qué está pasando en mi aplicación, cuánta gente la está usando, con qué frecuencia, si realmente terminan el sorteo…

La otra alternativa, los awstats y el google analytics, es decir, estadísticas, tampoco me son suficientes, ya que como los indicadores los tengo encapsulados en páginas jsp, no tengo referencia de qué acción se está ejecutando, solo el total.

Por lo que me he puesto manos a la obra, hoy que echan fútbol por la tele. Por ahora sigue todo igual, pero en los próximos días espero terminar este upgrade y ya os comentaré. Intentaré hacer públicos los resultados, para ir haciendo un seguimiento, qué tipo de mensajes, idiomas usados…

Bueno, he empezado la tarea de i18n el texto del Amigo Invisible, y he empezado por el Catalán (ca_ES), con ayuda de Eva, claro.
Desde la página de inicio podréis elegir el idioma que queráis, por ahora ES (Español) o CA (Catalán).
Pronto añadiré Inglés, Italiano, Francés, Alemán, Euskera, y ya veremos qué otros puedo encontrar por ahí. No es que los hable yo, solo inglés, pero espero que mis amigos me echen una mano…
Por supuesto, si alguien se anima a traducirlo a cualquier otro idioma, puede descargar el fichero de properties desde aquí, traducirlo y enviarme la copia.

Gracias!!!

En el post anterior comentaba que tenía un problema para subir el spring.jar al respositorio de código… No sé qué pasaba, pero lo he resuelto actualizándome a Spring 2.0 y subiéndo los jars por módulos, así que ya tenemos otra vez el proyecto Amigo Invisible Online completo en el repositorio y totalmente openSource.

También he i18n los errores que tenía hardcodeados en código, de la siguiente manera.

He utilizado el interceptor tal como se describe en la guía de Spring:

<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages"/>
</bean>

<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName"><value>siteLanguage</value></property>
</bean>

<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"/>

Esto permite que una página sea i18n, y que además, pasando la cadena siteLanguage como param GET, se cambia automáticamente el leguaje a la codificación elegida (si existe!):

http://127.0.0.1:8080/amigoinvisible/index.htm?siteLanguage=en

Pero cómo acceder desde una clase java a esta i18n? Facil, inyecta a tu controller los siguientes beans:

<property name="bundle" ref="messageSource"/>
<property name="localeResolver" ref="localeResolver"/>

Y en tu código utiliza lo siguiente:

bundle.getMessage("form.val.amigo.max",new String[0],localeResolver.resolveLocale(arg0))

Done.