Dynamické PDF pomocí JasperReports, iReport a Spring Frameworku

12. Červen 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í JRResultSetDa­taSource a také s Hibernatem – například JRHibernateIte­rateDataSource. Pokud chcete naplnit datasource ve Vašem java kódu můžete použít třeba JRMapCollecti­onDataSource.

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 a PAGE_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.

Okno iReportu s otevřeným modálním oknem Expression editoru pro field price

Okno iReportu s otevřeným modálním oknem Expression editoru pro variable averige_price

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í pdfViewResolve­ru by vůbec nedošlo. Cituji přímo a referenční příručky:

As a result of this, putting an InternalResou­rceViewResolver in the chain in a place other than the last, will result in the chain not being fully inspected, since the InternalResou­rceViewResolver 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 JasperReportsPdfVi­ew. 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 JREmptyDataSou­rce":http://jas­perreports.sou­rceforge.net/…a­Source.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();
                }
        }
}

Výsledek našeho příkladu v prohlížeči

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. Červen 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. Červen 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.set­ContentType(“tex­t/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. Leden 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:
    JRBeanCollecti­onDataSource 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. Leden 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. Leden 2008 v 23.49

    Postupoval jsem podle http://www.ja­va.cz/detail.do­?…


    package projekt;

    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Map;
    import java.util.Array­List;
    import java.io.IOExcep­tion;
    import java.util.Collec­tion;

    import net.sf.jasperre­ports.engine.*;
    import net.sf.jasperre­ports.engine.da­ta.JRBeanArray­DataSource;
    import net.sf.jasperre­ports.engine.da­ta.JRMapCollec­tionDataSource;

    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(„Chlad­nička“, 6000) );
    tabulka.add( new Projekt(„Žehlička“, 780) );
    tabulka.add( new Projekt(„Sporák“, 12000) );

    JasperReport jr = JasperCompile­Manager.compi­leReport(
    „example1.jrxml“);
    JRBeanCollecti­onDataSource datovy_zdroj = new JRBeanCollecti­onDataSource(ta­bulka); // Vložíme datový zdroj
    JasperPrint jp = JasperFillMana­ger.fillRepor­t(jr,
    params, // Vložíme parametry sestavy
    datovy_zdroj);

    JasperPrintMa­nager.printRe­port(jp, true);
    }

    }

Přidat komentář

Povinné

Povinné, skryté

Security Image Povinné
Opište text z obrázku

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


Kalendář

Červenec 2016
P Ú S Č P S N
« Led    
 123
45678910
11121314151617
18192021222324
25262728293031

Poslední články

Locations of visitors to this page