openxava / documentación / Migración

¿Cómo actualizar tu proyecto a la última versión de OpenXava?

Edita el archivo pom.xml de tu proyecto y cambia el valor de la propiedad openxava.version para que apunte a la última versión de OpenXava, así:

<properties>
    <openxava.version>7.0</openxava.version>
    ...
</properties>

Compila tu proyecto:


Si tu versión actual es la 6.x deberías seguir las instrucciones de la sección Para migrar de OpenXava 6.6.3 a OpenXava 7.0 primero.

Para finalizar, revisa las instrucciones de abajo para adaptar tu código.

Para migrar de OpenXava 6.6.3 a OpenXava 7.0

Convertir tu proyecto a estructura Maven

OpenXava 7 es compatible con Maven, por lo que la estructura clásica de un proyecto OpenXava ya no se reconoce. Has de convertir tu proyecto en un proyecto estándar de Maven. Por suerte, todo tu código actual es válido, sólo que tiene que estar colocado en carpetas diferentes. Si no estás familiarizado con Maven, mira la disposición estándar de directorios en un proyecto Maven.

La forma más fácil de migrar a Maven es crear un nuevo proyecto usando OpenXava 7.0 y después copiar el código fuente y los recursos desde tu proyecto actual al nuevo. Es decir, lo primero es crear un nuevo proyecto OpenXava usando Openxava Studio 7, para ello sigue las instrucciones de la guía de primeros pasos.

El siguiente paso es copiar todo el código fuente y los recursos desde el viejo proyecto a la carpeta correspondiente en el nuevo proyecto, como sigue:

El código de pruebas está en una carpeta diferente en Maven, por lo que deberías copiar:

Lo de arriba es suficiente para la mayoría de las aplicaciones.

El código web ahora está en src/main/webapp en lugar de web. Primero, crea una carpeta llamada xava en src/main/webapp para poner ahí tu código web.
Si tienes tu propios editores personalizados copia el código de los editores:

Copia también:

Los archivos servlets.xml, filters.xml y listeners.xml ya no se soportan. Puedes copiar su contenido a web.xml, que ahora está vacío y listo para tus propias cosas, en src/main/webapp/WEB-INF. Si usas las anotaciones @WebServlet, @WebFilter y @WebListener no tienes que hacer ningún cambio.

Si usas una librería de terceros extra en tu aplicación has de añadir una dependencia en el archivo pom.xml en la raíz de tu proyecto. Ya no vas a copiar jars en web/WEB/lib. Si no estás familiarizado con Maven lee sobre el mecanismo de depencia de Maven.

Si usas una base de datos que no sea HSQLDB has de añadir la dependencía para tu controlador JDBC. Por ejemplo, si usas MySQL añade la siguiente entrada a tu pom.xml:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.29</version>
</dependency>

Para otra base de datos simplemente pregunta a Google, algo como "maven dependency oracle" por ejemplo. Para que tu controlador JDBC se descargue y se incluya en tu proyecto has de ejecutar "mvn package" en tu proyecto. Puedes hacerlo desde la línea de órdenes (si tienes Maven instalado en tu máquina) o desde OpenXava Studio (no es necesario tener Maven instalado), con Run As > Maven Build... en tu proyecto y tecleando "package" como goal. Este paso no es necesario si usas HSQLDB como base de datos.

Ahora puede ejecutar tu aplicación. Pon tu ratón en src/main/java de to proyecto y escoge Run As > Java Application. A partir de ahora, puedes modificar tu código y relanzar tu aplicación como siempre, incluso si la lanzas en modo de depuración puedes modificar el código y ver el resultado sin relanzar la aplicación. Es decir, puedes trabajar en desarrollo como lo has hecho siempre.

Ya no usamos Ant. Ahora lo vas a hacer todo usando comandos de Maven. Si no conoces Maven muy bien lee alguna documentación de introducción a Maven. Por ejemplo, para crear el war para desplegar en producción usaras "mvn package", y obtendrás el war listo para ser desplegado en Tomcat en la carpeta target de tu proyecto.  Puedes ejecutar todos los comandos Maven desde dentro de OpenXava Studio, con la opción Run As en tu proyecto.

