JAXBContext y el error típico que afecta a la performance


Desgraciadamente, por un sinfín de motivos, hay varios aspectos en el desarrollo de un sistema que nunca se le dan la importancia que debiera y cuando dicho sistema se encuentra en producción, saltan situaciones que son muy costosas de arreglar.

En mi experiencia laboral me he encontrado de forma recurrente que sistemáticamente se tratan con absoluta dejadez la documentación del sistema a desarrollar y toda la fase de pruebas de carga, estrés, performance, etc...

Todo el mundo tendrá miles de experiencia al respecto y estoy seguro que todo el mundo se habrá encontrado en la situación que una vez puesto el sistema en producción, cuando tenemos una carga de usuarios alta, ciertas partes del sistema empiezan a reaccionar de forma lenta en el mejor de los casos y provocando un apocalipsis en cascada en el peor de los casos.

En este post tampoco se trata de hacer un simposium sobre las buenas prácticas de las pruebas, pero si que me gustaría tratar un error que me he encontrado una vez y otra a lo largo de los años y que a día de hoy, sigo encontrándolo irremediablemente.

Todo aquel que ha tenido que trabajar con XML y Java, probablemente haya implementado algo con JAXBContext, módulo que nos permite la transformación de un XML a Object y viceversa.

El siguiente ejemplo contiene una situación que afecta al rendimiento y que estoy seguro que en un futuro continuaré encontrándome y haciendo aspavientos de indignación cada vez que la vea.


public class XmlConverter {

  // Metodo para convertir XML a Object
  public Object unmarshall(Reader source) throws JAXBException {
    JAXBContext jaxbContext = JAXBContext.newInstance(Student.class);
    Object result = jaxbContext.createUnmarshaller().unmarshal(source);
    return result;
  }

  // Metodo para convertir Object a XML
  public Writer marshall(Object source) throws JAXBException {
    JAXBContext jaxbContext = JAXBContext.newInstance(Student.class);
    Writer writer = new StringWriter();
    jaxbContext.createMarshaller().marshal(source, writer);
    return writer;
  }
}

En estos dos métodos se está inicializando el JAXBContext y el Marshaller/Unmarshaller, que obviamente son necesarios para realizar las conversiones XML/Object. Pero el error de concepto es inicializar el JAXBContext cada vez que alguien realiza la invocación a uno de los dos métodos, dado que su inicialización es costosa y JAXBContext al ser ThreadSafe puede ser creado una vez y ser reutilizado en cada invocación.

Si realizamos una pequeña refactorización en el código podremos mejorar la performance.

public class XmlConverter {

  private JAXBContext jaxbContext;

  public XmlConverter() {
    try {
      this.jaxbContext = JAXBContext.newInstance(Student.class);
    }
    catch(JAXBException e) {
      // Realizar el tratamiento de excepciones que se considere oportuno
    }
  }

  // Metodo para convertir XML a Object
  public Object unmarshall(Reader source) throws JAXBException {
    Object result = jaxbContext.createUnmarshaller().unmarshal(source);
    return result;
  }

  // Metodo para convertir Object a XML
  public Writer marshall(Object source) throws JAXBException {
    Writer writer = new StringWriter();
    jaxbContext.createMarshaller().marshal(source, writer);
    return writer;
  }
}

Simplemente con pasar JAXBContext a una atributo de la clase e inicializarlo una sola vez en la constructora, hemos mejorado los tiempos de respuesta de todas aquellas funcionalidades que requerían de estos dos métodos.

No hay comentarios