Tag Archive: reports


Antes de nada, Feliz año a mis escasos lectores. Últimamente ando enfangado de nuevo con Java y por supuesto, con JasperReport para sacar informes. Con la aplicación que estoy desarrollando actualmente, estoy utilizando Icefaces 3 para JSF, que da muchas facilidades para crear aplicaciones rápidamente y atractivas, tanto visual como funcionalmente. Una de las cualidades de esta versión del framework, esque todo va por AJAX. Esto, al llegar al momento de presentar al usuario los informes creados con JasperReport, supone un problema. Ya que normalmente, nos inmiscuimos en el ciclo de vida de JSF para capturar la respuesta e imprimir nuestro informe, y esto no le sienta muy bien al framework.  Así que tras unas cuantas peleas y cabezazos contra el teclado, he conseguido llegar a una solución. Dicha solución consiste en sacar el informe en otra ventana, fuera del flujo de Icefaces. Y para presentar el informe, utilizo directamente un Servlet para generar la respuesta. Paso a explicar paso a paso.

Lo primero, tenemos en nuestro archivo .xhtml lo siguiente:

<h:commandButton id="pushButtonId12"  actionListener="#{informes.pdfEdificios}"  />

Utilizamos actionListener, para que no haga ningún refresco del contenido. Ahora, el código del método encargado de manejar dicho evento es el siguiente:


public String pdfEdificios(ActionEvent event) throws IOException{
try {
Connection con = edificiosDao.getConnection();
HashMap parameters = new HashMap();
String reportsDirectory = "c:/Users/Usuario/Documents/NetBeansProjects/Proyecto/build/web/";
String jasperName = "report1.jasper";
JasperPrint jasperPrint = UtilReports.generateReport(con, reportsDirectory, jasperName, parameters);
getSessionMap().put("report",jasperPrint);
JavascriptContext.addJavascriptCall(FacesContext.getCurrentInstance(), "window.open('Report', 'myWindow');");
} catch (NamingException ex) {
Logger.getLogger(Informes.class.getName()).log(Level.SEVERE, null, ex);
} catch (SQLException ex) {
Logger.getLogger(Informes.class.getName()).log(Level.SEVERE, null, ex);
} catch (JRException ex) {
Logger.getLogger(Informes.class.getName()).log(Level.SEVERE, null, ex);
} catch (FileNotFoundException ex) {
Logger.getLogger(Informes.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}

En este método, simplemente generamos el informe mediante UtilReports.generateReport(con, reportsDirectory, jasperName, parameters), una vez hecho, guardamosel objeto JasperPrint a la sesión, para poder recuperarlo después en el Servlet.
Hasta este punto, ya tenemos el informe creado y guardado en la sesión, ahora necesitamos abrir el Servlet automáticamente para llevar el informe al usuario. Para ello, Icefaces nos proporciona JavascriptContext, desde el cual podemos añadir código javascript enviado desde el servidor.
Así que mediante JavascriptContext.addJavascriptCall(FacesContext.getCurrentInstance(), “window.open(‘Report’, ‘myWindow’);”); enviamos el código para que se abra una ventana nueva que lleva al servlet Report. Que habremos creado previamente con el siguiente código para manejar la respuesta:

protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
JasperPrint report =(JasperPrint) request.getSession().getAttribute("report");
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment; filename=" + "report" + ".pdf;");
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
JRExporter exporter = new JRPdfExporter();
exporter.setParameter(JRExporterParameter.JASPER_PRINT, report);
exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, response.getOutputStream());
exporter.exportReport();
} catch (JRException ex) {
Logger.getLogger(Report.class.getName()).log(Level.SEVERE, null, ex);
}finally{
request.getSession().removeAttribute("report");
}

}

Aquí, recuperamos de la sesión el objeto JasperPrint de nuestro informes y preparamos las cabeceras de la respuesta para indicar el tipo, y utilizamos un exporter para exportar el informe en el formato deseado (normalmente pdf). Por último, es importante poner en el bloque finally, la liberación del objeto de la sesión, para no sobrecargar la memoria del servidor.
Esta es la única forma que he encontrado, de momento, para poder mostrar los informes sin entrar en conflicto con Icefaces. Si alguién tiene una solución mejor, que no dude en comentarlo.

