Thinking on hiring me?

Please read

Fernando Guillén

a Freelance Web Developer

cabecera decorativa

software development as an artistic expression

Archive for Julio, 2007

Lunes, Julio 30th, 2007

Descubrimientos curiosos en el mundo del Software Social

Diarios de viajeros

www.wikitravel.com

“Una guía mundial de viajes, gratuita, completa, actualizada y para todos”

Una nueva demostración que la aplicación web 2.0 por excelencia es mediawik. Esta vez se trata de un recopilatorio de experiencias realizado por viajeros anónimos que nos cuentan trucos y detalles sobre un posible objetivo de nuestro próximo viaje.

* nacimiento en julio del 2003
* 16.000 destinos en inglés
* publicado en 17 idiomas

Dormir en aeropuertos

www.sleepinginairports.net

“Para viajeros que realmente andan justos de presupuesto y buscan una manera de ahorrarse unos dólares, ¿por qué no considerar dormir en el aeropuerto?. Muchos aeropuertos son actualmente mejores que la hospedería local […]”

Una de las aplicaciones de Software Social más feas que he visto nunca (se enorgullecen de “Site made with Paintbrush”), también una de las más antiguas, aunque no por ello deja de ser realmente útil. Justamente hace no 10 días que tuve que hacer un transbordo de 6 horas en el aeropuerto de Praga, me habría venido estupendo conocer trucos para agenciarme un hueco cómodo donde esperar agusto.

* nacimiento: hace 11 años
* más de 5.000 experiencias

Reservas en cenas caseras en París

www.meetingthefrench.com

“[…] abre para ti las puertas al íntimo y auténtico París. Encuentra una cama y desayuno, escoge un tour guiado por un vencindario de París, […]”

Una cena íntima ofrecida por un chef aficionado en su propia casa de París. Interesante y curiosa oferta de turismo.

Turismo como un vecino

www.like-a-local.com

“¿Cómo puedo experimentar la vida real en esta ciudad?”

Elige un vecino de la ciudad que se ofrece voluntariosamente a mostrarte sus secretos.

Jueves, Julio 26th, 2007

Struts: cargando un ActionForm desde atributos en la ‘Request’

Struts tiene pegas, tiene grandes virtudes que hace que sigamos amándolo y respetándolo… hasta que Spring nos separe, pero tiene grandes pequeñas pegas.

Una de ellas es que cuando ‘un caso de uso’ requiere de uno o varios parámetros para iniciar la secuencia siempre se esperan como parámetros de la petición al estilo:

http://miaplicacion.com/peticion.do?parametro=valor

Mismamente el detalle de un registro o de un objeto cuya id se la pasamos por parámetro de la petición:

http://miaplicacion.com/detalle.do?id=1

Entonces nuestro Action recibe la petición y automáticamente carga el ActionForm asociado a partir de los parámetros, en este caso ‘id’.

Pero ¿qué pasa si quiero invocar a este ‘caso de uso’ desde otro Action al estilo?:

<forward name="exito" path="/detalle.do" />

Pues que no hay manera de pasarle el parámetro id a ese Action y el ActionForm no se cargará.

La solución más rápido es hacer que el Action sea ambidiestro y sea capaz de recibir valores tanto desde parámetros como desde un atributo de la ‘Request’ de tal manera que el Action origen que invoca a este Action destino cargue el valor que le quiere pasar en un atributo de la ‘Request’.

Esta solución tiene dos problemas:

* Se genera un montón de código basura en el inicio del Action para proporcionarle la capacidad de recoger valores desde las dos fuentes.

* No aprovechamos la funcionalidad de Struts para gestionar la auto-carga de valores.

* No se puede validar en formulario pues si enviamos los datos en los atributos de la Request el formulario ni se entera.

Entonces debemos buscar una solución más sólida, ágil y completamente reutilizable.

El RequestForm

Se trata de un ActionForm con la cualidad de buscar entre la Request actual atributos que coincidan con alguna de sus propiedades. Si el ActionForm encuentra el atributo con el mismo nombre que una de sus propiedades y también tiene un método ’set’ que recibe un objeto del mismo tipo que el valor contenido en el atributo de la Request entonces lo invoca y así sucesivamente hasta que el ActionForm se autorrellena.

Toda esta funcionalidad está encerrada en un método del RequestForm, pero este método no se invoca automáticamente así que hay que invocarlo a mano y hay que buscar un buen momento para hacerlo. Si miramos la secuencia con que Struts invoca a los métodos del ActionForm vemos que el mejor momento para invocar la carga de propiedades desde la Request es:

* Justo después del reset(). Los valores cargados desde la Request serán machacados con los valores que se encuentren entre los parámetros de la petición.

