Publica tu how-to

Dinos como hacer lo que sabes hacer, mándanos un email a wdonet@gmail.com y lo publicamos (dos días máximo) o si te interesa unirte al equipo de redactores, también háznoslo saber por correo.

Configurar JasperReports en Spring 3.0

Cada vez es más sencillo realizar las configuraciones en Spring.  A pesar de que la configuración se puede seguir usando de diferentes maneras, en la documentación de referencia de spring 3 indica lo siguiente de forma general:
  1. Agregar dependencias a tu proyecto
  2. Configurar el ViewResolver
  3. Crear el template con iReports
  4. Configurar las vistas
  5. Crear el controller

Ahora vamos a los detalles.



1. Las dependencias que nos dice debemos tener son las siguientes:
Obviamente el Jasper
        <dependency>
            <groupId>jasperreports</groupId>
            <artifactId>jasperreports</artifactId>
            <version>3.5.3</version>
        </dependency>
• BeanShell (esta no la ocupe)
• Commons BeanUtils (esta no la ocupe)
• Commons Collections (esta es requerida)
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.1</version>
        </dependency>
• Commons Digester (esta no la ocupe)
• Commons Logging (esta es para logear, si ya usas otra, puedes omitirla)
• iText (esta es por si generas PDF pero yo no la ocupe)
• POI (esta es para generar archivox XLS)
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>3.2-FINAL</version>
        </dependency>
2. Configurar el ViewResolver via un properties
Antes ya lo había hecho mediante la clase de spring XmlViewResolver para configurar las vistas en un archivo xml (views.xml) pero me pareció más sencillo mediante un archivo properties y por eso difundo esta alternativa.
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basename" value="views"/>
</bean>
Se esta configurando una instancia de ResourceBundleViewResolver que busca por los mapeos de las vistas en un archivo properties con el nombre indicado por la propiedad basename, en este caso views.properties.  Donde colocarlo? en el classpath de la aplicación.
El contenido del archivo views.properties se explica en el punto 4.

3. Crear el Template con iReports (yo trabajé con la version 3.5.3)
Una cosa que se debe tomar en cuenta es que se debe usar la misma version de iReports que la que se esta usando en el jar como dependencia.

Descarga la aplicacion iReports desde la pagina.  Luego instalala y cuando la abras basta con que crees un proyecto en blanco. Este se guarda como un archivo .jrxml que básicamente es un xml plano.

Puedes encontrar un buen manual de uso de iReports aqui.

Cuando se compila un archivo jrxml, se genera otro con el mismo nombre .jasper, el cual es necesario indicar en la configuración de spring para que se genere el reporte en el formato deseado.

4. Configurar las vistas en el archivo properties.
El framework de spring contiene 5 diferentes implementaciones para crear reportes con Jasper, cuatro de ellas corresponden a formatos específios (csv, pdf, html y xls) y una que permite determinar una de las anteriores en tiempo de ejecución (JasperReportsMultiFormatView).

Para generar un reporte en pdf, por ejemplo, vamos usar el siguiente contenido en nuestro archivo views.properties:

reciboPago.(class)=org.springframework.web.servlet.view.jasperreports.JasperReportsPdfView
reciboPago.url=/WEB-INF/jasperTemplates/recibo.jasper
reciboPago.reportDataKey=reciboKey

El nombre de la vista es "reciboPago", su propiedad class define la clase de jasper que la mapea, en este caso es la clase que genera PDFs.  Es importante que esta propiedad esté configurada como .(class) y no .class debido a que spring busca este valor vía la propiedad y no usando el acostumbrado getter/setter.  De esta manera se entiende que si no usamos los paréntesis, spring lo interpreta como el hecho que debe usar el getter/setter de dicha propiedad para acceder a su valor.

La propiedad url se utiliza para definir la ruta de acceso al archivo compilado por el programa iReports, en este caso se esta colocando dentro de la carpeta WEB-INF de la aplicación.

Finalmente reportDataKey define el nombre llave para obtener de un mapa el POJO o DTO que contiene cada valor a mostrar en el reporte. En este caso, se le esta diciendo a spring que su fuente de datos se encuentra en la llave "reciboKey" del mapa a recibir (ver punto 5, sobre el controller).