Bueno, después de unas cuantas semanas sin postear, volvemos con las pilas recargadas. Vamos a empezar con un pequeño tutorial, para introducir informes creados con Jasper Reports en nuestras aplicaciones webs usando el framework Struts 2.

Para este tutorial, vamos a presuponer que se saben crear informes con jasper report (mejor, si utilizamos la herramienta iReport) y que tenemos ya montada una aplicación con Struts 2.

Lo primero que tenemos que hacer, es importar tantos las librerías de Jasper repots como el plugin para struts 2. El jar del pluggin lo podreís encontrar, con las librerias de struts 2, se llama struts2-jasperreports-plugin-2.1.x.jar , como veis, es muy facil identificarla.  Para ver que librerias necesitais de jasper report, podeis remitiros a la documentación oficial.

Una vez importadas las librerias, lo siguiente, sería abrir nuestro fichero de configuración struts.xml y hacer que el package en el que está la acción que nos mostrará el report, incluya el soporte para interpretar los informes:


<package name="default" namespace="/" extends="struts-default,json-default,jasperreports-default">

 <action name="Certificados">
 <result name="success" type="jasper">
 <param name="location">informes/report3.jasper</param>
 <param name="dataSource">resultados</param>
 <param name="format">PDF</param>
 <param name="reportParameters">params</param>
 </result>

 <result name="informe2" type="jasper">
 <param name="location">informes/peticion_justificacion.jasper</param>
 <param name="connection">conexion</param>
 <param name="format">PDF</param>
 <param name="reportParameters">params</param>
 </result>
 </action>
 </package>

Como podemos ver, el paquete, extiende la clase jasperreports-default , con lo que las acciones podrán devolver resultados del tipo jasper, para poder imprimir nuestros informes.  Después de eso, podeis ver dos acciones, y es de notar que el tipo es  jasper, con lo cual, ya se entiende, que es un informe.  Las acciones,  tienen que llevar los siguientes parámetros de forma obligatoria:

  • location: es la localización del informe compilado que queremos que se ejecute como resultado.
  • datasource : podemos especificar el nombre de una propiedade de la acción, que deberá devolver una colección de beans, de la cual queremos extraer las propiedades.
  • connection: pondremos el nombre de una propiedade de la acción que devuelva una conexión a la base de datos, que usará el report para conectarse y ejecutar la sentencia que lleve este implicita.

Entre datasource y connection, solo hace falta especificar una de las dos. En el código de ejemplo, hay dos parámetros más:

  • format: Será el formato de salida del informe, podrá ser PDF o cualquier otro que permita Jasper Report, (ver la documentación oficial)
  • reportParameters: Será un mapa, con una lista de parámetros que se utilizará dentro del report, por ejemplo para parametrizar la consulta del report.

Ahora solo nos queda ver, el código de una acción, que como veremos a continuación, puede llegar a ser muy simple:


public class CertificadosAction extends ActionSupport implements ApplicationAware{
private Collection resultados;
private Map<String,Object> app;
private Map<String,Object> params;

@Override
public String execute(){
ProxyBD proxy = (ProxyBD)app.get("db"); // Un objeto que envuelve la conexión
resultados = proxy.getDatos();
params = new HashMap<String,Object>();

params.put("param1","dato 1");
params.put("fecha",new Date());

return SUCCESS;
}

public Connection getConexion(){
ProxyBD proxy = (ProxyBD)app.get("db");
return  proxy.getConnection();
}

public Map getParams(){
return params;
}
public Collection getResultados(){
return resultados;
}

public void setApplication(Map<String, Object> arg0) {
app = arg0;
}
}

Como se puede ver, para un informe sencillo, el código de la acción es realmente reducido, incluso para informes más complejos, no es necesario complicar mucho más la acción. Se puede ver, que tiene varios atributos,  uno los resultados, que sería el datasource para el informe, otro son los parámetros adicionales que se desean enviar al report y por último, la conexión, de forma que esta misma acción, se puede reutilizar para diferentes report, dependiendo del resultado de la acción.

De momento, esto es todo por ahora,  en próximas entregas veremos como tratar con subreports sin perder la cabeza y algún que otro truquillo más.

Enlaces: