Geek & Roll » 2010 » March

Último post de Geek&Roll (April’s Fool)

Rafyta March 31st, 2010 geekandroll 32 comentarios

las-golondrinas

Chatos lo sentimos mucho, con el corazón (o un águila) en la mano nos despedimos. Fue buena experiencia, pero lamentablemente “algunas” personas se ponen muy rockstars y lo más sano es que cada quien continúe con su camino.

Por mi parte saben en qué dominio encontrarme. GeekAndRoll.com va a ser subastado y vamos a donar el dinero a la beneficencia que resulte elegida en un posterior poll.

Chilo Compas…

Cuando Hibernate cascade te juega bromas

Cesar March 23rd, 2010 programacion 7 comentarios

Ah, Hibernate. ¿Que podemos decir? Te atrae con promesas de cerrar la brecha entre lo relacional y el mundo de los objetos, pero luego te deja a que trates de entender por qué demonios no funciona como tu lo esperas. En cierto modo es como C++ que evita que te dispares en el pie, pero cuando lo haces te vuelas la pierna completa.

De lo que quiero hablar en este post es de las operaciones en cascada. Hibernate te permite configurar operaciones en cascada, de tal manera que si así tu lo deseas, cuando creas un objeto que a su vez tiene relación con otro objeto, ambos pueden ser guardados con una sola operación. Por ejemplo:

public class Member(){
    private ExpertiseArea expertiseArea;
}

En este caso, Member tiene una relación uno a uno con ExpertiseArea, y sería deseable que al guardar Member también se guardara ExpertiseArea. Para lograrlo, le decimos a Hibernate cuando debe realizar las operaciones en cascada:

<one-to-one name="expertiseArea" 
    class="ExpertiseArea" 
    cascade="persist,save-update" />

Con lo anterior le decimos a Hibernate “Cuando ejecutes las operaciones de crear (persist) y de actualizar (save-update) en Member, también hazlo para ExpertiseArea”.

Creamos una prueba para verificar que lo anterior funciona como debería:

	@Test
	public void testMemberRetrieval(){
		Member m = buildMember();
		md.makePersistent(m);
		md.flush();
		
		Member retrievedMember = md.findById(m.getId(), false);
		assertNotNull("Entity should not be null",retrievedMember);
		Member retrievedMember = md.findById(m.getId(), true);
		assertNotNull("Entity should not be null",retrievedMember);
		assertNotNull("ExpertiseArea should be persistent", 
                    retrievedMember.getExpertiseArea());
	}
	
	private Member buildMember(){
		ExpertiseArea ea = new ExpertiseArea("Singer");
		Member m = new Member("Britney Spears", ea, "Queen of Pop");
		return m;
	}

¿Qué estamos haciendo aquí? Creamos un nuevo Member, le asignamos un ExpertiseArea, y lo guardamos todo en una sola instrucción makePersistent. Como tenemos configuradas las operaciones en cascada, esperamos que se guarden en la base de datos tanto la instancia de Member, como la de ExpertiseArea. Efectivamente, la prueba pasa y podemos revisar en los logs lo que Hibernate hace tras bambalinas:

Hibernate: insert into Member (VERSION, name, tagLine, id) values (?, ?, ?, ?)
Hibernate: insert into ExpertiseArea (VERSION, name, id) values (?, ?, ?)

Pass

No esperaba menos. Leer el resto de este post.

Formas dinámicas con GWT

Cesar March 8th, 2010 javascript, programacion Haz un comentario

Mi relación con GWT es una de amor – odio. Por un lado, tenemos a un toolkit que me permite utilizar mis conocimientos de Java para crear aplicaciones Web dinámicas, aplicando mejores prácticas en la arquitectura del código del cliente, mejorando el tiempo de respuesta gracias al uso de técnicas como agrupar imágenes para usarlas en el cliente con una sola descarga y código segmentado.

Por el otro lado, algunas tareas se vuelven más complicadas. Estoy convencido que las herramientas como GWT son el futuro, más no me queda claro si la actual implementación sea adecuada para toda la programación del lado del cliente. Además la curva de aprendizaje es más inclinada de lo que debería de ser; existen muchos gotchas todavía que hacen que el programador tenga que estar consciente de que esto es Web y no una aplicación regular.

En esta ocasión el requerimiento era el siguiente: tengo una aplicación que hace uso extensivo de formas para captura de datos. Necesito una manera de declarar dichas formas y que la aplicación automáticamente las ordene en diferentes tabs cuando estas crezcan demasiado. Las formas generadas deben poder ser insertadas en cualquier sitio, además de poder aplicarles estilos con CSS.