Fíjate que cuando creas el proyecto con OpenXava el nombre del proyecto está en minúsculas, esto es porque la nomenclatura para Maven es usar minúsculas para el nombre del artifactId, que coincide con el nombre del proyecto y el archivo a generar (el war). Para proyectos nuevo deberías usar minúsculas para tu nombre de proyecto. Sin embargo, por años hemos promovido el usar mayúscula para la primera letra del nombre del proyecto en la documentación, por lo que probablemente tengas tu viejo proyecto con el nombre empezando por mayúscula. Puedes elegir migrar a minúsculas y adoptar el estándar Maven, pero ten en cuenta que esto cambiaría la URL que usan tus usuarios.
Es decir, si el nombre de tu aplicación era "Almacenes" y lo cambias a "almacenes", ahora la siguiente URL: https://tusitio.com/Almacenes ya no va a funcionar. Ahora has de usar https://yoursite.com/almacenes (con almacenes en minúsculas). Si esto es aceptable para tus usuarios, perfecto. Si no, puedes renombrar tu proyecto Maven para que use el viejo nombre (Almacenes en este caso) y tus usuarios pueden continuar usando las URLs originales.
Digamos que quieres cambiar el nombre de proyecto de "almacenes" a "Almacenes". Para ello edita pom.xml y cambia:
<artifactId>almacenes</artifactId>
Por:
<artifactId>Almacenes</artifactId>
También en pom.xml cambia:
<finalName>almacenes</finalName>
Por:
<finalName>Almacenes</finalName>
Después edita aplicacion.xml (en src/main/resources/xava) y cambia:
<aplicacion nombre="almacenes">
Por:
<aplicacion nombre="Almacenes">
También en la clase lanzadora, es decir almacenes.java en el paquete com.tuempresa.almacenes.run, cambia:
AppServer.run("almacenes");
Por:
AppServer.run("Almacenes");
Para finalizar, deberías hacer en tu proyecto un Run As > Maven clean (mvn clean) y después Run As > Maven Build... tecleando package como goal (mvn package).

Hibernate actualizado desde 5.3 a 5.6

Hemos trabajado duro adaptando OpenXava para que funcione bien con Hibernate 5.6. Sin embargo, puedes encontrar problemas en tu propio código JPA/Hibernate. Si usas la API de JPA todo debería funcionar tal cual, pero hay nuevos bug en Hibernate 5.6 que pueden hacer que tu código falle. Hemos encontrado algunos.

Join requerido en referencias en clave

Con Hibernate 5.3 cuando usas una propiedad de una referencia y esta es parte de la clave, no necesitas añadir un join explicito en la consulta. Esto es de esta forma no importa el nivel de profundidad. En Hibernate 5.6, no necesitas añadir un join si tienes sólo un nivel de referencia, pero si tienes un segundo nivel, has de añadirlo, si el segundo nivel tiene una clave compuesta.

Diciéndolo con código. Si tienes:

@Entity
public class CargoTransporte {
	
    @Id @ManyToOne
    private Albaran albaran;

    ...
	
}

@Entity
public class Albaran {
	
    @Id 
    private int numero;

    @Id @ManyToOne
    private Factura factura;

    ...
	
}

@Entity
public class Factura {
	
    @Id 
    private int anyo;
	
    @Id 
    private int numero;

    ...
	
}

Con el código de arriba la siguiente consulta funciona bien con Hibernate 5.3:

String consulta="from CargoTransporte c where c.albaran.factura.anyo = :albaran_factura_anyo";
Query query = XPersistence.getManager().createQuery(consulta);
query.setParameter("albaran_factura_anyo", 2022);

Pero falla con Hibernate 5.6. Curiosamente si ejecutas la consulta contra Albaran preguntando por factura.anyo funciona, sólo falla con 2 niveles o más. Además, si Factura hubiera tendido clave simple, en lugar de compuesta, hubiera funcionado también. Hibernate 5.6 falla cuando combinas más de un nivel con claves compuestas.

No te preocupes, la solución es sencilla, simplemente añade un join explicito para la primera referencia. Es decir, de la siguiente forma funciona con Hibernate 5.6:

String consulta="from CargoTransporte c join fetch c.albaran a where a.factura.anyo = :albaran_factura_anyo";
Query query = XPersistence.getManager().createQuery(consulta);
query.setParameter("albaran_factura_anyo", 2022);

La solución es el join fetch c.albaran a. Moraleja, si alguna consulta te falla con Hibernate 5.6 prueba añadir un join explicito para tu referencia.

Esquema no creado automáticamente

Un nuevo bug en Hibernate 5.6 es que ya no reconoce javax.persistence.create-database-schemas, por lo que si tienes la siguiente configuración en tu persistence.xml:

<persistence-unit name="default">
    ...
    <properties>
        <property name="javax.persistence.schema-generation.database.action" value="update"/>
        <property name="javax.persistence.create-database-schemas" value="true"/> 
        <property name="hibernate.default_schema" value="MIESQUEMA"/>
    </properties>
