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.1.6</openxava.version>
...
</properties>
Compila tu proyecto:
Hemos tenido que actualizar a la última versión de HtmlUnit debido a una vulnerabilidad de seguridad crítica en las versiones 2.x. Si utilizas los métodos de ModuleTestBase en tus pruebas, no necesitas cambiar nada. Sin embargo, si utilizas la API de HtmlUnit directamente, usando getHtmlPage() o getWebClient() en tus pruebas, tendrás que realizar algunos ajustes de migración.
Sigue las instrucciones de la documentación de HtmlUnit para migrar de HtmlUnit 2.x.x a 3.x.x.
Esto es por razones de seguridad. Esto significa que debes mover el estilo de HTML a CSS en tus vistas personalizadas, editores, formateadores, servlets, etc.
Por ejemplo, si tienes este código HTML:
<div style="padding: 20px; background-color: green;">
La parte de style ya no es reconocida. Ahora, debes cambiar el style por una class:
<div class="mi-fondo">
Y agregar la clase a tu archivo CSS (ya sea el CSS de tu tema o custom.css):
.mi-fondo {
padding: 20px;
background-color: green;
}
También puedes cambiar el estilo por otras etiquetas HTML clásicas, como <b><i> o <font>. Especialmente cuando necesitas usar algún valor dinámico, como un color de una base de datos. Por ejemplo, podrías cambiar este código Java que genera HTML:
return "<i class='mdi mdi-square' style='color: " + valorHexColor + "'></i>";
Por esto:
return "<font color='" + valorHexColor + "'><i class='mdi mdi-square ox-color-inherit'></i></font>";
Fíjate en el uso de <font> para indicar el color. En este caso también utilizamos la clase ox-color-inherit (incluida en OpenXava) para sobrescribir el color dado por el CSS de OpenXava a los elementos <i>.
Esto es por razones de seguridad. Tus servlets requieren establecer explícitamente el tipo de contenido. Por lo tanto, si todavía no lo hacías, debes agregar la siguiente línea a tus servlets:
response.setContentType("text/html");
Debes indicar el tipo MIME correspondiente, en este ejemplo
"text/html" para un documento HTML.Esto es por razones de seguridad. Rara vez se utiliza JavaScript en línea en el código de las aplicaciones de OpenXava, ya que no funcionan desde OpenXava 3.1 para editores o vistas personalizadas. Sin embargo, si tienes código personalizado fuera de un módulo OpenXava normal, como welcome.jsp, o algún encabezado o pie de página personalizado para tu aplicación, tal vez tengas algunos scripts en línea. En este caso, debes agregar un atributo nonce a tu script en línea utilizando la nueva etiqueta <xava:nonce/> de la biblioteca de etiquetas.
Por ejemplo, si tu página welcome.jsp tiene este código:
<script type="text/javascript">
var button = document.getElementById('welcome_go_signin');
button.onclick = function () { window.location='m/SignIn'; }
</script>
Deberías cambiarlo por:
<%@include file="../xava/imports.jsp"%>
...
<script type="text/javascript" <xava:nonce/>>
var button = document.getElementById('welcome_go_signin');
button.onclick = function () { window.location='m/SignIn'; }
</script>
Fíjate en el nuevo elemento <xava:nonce> y que has de incluir imports.jsp.
Aún mejor, podrías buscar una alternativa que no use ningún script en línea. En este caso, por ejemplo, podríamos cambiar el evento onclick con un window.location por un simple <a ref="" />, si es posible.
Si tienes que escribir tus propios editores o vistas personalizadas y estás usando onclick, onfocus, onblur, etc., ya no van a funcionar. Esto es por razones de seguridad. Tienes que eliminar los eventos del HTML y moverlos a un archivo JS.
Es decir, si tienes un código como este:
<input type="button" tabindex="1" onclick="window.location='m/SignIn'" value="<xava:label key='SignIn'/>">
Quita el evento onclick y añade un id al elemento, dejándolo de esta manera:
<input id="welcome_go_signin" type="button" tabindex="1" value="<xava:label key='SignIn'/>">
Después añade un archivo JS, digamos eventos.js (o cualquier otro nombre que prefieras), en src/main/webapp/xava/editors/js (crea la carpeta si todavía no existe) de tu proyecto, con el siguiente contenido:
var button = document.getElementById('welcome_go_signin');
button.onclick = function () { window.location='m/SignIn'; }
Observa cómo utilizamos el ID para añadir el evento usando código JavaScript, un código seguro porque es descargado desde el servidor que aloja la aplicación.
Dadas las nuevas restricciones de seguridad, no es posible redirigir a JavaScript, por lo que si utilizas IForwardAction para redirigir a una URL que comienza con javascript: para ejecutar código, eso no funcionará. En su lugar, utiliza IJavaScriptPostAction para ejecutar JavaScript. Es decir, si tienes una acción como esta:
public class DecirHola extends BaseAction implements IForwardAction {
public void execute() throws Exception {
}
public String getForwardURI() {
return "javascript:alert('Hola')";
}
public boolean inNewWindow() {
return false;
}
}
Reescríbela de esta manera:
public class DecirHola extends BaseAction implements IJavaScriptPostAction {
public void execute() throws Exception {
}
public String getPostJavaScript() {
return "alert('Hola')";
}
}
Notamos que las etiquetas en listas y colecciones, cuando están traducidas a inglés, si bien se entendían, podían estar mejor. Para esto, cambiamos el orden de las palabras y afecta al código JUnit que use métodos como assertValue(), assertLabelInList(), assertValueInCollection(), entre otros que se usen para verificar etiquetas. Por ejemplo, si tienes algo así:
assertLabelInList(0, "Year of invoice");
Cambiala por:
assertLabelInList(0, "Invoice year");
Si antes las etiquetas eran:
customer.address = Address of customer
customer.address.street = Street of address of customer
customer.address.street.number = Number of street of address of customer
Ahora son:
customer.address = Customer address
customer.address.street = Customer address street
customer.address.street.number = Customer address street number
getWebClient().getOptions().setCssEnabled(true); // Si haces esto...
reload(); // ...has de añadir esta línea
String [] acciones= {
"CRUD.new",
"CRUD.save",
"CRUD.refresh",
"Mode.list",
"Reference.search",
"Reference.createNew",
"Reference.modify",
"Reference.clear", // AÑADE ESTA ENTRADA
"Sections.change"
};
assertActions(acciones);
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
...
<!-- Añade los siguiente parametros para compilar con Java 8 -->
<init-param>
<param-name>compilerSourceVM</param-name>
<param-value>1.8</param-value>
</init-param>
<init-param>
<param-name>compilerTargetVM</param-name>
<param-value>1.8</param-value>
</init-param>
...
</servlet>
<class>com.openxava.naviox.model.SSORecord</class>
Hemos arreglado un fallo relacionado con que scale=0 para @Column no funciona, y dado que cuando scale no está definido su valor por defecto es 0, ahora @Column(length=x) tiene scale=0 como valor por defecto en lugar de scale=2. Es decir, has de cambiar este código:
@Column(length=6)
BigDecimal factor;
Por este otro:
@Column(length=6, scale=2) // AÑADE scale=2
BigDecimal factor;
Si no defines length o precision el valor de scale todavía es 2, como siempre, por tanto si tienes:
@Column(column="FCTR")// scale es 2 por defecto, como siempre
BigDecimal factor;
No necesitas cambiar nada. Además, si la anotación @Column se
omite por completo scale todavía es 2, como siempre.
Para arreglar un fallo, los combos de las referencias marcadas como @DescriptionsList en el filtro de la lista o colecciones ya no usan la clave, sino el valor del campo. Esto es algo interno, no afecta el funcionamiento de la aplicación. Pero es posible que tengas que adaptar algo de código. Si tienes una condición como esta en una acción:
getTab().setConditionValue("vendedor.nombre", 1);
Cámbiala por:
getTab().setConditionValue("vendedor.nombre", "JUAN HERRERO");
Fíjate como ahora para vendedor.nombre usamos el nombre del vendedor, no el id. Hemos hecho esto para arreglar un bug, pero como efecto secundario ahora es más natural y además funciona de la misma manera para referencias con o sin @DescriptionsList, así que añadir o quitar un @DescriptionsList a una referencia no afecta al código de tus acciones.
También tienes que adaptar tu código JUnit que pone valor al filtro de la lista si se usan referencias con @DescriptionsList. Es decir, cambia:
setConditionValues("", "1");
setConditionValues("", "AUTOMÓVIL");
Aquí estás cambiando el id por la descripción o el nombre. Haz lo mismo con el código JUnit que verifica el contenido del combo. Cambia:
String [][] valoresValidos = {
{ "", "" },
{ "2:_:FAROLA", "FAROLA" },
{ "0:_:CASA", "CASA" },
{ "3:_:PUERTA", "PUERTA" },
{ "1:_:AUTOMÓVIL", "AUTOMÓVIL" }
};
assertValidValues("conditionValue___3", valoresValidos);
Por:
String [][] valoresValidos = {
{ "", "" },
{ "FAROLA", "FAROLA" },
{ "CASA", "CASA" },
{ "PUERTA", "PUERTA" },
{ "AUTOMÓVIL", "AUTOMÓVIL" }
};
assertValidValues("conditionValue___3", valoresValidos);
Nota como hemos quitado el prefijo id:_: en la parte de la clave del mapa. Ahora, las claves y los valores son iguales.
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.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.<artifactId>almacenes</artifactId>
<artifactId>Almacenes</artifactId>
<finalName>almacenes</finalName>
<finalName>Almacenes</finalName>
<aplicacion nombre="almacenes">
<aplicacion nombre="Almacenes">
AppServer.run("almacenes");
AppServer.run("Almacenes");
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.
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.
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.
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.
uploadFile("scripts", "informes/Corporacion.html");
uploadFile("scripts", "src/main/resources/informes/Corporacion.html");
HtmlElement tarjeta = body.getElementsByAttribute("div", "class", "ox-card").get(2);
assertEquals("Precio unitario: 0.00, Precio unitario en pesetas: 0"), tarjeta.asText());
HtmlElement tarjeta = body.getElementsByAttribute("div", "class", "ox-card").get(2);
assertEquals("Precio unitario: 0.00, Precio unitario en pesetas: 0"), tarjeta.asNormalizedText());
assertDiscussionCommentText("discusion", 0, Strings.multiline("admin - Ahora", "Hola, soy yo"));
assertDiscussionCommentText("discusion", 0, "admin - Ahora\nHola, soy yo");
assertValueInList(2, "XAVA\r\n3\r\nPrecio unitario: 0");
assertValueInList(2, "XAVA\n3\nPrecio unitario: 0");
assertEquals(
"javascript:openxava.executeAction('openxavatest', 'Carrier', 'Effacer l"
+ (char) 145 // ANSI
+"entité courante: Etes-vous sûr(e) ?', false, 'CRUD.delete')",
deleteLink.getHrefAttribute());
assertEquals(
"javascript:openxava.executeAction('openxavatest', 'Carrier', 'Effacer l"
+ (char) 8216 // UNICODE
+"entité courante: Etes-vous sûr(e) ?', false, 'CRUD.delete')",
deleteLink.getHrefAttribute());
assertValue("descripcion", "Esto es una gran discusión sobre jUnit");
assertValue("descripcion", "<p>Esto es una gran discusión sobre jUnit</p>");
assertValue("poblacion", "46540 ");
assertValue("poblacion", "46540");
getWebClient().getOptions().setCssEnabled(true); // Si haces esto...
reload(); // ...has de añadir esta línea
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
Cell cell ...
// cell.setCellType(Cell.CELL_TYPE_FORMULA); // Ya no existe, no es necesario
cell.setCellFormula(text);
font.setBoldweight(Font.BOLDWEIGHT_BOLD);
font.setBold(true);
Para migrar a OpenXava 6.6.3 desce cualquier versión anterior de OpenXava, incluso desde la 1.0: