viernes, 2 de marzo de 2012

Conversiones de datos entre el cliente y servidor en Apache Tapestry

Apache Tapestry
Las clases que implementan la interfaz Translator en Tapestry permiten convertir el valor de un campo de texto a un objeto (a través del método parseClient) y de un objeto a un texto que será incluido en un elemento de formulario en el cliente (a través del método toClient). Esta conversión es necesaria ya que lo que enviamos al cliente y lo que recibimos de él es un String. Al recibir los datos desde cliente en el servidor necesitaremos alguna forma de convertir esos datos representados en formato texto a su representación en objeto que hagamos en el el servidor. Estas dos tareas que en un principio no son muy complejas son tremendamente necesarias y básicas en cualquier aplicación web, siendo algo básico es el framework que usemos el que debería dar un buen soporte para estas tareas. Con los translators podremos evitar repetirnos en diferentes puntos de la aplicación.

Una vez que tengamos definido el translator, Tapestry buscará el adecuado según el tipo de objeto a traducir y lo usará según sea necesario sin necesidad de que tengamos que hacer nada más. Vamos a ver un ejemplo, supongamos que en un campo de un formulario necesitamos mostrar una fecha con un determinado formato. En nuestras clases trabajaremos con objetos de tipo Date. El usuario deberá introducir la fecha con formato «dd/MM/yyyy».

...
import org.apache.tapestry5.internal.translator.AbstractTranslator;
import org.apache.tapestry5.services.FormSupport;

import com.evandti.ticketbis.domain.CodigoDescuento;
import com.evandti.ticketbis.misc.Utilidades;
import com.evandti.ticketbis.tapestry.services.TicketbisService;

public class DateTranslator extends AbstractTranslator<Date> {

 private String patron;

 public DateTranslator(String patron) {
  super("date", Date.class, "date-format-exception");
  this.patron = patron;
 }

 @Override
 public String toClient(Date value) {
  if (value == null) {
   return null;
  }
  
  return new SimpleDateFormat(patron).format(value);
 }

 @Override
 public Date parseClient(Field field, String clientValue, String message) throws ValidationException {
  if (clientValue == null) {
   return null;
  }

  try {
   return new SimpleDateFormat(patron).parse(clientValue);
  } catch (ParseException e) {
   throw new ValidationException(message);
  }
 }

 @Override
 public void render(Field field, String message, MarkupWriter writer, FormSupport formSupport) {
 }
}

Estamos extendiendo una clase del paquete org.apache.tapestry5.internal que es algo no recomendado pero la utilizamos por sencillez y para no tener que implementar nosotros lo que hace el propio AbstractTranslator. Para que Tapestry lo utilice deberemos hacer una contribución en el módulo de nuestra aplicación donde básicamente decimos que para una determinada clase se utilice un determinado Translator.

/* AppModule.java */
 public static void contributeTranslatorSource(MappedConfiguration configuration) {
  configuration.add(Date.class, new DateTranslator("dd/MM/yyyy"));
 }

A partir de este momento podríamos tener en archivo .tml de una página o componente lo siguiente y en la propiedad fecha del componente o página tendríamos un objeto de tipo Date olvidándonos por completo de la traducción.

<t:label for="fecha"/>: <t:textfield t:id="fecha" value="fecha" size="12" label="Fecha"/>

Hay otra interfaz que hacen algo similar a los Translators, es la interfaz ValueEncoder pero la diferencia entre las dos está en que en los translators puede ser necesaria algún tipo de validación por nuestra parte ya que son datos que introduce el usuario y en los encoders no ya que no son datos que introduce el usuario. Esto se ve claramente en los parámetros de los componentes TextField, Hidden y Select, el primero utiliza un Translator y los dos últimos un ValueEndoder.

Tapestry ya proporciona un ValueEncoder para las entidades de nuestro dominio si utilizamos Hibernate, la clase es HibernateEntityValueEncoder. Para terminar, indicar Tapestry también proporciona un ValueEncoder por defecto para los tipos Enum.

Referencia:
Documentación sobre Apache Tapesty