5. Crear el controller.
Es sumamente sencillo generar un controller en spring mediante las anotaciones:

@Controller
@RequestMapping("/pago")
public class PagoController {
    @RequestMapping(value="/recibo", method=RequestMethod.GET)
    public String generarReciboPago(ModelMap modelMap, HttpServletResponse response) {
        List<ReciboDTO> lista = new ArrayList<ReciboDTO>(1);
        ReciboPagoJasperDTO dto = new ReciboDTO();
        dto.setMonto("6789.0");
        dto.setNombre("Pedro Romero");
        lista.add(dto);
        response.setHeader("Content-type", "application/pdf");
        response.setHeader("Content-Disposition","attachment; filename=\"recibo.pdf\"");
        JRBeanCollectionDataSource jrbean = new JRBeanCollectionDataSource(lista, false);
        modelMap.put("reciboKey", jrbean);
        return("reciboPago");
    }
}

@Controller define a la clase como un controller y @RequestMapping define una uri desde la cual se puede acceder a nuestro controller cuando nuesta aplicación ya está montada en un server.  Si la aplicacion levanta en un contexto "proyecto" y el servidor levanta en un puerto local 8080, podriamos acceder a nuestro controller anterior como: "http://localhost:8080/proyecto/pago/recibo", aunque esto es asunto de otro tema, basta con observar como se concatenan los valores del contexto de la aplicación, y de las anotaciones @RequestMapping de clase y método usado.  

