openxava / documentación / ¿Cómo ...

Tabla de contenidos

¿Cómo evitar que se pueda modificar cierto campo en una vista?
¿Cómo usar una vista diferente para crear y para actualizar?
¿Cómo desactivar una propiedad para actualizar pero no para crear?
¿Cómo acceder a una propiedad del modelo que no se muestra en la vista?
¿Cómo mostrar la fecha en un dato tabular cuando usas PostgreSQL?
¿Cómo almacenar preferencias de usuario (nuevo en v3.0.2)?
¿Cómo trabajar con OX desde la línea de comandos?
¿Cómo enviar correos electrónicos en OX?
¿Cómo crear tu propio archivo .properties para configurar tu aplicacion?
¿Cómo añadir tu propio servlet, filter, listener o resource-ref a tu aplicación OpenXava?
¿Cómo crear un elemento nuevo directamente desde una colección @ManyToMany? (nuevo en v4m4)
¿Cómo cambiar el esquema, idioma o usuario a través de parametros url? (nuevo en v4m4)
¿Cómo modificar los parámetros en los listados por defecto?
¿Cómo generar informes HTML?
¿Cómo generar varios informes desde una única acción?
¿Cómo unir varios reportes en un único PDF?
¿Cómo añadir tu propio portlet a tu aplicación OpenXava (nuevo en v4.6)?
¿Cómo definir tu propio contenido para la página de Bienvenida y la página de Primeros pasos (nuevo en v5.0)?
¿Cómo hacer que @Required permita números positivos, negativos o cero pero no vacío?
¿Cómo tener acceso al objeto View desde un JSP propio?
¿Cómo generar Excel de verdad en lugar de CSV (nuevo en v5.5)?
¿Cómo redireccionar a otra página en una ventana nueva sin recargar la página actual?
¿Cómo tener los proyectos OpenXava y Addons en una carpeta diferente a tu propio proyecto (nuevo en v6.1.1)?
¿Cómo correr tu aplicación en el contexto raíz (nuevo en v6.3)?

¿Cómo evitar que se pueda modificar cierto campo en una vista?

Al actualizar en una vista, por defecto todos los campos (excepto la clave) son modificables.
Pero podemos declarar cualquier propiedad de la vista de solo lectura, de esta forma:
<vista nombre="UnMiembroSoloLectura">
    <vista-propiedad propiedad="nombre" solo-lectura="true"/>
</vista>
 
O, si estamos usando OX3:
@ReadOnly(forViews="UnMiembroSoloLectura")
private String nombre;
 

¿Cómo usar una vista diferente para crear y para actualizar?

Obviamente, tenemos que definir un vista para crear y otra para actualizar, así:
<componente ... >
...
    <vista nombre="Crear">
    ...
    </vista>
 
    <vista nombre="Actualizar">
    ...
    </vista>
 
</componente>
 
