Dynamické PDF pomocí JasperReports, iReport a Spring Frameworku
12. June 2007
V dnešním článku přiblížím tvorbu dymických PDF dokumentů pomocí JasperReports, iReport a Spring frameworku. V článku uvedu základy JasperReports. Následovat bude seznámení s návrhářem iReport a nakonec ukážu integraci se Spring Frameworkem.
Stručně o JasperReports
JasperReports je reportovací nástroj, který dovede vytvářet dokumenty ve formátech PDF, XML, HTML, CSV, XLS, RTF a TXT. Design dokumentu se definuje pomocí XML. Nadefinuje se zde jednak layout dokumentu i práce s daty.
Data pro dokument se předávají pomocí interfacu JRDataSource. Existují implementace, které umožňují, aby JR pracovaly přímo s databází JRResultSetDataSource a také s Hibernatem – například JRHibernateIterateDataSource. Pokud chcete naplnit datasource ve Vašem java kódu můžete použít třeba JRMapCollectionDataSource.
XML soubor, ve kterém je dokument nadefinován, má koncovku
jrxml
. Jeho kompilací vznikne soubor s koncovkou
jasper
.
Pro běh JR potřebujete několik knihoven. Jsou to knihovny z rodiny Jakarta Commons (Jakarta Commons BeanUtils, Jakarta Commons Collections, Jakarta Commons Logging, Jakarta Commons Digester). Pro vytváření PDF souborů je potřeba knihovna iText. Další knihovny jsou nepovinné. Uvedu alespoň JFreeChart, pomocí které lze vytvářet grafy. Kompletní přehled najdete na stránce JasperReports Requirements.
Zajímavým projektem jsou OpenReports
Typy proměnných v JasperReports
Pokud se budeme bavit o datavých typech proměnných v zásadě se dá říci, že lze používat většinu primitive wrappers a další základní datové typy (Boolean, Integer, Double, Date, Time, Byte, String, Object…).
Co se týče kontextu můžeme v JR ((JasperReports)) můžete pracovat s následujícími typy proměnných:
- FIELD
- FIELD je položka objektu, který tvoří řádky reportu (v našem příkladu jsou to name a price). Tyto data obsahuje JRDataSource.
- VARIABLE
- položky definované uživatelem. Lze si zvolit způsob výpočtu každé
variable (Calculation type). Jsou připraveny například Averige,
Highest, Lowest, Sum. Dále specifikujete Variable expression –
vzorec pro výpočet. V našem příkladu jsem zacal Calculation
type Average a Variable expression jsem jako
$F{price}
(chceme počítat průměrnou cenu). Lze zvolit i Calcuation type Nothing a pak si můžeme způsob výpočtu zadefinovat ve známé části Variable expression zcela svobodně. JR obsahují několik implicitních proměnných. Jsou to například:PAGE_NUMBER
,COLUMN_NUMBER
aPAGE_COUNT
. - PARAMETER
- jako parameter můžeme do JR nastavit další data, která jsou společná pro všechny řádky reportu (sekernicky řešeno). Může to být například titulek stránky nebo datum (jak je vidět v příkladu). Opět existuje velké množství implicitních parametrů zde již ovšem odkazuji na dokumentaci.
Vytváření jrxml souborů pomocí iReport
iReport je IDE, ve který slouží pro vytváření zdrojovým (jrxml) souborů. Velmi dobře se s ním pracuje i bez složitého pročítání manuálů. Pomocí grafického návrháře v něm nadesignujete váš formulář a připojíte datasource. Tlačítko Spustit report nejprve provede převod jrxml souboru do java a následně kompilaci na jasper soubor a poté výsledek zobrazí v zabudovaném prohlížeči.
Podpora JasperReports ve Spring Frameworku
Spring Framework obsahuje přímou podporu JasperReports.
Následuje kód z aplikačního kontextu spring aplikace, který slouží pro integraci JasperReports do Spring Frameworku:
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/my-report.pdf">myPdfController</prop>
</props>
</property>
</bean>
<bean id="myPdfController" class="cz.vavru.sample.jasperreports.controller.MyFormController" />
...
<!-- View resolvers -->
<bean id="pdfViewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basename" value="views" />
<property name="order" value="1" />
</bean>
<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass">
<value>org.springframework.web.servlet.view.JstlView</value>
</property>
<property name="prefix">
<value>/WEB-INF/jsp/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
<property name="order" value="2" />
</bean>
Díky tomu, že Spring Framework umožňuje řetězení view
resolverů stačí přidat beanu pdfViewResolver
do
aplikačního kontextu. Velmi důležité je nastavit této beaně vlastnost
order
na hodnotu 1. Jinak by mohlo dojít
k tomu, že Spring Framework při „hledání“ správného view
pro daný požadavek nejdříve zavolá jspViewResolver
, který,
jak víme, vrací view vždy, takže k zavolání
pdfViewResolver
u by vůbec nedošlo. Cituji přímo a
referenční příručky:
As a result of this, putting an InternalResourceViewResolver in the chain in a place other than the last, will result in the chain not being fully inspected, since the InternalResourceViewResolver will always return a view!
Zpět k našemu příkladu. Na classpath je potřeba umístit soubor
views.properties
(jak jsme specifikovali v beaně
pdfViewResolver
). Jeho obsah může vypadat následovně:
myPdfForm.class=org.springframework.web.servlet.view.jasperreports.JasperReportsPdfView
myPdfForm.url=/WEB-INF/reports/my-form.jasper
Ze zápisu vyplívá, že pokud kontroler vrátí view s názvem
myPdfForm
, bude požadavek obsloužen souborem ,
který společně s modelem zpracuje JasperReportsPdfView.
Model musí obsahovat implementaci JRDataSource.
Spring Framework umožňuje i kompilaci jrxml souborů. Pak musíte mít v aplikaci compiler.
A ještě poznámečka – existuje i opačná cesta – převod jrxml na jasper soubor.
A nakonec ukázka controlleru:
package cz.vavru.sample.jasperreports.controller;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sf.jasperreports.engine.data.JRMapArrayDataSource;
import org.springframework.web.bind.RequestUtils;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
public class MyFormController implements Controller {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
throws Exception {
Map model = new HashMap();
Collection col = new java.util.Vector();
Map map = new HashMap();
map.put("name", "Výrobek 1");
map.put("price", new Double(100));
col.add(map);
map = new HashMap();
map.put("name", "Výrobek 2");
map.put("price", new Double(200));
col.add(map);
JRDataSource jrds = new JRMapCollectionDataSource(col);
model.put("JRDataSource", jrds);
model .put("date", new java.util.Date());
return new ModelAndView("payoutForm", model);
}
}
V metodě handleRequest
je vidět model, který
obsahuje položku s klíčem JRDataSource a parametr
date.
Model musí obsahovat položku s klíčem JRDataSource. Pokud nechcete posílat reportu žádná data použijte JREmptyDataSource":http://jasperreports.sourceforge.net/…aSource.html.
JasperReports bez použití Spring Frameworku
Ukázka použití JR z klasického Servletu:
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JasperExportManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.data.JRMapCollectionDataSource;
public class MyFormServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
try {
Map params = new HashMap();
params.put("date", new java.util.Date());
Collection col = new Vector();
Map map = new HashMap();
map.put("name", "Výrobek 1");
map.put("price", new Double(100));
col.add(map);
map = new HashMap();
map.put("name", "Výrobek 2");
map.put("price", new Double(200));
col.add(map);
JRDataSource jrds = new JRMapCollectionDataSource(col);
JasperPrint jp = JasperFillManager.fillReport(getServletContext().getRealPath("/")
+ "/WEB-INF/reports/my-form.jasper", params, jrds);
byte[] bytes = JasperExportManager.exportReportToPdf(jp);
ServletOutputStream outstream = response.getOutputStream();
response.setContentType("application/pdf");
response.setContentLength(bytes.length);
response.setHeader("Content-disposition", "inline; filename=\"MyForm.pdf\"");
outstream.write(bytes);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
Závěr
V tomto článku jsem chtěl pomoci těm z vás, kteří začínají s dynamickým vytvářením PDF v Javě.
Odkazy
Článek patří do kategorie: Spring Framework
5 Komentářů Přidat komentář
1. Marek | 14. June 2007 v 14.42
Vďaka za článok. Mám otázku.
Je možné integrovať Jasper reporty aj s klasickými JSP stránkami? Teda niečo ako uvedený príklad so servletom…
2. Vlasta | 14. June 2007 v 19.22
[2] V jakém smyslu integraci myslíte? Myslíte generování PDF v rámci JSP stránky? Servlet obsluhuje požadavek jako celek – a v případě příkladu v článku vrátí PDF dokumentu (a nastaví správnou hlavičku). U JSP stránky je jasné, že typ dokumentu je HTML. JSP stránka je po deploynutí na server „převedena“ to Java třídy (signatura public class myJSPFile_jsp extends HttpJspBase {..}, která v obsluhující metodě „_jspService“ mimo jiné nastaví „response.setContentType(“text/html; charset=utf-8");"
Podle mého to tedy nejde…ale co vím určitě – i kdyby to šlo není to vůbec „best practise“.
V čem vám nevyhovují servlety?
3. Ondřej Hradský | 19. January 2008 v 22.15
Dynamické PDF? Koho by to nezaujalo (prosím, žádná ironie)! I já jsem se jal vyzkoušet tento mocný nástroj.
Mohu však mít dotazy laika (pro které tento blog samozřejmě není určen :))?
Vytvořil jsem si iReport a nasázel tam statický text, ale i dynamické položky (cena, suma, název zboží,…).
Vytvořil jsem i v programu Eclipse soubor *.java. Ted mi mimochodem dává na výstup chybu:
Exception in thread „main“ java.lang.Error: Unresolved compilation problems:
JRBeanCollectionDataSource cannot be resolved to a type
Jak propojit java soubor a jasper report, aby tahalo data z java souboru? ZKoušel jsem a hledal v iReportu v záložce „Data“ > „Spojení / Datové zdroje“. Tam jsem přidal nový a dal ho jako Java Bean Collection. Ale nikde to po mě nechce java soubor…
Mohl by prosím někdo pomoci ztracenému človíčkovi? Opravdu jsem se snažil sám, až do vyčerpání sil.
4. Vlasta | 19. January 2008 v 23.25
[3] Nepřipojil jste zdrojový kód Vašeho java souboru. Bez něj je těžké radit.
5. Ondřej Hradský | 19. January 2008 v 23.49
Postupoval jsem podle http://www.java.cz/detail.do?…
package projekt;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import java.io.IOException;
import java.util.Collection;
import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.data.JRBeanArrayDataSource;
import net.sf.jasperreports.engine.data.JRMapCollectionDataSource;
public class Projekt {
private String zbozi;
private int cena;
public Projekt() {
}
public Projekt(String zbozi, int cena) {
this.zbozi = zbozi;
this.cena = cena;
}
public int getCena() {
return cena;
}
public void setCena(int cena) {
this.cena = cena;
}
public String getZbozi() {
return zbozi;
}
public void setZbozi(String zbozi) {
this.zbozi = zbozi;
}
public Projekt[] toArray() {
Projekt[] result = new Object[2];
result[0] = cena;
result[1] = zbozi;
return result;
}
public static void main(String[] args) throws JRException {
// Potřebujeme Mapu s parametry sestavy
Map params = new HashMap();
params.put(„firma“, „Nekupto“);
List tabulka = new ArrayList();
tabulka.add( new Projekt(„Pračka“, 5000) );
tabulka.add( new Projekt(„Chladnička“, 6000) );
tabulka.add( new Projekt(„Žehlička“, 780) );
tabulka.add( new Projekt(„Sporák“, 12000) );
JasperReport jr = JasperCompileManager.compileReport(
„example1.jrxml“);
JRBeanCollectionDataSource datovy_zdroj = new JRBeanCollectionDataSource(tabulka); // Vložíme datový zdroj
JasperPrint jp = JasperFillManager.fillReport(jr,
params, // Vložíme parametry sestavy
datovy_zdroj);
JasperPrintManager.printReport(jp, true);
}
}
Přidat komentář
Povolené HTML značky:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>
Odkazovat na tento článek | Přihlásit se k odběru těchto komentářů přes RSS Feed