
Para realizar este tutorial he usado la versión 6.16 de Drupal y los módulos:
Vamos a partir de una instalación limpia de Drupal con estos módulos contenidos en ella.
Video:
$db_url = 'mysql://user:pass@localhost/drupallocal';
$update_free_access = TRUE;
LoadModule rewrite_module modules/rewrite_module.so
Dynamic Jasper es una librería que corre sobre JasperReports y que nos simplifica enormemente la realización de informes. Las posibilidades son enormes (posibilidad de agregar Charts, encadenar reportes, añadir códigos de barras a las columnas de un informe, …) además de usos totalmente básicos. En este artículo nos centraremos en conceptos introductorias que podemos ir combinando para obtener resultados cada vez mejores y más elegantes a la vez de poder sumar aspectos más complejos.
Su funcionamiento básico de DJ se centra en hacer más amigable e intuitivo el uso de JasperReports que para nosotros será una caja negra. Es decir, DJ actuará como un disfraz, aunque realmente seguiremos usando JasperReports, pero ahorrándonos todos los engorrosos pasos que supone usarlo.
La web del proyecto contiene un amplio abanico de documentación y ejemplos que nos pueden ser útiles de cara a realizar elegantes informes.
Dynamic Jasper es una librería escrita 100% en Java con lo que es fácilmente integrable en proyectos que empleen el lenguaje Java.
No es necesaria ninguna herramienta adicional o “plug-in” al margen de nuestro entorno de desarrollo (IDE) preferido.
Este esqueleto se puede definir de varias formas como pueden ser las siguientes:
FastReportBuilder drb = new FastReportBuilder();
DynamicReportBuilder drb = new DynamicReportBuilder();
Hagámonos a la idea de que si generáramos el informe de estos esqueletos obtendríamos un informe en blanco, sin nada que mostrar. Eso es lo que vamos a ir haciendo ahora, añadir datos.
Podemos añadir un título a informe
drb.setTitle("Equipos");Definición de márgenes en la página
drb.setLeftMargin(margen).setRightMargin(margen).setTopMargin(margen).setBottomMargin(margen);Definimos la anchura de cabecera del informe y del detalle del informe.
drb.setDetailHeight(anchura).setHeaderHeight(anchura);Podemos hacer que la página se divida en varias columnas y ahí insertar los datos
//Si optamos por más de una columna podemos definir la separación entre ellasPodemos añadir autotextos
drb.setColumnsPerPage(columnasPorPagina, separacion);
//Definimos una columna por página
drb.setColumnsPerPage(1);
//Insertamos una Fecha en la cabecera a la izquierdaComo vemos podemos usar más autotextos cuyo tipo será definido en el primer parámetro:
drb.addAutoText(sdf.format(new Date()), AutoText.POSITION_HEADER, AutoText.ALIGMENT_LEFT);
//Insertamos “Pagina X/Y” en la cabecera a la derecha
drb.addAutoText(AutoText.AUTOTEXT_PAGE_X_SLASH_Y, AutoText.POSITION_HEADER, AutoText.ALIGMENT_RIGHT);
Como hemos comentado con anterioridad, en la página de la librería podemos obtener muchísima más información acerca añadir bastantes más cosas al reporte.
public class Equipo {Queremos un informe que muestre el listado de datos de esa Collection.
private int id;
private String nombre;
private String fundacion;
private String presidente;
private String entrenador;
private LinkedList<Jugador> jugadores;
private String rutaEscudo;
private InputStream escudo;
public Equipo() {
}
public Equipo(int id, String nombre, String fundacion, String presidente, String entrenador, LinkedList<Jugador> jugadores) {
this.id = id;
this.nombre = nombre;
this.fundacion = fundacion;
this.presidente = presidente;
this.entrenador = entrenador;
this.jugadores = jugadores;
}
public InputStream getEscudo() {
return escudo;
}
public String getRutaEscudo() {
return rutaEscudo;
}
public void setRutaEscudo(String rutaEscudo) {
this.rutaEscudo = rutaEscudo;
try {
File fileIn = new File(rutaEscudo);
this.escudo = new FileInputStream(fileIn);
} catch (Exception e){
e.printStackTrace();
}
}
/*resto de Getters and Setters */
…
Vamos a definir un informe donde podamos ver los datos en columnas, por tanto, tenemos que definir esas columnas. Veamos como esto nos va a resultar bastante sencillo:
AbstractColumn c1 = ColumnBuilder.getInstance().setColumnProperty("id", Integer.class.getName()).setTitle("ID").setWidth(10).build();
AbstractColumn c2 = ColumnBuilder.getInstance().setColumnProperty("nombre", String.class.getName()).setTitle("Nombre").setWidth(30).build();
AbstractColumn c3 = ColumnBuilder.getInstance().setColumnProperty("fundacion", String.class.getName()).setTitle("Fundacion").setWidth(30).build();
AbstractColumn c4 = ColumnBuilder.getInstance().setColumnProperty("presidente", String.class.getName()).setTitle("Presidente").setWidth(30).build();
//Añadimos las columnas al informe
drb.addColumn(c1);
drb.addColumn(c2);
drb.addColumn(c3);
drb.addColumn(c4);
Para crear las columnas indicamos nombre del atributo, cadena que representa la clase del atributo (class.getName()), el título que queremos mostrar en el informe y la anchura (medida proporcional) de la columna. Existen muchas más opciones que podemos añadir a la columna a la hora de crearla.
También podemos crear las columnas de la siguiente manera
drb.addColumn("TITULO", "id", Integer.class.getName(),10);
Una vez hemos realizado esto tenemos el informe disponible para ser visualizado. Debemos realizar una serie de operaciones antes que veremos en el siguiente apartado.
drb.setUseFullPageWidth(true);Indicamos que queremos usar todo el ancho de la página a la hora de mostrar el informe.
Ahora realizamos la compilación del esqueleto rellenado para comprobar si hay algún problema y no se puede continuar con el mostrado del informe.
DynamicReport dr = drb.build();
Una vez que se ha realizado el compilado y todo es correcto procedemos a añadirle la Collection que contiene los datos
//Definimos la fuente de datos
JRDataSource ds = new JRBeanCollectionDataSource(l);
//Generamos el informe como JasperPrint con los datos ya añadidos.
JasperPrint jp = DynamicJasperHelper.generateJasperPrint(dr, new ClassicLayoutManager(), ds);
//Podemos también añadir un mapa de parámetros que hayamos definido anteriormente
//JasperPrint jp = DynamicJasperHelper.generateJasperPrint(dr, new ClassicLayoutManager(), ds, map);
Una vez todos estos procesos se han realizado correctamente con el objeto JasperPrint podemos hacer multitud de operaciones: podemos mostrarlo usando la herramienta JasperViewer, podemos exportarlo a otros formatos (apartado 6), ...
JasperViewer es un visor que proporciona la librería que muestra el informe además de permitirnos la exportación de los datos a través de él a PDF, XML, ODT, ...
//Mostramos el reporte mediante el visor JasperViewer. Con el false, indicamos que no
//cierre la aplicación al cerrar el JasperViewer
JasperViewer.viewReport(jp, false);
El resultado debería ser algo parecido a esto:
Una vez guardada la plantilla debemos asociarla a nuestro informe para indicarle que debe ajustarse a ella a la hora de mostrar los datos. Asignar una plantilla a un informe es tan fácil como poner la siguiente instrucción:
//Asociamos una plantilla al informeYa que le hemos asociado una plantilla debemos crear una 'conexión' entre los datos que tenemos en la aplicación y la plantilla. Como hemos visto anteriormente habíamos creado unos Fields en la plantilla. Ahora crearemos unos campos (Fields) en el informe. Esos campos serán el nombre de los atributos y definiremos su tipo. Vemos como lo hemos realizado en el código.
drb.setTemplateFile("resources/equipos.jrxml");
drb.addField("escudo", InputStream.class.getName());A modo de resumen podemos decir que en la plantilla hemos representando gráficamente como mostrar el objeto indicando donde aparecen los atributos y definiendo éstos en la misma. Del mismo modo asociamos esa plantilla al informe y en éste definimos los atributos del objeto.
drb.addField("nombre", String.class.getName());
drb.addField("presidente", String.class.getName());
drb.addField("fundacion", String.class.getName());
Para empezar, vamos a definir la estrategia para realizar este informe.
Por tanto, procedemos a definir ambos informes.
public static FastReportBuilder initMainReport() throws Exception {
FastReportBuilder report = new FastReportBuilder();
report.setTitle("EQUIPOS");
report.setMargins(margen, margen, margen, margen);
report.setTemplateFile("resources/equipos.jrxml");
report.addField("id", Integer.class.getName());
AbstractColumn c1 = ColumnBuilder.getInstance().setColumnProperty("id", Integer.class.getName()).setTitle("ID").setWidth(10).build();
report.addColumn(c1);
report.addField("nombre", String.class.getName());
report.addField("fundacion", String.class.getName());
report.addField("presidente", String.class.getName());
report.addField("entrenador", String.class.getName());
report.addField("escudo", InputStream.class.getName());
return report;
}
Importante: Podemos observar como hemos definido una columna en el informe a pesar de ser de tipo plantilla. En el siguiente apartado explicaremos la razón.
public static FastReportBuilder initSubreport() throws Exception {Como vemos, nada extraño respecto a lo que explicamos en el apartado de informes de tipo listado
FastReportBuilder report = new FastReportBuilder();
AbstractColumn c1 = ColumnBuilder.getInstance().setColumnProperty("id", Integer.class.getName()).setTitle("ID").setWidth(10).build();
AbstractColumn c2 = ColumnBuilder.getInstance().setColumnProperty("dorsal", Integer.class.getName()).setTitle("Dorsal").setWidth(30).build();
AbstractColumn c3 = ColumnBuilder.getInstance().setColumnProperty("nombre", String.class.getName()).setTitle("Nombre").setWidth(30).build();
AbstractColumn c4 = ColumnBuilder.getInstance().setColumnProperty("posicion", String.class.getName()).setTitle("Posicion").setWidth(30).build();
report.addColumn(c1);
report.addColumn(c2);
report.addColumn(c3);
report.addColumn(c4);
report.setUseFullPageWidth(true);
return report;
}
Bien, hemos visto que se ha creado una columna en el informe de plantilla que realmente no es necesaria pues hemos añadido un Field con ese atributo pero, ¿por qué hacemos esto?.
A la hora de colocar el subinforme debemos indicar un campo significativo para colocar el subinforme, es decir, necesitamos un identificador del objeto para añadir el subinforme. Veamoslo en el ejemplo: Tenemos equipos que poseen un entero llamado 'id' que es el identificador. A la hora de crear el informe para cada valor de ese 'id' se añadirá un subinforme con los datos correspondientes al objeto de ese id.
La agrupación de puede realizar creando un grupo o como veremos en el siguiente apartado
private static DynamicReport definirSubreport(FastReportBuilder report, FastReportBuilder subreport, String atributoColeccion, String claseColeccion) throws Exception {Para empezar definimos un Campo en el informe 'padre' que no es necesario que sea definido en la plantilla. Ese campo contiene el nombre del atributo del objeto (equipo en nuestro caso) de la colección que mostraremos en el subinforme. Esa colección actuará de DataSource del subinforme.
report.addField(atributoColeccion, claseColeccion);
report.addGroups(1).setGroupLayout(1, GroupLayout.EMPTY);
report.addSubreportInGroupFooter(1, subreport.build(),
new ClassicLayoutManager(), atributoColeccion,
DJConstants.DATA_SOURCE_ORIGIN_FIELD,
DJConstants.DATA_SOURCE_TYPE_COLLECTION);
report.setUseFullPageWidth(true);
return report.build();
}
Definimos el grupo, con la función 'addGroups(1)' donde el parámetro indica el número de columnas que cogeremos para agrupar. En nuestro caso, la primera y única columna definida para el informe con plantilla.
Una vez realizado esto, usamos la función 'addSubreportInGroup(Header|Footer)' para asociar ambos informes. Los parámetros que se pueden observar son: el primero (número 1) es el número del grupo que usamos para agrupar, en este caso el primero; el subreport compilado, es decir, DynamicReport del subinforme; un Layout, nosotro usaremos el clásico; el nombre del atributo de la clase que alimenta a modo de DataSource al subinforme; constante que indica que el DataSource proviene de un campo/field del informe y constante que indica que el tipo del DataSource es una colección.
Una vez realizado esto compilamos el informe y realizamos la generación del informe y obtendremos los siguiente:
La ventaja que proporciona DJ es que exportar a cualquiera de estos formatos nos cuesta una sóla línea. Simplemente, en lugar de notificar a JasperViewer lo que queremos mostrar usaremos una clase llamada 'JasperExportManager' que nos proporcionará soluciones de cara a las exportaciones. Vemos que métodos podemos usar:
//Exportación a formato HTMLComo vemos la exportación es bastante simple, una sóla línea nos proporciona un fichero o la información del informe.
JasperExportManager.exportReportToHtmlFile(jp, EXPORT_PATH + "exportacionHTML.html");
//Exportación a formato PDF
JasperExportManager.exportReportToPdfFile(jp, EXPORT_PATH + "exportacionPDF.pdf");
//Exportación a formato XML, sin embeber imágenes
JasperExportManager.exportReportToXmlFile(jp, EXPORT_PATH + "exportacionXML.1.xml", false);
//Exportación a formato XML, embebiendo imágenes
JasperExportManager.exportReportToXmlFile(jp, EXPORT_PATH + "exportacionXML.2.xml", true);
Vamos a incluir un ejemplo de método para exportar un objeto JasperPrint (el jp que hemos mostrado anteriormente) a un archivo Excel.
private static void exportToExcel(JasperPrint jp) throws Exception {Todos estos atributos los podemos ver en la documentación de Jasper Reports, o podemos verlo en el Javadoc que podemos encontrar en la red.
//Creamos una instancia del objeto JRXlsExplorer que se encuentra en la librería jasperreports-X.X.X.jar
JRXlsExporter exporter = new JRXlsExporter();
//Si queremos definir varios Sheet lo hacemos en un array de cadenas que posteriormente la añadiremos por parámetros.
String[] sheetNames = {"Hoja 1"};
//Creamos el fichero de salida donde exportaremos el informe a Excel
File outputFile = new File(EXPORT_PATH + "exportacionEXCEL.xls");
FileOutputStream fos = new FileOutputStream(outputFile);
//Empezamos a definir los parámetros de la exportacion
//Indicamos el objeto JasperPrint que deseamos exportar
exporter.setParameter(JRExporterParameter.JASPER_PRINT, jp);
//Indicamos el fichero donde vamos a exportar el informe
exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, fos); //and output stream
//Excel specific parameter
//Indicamos si queremos una página por Sheet
exporter.setParameter(JRXlsExporterParameter.IS_ONE_PAGE_PER_SHEET, Boolean.FALSE);
//Indicamos si deseamos eliminar los espacios vacíos entre filas
exporter.setParameter(JRXlsExporterParameter.IS_REMOVE_EMPTY_SPACE_BETWEEN_ROWS, Boolean.TRUE);
//Indicamos si quremos mostrar una página en blanco como fondo
exporter.setParameter(JRXlsExporterParameter.IS_WHITE_PAGE_BACKGROUND, Boolean.FALSE);
//Definimos los nombres de los Sheet que es el Array de String que definimos arriba
exporter.setParameter(JRXlsExporterParameter.SHEET_NAMES, sheetNames );
//Usamos la función exportReport para crear el nuevo Excel.
exporter.exportReport();
}
Nota: Indicar que para que podamos realizar la exportación a EXCEL las últimas versiones de DJ no soportan la exportación con las librerías del POI de Apache 3.5, ya que según el equipo de desarrollo de DJ hasta que no dejara de estar POI 3.5 en fase beta no se adaptaría DJ a dicha librería.