</persistence-unit>

Con Hibernate 5.3 crea el esquema MIESQUEMA y después las tablas dentro, sin embargo con Hibernate 5.6 el esquema MIESQUEMA no se crea, por ende las tablas tampoco se crean. La solución es crear el esquema MIESQUEMA a mano. Has de contectarte a tu base de datos con tu explorador de base de datos y ejecutar una sentencia CREATE SCHEMA MYSCHEMA (o equivalente). Después, puedes arrancar tu aplicación y las tablas serán creadas.

Clase de utilidad XHibernate quitada

La clase de utilidad XHibernate se ha quitado porque se usaba en las aplicaciones con componentes XML, en las aplicaciones JPA usamos XPersistence en su lugar, y hemos quitado el soporte para componentes XML en v7.0. El API de Hibernate todavía está disponible. En el raro caso de que uses XHibernate en algún punto de tu código, cambia:

Session session = XHibernate.getSession();
session.save(factura);

Por:

Session session = (Session) XPersistence.getManager().getDelegate();
session.save(factura);
Es decir, puedes obtener un objeto Session de Hibernate desde el manager de JPA. Otra alternativa sería obtener la sesión de Hibernate directamente a la manera de Hibernate, aunque en este caso has de encargarte tú de crear la SessionFactory y ser responsable de cerrar la transacción y la sesión.

Recursos del proyecto en lugar diferente (para las pruebas JUnit)

Dado que ahora tus proyectos OpenXava tienen un estructura Maven estándar, la mayoría de los recursos de tu proyecto están en un lugar diferente ahora, por lo que si los usas en alguna de tus pruebas JUnit has de adaptar la ruta del recurso.
Por ejemplo, si tienes la siguiente línea en tu prueba JUnit:
uploadFile("scripts", "informes/Corporacion.html");
Deberías cambiarlo por:
uploadFile("scripts", "src/main/resources/informes/Corporacion.html");
Fíjate como cambiamos  informes por src/main/resources/informes en la ruta, porque ahora todos los recursos están en src/main/resources.

HtmlUnit actualizado de 2.32 a 2.63 (para las pruebas JUnit)

Si usas para las pruebas los métodos de ModuleTestBase no necesitas tocar nada. Sin embargo, si usas directamente el API de HtmlUnit, algo que puede ocurrir si usas getWebClient() o getHtmlPage() en tu prueba, en ese caso puede ser que necesites adaptar tu código. HtmlUnit mantiene viva su tradición de añadir cambios que producen incompatibilidad hacía atrás en cada versión menor, para ello cambian caprichosamente el nombre de los métodos, mueven las cosas de un paquete a otro o cambian el comportamiento de algún método. Hemos encontrado la siguientes cosas, por ejemplo.
El método asText() de HtmlElement ha sido renombrado como asNormalizedText(), por lo que si tienes un código como este en tu prueba:
HtmlElement tarjeta = body.getElementsByAttribute("div", "class", "ox-card").get(2);
assertEquals("Precio unitario: 0.00, Precio unitario en pesetas: 0"), tarjeta.asText());
Cámbialo por:
HtmlElement tarjeta = body.getElementsByAttribute("div", "class", "ox-card").get(2);
assertEquals("Precio unitario: 0.00, Precio unitario en pesetas: 0"), tarjeta.asNormalizedText());
Por si fuera poco asNormalizedText() no funciona exactamente igual que el antiguo asText(), ahora cambia cada salto de línea por un único \n. Esto significa que quizás tengas que cambiar algo en el código de prueba si usas asText() y examinas los saltos de línea. Por ejemplo, si tienes el siguiente código de prueba:
assertDiscussionCommentText("discusion", 0, Strings.multiline("admin - Ahora", "Hola, soy yo")); 
Cámbialo por:
assertDiscussionCommentText("discusion", 0, "admin - Ahora\nHola, soy yo");
Fíjate que ya no usamos la utilidad Strings.multiline() y usamos un único \n. En este caso no hemos usado asNormalizedText() directamente, sino assertDiscussionCommentText() de ModuleTestBase que usa asNormalizedText() para su implementación. Es decir, algunos métodos de ModuleTestBase están afectados por el nuevo comportamiento, entre ellos assertValueInList() del formato tarjetas de la lista, ahora has de usar un único \n en lugar de \r\n. Por lo que has de cambiar:
assertValueInList(2, "XAVA\r\n3\r\nPrecio unitario: 0");
Por:
assertValueInList(2, "XAVA\n3\nPrecio unitario: 0");
Otro cambio en la forma en que HtmlUnit funciona es que usa caracteres UNICODE en lugar de ANSI, por lo que si verificas caracteres especiales has de adaptar tu código. Por ejemplo, deberías cambiar:
assertEquals(
    "javascript:openxava.executeAction('openxavatest', 'Carrier', 'Effacer l" 
        + (char) 145 // ANSI  
        +"entité courante: Etes-vous sûr(e) ?', false, 'CRUD.delete')", 
    deleteLink.getHrefAttribute());