O, si usamos OX3:
@Views({
    @View(name="Crear", members="  .... "),
    @View(name="Actualizar", members=" ... ")
})
public class MiEntidad { ...
 
Ahora hemos de refinar la acción new para escoger la vista Crear y la accion de búsqueda para escoger la vista Actualizar. Primero, definamos nuestro propio controlador, en controladores.xml, así:
<controlador nombre="MiTipico">
    <accion nombre="new"
        clase="com.miempresa.miaplicacion.acciones.MiAccionNuevo"
        imagen="images/new.gif" on-init="true"
        atajo-de-teclado="F2">
        <usa-objeto nombre="xava_view"/>  <!-- No obligatorio desde v4m2 -->
    </accion>
    <accion nombre="search"
        por-defecto="si-posible" oculta="true"
        clase="com.miempresa.miaplicacion.acciones.MiAccionBuscar"
        atajo-de-teclado="F8">
        <usa-objeto nombre="xava_view"/>  <!-- No obligatorio desde v4m2 -->
    </accion>
 </controlador>
Y ahora asignamos este controlador a nuestro módulo, y definimos la acción de búsqueda para el módulo. Escribimos nuestro módulo de esta manera en aplicacion.xml:
<modulo nombre="MiModulo">
    <var-entorno nombre="XAVA_SEARCH_ACTION" valor="MiTipico.search"/>
    <modelo nombre="MiComponente"/>
    <controlador nombre="MiTipico"/>
</modulo>
 
Y ahora solo queda refinar la lógica de nuestras acciones. Para MiAccionBuscar podemos escribir:
public class MiAccionBuscar extends SearchByViewKeyAction {
    public void execute() throws Exception {
        Map clave = getView().getKeyValuesWithValue();   //1
        getView().setViewName("Actualizar");             //2
        getView().setValues(clave);                      //3
        super.execute();
    }
}
Los valores de la clave se han de capturar (1) para que puedan ser restaurados (3) después de la inicialización que hace el método setViewName(...).
Y para MiAccionNuevo:
public class MiAccionNuevo extends NewAction {
    public void execute() throws Exception {
        getView().setViewName("Crear");
        super.execute();
    }
}

¿Cómo desactivar una propiedad para actualizar pero no para crear?

A partir de v6.2 puedes desactivar una propiedad sólo para crear con @ReadOnly(onCreate=false) como se explica en StackOverflow. por lo que las instrucciones de abajo son para las versiones anteriores a la 6.2.
Hemos de refinar la acción new y la accion de búsqueda. Primero, definamos nuestro propio controlador, en controladores.xml, así:
<controlador nombre="MiTipico">
    <accion nombre="new"
        clase="com.miempresa.miaplicacion.acciones.MiAccionNuevo"
        imagen="images/new.gif" on-init="true"
        atajo-de-teclado="F2">
        <usa-objeto nombre="xava_view"/>  <!-- No obligatorio desde v4m2 -->
    </accion>
    <accion nombre="search"
        por-defecto="si-posible" oculta="true"
        clase="com.miempresa.miaplicacion.acciones.MiAccionBuscar"
        atajo-de-teclado="F8">
        <usa-objeto nombre="xava_view"/>  <!-- No obligatorio desde v4m2 -->
    </accion>
 </controlador>
Y ahora asignamos este controlador a nuestro módulo, y definimos la acción de búsqueda para el módulo. Escribimos nuestro módulo de esta manera en aplicacion.xml:
<modulo nombre="MiModulo">
    <var-entorno nombre="XAVA_SEARCH_ACTION" valor="MiTipico.search"/>
    <modelo nombre="MiComponente"/>
    <controlador nombre="MiTipico"/>
</modulo>
Y ahora solo queda refinar la lógica de nuestras acciones. Para MiAccionBuscar podemos escribir:
public class MiAccionBuscar extends SearchByViewKeyAction {
    public void execute() throws Exception {
        super.execute();
        getView().setEditable("mipropiedad", false);
    }
}
Y para MiAccionNuevo:
public class MiAccionNuevo extends NewAction {
    public void execute() throws Exception {
        super.execute();
        getView().setEditable("mipropiedad", true);
    }
}
 

¿Cómo acceder a una propiedad del modelo que no se muestra en la vista?

Desde la vista solo podemos obtener la información visualizada. Pero es posible, usando la clase MapFacade acceder directamente al modelo.
Podemos escribir en nuestra vista un código parecido a este:
public class MiAccion extends ViewBaseAction {
 