En este caso ReciboDTO es el pojo usado para portar los valores que alimentaran el reporte, se inserta en una instancia de JRBeanCollectionDataSource y luego se agrega al mapa del modelo con el key: reciboKey (observa que es el mismo metodo que usamos en el properties.

Finalmente se devuelve una cadena que indica el nombre de la vista configurada en el properties, la cual esta mapeada a la clase jasper: JasperReportsPdfView.

Notas importantes:
Spring nos provee de la capacidad de agregar elementos dentro de una Collection, dado que internamente Spring los envuelve dentro de un objeto JRBeanCollecionDataSource (implementa interface JRDataSource), también nos da la capacidad de agregar mas beans al modelo en diferentes llaves del mapa, ejemplo:

modelMap.put("reciboKey", jrbean);
modelMap.put("myKey1", jrbean1);
modelMap.put("personal2", jrbean2);
El punto está en que si la vista no esta apuntando a uno de estas llaves en particular (mediante la propiedad de la vista: reportDataKey), usará la primera instancia de JRDataSource que encuentre.

11 comentarios:

  1. como puedo pasarle parametros al jasper?

    ResponderEliminar
  2. Que tal Diego,

    A diferencia de los campos ($F{campo}), los parametros se identifican como $P{Parámetro}, recuerda que primero debes declararlos dentro de tu .jrxml, algo como esto:
    <parameter name="P_INSTITUCION" class="java.lang.String"/>

    y despues usarlos, dentro del reporte (también en el jrxml):
    <textField>
    <reportElement x="0" y="0" width="530" height="30" />
    <textElement>
    <font pdfFontName="Helvetica-Bold" size="20" isBold="true"/>
    </textElement>
    <textFieldExpression class="java.lang.String">
    <![CDATA[$P{P_INSTITUCION}]]>
    </textFieldExpression>
    </textField>

    Ahora bien, para enviar los valores de dichos parámetros se pasan usualmente desde nuestro controller (mediante el modelMap), siguiendo el mismo esquema del ejemplo del post, sería:

    modelMap.put("P_INSTITUCION", "Escuela Superior de Cómputo");

    Mas facil no pudo ser, jeje. Espero que haya quedado claro, ;) -> avísanos si no.

    Por cierto, te dejo una liga sobre como usar iReports para editar tu jrxml de forma gráfica y agregar parametros: http://javatutoriales.blogspot.com/2009/03/creacion-de-reportes-con-jasperrepots-y_30.html

    No olvides comentarnos como te fue.

    ResponderEliminar
  3. Esta interesante el manual, como soy nuevo en esto tengo algunas cosillas no muy claras de como levantar el ejemplo, sabes me baje el ejemplo cuando lo hago correr con toncat me sale el siguiente error

    Error creating bean with name 'subReport' defined in ServletContext resource [/WEB-INF/jrtest-servlet.xml]: Initialization of bean failed;

    ested exception is java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/reports/subReportParent.jasper]

    bueno aun no veo bien el problema aver si me das una manito

    ResponderEliminar
  4. Que tal Enrique, no nos das mucha información para tratar de llegar al problema, necesitamos q envíe s tu xml con la config del contexto de spring o al menos la config del bean subReport, el views.properties. Al parecer se podria resolver si se coloca bien la ruta para llegar al archivo jasper, asegurate que en el desplegado exista dentro de WEB-INF la ruta para llegar al archivo jasper, pero podría ser otra cosa, manda mas info para ser certero. Gracias y no olvides dejar tu correo para la próxima.

    ResponderEliminar
  5. Grasias por responder creo que si eso es el problema, creo que no esta encontrando el .jasper en mi carpeta web-inf, no se si tendras la fuente del ejemplo me gustaria echarle un vistaso mi correo es elmestas@gmail.com grasias de antemano

    ResponderEliminar
  6. Hola. Ojala me puedas ayudar con una duda.

    Llevo usando Jarperreports desde hace algunos años, sin embargo apenas lo voy a implementar en una aplicación Spring. La particularidad de mi caso es la siguiente:

    Resulta que los reportes que debo generar son de columnas dinámicas(ouch!) Para este tipo de casos suelo usar una librería llamada Jasperberry (http://www.softwarepassion.com/jasperberry-the-most-dynamic-reports-ever/) Para la cual obviamente debo tomar el objeto .jasper para manipularlo antes de pasarlo al JRFiller y al JRexporter (En el caso de tu ejemplo veo que ya lo pasas directamente a un ViewResolver el cual no he utilizado ).

    Siendo así ¿Como podría agregar a tu ejemplo una etapa en la cual pueda manipular el objeto .jasper antes de generar el reporte?

    De antemano muchas gracias por la ayuda que me puedas brindar.

    ResponderEliminar
  7. Que tal, lamento contestar hasta ahora pero el trabajo no me ha dejado, creo que en el view.properties deberías indicar una subclase de la de jasper que estiy usando en el ejmplo y ahí dentro (la clase que hereda de jasper de spring, implementar lo que quieres, revisa la documentación de spring y la api de esa clase que implementa jasper en spring para que veas como sobreescribir los metodos del filler. Es lo que se me ocurre dado que no he tenido esa necesidad ni el tiempo para buscarle, peo no dejes de decirnos como te fue.

    ResponderEliminar
  8. Hola Wdo,

    Me estoy peleando con la pagina de spring http://static.springsource.org/spring/docs/2.0.x/reference/view.html y tu tutorial para conseguir un jasper report pasandole parametros desde el Controller.

    1. Tienes el codigo completo de tu ejemplo? No veo de donde sacas el objeto response.
    2. Donde llenas el ModelMap ? No lo veo instanciado en ningun lugar.

    ResponderEliminar
  9. Que tal anónimo! ;)

    En primer lugar te recomiendo que revises la doc de Spring 3 como mencioné en el primer párrafo.

    Luego, tenías razón! olvide colocar unos argumentos que recibe el método generarReciboPago(), pero ya lo arreglé, este recibe: ModelMap modelMap y HttpServletResponse response, es de ahí que se sacan esas variables, pues Spring automáticamente envía esos valores, sino, revisa la doc referente a MVC en la doc de spring 3.

    ResponderEliminar
  10. puedo mandar imagen desde el jasper.view

    ResponderEliminar
    Respuestas
    1. Yo te recomendaria tener un tercer repositorio de archivos para tus imagenes y generaras links para acceder a ellos, si los almacenas en tu BD y los relacionas con tus datos, al generar el pdf por ejemplo, lo que este haria seria mandar llamar a tus imagenes por medio de ese link. Por otro lado, se que el jasper pudiera recibir un stream de datos (tu imagen) directamente de tu aplicacion, pero no estoy seguro de como se haria en el template de ireports para que leyera esa infor y te la despliegue como info.. si alguien sabe o tu encuentras como hacer eso, seria muy bueno q lo compartieras.

      Eliminar

Que opinas sobre esta publicación?