Reduciendo el archivo TLD con un listener en JAXB

Ahora con el soporte completo de la versión jsp 2.1 para archivos TLD, el descriptor generado puede ser bastante grande. Según la especificación del archivo TLD, si un elemento no se encuentra presente, toma un valor default. Tal es el caso de required para un atributo ya que por omisión es false.

Este es un fragmento para la descripción de un atributo:

       <attribute>
            <name>url</name>
            <required>false</required>
            <rtexprvalue>false</rtexprvalue>
            <type>java.lang.String</type>
            <fragment>false</fragment>
        </attribute>       

Si se omitieran los elementos con un valor default, el xml podría quedar así:

        <attribute>
            <name>url</name>
            <type>java.lang.String</type>
        </attribute>

JAXB realiza el mapeo de todos los atributos cuyo valor sea diferente de null. Dentro del proyecto, los valores de estos atributos son copiados automaticamente de los valores de las anotaciones o en otros casos derivado del elemento que contiene la anotación. Debido a esta copia automática y que no creo que sea conveniente incluir condicionales para manejar valores default dentro del procesador, busqué y afortunadamente encontré una forma de limpiar estos atributos antes de ser transformados a XML.

Lo que hice fue utilizar un listener para el marshaller y antes de que el objeto sea convertido, explorar sus atributos y aquellos que tengan un valor que sea igual al default establecerlos en null.

Este es el código del listener:

package tldgen.processor;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlElement;

/**
 *
 * @author Victor
 */
public class DefaultValueCleaner extends Marshaller.Listener{

    @Override
    public void beforeMarshal(Object source) {
        try {
            PropertyDescriptor[] descriptors = Introspector
                 .getBeanInfo(source.getClass()).getPropertyDescriptors();
            for(PropertyDescriptor descriptor:descriptors){
                if(descriptor.getReadMethod() != null){
                    Object currentValue=descriptor
                          .getReadMethod().invoke(source);
                    XmlElement xmlElement = descriptor
                          .getReadMethod().getAnnotation(XmlElement.class);
                    String defaultValue=null;
                    if(xmlElement != null){
                        defaultValue=xmlElement.defaultValue();
                    }
                    if(currentValue != null 
                        && currentValue.toString().equals(defaultValue) 
                        && descriptor.getWriteMethod() != null){
                            descriptor.getWriteMethod().invoke(source, new Object[]{null});
                    }
                }
            }
        } catch (IntrospectionException ex) {
            throw new RuntimeException(ex);
        } catch (IllegalAccessException ex) {
            throw new RuntimeException(ex);
        } catch (InvocationTargetException ex) {
            throw new RuntimeException(ex);
        }
    }
    
}

Y así se establece en el marshaller:

   marshaller.setListener(new DefaultValueCleaner());

Sé que es ineficiente establecer el valor de un atributo para posteriormente quitarlo pero opté por esta opción por las siguientes razones:

  • Para que al procesador de anotaciones le sea transparente el uso o no de valores default.
  • Porque el proceso de generación del archivo se realiza solamente una vez durante la compilación y esto no afectará el performance de una aplicación en producción.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s