Como dije anteriormente, lo que me gusta de GWT es que puedo generar la solución a mis requerimientos de la misma manera en la que estoy acostumbrado a hacerlo. En este caso, creo una clase DynaForm que representa mi forma dinámica:

/*Represents the client side DynaForm*/
public class DynaForm {
	private String formSource;
	private ArrayList<String> panelTitles;
	private ArrayList<HTML> panels;
	private Node root;

	public DynaForm(String formSource){
		setFormSource(formSource);
	}

Ahora viene algo interesante. El método setFormSource(). Es aquí donde vamos a parsear el código de la forma para extraer los valores que nos interesan, y descomponerla en sus partes para después volverla a construir pero con las propiedades que nos interesan, en este caso los tabs.

public void setFormSource(String formSource){
	try{
		Document d = XMLParser.parse(formSource);
		root = d.getFirstChild();
		XMLParser.removeWhitespace(root);
		setPanels(root, formSource);
	}catch(Exception e){
		System.out.println(e.getMessage());
		e.printStackTrace();
	}
	
	this.formSource = formSource;
}

Como vemos, parseamos la fuente y de esa manera nos aseguramos que es código válido. Aquí podríamos aplicar otras reglas, como por ejemplo eliminar cualquier tag o propiedad maliciosa como una llamada a JavaScript. El método setPanels() es el que se encarga de construir los páneles necesarios.

/*
 * This method creates an array of HTML panels. Both panels and panelTitles must
 * contain the same number of elements. In case a <span> element doesn't provide 
 * a 'name' attribute, it is replaced by an empty string.
 * 
 * The number of HTML panels created is = # of <span> elements. All elements inside <form>
 * without a <span> are ignored.
 * */
private void setPanels(Node root, String formSource){
	panelTitles = new ArrayList<String>();
	panels = new ArrayList<HTML>();
	NamedNodeMap attribs;

	NodeList allElements = root.getChildNodes();
		
	/*
	 * Start looping all elements, and create new HTML panels as needed
	 * */
	for(int i=0; i<allElements.getLength(); i++){
		Node n = allElements.item(i);
		//if it's a span, create a new empty HTML panel and set its name.
		if(n.getNodeName().equals("span")){
			//Set the panel title entry
			attribs = n.getAttributes();
			panelTitles.add(getAttribValue(attribs, "name"));
			panels.add(new HTML());
		}else{
			try{
				//get the last panel
				HTML panel = panels.get(panels.size()-1);
				//add the node source to the panel HTML
				panel.setHTML(panel.getHTML() + n.toString());
			}catch(Exception e){
				System.out.println(e.getMessage());
			}
		}
	}
}

El método setPanels() toma todos los elementos de la forma y los analiza de la siguiente manera: Si es un elemento span, quiere decir que es el inicio de un nuevo tab. Toma la propiedad name del span y lo convierte en el título de la nueva tab. Si el elemento no es un span, entonces lo agrega a la lista de elementos que forman el tab actual.

Después, en el punto de entrada de la aplicación:

TabPanel tabPanel = new TabPanel();
dynaForm = new DynaForm(formSource);

//All each HTML panel to the tabPanel
for(int i=0; i<dynaForm.getPanels().size(); i++){
	quirksTabPanel.add(dynaForm.getPanels().get(i), dynaForm.getPanelTitles().get(i));
}

RootPanel.get("dynaForm").add(tabPanel);

Utilizando el siguiente código HTML como fuente de la forma:

<form method='POST' action='contactengine.php' name='contact'>
	<span name='Datos' />
	<label for='Nombre'>Nombre:</label>
    	<input type='text' name='Nombre' id='Nombre' />"
   
    	<label for='Correo'>Correo:</label>
    	<input type='text' name='Correo' id='Correo' />
    
	<label for='Mensaje'>Mensaje:</label>
	<textarea name='Mensaje' rows='20' cols='20' id='Mensaje'></textarea>
		
	<span name='Enviar' />
	<input type='submit' name='submit' value='Enviar' class='submit-button' />
</form>

El resultado es:
DynaForm

Claro que se deben usar estilos para hacer que la forma se vea más presentable. También creo que se puede lograr exactamente el mismo resultado utilizando una combinación de jQuery, selectores CSS y jQuery UI.