Por:
assertEquals(
    "javascript:openxava.executeAction('openxavatest', 'Carrier', 'Effacer l" 
        + (char) 8216 // UNICODE
        +"entité courante: Etes-vous sûr(e) ?', false, 'CRUD.delete')", 
    deleteLink.getHrefAttribute());
Fíjate que hemos cambiado 145 por 8216. Mira en esta tabla para saber el mapeo entre ANSI y UNICODE.
Ahora en las propiedades de tipo @HtmlText o @Stereotype("TEXTO_HTML") obtienes el contenido real, incluyendo las etiquetas de HTML, seguramente por causa de un mejor soporte de JavaScript en HtmlUnit. Por lo tanto, si tienes una propiedad @HtmlText llamada descripcion, cambia:
assertValue("descripcion", "Esto es una gran discusión sobre jUnit");
Por:
assertValue("descripcion", "<p>Esto es una gran discusión sobre jUnit</p>");
También, getValue() y assertValue() ahora hacen un trim() para funcionar mejor con el nuevo comportamiento de HtmlUnit, por lo que has de cambiar:
assertValue("poblacion", "46540 ");
Por:
assertValue("poblacion", "46540");
Fíjate como hemos quitado el espacio después de 46540.
El nuevo HtmlUnit requiere que recargues la página para que el CSS funcione correctamente, por lo que si activas el CSS para tu prueba tienes que hacer un reload() después. Así:
getWebClient().getOptions().setCssEnabled(true); // Si haces esto...
reload(); // ...has de añadir esta línea
Curiosamente, el añadir el reload() no es necesario siempre, añádelo cuando te encuentres una prueba con CSS que falle.

Apache POI actualizado de 3.15 a 5.1

Apache POI es una librería Java usada para manipular documentos de Microsoft, como Excel. JasperReports la usa por eso ha sido necesario actualizar POI para que la última versión de JasperRerpots funcione. Si no usas POI directamente no tienes que hacer nada. Sin embargo, si usas la API de POI en tu código, has de adaptarla, porque ellos han refactorizado todo el código cambiando nombres y lugares, para hacerlo incompatible. Estos son algunos de los cambios de los que nos hemos dado cuenta.
Han convertido todas las variables finales en enums, estos son algunos ejemplos:
CellType.NUMERIC // En lugar de Cell.CELL_TYPE_NUMERIC
HyperlinkType.FILE // En lugar de Hyperlink.LINK_URL
FillPatternType.SOLID_FOREGROUND // En lugar de CellStyle.SOLID_FOREGROUND
HSSFColor.HSSFColorPredefined.RED // En lugar de HSSFColor.RED
BorderStyle.THIN // En lugar de CellStyle.BORDER_THIN
HorizontalAlignment.LEFT // En lugar de CellStyle.ALIGN_LEFT
Si necesitas usar los valores short de las viejas variables finales puedes usar los métodos getCode() y valueOf() presentes en la mayoría de los nuevos enums.
El método setCellType() de Cell se ha eliminado:
Cell cell ... 
// cell.setCellType(Cell.CELL_TYPE_FORMULA); // Ya no existe, no es necesario
cell.setCellFormula(text);
setCellType() ya no es necesario porque Cell colige el tipo del valor de la celda.
El método setBoldweight() de Font se ha eliminado. Por lo que tienes que cambiar esto:
font.setBoldweight(Font.BOLDWEIGHT_BOLD);
Por:
font.setBold(true);
Fíjate en el nuevo método setBold() en Font.
Hay más cambios incompatibles en la nueva versión de Apache POI. No te preocupes, el compilador te avisará de ellos y StackOverflow te ayudará a arreglarlos.

Para migrar a OpenXava 6.6.3

Para migrar a OpenXava 6.6.3 desce cualquier versión anterior de OpenXava, incluso desde la 1.0:

Sigue las instrucciones de migración para OpenXava 6.x