Jasper Report + Tapestry
Editado:
Les dejo como implemente mi generacion de reportes usando jasper Reports en Tapestry5.
Comienzo desde la parte en que ya se tiene el reporte.
Caracteristicas:
Manejo JRBeanCollectionDataSource puesto que estamos utilizando un ORM para la obtencion de los datos y no queremos queries en el reporte.
Manejamos el template en otro servidor para poderlos cambiar si asi se requiere (creanme funciona lo he realizado incontables veces)
De ahi tengo 2 Clases principales: una que genera el reporte y otra mas para generar la respuesta de stream de tapestry.
Como manejo archivos dentro de la app tengo un wrapper para eso.
Receta:
Primeramente agregamos la dependencia de reports yo estoy utilizando maven, esta version tendra que ser compatible con nuestro editor de las plantillas:
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports</artifactId>
<version>5.0.0</version>
</dependency>
Como les mecione utilizo archivos en la app asi que tengo una clase wrapper de archivos, la cual es la siguiente. P.D. utilizo lombok por eso las anotaciones:
@Setter
@EqualsAndHashCode(of = {"id"})
public final class File {
private long id;
private String title;
private String name;
private String type;
private long size;
private byte[] content;
private String extension;
//Getters y setters si no usan lombok
/**
* NO CAMBIAR esta parte pues es utilizada en la clase de creacion de StreamResponse (StreamResponseFactory.java)
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("inline;filename=");
builder.append(name);
builder.append(".");
builder.append(extension);
return builder.toString();
}
}
Por aqui tenemos la clase que es encargada de tomar la informacion que se exportara al reporte, el tipo de reporte que se requiere CSV, PDF, EXCEL o HTML, en mi caso solo tengo implementados PDF y EXCEL y para eso utilizo un enumerador.
*Clase para la generacion del reporte.
*/
public class JasperReportGenerator {
private static final Logger LOG = LoggerFactory.getLogger(JasperReportGenerator.class);
/**
* name: Nombre del reporte
* asset: Enum que contiene la URL de la plantilla del reporte ya compilado
* type: Enum que contiene el tipo de impresora de jasper, asi como el tipo de archivo y extencion del archivo
* beanCollection: Es la colleccion de objetos que se desplegaran en el reporte y puede ser cualquier POJO, las propiedades del POJO tendran que estar como fields en el reporte de jasper, jasper se encargara de la obtencion de los valores de cada objeto.
*/
public File generate(
String name,
JasperAssets asset,
JasperReportType type,
Collection<?> beanCollection) {
// baos se encarga de almacenar el reporte ya impreso en un arreglo de bytes
ByteArrayOutputStream baos = new ByteArrayOutputStream();
JasperReport report = null;
JasperPrint jasperPrint = null;
try {
report = (JasperReport) JRLoader
.loadObject(new URL(asset.getURL()));
jasperPrint = JasperFillManager.fillReport(
report,
null,
new JRBeanCollectionDataSource(beanCollection));
JRExporter exporter = type.getExporter();
exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);
exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, baos);
exporter.exportReport();
} catch (MalformedURLException e) {
LOG.error("------ [ Error de malformacion ] ------", e);
LOG.error("------ [ {} ] ------", e.getMessage());
} catch (JRException e) {
LOG.error("------ [ Error de JasperReport ] ------", e);
LOG.error("------ [ {} ] ------", e.getMessage());
}
//Retornamos un File pues es nuestro wrapper de archivos
return buildFile(name, baos, type);
}
private File buildFile(
String name,
ByteArrayOutputStream baos,
JasperReportType type) {
File file = new File();
file.setContent(baos.toByteArray());
file.setExtension(type.getExtension());
file.setName(name);
file.setSize(baos.size());
file.setTitle(name);
file.setType(type.getType());
return file;
}
}
Esta clase se encargara de generar las respuestas que acepta Tapestry (StreamResponse) y que la utilizaremos en nuestras paginas Tapestry:
* Genera StreamResponse de tapestry. Se puede utilizar para abrir una nueva pagina o descarga con liga en la misma pagina.
* Se utiliza para la obtencion de Reportes en Jasper u obtencion de archivos almacenados en DB.
*/
public final class StreamResponseFactory {
/**
* Se le pasa un File que contiene la informacion del archivo para generar un streamResponse
*/
public StreamResponse getResponse(final File file) {
return new StreamResponse() {
@Override
public void prepareResponse(Response response) {
response.setHeader("Content-Disposition", file.toString());
}
@Override
public InputStream getStream() throws IOException {
return new ByteArrayInputStream(file.getContent());
}
@Override
public String getContentType() {
return file.getType();
}
};
}
}
Como les mencione tengo implementados unicamente PDF y EXCEL (uds pueden agragar los que faltan o necesitan) en mi app, asi que tengo un Enumerador que almacena el comportamiento de: impresion de reporte, extencion de archivo y content-type de http:
PDF(new JRPdfExporter(), "pdf", "application/pdf"),
XLS(new JRXlsExporter(), "xls", "application/vnd.ms-excel");
private JRExporter exporter;
private String extension;
private String type;
/**
* @param exporter
* @param extension
* @param type
*/
private JasperReportType(
JRExporter exporter,
String extension,
String type) {
this.exporter = exporter;
this.extension = extension;
this.type = type;
}
/**
* @return the exporter
*/
public JRExporter getExporter() {
return exporter;
}
/**
* @return the extension
*/
public String getExtension() {
return extension;
}
/**
* @return the type
*/
public String getType() {
return type;
}
}
Por este lado tenemos el Enumerador que contiene las direcciones y nombres de los reportes, recordemos que los manejo en otro servidor.
Y para darle uso a todo esto pues tengo en una parte de mi servicio un metodo que invoca el llamado dada una colecion de objetos que se mandaran a un reporte:
En este caso invocare una exportacion a EXCEL asi que queda de la siguiente manera:
File file = jasperReportGenerator.generate(
"Concentrado", //Nombre del reporte
JasperAssets.TICKET_EXPORTER, //Que plantilla deseo
JasperReportType.XLS, //Que tipo de plantilla es (EXCEL en este caso)
tickets); // Y la informacion que llevara el reporte
LOG.info("------ [ Saliendo: toExcel() ] ------");
return streamResponseFactory.getResponse(file); // Dado el File generado lo mando aun StreamResponse
}
Y finalmente mi invocacion desde un evento en la pagina de Tapestry
StreamResponse onActionFromToExcel(){
return ticketService.toExcel(tickets);
}
Bueno se preguntaran por que tengo separado el generador de reporte y la generacion del stream, por dos cosas una codigo mas limpio y dos por que tambien tengo que enviar por correo el reporte asi que mando el file :) a mi servicio de correos y listo...
Recordemos que el POJO que se envia en la coleccion de datos debera tener las propiedades que el reporte tenemos como fields,
Si en el reporte tenemos:
String name
String age
String description
Date date
en el POJO debemos tener
Bueno espero les ayude y si tienen preguntas o comentarios son bien venidos...
- arterzatij's blog
- Inicie sesión o regístrese para enviar comentarios
Comentarios
Duda
en que direccion van los archivos .java no se en que parte ubicarlos...
Va a depender que archivos
Va a depender que archivos tienes pero segun la estructura de Tapestry
Ticket es una entidad asi que va con los modelos
ReportGenerator es un servicio
StreamResponseFactory es una utileria desde mi punto de vista asi que la puedes ubicar en un utils
Las paginas pues con los pages
Enumeradores los ubico en un paquete data
foo.bar.data
foo.bar.model
foo.bar.pages
foo.bar.services
foo.bar.utils
Errores
Disculpa me salen algunos errores como en la clase para generar el reporte en esta parte..
File file = new File();
file.setContent(baos.toByteArray());
file.setExtension(type.getExtension());
file.setName(name);
file.setSize(baos.size());
file.setTitle(name);
file.setType(type.getType());
Lo mas seguro es que no estan
Lo mas seguro es que no estan generados los getters y setters (lombok), solo remueve las anotaciones @Getter @Setter Y @HashCode... y genera tu los getters y setters.