Menu

Crear reporte de colección

2014-08-03
2014-08-21
  • Juanjo Vega

    Juanjo Vega - 2014-08-03

    Hola,

    ¿Como puedo añadir un botón a la cabecera de una colección para generar informes (pdf y excel)? Como en el modo lista, que viene por defecto, pero para las colecciones dentro de la lista detalle.

    Por ejemplo, tengo una entidad "evento" con una colección de "socios", y quiero guardar un excel o pdf con solo ese subconjunto de socios de la BD.

    Adjunto una imagen con lo que quiero.

     

    Last edit: Juanjo Vega 2014-08-03
  • Javier Paniza

    Javier Paniza - 2014-08-05

    Hola Juanjo,

    las colecciones calculadas y las @ManyToMany no tienen las acciones de generar listado PDF y exportar a excel, porque no tenemos la implementación. En OpenXava tenemos escrita la lógico para generar los listados a partir de un Tab, y como estos tipos de colecciones no usan Tab, sino que sacan los datos del getter, no tenemos los listados (ni la paginación, ni la ordenación, ni los filtros). Todo esto se podría implementar, sí. Pero sería bastante trabajo.

    Tendrás que añadir acciones propias a la colección con @Action y escribir tu mismo la lógica para el listado y la exportación a excel.


    Ayuda a otros en este foro como yo te ayudo a ti.

     
  • Juanjo Vega

    Juanjo Vega - 2014-08-12

    ¿Hay alguna documentación por ahí? Porque no consigo avanzar mucho en esto.

     
  • Javier Paniza

    Javier Paniza - 2014-08-14

    Hola Juanjo,

    esa funcionalidad no la tiene OpenXava la tienes que implementar tú. En otros marcos de trabajo, como Struts o Spring, practicamente lo tienes que implementar tu todo, porque solo ofrecen infraestructura básica. En OpenXava tienes muchas cosas hechas, pero hay muchas otras cosas que te toca hacerlas a ti y esta es una de ellas.

    En tu collección has de definir la acción:

    @Action("MiColeccion.generarPDF")
    private Collection<MiClasse> miColeccion;
    

    Has de definir el controlador MiColeccion en controladores.xml con las acciones que quieras, como generarPDF y exportarEXcel.

    Ahora tienes que escribir la lógica de esas acciones, pero ese ya es tu negocio. Para generar el PDF, por ejemplo, puedes definir el informe usando iReport y después lanzarlo desde tu acción, como se explica en el wiki.

    Para exportar a excel tu acción deberá redireccionar a un servlet que devuelva el contenido de la colección en formato CSV.


    Ayuda a otros en este foro como yo te ayudo a ti.

     
  • Juanjo Vega

    Juanjo Vega - 2014-08-14

    Esa era la idea que tenía en la cabeza estos días, pero no sabía si habría alguna otra forma más sencilla. Recordé que hice algo en el pasado con servlets para descargar archivos, así que generando un archivo de texto con los valores quizás sea suficiente. Espero recordar como iba el tema.

    Empezaré con el Excel que es más sencillo y no se si realmente necesitaré implementar el PDF.

    Gracias.

     

    Last edit: Juanjo Vega 2014-08-14
  • Juanjo Vega

    Juanjo Vega - 2014-08-18

    Tengo el servlet funcionando, pero no se como acceder a los datos para escribir la salida.

    Esta es la acción que lo invoca:

    public class GenerateCollectiveReport extends ViewBaseAction implements
    IForwardAction {

    @Override
    public void execute() throws Exception {
        // TODO Auto-generated method stub
        System.out.println(" +++ EXECUTE +++ ");
    }
    
    @Override
    public String getForwardURI() {
        return "/getCollectiveMembers";
    }
    
    @Override
    public boolean inNewWindow() {
        return false;
    }
    

    }

    El servlet en si:

    public class CollectiveMembersServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
    
        PrintWriter out = response.getWriter();
        out.println("MyServlet is working");
    }
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        doGet(req, resp);
    }
    

    }

    Lo ideal sería que pudiera acceder a los campos de la vista en lugar de hacerlo directamente, por si se cambian después. Con lo que veo en el código fuente no me aclaro.

     
  • Javier Paniza

    Javier Paniza - 2014-08-18

    Hola Juanjo,

    una forma es poniendo la colección en la sesión desde la acción y recogiendo esa colección en tu servlet. Recuerda quitar la colección de la sesión en el servlet.

    Eso es lo que hace GenerateReportServlet de OpenXava con el Tab, échale un vistazo. Tu colección no usa Tab, pero puedes hacer lo mismo con la colección.


    Ayuda a otros en este foro como yo te ayudo a ti.

     
  • Juanjo Vega

    Juanjo Vega - 2014-08-19

    una forma es poniendo la colección en la sesión desde la acción y recogiendo esa colección en tu servlet.

    Intento declarar el objeto de sesión en controllers.xml y luego usar @Inject

    <object name="xava_cmembers" class="java.util.Set" scope="global"/>
    

    pero me da una excepción de instanciación y yo creo que es por ser un "Set".

    ¿Que hago mal? ¿Quizás debería pasar el nombre de la propiedad como "String" y luego obtener el set dentro de la acción?

    Realmente me interesaría pasar más parámetros o incluso el objeto "Collective" entero.

    Recuerda quitar la colección de la sesión en el servlet.

    ¿Eso como se hace? Porque no me suena.

    Eso es lo que hace GenerateReportServlet de OpenXava con el Tab, échale un vistazo. Tu colección no usa Tab, pero puedes hacer lo mismo con la colección.

    No veo ningún @Inject en esta clase, pero supongo que se accede de esta forma:

    request.getSession().getAttribute("xava_selectedKeysReportTab");
    

    También me ha servido para ver como poner las cabeceras de la respuesta de la servlet.

     
  • Javier Paniza

    Javier Paniza - 2014-08-20

    Hola Juanjo,

    en los servlets no se puede usar el @Inject, tendrás que usar la sesión de la aplicación directamente usando request.getSession() en tu acción y en el servlet. Así lo hacen las acciones de impresión de OpenXava.


    Ayuda a otros en este foro como yo te ayudo a ti.

     
  • Juanjo Vega

    Juanjo Vega - 2014-08-20

    Gracias Javier, pero algo no consigo entender:

    En mi acción no tengo acceso a ningún objeto "request", y sigo sin poder acceder al objeto de la entidad.

    He probado con esto que he visto en el código fuente de OX, pero no funciona:

    private transient HttpServletRequest request;
    
    public void setRequest(HttpServletRequest request) {
        this.request = request;
    }
    
        @Override
    public void execute() throws Exception {
        System.out.println(" +++ EXECUTE +++ ");
        request.setAttribute("val", new Integer(10));
    }
    

    Desde la servlet:

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        int v = (Integer) request.getAttribute("val");
                ...
        }
    

    En el códgio fuente no encuentro más usos de "request.getSession()"

     
  • Juanjo Vega

    Juanjo Vega - 2014-08-20

    Estaba haciéndolo mal :(

    Al final he conseguido averiguar esto...

    Para enviar la entidad desde la acción:

    getRequest().getSession().setAttribute("entity", getView().getEntity());
    

    Para recoger la entidad en la servlet:

    Collective c = (Collective) request.getSession().getAttribute("entity");
    

    ¿Como quito la entidad de la sesión?. Como curiosidad, ¿daría algún error si no se hace?. ¿Podría ser algo así?:

    getRequest().getSession().setAttribute("entity", null);
    

    Creo que eso sería todo (crucemos los dedos), ya puedo acceder a los datos de la entidad así que no debería tener problemas en escribirlos

     
  • Juanjo Vega

    Juanjo Vega - 2014-08-21

    Pues no ha sido tan sencillo...

    Este es el get de mi servlet:

    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        Collective c = (Collective) request.getSession().getAttribute(
                "collective");
    
        // response.setContentType("application/vnd.ms-excel");
        // response.setHeader("Content-Disposition",
        // "inline; filename=\"" + c.getName() + ".xls\"");
    
        PrintWriter out = response.getWriter();
    
        // Builds file.
        out.println(c.getName());
        out.println();
        out.println(c.getResponsible().getRowString());
        out.println();
        //for (Member m : c.getCmembers()) {
        //  out.println(m.getRowString());
        //}
    
        out.println("Total miembros, " + c.getCmembers().size());    // <<<< AQUI FALLA
    
        out.flush();
        out.close();
    
        request.getSession().setAttribute("collective", null);
    }
    

    Falla al acceder a la colección pero no consigo averiguar como hacerlo bien:

    Grave: Servlet.service() para servlet CollectiveMembersServlet lanzó excepción org.hibernate.LazyInitializationException: failed to lazily initialize a collection of    role: org.openxava.laoficina.model.Collective.cmembers, no session or session was closed
    at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:383)
    at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:375)
    at org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:122)
    at org.hibernate.collection.PersistentSet.size(PersistentSet.java:162)
    at org.openxava.laoficina.servlets.CollectiveMembersServlet.doGet(CollectiveMembersServlet.java:34)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:409)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1044)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:315)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
    
     
  • Javier Paniza

    Javier Paniza - 2014-08-21

    Hola Juanjo,

    ¿Como quito la entidad de la sesión?

    La respuestá está en el API de HttpSession

    http://docs.oracle.com/cd/E17802_01/products/products/servlet/2.5/docs/servlet-2_5-mr2/javax/servlet/http/HttpSession.html#removeAttribute(java.lang.String)

    Ya sé que preguntar en el foro es muy cómodo, pero antes de hacerlo deberías consultar la documentación.

    Como curiosidad, ¿daría algún error si no se hace?

    No pero consumirías una memoria en el servidor que solo se liberaría cuando se acabara la sesión del usuario. Eso es algo muy malo. Hemos de usar la menos memoría de sesión posible y durante poco tiempo, sino tu sistema no será escalable, es decir necesitaras un servidor muy caro para pocos usurios.


    Ayuda a otros en este foro como yo te ayudo a ti.

     
  • Juanjo Vega

    Juanjo Vega - 2014-08-21

    Ya sé que preguntar en el foro es muy cómodo, pero antes de hacerlo deberías consultar la documentación.

    Te aseguro que siempre busco, entre el wiki, el foro y google, mas que nada porque es más rápido, pero muchas veces me quedo atascado. En este caso pensaba que la sesión era de OX y no de la servlet, por eso estuve buscando en el foro.

    No pero consumirías una memoria en el servidor que solo se liberaría cuando se acabara la sesión del usuario. Eso es algo muy malo. Hemos de usar la menos memoría de sesión posible y durante poco tiempo, sino tu sistema no será escalable, es decir necesitaras un servidor muy caro para pocos usurios.

    Tienes razón. Gracias por la aclaración :)

    ¿Alguna idea sobre la excepción "LazyInitializationException"? Seguiré investigando cuando tenga otro rato libre y si consigo resovlerlo lo pondré por aquí.

     

    Last edit: Juanjo Vega 2014-08-21
  • Juanjo Vega

    Juanjo Vega - 2014-08-21

    He conseguido resolverlo!

    Había que cambiar el "fetch", que estaba puesto como "LAZY", así:

    @ManyToMany(cascade = CascadeType.DETACH, fetch = FetchType.EAGER)
    

    Ahora ya no lanza la excepción, así que en cuanto pueda terminaré los reports, que están casi listos.

    Muchas gracias!

     

    Last edit: Juanjo Vega 2014-08-21

Log in to post a comment.