* Justo antes del validate(). Los valores cargados desde los parámetros de la petición serán sobrescritos por los valores encontrados en la Request.

Este es el ActionForm que se precarga rebuscando en la Request:

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;

import org.apache.log4j.Logger;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;

import com.constela.tronco.util.StringUtilities;
import com.us3.util.Util;

/**
 * <p>
 * </p>
 *
 * @internal_author fguillen@constelanetworks.com
 * @date 25/07/2007
 *
 */
public class RequestForm extends ActionForm {

	/**
	 * <p>
	 * This override for reset method invokes to chargeFromRequest
	 * this is the best place to invoke this method.
	 * </p>
	 * @date: 25/07/2007
	 * @author: fguillen@constelanetworks.com
	 *
	 * @param arg0
	 * @param arg1
	 */
	@Override
	public void reset(
		ActionMapping mapping,
		HttpServletRequest request
	) {

		super.reset( mapping, request );

		try {

			this.chargeFromRequest( request );

		}	catch ( Exception e ) {
			throw new Error( e );
		}

	}

	/**
	 * <p>
	 * Method that allows to the ActionForm to
	 * charge its fields from the Request's attributes
	 * </p>
	 *
	 * <p>
	 * The method will look for the fields on the ActionForm
	 * and will look for attributes on Request with the same name
	 * and will look for 'set' method on the ActionForm. If there exist
	 * both Request's attribute and 'set' method the 'set' method is invoked
	 * with the Request's attribute's value.
	 * </p>
	 *
	 * @date: 25/07/2007
	 * @author: fguillen@constelanetworks.com
	 *
	 * @param request
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	public void chargeFromRequest(
		HttpServletRequest request
	) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException{

		//
		// the fields of the ActionForm
		//
		Class c = this.getClass();
		Field[] fields = c.getDeclaredFields();

		//
		// walk throw the array of fields looking
		// for attributes on request with same name
		//
		for (int i = 0; i < fields.length; i++) {
			Field field = fields[i];

			String fieldName = field.getName();
			Object attributeValue =
				request.getAttribute( fieldName );			

			//
			// if attributeValue is not null
			// and the ActionForm has a set method
			// try to asign value to a field
			//
			if( attributeValue != null ){
				//
				// has it a public set method?
				//
				String methodSet =
					"set" +
					StringUtilities.capitalizeWord( fieldName );

				Method method = null;
				try{
					method =
						c.getDeclaredMethod(
							methodSet,
							new Class[] { attributeValue.getClass() }
						);
				} catch ( Exception e ) {
					// the method does not exists
				}

				if( method != null ){
					method.invoke(
						this,
						new Object[] { attributeValue }
					);
				}
			}
		}
	}
}

Entonces ahora, cada vez que queramos que un ActionForm sea capaz de precargarse desde la ‘Request’ debemos heredar de nuestro RequestForm y no directamente de ActionForm. Hay que acordarse que si sobrescribimos el método reset() en nuestra implementación hay que hacer una llamada a super.reset() al finalizar nuestras sentencias de reseteo.

Martes, Julio 24th, 2007

Last ID, iBATIS y MySql

Adicto como soy a las claves primarias auto-incrementales en MySql me perseguía el problema de no saber cuál era el ID con el que el último registro había sido insertado.

Esto me obliga a hacer indecorosos malabarismos para conseguirlo: sincronizar el método y solicitar el último ID insertado justo después de la inserción, solicitar la ID del registro por algún otro campo de clave única…

Hoy me dá por googlear un poco y me encuentro con una joyita del 2005 entre los archivos de una lista de correo.

Aquí está la solución para que la propia llamada al ‘insert’ te retorne el ID creado:

Si por ejemplo tenemos esta sentencia ‘insert’ en nuestro fichero de iBATIS:

	<insert
		id="insertSentence"
	>
		insert into
			MY_TABLE (
				FIELD_1,
				FIELD_2
			)
			values (
				#value1#,
				#value2#
			)
	</insert>

Hay que modificarla de esta manera:

	<insert
		id="insertSentence"
	>
		insert into
			MY_TABLE (
				FIELD_1,
				FIELD_2
			)
			values (
				#value1#,
				#value2#
			)
		<selectKey
			resultClass="long"
			keyProperty="ID"
		>
			select LAST_INSERT_ID()
		</selectKey>
	</insert>

Y el ID podrás recogerlo desde la llamada al mapeador de iBATIS:

Long key =
	(Long)
	sqlMap.insert( "insertSentence" );

Ahora soy un poquitín más feliz… espero que tú también.

Martes, Julio 17th, 2007

Cuando el modelado de una relación entre 3 tablas se vuelve un ejercicio de arquitectura.

Este va a ser un post muy técnico así que, querido/a amigo/a, si pasabas por aquí en busca de lectura ligera mejor continuas surcando tu blog-roll.

Lo que voy a intentar explicar aquí es una cuestión de diseño de un modelo relacional de base datos aparentemente muy sencillo pero que me está generando enormes dudas existenciales y repetidos re-factors de nuestra implementación.

Supongamos que tenemos en nuestro diseño de clases un bean (pojo o como queramos llamarlo), al que llamaremos Element que contiene un número indeterminado de elementos de otra clase bean que llamaremos SubElement.

Hasta aquí todo bien, para traducir esto a un modelo de base de datos nos basta con un relación 1 a n entre una tabla ELEMENT y otra SUB_ELEMENT.

El problema viene cuando existe otra clase de beans que también, entre sus atributos, se encuentra uno que contiene un número indeterminado de beans de clase SubElement, como se muestra en la figura 1.

figura 1

Es aquí cuando la relación 1 a n se complica pues ya no puedo definir una clave entre la tabla SUB_ELEMENT y la tabla ELEMENT_TYPE_1 pues puede que el registro de la tabla SUB_ELEMENT deba estar relacionado en realidad con registro de la tabla ELEMENT_TYPE_2.

La primera aproximación que se me ocurre es definir dos tablas diferentes para los objectos de clase SubElement. Una tabla (SUB_ELEMENT_TYPE_1) contendrá aquellos registros que se tengan que relacionar con la tabla ELEMENT_TYPE_1 y otra (SUB_ELEMENT_TYPE_2) contendrá los que se tengan que relacionar con la tabla ELEMENT_TYPE_2. Como se muestra en la figura 2.

figura 2

Esta implementación me empezó a disgustar desde el primer momento por el hecho de tener 2 tablas diferentes que en realidad contenían registros del mismo tipo de objeto, sin contar con que si aparece otro tipo de objeto que a su vez contenga también objetos del tipo SubElement habría que sumar otra tabla más con toda la lógica de persistencia que conlleva. Así que seguí buscando.

En un pequeño brainstorming entre mis compañeros se nos ocurrió que podíamos diseñar una tabla para acoger los objetos de tipo SubElement que tuviera una referencias externa a una de las tablas ELEMENT_TYPE_X pero sin definir a cual. Para saber a qué tabla había pertenecía había que hacer uso de un nuevo campo en la tabla SUB_ELEMENT cuyo valor, sacado de un catálogo limitado, nos indicase a qué tabla real pertenecía el ID que hacía las veces de clave externa. Como se muestra en la figura 3.

figura 3

Esta propuesta cumple nuestro deseo de agrupar todos los elementos de tipo SubElement en la misma tabla reutilizando también toda la implementación de la capa de persistencia. Nos ahorramos también el tener que crear nuevas tablas para alojar objetos tipo SubElement cada vez que surja una nueva clase tipo ElementTypeX. Pero surge un enorme problema de integredad referencial y es que tenemos que desprendernos de crear una clave externa en la tabla SUB_ELEMENT pues esa clave aveces pertenecerá a la tabla ELEMENT_TYPE_1 y otras a la tabla ELEMENT_TYPE_2, como ya hemos dicho esto depende de campo ELEMENT_TYPE. Esto puede causar que existan registros en la tabla SUB_ELEMENT que estén apuntando a registros en la tablas ELEMENT_TYPE_X que no existan.

Estaba apunto de elegir esta propuesta como definitiva, pues aunque tiene peligro de integridad me resultaba la más fácil de administrar y era un modelo mucho más escalable, cuando apareció este dibujo en mi cuaderno, ver figura 4.

figura 4

A primera vista parece bastante más complicado que los anteriores, y lo es, pero no mucho. Aparecen unas nuevas tablas que hacen de puente entre la tabla SUB_ELEMENT y las tablas ELEMENT_TYPE_X. Estas tablas hay que duplicarlas cada vez que aparezca un nuevo objeto tipo ElementTypeX pero no acogen más que una relación y los objetos reales, los SubElement, se encuentran todos agrupados en la misma tabla.

Esta implementación dificulta las sentencias SQL de inserción, búsqueda y eliminación. Es la única pega que todavía me carcome.

Ruego, paciente lector que has llegado hasta aquí, que si tienes alguna sugerencia o corrección que hacer para llegar a una solución más ágil que ésta, sin que se perjudique la seguridad en la integración, seas tan amable y orgulloso de resumírmela.

a Freelance Web Developer is proudly powered by WordPress
Entries (RSS) and Comments (RSS).

Creative Commons License
Fernando Guillen's blog by Fernando Guillen is licensed under a Creative Commons Attribution-NoDerivs 3.0 Unported License.