    public void execute() throws Exception {
        Factura factura = (Factura) MapFacade.findEntity(getModelName(), getView().getKeyValues());
        // Descuento especial no se muestra en la vista
        BigDecimal descuentoEspecial = factura.getDescuentoEspecial();
        ...
    }
    ...
 
De esta manera podemos acceder a la propiedad descuentoEspecial aunque no este visualizada en la vista actual.


¿Cómo mostrar la fecha en un dato tabular cuando usas PostgreSQL?

*Resuelto en OpenXava 3.0.2, este metodo es util solo si usas una version inferior.
Cuando usas OpenXava en combinación con PostgreSQL suele suceder que al actualizar esquema y tienes una entidad del tipo DATE se crea un campo del tipo TIMESTAMP en lugar de DATE, esto provoca un error en los datos tabulares(listas) y el resultado es que la fecha no se lista y al exportar a PDF aparece el texto "ERROR" en lugar de la fecha, la solución es pasar el tipo de dato de Timestamp a DATE, puedes hacerlo usando pgadmin.

Seleccionas el campo, luego con el botón secundario haces clic y seleccionas la opción propiedades, aparecerá la siguiente ventana.
...
pgsqltimestamp.png
Campo con data type Timestamp


Luego donde dice Data Type haces clic en el listbox y seleccionas DATE
pgsqldate.png

finalmente presionas OK y ya podrás visualizar en los datos tabulares de tu aplicación la fecha, en este caso el campo "fecha ingreso"
fue corregido y se ve bien, pero el campo "fecha" esta sin corregir y todavía no aparece la fecha.
timestamp.png
Este procedimiento se debe hacer luego de actualizar esquema.

¿Cómo almacenar preferencias de usuario (nuevo en v3.0.2)?

Almacenar las preferencias del usuario es un asunto que cada uno tiene que resolver, pero por comodidad OpenXava nos permite usar su propio sistema para almacenar las preferencias.
OpenXava usa Java Preferences API para almacenar y leer las preferencias de usuario, pero adaptada a un entorno de servidor multiusuario. Podemos acceder al objeto Preferences usando la clase Users de OpenXava. Simplemente así:
// Obtenemos las preferencias para el usuario actual
// y nodo (una categoria arbitraria de nuestra elección)
Preferences preferencias = Users.getCurrentPreferences().node("minodo");
 
// Leemos el valor de una propiedad
boolean filasOcultas = preferencias.getBoolean(FILAS_OCULTAS, false);
 
...
 
// Modificamos y grabamos la propiedad
preferencias.putBoolean(FILAS_OCULTAS, filasOcultas);
preferencias.flush();
Hemos de llamar explícitamente a flush() para guardar los cambios.

¿Cómo trabajar con OX desde la línea de comandos?

Los proyectos nuevos creados mediante la plantilla OX, obtienen un fichero build.xml que podríamos calificar como un tanto filo-eclipse ;-)
Esto se concreta en que el fichero build carece del target de compilación, que es necesario cuando se trabaja desde la línea de comandos y no lo es desde Eclipse, pues este entorno compila automáticamente antes de ejecutar tareas ant (ver http://sourceforge.net/forum/message.php?msg_id=4962325).

Para solucionarlo, edita primero el build.xml de tu proyecto creado a partir de la plantilla y haz depender todas las tareas ant de una tarea de compilación. Así:


<?xml version="1.0"?>
 
<project name="miProyecto" basedir=".">
    ...
    <target name="desplegarWar" depends="compilar">
        ...
    </target>
    <target name="generarPortlets" depends="compilar">
        ...
    </target>
    <target name="actualizarEsquema" depends="compilar">
        ...
    </target>
    <!-- Compila llamando al target compile
         del build de OpenXava -->
    <target name="compilar">
        <ant antfile="../OpenXava/build.xml" target="compile"/>
    </target>
    ...
</project>
 

Hay que modificar también, del modo oportuno, el target compile de $DIR_INSTALACION_OX/workspace/OpenXava/build.xml para crear unos directorios destino que precisa el script e incluir en los directorios de fuentes src, xava, persistence, i18n y properties de cada proyecto (ésto último está ya corregido en la OX 3.0.2). El target modificado tiene este aspecto OX 3.0.1:
<?xml version="1.0"?>
 
<project name="OpenXava" default="deployWar">
    ...
        <target name="compile">
                <!-- We create the 'lib' folder for it does not fails at the first time -->
                <mkdir dir="web/WEB-INF/lib"/>
        <!-- hay que disponer aquí la creación de dos directorios más para que no falle -->
                <mkdir dir="web/WEB-INF/classes"/>
                <mkdir dir="../${project}/gen-src-xava"/>
        <javac destdir="../${project}/web/WEB-INF/classes">
                        <src path="../${project}/src"/>
                        <!-- Only needed if you uses XDoclet generated code
                        <src path="../${project}/gen-src"/>
                        -->
                        <src path="../${project}/gen-src-xava"/>
                        <classpath>
                                <pathelement path="../OpenXava/bin"/>
                                <fileset dir="../OpenXava/web/WEB-INF/lib">
                                        <include name="**/*.jar"/>
                                </fileset>
                                <fileset dir="web/WEB-INF/lib">
                                        <include name="**/*.jar"/>
                                </fileset>
                                <pathelement path="../OpenXava/lib/j2ee.jar;${xava.compiler.path}"/>
                        </classpath>
                </javac>
                <copy todir="../${project}/web/WEB-INF/classes">
                        <fileset dir="../${project}/gen-src-xava" excludes="**/*.java"/>
            <!-- Esto es lo que he añadido. Desde aquí -->
                        <fileset dir="../${project}/xava"/>
                        <fileset dir="../${project}/persistence"/>
                        <fileset dir="../${project}/i18n"/>
                        <fileset dir="../${project}/properties"/>
            <!-- Hasta aquí -->
         </copy>
        </target>
    ...
</project>
 
Después de estos cambios, deberíamos poder ejecutar sin problemas los targets como el de actualizarEsquema, que antes de los cambios producían error.

¿Cómo enviar correos electrónicos en OX?

Enviar correos electronicos con OX puede ser trivial, solo que se deben tomar en cuenta los detalles para lograr que todo salga bien.
En este ejemplo veremos como configurar los datos del servidor SMPT y como enviar un mail en un @PostUpdate, es decir luego de actualizar un registro.

Primero debemos editar el archivo xava.properties ubicado en la carpeta /workspace/MiProyecto/properties/
Ahora que tenemos ambos archivos abiertos debemos agregar las siguientes lineas:
smtpHost=smtp.miserver.com
smtpPort=25
smtpUserId=pepitoesminombre
smtpUserPassword=estaesmiclavesecreta
guardamos ambos archivos, ahora en cualquiera de nuestras clases agregamos lo siguiente(puede ser alfinal de los getters and setters):
@PostUpdate void enviar() throws ParseException, MessagingException{
    Emails.send("mailorigen@origen.com", "mail@destino.com", "asunto de prueba", "contenido de prueba");
}
Con esto ya seremos capaces de enviar mail desde OX sin tener que implementar alguna clase por nuestra cuenta, recomiendo mirar el codigo fuente de el proyecto QaManager esta hecho en OX y tiene algunas caracteristicas que seran utiles para aprender a usar OX.

¿Cómo crear tu propio archivo .properties para configurar tu aplicacion?

En ocasiones es necesario dar la posibilidad que ciertos comportamientos u opciones de tu aplicacion se puedan configurar desde un archivo .properties en lugar de volver a realizar un deploy con los cambios, en mi caso fue necesario para indicar si algunos modulos debian mostrar un formulario en blanco o seguir trabajando con el mismo registro despues de guardar los datos, asi tambien necesitaba configurar los correos electronicos de quien enviaria y recibiria mails desde una accion, el ejemplo estara basado sobre esto ultimo.

En el primer ejemplo veremos en pocos pasos como manejar desde nuestra aplicacion OX nuestro propio archivo .properties.

  1. En la carpeta properties de nuestro proyecto crearemos el archivo proyecto.properties(si lo deseas puedes cambiar el nombre), y dentro de este archivo escribiremos lo siguiente:
    #Esto es un comentario
    #Mail de quien envia, cambiarlo por el que corresponda
    MailEnvia=sender@sender.com
    #Mail recipiente, cambiarlo por el que corresponda, para varios mails separarlos por coma(,)
    MailRecipiente=recipiente1@recipiente.com,recipiente2@recipiente.com
  2. Guardamos el documento y ahora creamos una nueva clase en org.ejemplo.com.utils(pueden ponerlo en el package que quieran) llamada ConcesionesPreferencias (deberan cambiar el nombre por el de su propia aplicación).
    package org.ejemplo.com.utils;
    import java.io.*;
    import java.util.*;
    import java.util.logging.*;
    import org.apache.commons.logging.*;
    import org.openxava.util.*;
    /**
     * @author Andres Molina
     */
    public class ConcesionesPreferencias {
     
        private final static String FILE_PROPERTIES="proyecto.properties";
        private final static String JAVA_LOGGING_LEVEL_DEFAULT_VALUE="INFO";
        private static Log log = LogFactory.getLog(**ConcesionesPreferencias**.class);
     
        private static ConcesionesPreferencias instance;
     
        private Properties properties;
     
        private boolean ejb2PersistenceLoaded=false;
        private boolean ejb2Persistence=false;
        private boolean jpaPersistenceLoaded=false;
        private boolean jpaPersistence=false;
        private boolean hibernatePersistenceLoaded=false;
        private boolean hibernatePersistence=false;        
        private boolean duplicateComponentWarningsLoaded=false;
        private boolean duplicateComponentWarnings=false;
        private int maxSizeForTextEditor;
        private Level javaLoggingLevel;
        private Level hibernateJavaLoggingLevel;    
     
        private ConcesionesPreferencias() {         
        }
     
        public static ConcesionesPreferencias getInstance() {
            if (instance == null) {
                instance = new **ConcesionesPreferencias**();
            }
            return instance;
        }
     
     
        private Properties getProperties() {
            if (properties == null) {
                PropertiesReader reader = new PropertiesReader(**ConcesionesPreferencias**.class, FILE_PROPERTIES);
                try {
                    properties = reader.get();
                } 
                catch (IOException ex) {            
                    log.error(XavaResources.getString("properties_file_error", FILE_PROPERTIES),ex);
                    properties = new Properties();
                }
            }        
            return properties;
        }
     
        public String getMailEnvia() {
            return getProperties().getProperty("MailEnvia");
        }
        public String getMailRecipiente() {
            return getProperties().getProperty("MailRecipiente");
        }
     
    }
  3. Guardamos el archivo y ahora podemos acceder a los valores de nuestro archivo properties de la siguiente forma:
ConcesionesPreferencias.getInstance().getMailEnvia();
 
por ejemplo desde una accioon que envia mail luego de guardar lo tengo asi
if (isResetAfter()) {
 
    MapFacade.create(getModelName(), getValuesToSave());
    addMessage("entity_created", getModelName());
 
    Emails.send(ConcesionesPreferencias.getInstance().getMailEnvia(),
        ConcesionesPreferencias.getInstance().getMailRecipiente,
        "Nueva Concesion en espera de una asignacion",
        "Se ha creado una nueva    Concesion, usted puede asignar un responsable.");
}

¿Cómo añadir tu propio servlet, filter, listener o resource-ref a tu aplicación OpenXava?

A partir de OpenXava 7.0 los archivos servlets.xml, filters.xml y listeners.xml de los que hablamos abajo ya no se soportan. Puedes definir tus servlets, filters o listeners directamente en web.xml, que desde v7.0 está vacío y listo para tus propias cosas, en src/main/webapp/WEB-INF. También puede usar las anotaciones estándar @WebServlet, @WebFilter y @WebListener.
A partir de OpenXava 6.0 puedes usar las anotaciones estándar de Servlet @WebServlet, @WebFilter y @WebLister para definir tus propios servlets, filters o listeners.
Si usas una versión de OpenXava anterior a 7.0, para añadir tus propios resource-ref a tu aplicación OX no modifiques el archivo web.xml, en vez de eso puedes crear un resources.xml con elementos resource-ref (desde v5.4) en la carpeta WEB-INF.
Si usas una versión de OpenXava anterior a v6.0 tendrás que usar servlets.xml, filters.xml y listeners.xml. En este caso, para añadir tus propios servlets a tu aplicación OX no modifiques el archivo web.xml, en vez de eso puedes crear un servlets.xml (con elementos servlet y servlet-mapping), un filters.xml (con elementos filter y filter-mapping), un listeners.xml (con elementos listener, desde v4.0.1) y un resources.xml (con elementos resource-ref, desde v5.4) en la carpeta WEB-INF. Aunque este método es obsoleto todavía funciona con las últimas versiones de OpenXava.
Por ejemplo, si quieres añadir un servlet llamado TestServlet, crea un archivo servlets.xml en WEB-INF con el siguiente contenido:
<!-- Con v6.0 o superior es mejor usar @WebServlet -->
<servlet>
<servlet-name>testServlet</servlet-name> <servlet-class>org.openxava.test.servlets.TestServlet</servlet-class> </servlet>   <servlet-mapping> <servlet-name>testServlet</servlet-name> <url-pattern>/test</url-pattern> </servlet-mapping>  
Este fragmento será insertado automáticamente en web.xml cuando llames a las tareas ant deplegarWar o actualizarOX.

¿Cómo crear un elemento nuevo directamente desde una colección @ManyToMany? (nuevo en v4m4)

Por defecto en una colección @ManyToMany el usuario puede añadir elementos existentes a la colección, sin embargo no puede crear nuevos. Afortunadamente, puedes añadir esta funcionalidad usando la acción ManyToMany.new (incluida en OpenXava) como una acción de lista. Como sigue:
@ManyToMany
@ListAction("ManyToMany.new")
private Collection<Humano> miembros;

¿Cómo cambiar el esquema, idioma o usuario a través de parametros url? (nuevo en v4m4)

Usted puede cambiar el esquema, idioma o usuario a través de parámetros url con sólo incluirle al módulo el controlador UrlParameters (1). Por ejemplo, incluiremos en application.xml el controlador:
<application name="MyApplication">
    <module name="MyModule">
        <model name="MyModel"/>
        <controller name="Typical"/>
        <controller name="UrlParameters" />          // 1
    </module>
</application>
Ahora el URL para invocar el módulo, basta con incluir el parametro schema.
http://localhost:8080/MyApplication/xava/module?application=MyApplication&module=MyModule&schema=companyA
o el idioma:
http://localhost:8080/MyApplication/xava/module?application=MyApplication&module=MyModule&locale=es
o el usuario actual (el valor se guarda en el atributo de sesión xava.user):
http://localhost:8080/MyApplication/xava/module?application=MyApplication&module=MyModule&user=theUser
Estos parámetros no son exclusivos, pueden ser utilizados todos al mismo tiempo o en cualquier combinación.

¿Cómo modificar los parámetros en los listados por defecto?


Podemos cambiar los parámetros que se incluyen en los listados por defecto mediante código. En este caso cambiaremos el nombre de la organización:
default_pdf.GIF
Por defecto este nombre de organización se recoge del valor de 'xava.organization' desde los ficheros i18n, pero nosotros lo modificaremos para que recoja un valor variable.

Simplemente hay que crear una clase que implemente a IReportParametersProvider y añadir el código que necesitemos al método 'getOrganization()':

package org.openxava.test.util
 
import org.openxava.util.*;
 
/**
 */
class MyReportParametersProvider implements IReportParametersProvider {
 
    String getOrganization() {
        return "report to " + Users.getCurrent()
    }
 
}
Por último tendremos que añadir a nuestro fichero 'xava.properties' una nueva línea indicando la clase que vamos a usar:

reportParametersProviderClass=org.openxava.test.util.MyReportParametersProvider

¿Cómo generar informes HTML?

Un opción es redireccionar a un JSP que genere el informe HTML, podemos hacer esto por medio de IForwardAction. Otra opción es usar la acción SimpleHTMLReportAction (desde v4.3) que permite trabajar con plantillas de HTML simples.

¿Cómo generar varios informes desde una única acción?

Extiende tu acción de JasperMultipleReportBaseAction (nuevo en v4.3). Tienes un ejemplo en InvoiceTwoReportsAction de OpenXavaTest.

¿Cómo unir varios reportes en un único PDF?

Extiende la acción JasperConcatReportBaseAction (nuevo en v5.0). Útil cuando necesitas concatenar varios reportes con diferentes formatos de página (landscape, portrait). Hay un ejemplo en MovieReportAction de OpenXavaTest.

¿Cómo añadir tu propio portlet a tu aplicación OpenXava (nuevo en v4.6, hasta v6.6.3)?

Para añadir tu propio portlet a tu aplicación OX no modifiques el archivo portlet.xml directamente, en vez de eso crear un archivo portlet-ext.xml (con elementos portlet) y un liferay-display-ext.xml (con elementos portlet) en la carpeta WEB-INF.
Por ejemplo, si quieres crear un portlet llamado VersionPortlet, crea un archivo portlet-ext.xml en WEB-INF con el siguiente contenido:
<portlet id="Version">
    <description>OpenXavaTest - Version</description>
    <description xml:lang="ca">OpenXavaTest - Versió</description>
    <description xml:lang="es">OpenXavaTest - Versión</description>
    <description xml:lang="fr">OpenXavaTest - Version</description>
    <portlet-name>Version</portlet-name>
    <display-name>OpenXava - Version</display-name>
    <display-name xml:lang="ca">OpenXavaTest - Versió</display-name>
    <display-name xml:lang="es">OpenXavaTest - Versión</display-name>
    <display-name xml:lang="fr">OpenXavaTest - Version</display-name>
    <portlet-class>org.openxava.test.portlets.VersionPortlet</portlet-class>
    <expiration-cache>0</expiration-cache>
    <supports>
        <mime-type>text/html</mime-type>
        <portlet-mode>VIEW</portlet-mode>
    </supports>
    <supported-locale>ca</supported-locale>
    <supported-locale>es</supported-locale>
    <supported-locale>en</supported-locale>
    <supported-locale>fr</supported-locale>
    <resource-bundle>portlets/Version</resource-bundle>
</portlet>
Si estás usando Liferay puedes crear opcionalmente el archivo liferay-display-ext.xml para añadir entradas a liferay-display.xml. Así, para el portlet de arriba puedes escribir un liferay-display-ext.xml con el siguiente contenido:
<portlet id="Version" />
Este fragmento se insertará automáticamente en liferay-display.xml cuando llames a la tarea ant generatePortlets.

Además, has de escribir el archivo de recursos, en este caso has de escribir Version_ca.properties, Version_es.properties, Version_en.properties y Version_fr.properties. Aquí tenemos un ejemplo para Version_es.properties:
javax.portlet.short-title=Versión
javax.portlet.title=OpenXavaTest - Versión
category.OpenXavaTest=OpenXavaTest

Y, por supuesto, necesitas escribir el código de tu portlet, en este caso:
package org.openxava.test.portlets;
 
import java.io.*;
import javax.portlet.*;
import org.openxava.controller.*;
 
public class VersionPortlet extends GenericPortlet {
 
    public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException {
        response.setContentType("text/html");
        response.getWriter().write("<table border=\"0\">");
        writeVersion(response.getWriter(), "The version of OpenXava is", ModuleManager.getVersion());
        response.getWriter().write("</table>");
    }
 
    private void writeVersion(PrintWriter out, String unit, String version) {
        out.write("<tr>");
        out.write("<td style=\"padding: 2px 5px 2px 5px;\"><b>" + unit + "</b></td>");
        out.write("<td style=\"padding: 2px 5px 2px 5px;\">v" + version + "</td>");
        out.write("</tr>");
    }
 
}

¿Cómo definir tu propio contenido para la página de Bienvenida y la página de Primeros pasos (nuevo en v5.0)?

Para modificar la página de Bienvenida, edita el archivo src/main/webapp/naviox/welcome.jsp de tu proyecto. Para modificar la página de Primeros pasos, utiliza la propiedad initialModule en naviox.properties, como se explica en la documentación de navegación entre módulos. Para v6, edita web/naviox/welcome.jsp y web/naviox/firstSteps.jsp en tu proyecto. Si tu proyecto fue creado con v7.1 o v7.2, primero tienes que copiar welcome.jsp del repositorio de GitHub a tu proyecto, sigue las instrucciones en la documentación de personalización para hacerlo.

¿Cómo hacer que @Required permita números positivos, negativos o cero pero no vacío?

Crear el archivo Miproyecto/xava/validators.xml y debe contener :
<?xml version = "1.0" encoding = "ISO-8859-1"?>
<validators>
    <required-validator>
        <validator-name name="NOT_EMPTY_STRING"/>
        <for-type type="java.math.BigDecimal"/>
        <for-type type="java.lang.Integer"/>
    </required-validator>
    <required-validator>
        <validator-name name="NOT_NULL"/>
        <for-type type="java.math.BigDecimal"/>
        <for-type type="java.lang.Integer"/>
    </required-validator>
</validators>
Nota: Adecuar los for-type con los tipos que se utilicen para manejar datos numéricos

¿Cómo tener acceso al objeto View desde un JSP propio?

En nuestro JSP añadimos las siguientes lineas:
<jsp:useBean id="context" class="org.openxava.controller.ModuleContext" scope="session"/>
<%
 
    String viewObject = request.getParameter("viewObject");
    viewObject = (viewObject == null || viewObject.equals(""))?"xava_view":viewObject;
    org.openxava.view.View view = (org.openxava.view.View) context.get(request, viewObject);
 
    System.out.println("modelo: " + view.getModelName()); // Aquí usamos view
 

¿Cómo generar Excel de verdad en lugar de CSV (nuevo en v5.5)?

Añade esto a tu aplicacion.xml:
<modulo-defecto>
    <controlador nombre="TypicalRealExcel"/>
</modulo-defecto>
Además, en tus propios controladores has de extender TypicalRealExcel en vez de Typical.

¿Cómo redireccionar a otra página en una ventana nueva sin recargar la página actual?

Recargar la página actual es el comportamiento correcto porque tu acción podría cambiar la vista, añadir mensajes, etc. De todas formas, si quieres redireccionar sin recargar es posible usando JavaScript, de esta forma:
package org.openxava.provaox.actions;
 
import org.openxava.actions.*;
 
public class RedireccionarSinCargar extends BaseAction implements IForwardAction {
 
    public void execute() throws Exception {
        // Hacer algo aquí, si quieres
    }
 
    public boolean inNewWindow() {
        return false; // Porque abriremos la ventana nosotros mismos usando JavaScript
    }
 
    public String getForwardURI() {
        return "javascript:void(window.open('/MiAplicacion/miurl'))";
    }
 
}

¿Cómo tener los proyectos OpenXava y Addons en una carpeta diferente a tu propio proyecto? (nuevo en v6.1.1)?

Por defecto, los proyectos Addons y OpenXava han de estar en el mismo directorio (normalmente el workspace) que tu propio proyecto. Sin embargo, a partir de v6.1.1 puedes especificar un directorio diferente para los proyectos OpenXava y Addons. Simplemente añade (o modifica) la propiedad openxava.base.dir de build.xml de tu proyecto:
<property name="openxava.base.dir" value="/home/juan/mi-openxava"/>
De esta manera usarás los proyectos OpenXava y Addons del directorio /home/juan/mi-openxava.

¿Cómo correr tu aplicación en el contexto raíz (nuevo en v6.3)?

Es decir, usándola desde el navegador sin el nombre de la aplicación en la URL, como esto: https://localhost:8080/
Para producción es suficiente con llamar ROOT.war al war a desplegar en Tomcat, no necesitas cambiar tu código.
Para desarrollo puedes modificar la clase lanzadora (com.tuempresa.tuaplicacion.run.tuaplicacion a partir de v7.0 o _Run_TuAplicacion con v6.x) cambiando:
AppServer.run("TuAplicacion");
Por:
AppServer.run("");
Y si quieres probar tu aplicación en el contexto raíz usando JUnit, añade la siguiente entrada en xava-junit.properties:
contextPath=/
Por supuesto, puedes usar el contexto raíz sólo en producción y no en desarrollo, o viceversa.