Por que eu devo ler este artigo:Neste artigo veremos os principais recursos do iReport para criação de relatórios tendo como premissa a independência da fonte de dados.

Desenvolver relatórios é muito comum em aplicações corporativas. Através desse artigo veremos como criar relatórios com JavaBeans, recursos de internacionalização e sub-relatórios.

O tema é útil quando há necessidade de criar relatórios de forma visual e que se integre facilmente a aplicações Java para ambientes desktop e web.

Neste artigo será mostrada de forma objetiva a elaboração e o desenvolvimento de relatórios em Java utilizando as ferramentas open source mais completas disponíveis no mercado.

Apesar de existirem outras ferramentas de relatório open-source como o BIRT, o iReport é uma ferramenta em constante evolução, muito poderosa, flexível e que tem o maior suporte da comunidade de desenvolvedores. O JasperReports suporta scriptlets, tem integração com JFreeChart para renderizar gráficos e com o Barbecue para gerar código de barras. Agora na versão 3.7 houve uma melhora significativa na interface gráfica e na exportação para formatos do padrão Office Open XML.

Não interprete JasperReports e iReport como uma ferramenta única, o iReport é uma ferramenta de designer de relatórios para o JasperReports. Já o JasperReports é uma biblioteca (engine) para geração de relatórios que pode funcionar sem o iReport. Quando geramos os relatórios em nossa aplicação temos que adicionar apenas os arquivos jars do JasperReports ao classpath.

O exemplo que apresentaremos não fará uso de nenhum SGDB (Sistema de Gerenciamento de Banco de Dados) específico para obtenção dos dados que alimentará o relatório. O objetivo do artigo é ensinar a utilizar o iReport para criar relatórios de forma a não depender da fonte de dados. Por isso, serão utilizadas apenas listas com JavaBeans.

Esta decisão foi motivada pelo fato de que atualmente os sistemas das empresas são heterogêneos, podendo ter os dados provenientes de um banco de dados Oracle, de arquivos texto, de vários web services ou até mesmo uma mescla de vários bancos de dados de diferentes fornecedores.

O modelo de classes a ser utilizado no exemplo é bastante simples e clássico, sendo formado por apenas quatro entidades: a entidade Cliente contém os atributos básicos de um cliente; Pedido possui atributos referentes à data em que foi feito o pedido, a situação do pedido e uma lista de itens de pedido representado pela entidade ItemPedido; e Produto, que possui o código, valor e descrição do mesmo (Figura 1).

JavaBean: O JavaBean é uma classe Java simples que contém apenas atributos além dos respectivos métodos de atribuição de valores (setXxxx()) e de leitura (getXxxx()). Por isso são componentes de software altamente reutilizáveis.Não confunda com Entreprise JavaBeans (EJB), os EJBs são componentes específicos da plataforma Java EE, e envolvem outros conceitos que os JavaBeans não possuem.
Diagrama de classes utilizado no exemplo do artigo
Figura 1. Diagrama de classes utilizado no exemplo do artigo.

Na Listagem 1 podemos observar a entidade Cliente codificada em Java. A Listagem 2 mostra a classe Pedido. Note que ela possui uma lista de ItemPedido (entidade implementada na Listagem 3). Além destas, como apresentado na Figura 1, um item de pedido deve estar obrigatoriamente associado a um Produto, cuja implementação é exibida na Listagem 4.

Listagem 1. Código da classe da entidade Cliente – Cliente.java.

package br.com.javamagazine.modelo;
 import java.io.Serializable;
  
  public class Cliente implements Serializable{
    private String nome;
    private String endereco;
    private String uf;
    // Métodos getters e setters omitidos
  }

Listagem 2. Código da classe da entidade Pedido – Pedido.java.

package br.com.javamagazine.modelo;
   
  import java.io.Serializable;
  import java.util.ArrayList;
  import java.util.Calendar;
  import java.util.List;
   
  public class Pedido implements Serializable{  
    private Cliente cliente;
    private List<ItemPedido> listaItensPedido;
    private Calendar data;
    private String situacao;
    // Métodos getters e setters omitidos
  }

Listagem 3. Código da classe da entidade ItemPedido – ItemPedido.java.

package br.com.javamagazine.modelo;
   
  import java.io.Serializable;
   
  public class ItemPedido implements Serializable {
   
    private Integer numeroItem;  
    private Produto produto; 
    private Double quantidade;  
    private Double valorTotalItem;
    // Métodos getters e setters omitidos
  }

Listagem 4. Código da classe da entidade Produto – Produto.java.

package br.com.javamagazine.modelo;
   
  import java.io.Serializable;
   
  public class Produto implements Serializable{ 
    private Long codigo;
    private String descricao; 
    private Double valor;
    // Métodos getters e setters omitidos
  }

Introdução ao iReport

Para que um relatório possa ser gerado é necessário uma fonte de dados (representada pela interface JRDataSource), os parâmetros que podem ser necessários ao relatório e que são passados dentro de um HashMap (veja o quadro “Parâmetros do JasperReports”) e o arquivo .jasper (o que é e como esse arquivo é criado será explicado logo mais).

O iReport é a principal ferramenta para criação visual dos arquivos .jrxml. Nestes arquivos encontramos as definições do relatório em formato XML. O arquivo .jasper é gerado automaticamente após a compilação do .jrxml pelo iReport.

É possível criar o arquivo .jrxml usando apenas um editor de texto como o bloco de notas, porém além da difícil tarefa de entender e escrever o XML no formato exigido pelo JasperReports, a dificuldade de posicionar os elementos de forma correta na página é muito grande.

Ao criar um relatório no iReport temos disponíveis várias seções, conhecidas também por band. Elas são divisões internas do relatório onde podemos inserir os elementos de texto e imagem. No iReport temos basicamente oito seções:

  1. Title: Título do relatório. Pode ser impresso em uma página separada;
  2. Page Header: Cabeçalho de cada página do relatório;
  3. Column Header: Seção exibida em cada página que contém uma seção Detail;
  4. Detail: Nesta seção será impresso todos os registros que o DataSource fornecer ao relatório. No nosso exemplo todos os pedidos da lista de JavaBeans serão mostrados aqui;
  5. Column Footer: Exibido em cada página que contém uma seção Detail;
  6. Page Footer: Rodapé de cada página;
  7. Summary: Seção exibida no fim do relatório. Pode ser impresso em uma página separada;
  8. No Data: Seção exibida quando não há registros;
  9. Background: Nesta seção é possível inserir uma imagem de fundo que será exibida em cada página do relatório.

Na Figura 2 é possível visualizar estas oito seções em uma página de um relatório em branco.

Bandas do relatório
Figura 2. Bandas do relatório.

Parâmetros do JasperReports

O JasperReports possui vários parâmetros pré-definidos que podem ser colocados no objeto Map passado como parâmetro no método fillReport() da classe JasperFillManager. Dessa forma substituímos os valores padrões quando necessitamos usar um valor customizado para a execução do relatório.

Um exemplo de uso desses parâmetros pré-definidos pode ser visto na Listagem 8, mais precisamente no método doProcess(), onde informamos no Map os parâmetros SUBREPORT_DIR, REPORT_RESOURCE_BUNDLE e REPORT_LOCALE. Veja na Tabela Q1 qual a utilidade de cada um deles e de mais alguns parâmetros.

Nome (Chave no Map) Descrição
REPORT_PARAMETERS_MAP Inserir um objeto do tipo Map com os parâmetros utilizados para preencher o relatório.
REPORT_LOCALE Inserir um objeto do tipo java.util.Locale. Este parâmetro é importante para que o JasperReports saiba corretamente qual o idioma utilizar no relatório.
REPORT_RESOURCE_BUNDLE Inserir um objeto do tipo java.util.ResourceBundle contendo mensagens internacionalizadas.
REPORT_TIME_ZONE Inserir um objeto do tipo java.util.TimeZone para formatar datas corretamente conforme o fuso horário.
REPORT_CONNECTION Inserir um objeto do tipo java.sql.Connection caso seu relatório necessite de uma conexão com o banco de dados.
REPORT_FORMAT_FACTORY Inserir um objeto do tipo FormatFactory para ser usado na formatação de campos data e número do relatório. Por exemplo, os números podem ser exibidos com o separador decimal ponto ou o separador vírgula, dependendo da localidade do usuário.
SUBREPORT_DIR Informar neste parâmetro o diretório onde está(ão) armazenado(s) o(s) arquivo(s) jasper do(s) sub-relatório(s).

Tabela Q1. Lista de parâmetros pré-definidos.

O ambiente de programação

Neste artigo a IDE utilizada será o Eclipse Galileo, cujo instalador pode ser encontrado no endereço http://eclipse.org/downloads/ – baixe a versão Eclipse for Java EE Developers. No site http://jasperforge.org/projects/ireport encontra-se o instalador do iReport 3.7.x para Windows. O Tomcat 6.0.26 será o container de JSP/Servlets utilizado para rodar o relatório na web e pode ser obtido em http://tomcat.apache.org/download-60.cgi.

A instalação detalhada dos itens acima não faz parte do escopo deste artigo. Para instalar as ferramentas Eclipse e Tomcat, geralmente basta descompactá-los em um diretório, e o iReport para Windows fornece um instalador bem intuitivo. O leitor pode recorrer ao site do projeto caso ocorra algum problema no momento da instalação.

Vale lembrar que no site da Java Magazine está disponível o código do projeto desenvolvido neste artigo.

Desenhando o relatório

Depois que as classes foram criadas no Eclipse é necessário exportá-las no formato de arquivo jar para que possam ser adicionadas ao classpath do iReport. Para isso, vá ao Eclipse e selecione a opção File | Export, na janela que se abre selecione Java, depois Jar File e clique em Next. Na próxima janela selecione apenas o diretório src e informe no campo Jar File o nome do arquivo jar a ser criado (incluindo o diretório onde ele será gravado; no exemplo informaremos “D:\relatorios\Relatorio.jar”) e clique em Finish.

O relatório desenvolvido terá um relatório principal para mostrar os pedidos e um relatório interno (sub-relatório) para listar os itens que estão em cada pedido. O sub-relatório é construído de forma idêntica ao relatório principal, porém é feito pensando que ele estará dentro de outro, geralmente sem as seções Title, Page header, Page footer e summary.

Agora inicie o iReport, clique em New | File, selecione Report, depois clique em Blank A4 e clique em Open this template. Preencha os campos Report Name com valor “RelatorioPedidos” e Location com o diretório onde deseja gravar o arquivo jrxml, por exemplo “D:\relatorios”. Em seguida clique em Next e depois em Finish.

Feito isso, é necessário configurar o classpath para que o iReport consiga ler as nossas classes e seja possível criar os campos. Então acesse o menu Ferramentas | Opções e na janela que será aberta selecione a aba Classpath (Figura 3). Clique no botão Add Jar para localizá-lo, marque a opção Reloadable e clique em OK.

Tela de configuração do classpath no iReport
Figura 3. Tela de configuração do classpath no iReport.

Com as configurações realizadas no classpath o iReport está apto a ler os atributos das classes de modelo que foram criadas.

Agora precisamos criar os campos (fields) do relatório baseado nos atributos da classe Pedido, o que permitirá utilizá-los em expressões. Eles serão usados no componente TextField para exibir os dados nas páginas dos relatórios. A seguir veremos como esse procedimento é feito.

Clique no botão Report Query (Report Query), na janela que se abrirá vá à aba JavaBean Datasource e insira no campo Class name o nome completo da classe: br.com.javamagazine.modelo.Pedido, conforme a Figura 4. Depois clique no botão Read Attributes.

Adicionando
campos ao relatório com base na classe Pedido
Figura 4. Adicionando campos ao relatório com base na classe Pedido.

Após a leitura dos atributos da classe devemos marcar os atributos que desejamos que se transformem em campos no relatório. Para este exemplo selecione: cliente, data, listaItensPedido e situacao. Clique em Add selected field(s) e depois em OK.

Com isso os campos adicionados ao relatório estão prontos para serem utilizados e ficam disponíveis no lado esquerdo do iReport.

Neste momento vamos começar a desenhar o nosso relatório. Selecione os campos: cliente, data e situação e arraste para dentro da seção Detail, posicionando-os da forma que desejar. Ao fazer esse procedimento o iReport cria automaticamente TextFields com base nesses campos, preenchendo também de forma automática a propriedade expressão dos TextFields.

Para que os dados sejam apresentados de forma correta no relatório, precisamos ajustar a propriedade Expressão de alguns TextFields. Isto é necessário pois o iReport coloca por padrão na expressão o nome do campo. Podemos ter casos onde o campo representa um objeto JavaBean e o que queremos na verdade é mostrar o valor de atributo deste objeto. Neste artigo o campo cliente é um exemplo desse caso.

Siga os procedimentos a seguir para ajustar os campos do nosso relatório:

  1. Dê um duplo clique sobre o TextField do campo cliente e altere para $F{cliente}.getNome() para exibir no relatório o nome do cliente. Devemos utilizar a sintaxe $F{<nome do campo>} quando necessitamos acessar o valor de um campo do relatório;
  2. Agora arraste mais um TextField da paleta de componentes e informe na propriedade Expressão $F{cliente}.getDocumento() para exibir no relatório o documento CPF/CNPJ do cliente;
  3. Altere a expressão do campo data para $F{data}.getTime(), e também a propriedade pattern para dd/MM/yyyy para que a data saia nesse formato. Em seguida, mude na propriedade Classe de Expressão o valor para java.util.Date. Isto fará com que o TextField interprete de forma correta o retorno do método getTime(). Sem essa alteração o relatório não compilará, pois o tipo do objeto que o método irá retornar é incompatível com o tipo que o campo espera;
  4. Na seção Detail adicione mais quatro TextFields que utilizaremos como label. Um será para o campo número pedido, outro para a data do pedido, outro para o nome do cliente e um para o documento do cliente. Feito isso, altere a propriedade Expressão de cada TextField com os valores $R{rep.pedido}, $R{rep.data}, $R{rep.cliente} e $R{rep. documento}, respectivamente. Utilizamos a sintaxe $R{chave} para indicar ao JasperReports que é necessário buscar um valor no resource bundle;
  5. Na seção title adicione mais um componente TextField. Ele será usado para mostrar o título do relatório. Na propriedade Expressão informe o valor $R{rep.titulo};
  6. Na seção Page footer coloque um componente Page X of Y para mostrar a página atual e a quantidade de páginas total. Uma curiosidade desse componente é que ele é composto por duas partes; em ambas encontramos a variável $V{PAGE_NUMBER}. A primeira parte mostra o número da página atual e a outra o total de páginas do relatório. Mas se a variável é a mesma, como são exibidos valores diferentes? A diferença entre as partes é o tempo de avaliação da expressão. A primeira parte é avaliada no momento em que o JasperReports está processando a página, e a segunda é avaliada em tempo de relatório, ou seja, no momento em que todo o relatório já estiver preenchido.

Resource Bundle: É um mecanismo utilizado quando o programa necessita de um recurso, uma String, por exemplo, que seja específico da localidade do usuário. Dessa forma o programa poderá ser utilizado por pessoas que falam diversas línguas e de vários países do mundo.

Além do que foi apresentado, estão disponíveis na paleta de componentes do iReport algumas ferramentas para desenho de linhas, elipses, retângulos e inserção de imagens dentro do relatório. Estas ferramentas podem ser utilizadas para melhorar o layout do relatório.

Adicionando um sub-relatório

Nesta etapa vamos adicionar ao relatório um sub-relatório que irá listar os produtos de cada pedido. Para isso, na paleta de componentes arraste o componente Subreport (Subreport) para a seção Detail. Logo em seguida o assistente para criação do sub-relatório será aberto; selecione uma página A4 e prossiga clicando em próximo aceitando os valores padrão até o passo seis, que requer uma atenção especial. Precisamos preencher os campos dessa etapa conforme a Figura 5. É importante que o sub-relatório fique no mesmo diretório do relatório principal, no nosso exemplo em “D:\relatorios”.

Dados
do sub-relatório
Figura 5. Dados do sub-relatório.

O passo sete também é muito importante para o funcionamento correto do sub-relatório. Nesta tela é definida de que forma os dados serão passados do relatório principal para o sub-relatório; o nosso exemplo utilizará uma expressão de fonte de dados. Então selecione a opção Use a JRDatasource expression e insira no campo o valor new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($F{listaItensPedido}). Esta expressão nos permite utilizar a lista de itens de pedido contido na classe Pedido como uma fonte de dados – neste caso uma fonte de dados de JavaBeans.

Para adicionar os campos referentes ao item de pedido, use a mesma técnica adotada para criar os campos da classe Pedido no relatório principal. Adicione ao sub-relatório os atributos da classe ItensPedido.

O layout final do sub-relatório ficará como ilustrado na Figura 6. É normal que durante o desenho o relatório fique com um aspecto bagunçado, com textos sobrepostos ou cortados. No entanto, em tempo de execução isso não acontecerá, pois iremos alimentar o relatório com valores compatíveis com o tamanho do campo.

Layout do sub-relatório
Figura 6. Layout do sub-relatório.

i18N – Internacionalizando o relatório

Os relatórios são facilmente internacionalizados, ou seja, apresentados de forma específica ao país e a língua do usuário usando o recurso de resource bundle existente na plataforma Java. Para utilizar este recurso temos que colocar em um ou mais arquivos .properties um conjunto de chaves e valores de mensagens que serão utilizadas no relatório. O nome desse arquivo é formado pelo prefixo, idioma, sigla do país e a extensão .properties. Por exemplo, se quiséssemos um arquivo para as mensagens em português do Brasil, teríamos Mensagens_pt_BR.properties.

Para configurar os relatórios (principal e sub-relatório) para ler estes arquivos, abra as propriedades do relatório clicando sobre a margem da página do mesmo com o botão direito e selecione Propriedades. Na janela que se abre, preencha o campo Resource Bundle com o nome do pacote onde se encontrará o arquivo mais o prefixo do nome do arquivo, neste exemplo o valor será br.com.javamagazine.resource.Mensagens.

Agora é necessário informar ao JasperReports um objeto do tipo java.util.ResourceBundle contendo os textos internacionalizados que o JasperReports irá usar para montar o relatório.

O parâmetro pré-definido REPORT_RESOURCE_BUNDLE será colocado no HashMap de parâmetros dessa forma:

parametros.put("REPORT_RESOURCE_BUNDLE", ResourceBundle
    .getBundle("br.com.javamagazine.resource.Mensagens"));

Para o desenvolvimento do relatório, coloque os arquivos .properties no mesmo diretório dos arquivos .jrxml para que o iReport possa lê-los e utilizá-los no relatório. Caso contrário, ele irá reclamar que não encontrou estes arquivos e a compilação falhará.

Para este exemplo crie dois arquivos de internacionalização no Eclipse, acessando o menu File | New > File. Para o primeiro arquivo, que conterá as mensagens em português do Brasil, informe o nome Mensagens_pt_BR.properties; para o segundo, que conterá mensagens em inglês americano, informe o nome Mensagens_en_US.properties. Lembre-se de que letras maiúsculas e minúsculas fazem diferença. As Listagens 5 e 6 mostram trechos dos arquivos criados.

Listagem 5. Fragmento do arquivo Mensagens_pt_BR.properties.

rep.titulo=Relatório de Pedidos Pendentes
  subrep.numitem=Item
  subrep.desc=Descrição
  subrep.valor=Valor
  subrep.qtd=Quantidade
  rep.pagina=Página 
  rep.de= de

Listagem 6. Fragmento do arquivo Mensagens_en_US.properties.

rep.titulo=Report of Pending Orders
  subrep.numitem=Item#
  subrep.desc=Description
  subrep.valor=Value
  subrep.qtd=Quantity
  rep.pagina=Page 
  rep.de= of

Para testar o relatório com outro idioma no iReport, acesse o menu Ferramentas | Opções, vá na aba Compilação e Execução e altere o campo Report Locale para outro de sua preferência. Para efetuar a alteração no programa, coloque no HashMap de parâmetros o parâmetro pré-definido REPORT_LOCALE e um objeto java.util.Locale. A seguir apresentamos um exemplo para criar um objeto Locale representando o idioma inglês americano:

parametros.put("REPORT_LOCALE", new Locale("en", "US"));

Na Figura 7 vemos a versão final do relatório principal após todos os ajustes.

Layout do relatório de pedidos
Figura 7. Layout do relatório de pedidos.

Visualizando o relatório

Para verificar como ficou o layout do relatório clique em Preview – localizado ao lado dos botões Designer e XML. Certifique-se que no menu Preview a opção PDF esteja selecionada para que o relatório seja aberto no leitor de PDF instalado em seu computador.

No momento que estamos desenhando o relatório é possível visualizar apenas os labels dos campos, pois nos lugares dos valores que seriam preenchidos através da lista de objetos Pedido será impresso null. A seguir veremos como alimentar o relatório utilizando o JRBeanCollectionDataSource.

Executando o relatório em uma aplicação desktop

Já temos o relatório desenhado e os dois arquivos .jasper criados no diretório D:\relatorios (um do relatório principal e outro do sub-relatório), dessa forma podemos utilizá-los dentro de uma aplicação Java. Para isso, crie no Eclipse um novo projeto Java, acessando o menu File | New > Java Project e informando o nome “Relatorio”.

Antes de escrevermos o código é necessário incluir no classpath do projeto os jars do JasperReports e o jar que contém as classes de modelo. Para resolver esta pendência, acesse as propriedades do projeto teclando Alt+Enter com o projeto selecionado, clique na opção Java build path e vá na aba Libraries. Clique no botão Add External JARs, selecione em <Diretório_Instalação_Ireport>/ireport/modules todos os arquivos jar e clique em Ok. Depois adicione o jar com as classes de modelo, que no nosso exemplo foi criado em D:\relatorios\Relatorio.jar.

Descreveremos abaixo os seis passos necessários para rodar o relatório criado em nossa aplicação:

1. Obter um InputStream do arquivo .jasper do relatório principal. Caso o caminho informado esteja errado, o InputStream vai ficar com valor null e na chamada do método fillReport() um NullPointerException será lançado;

2. Obter uma lista de objetos Pedido. Neste exemplo usaremos uma classe chamada PedidoDAO que cria um ArrayList de Pedidos fictícios. As classes DAO (Data Access Object) representam um padrão de projeto que visa separar as regras de negócios do acesso a dados. Neste padrão, as classes possuem a responsabilidade de tratar a entrada e a saída dos dados do sistema;

3. Criar um objeto JRBeanCollectionDataSource. Este será o datasource do relatório;

4. Instanciar um HashMap e preenchê-lo com os parâmetros necessários para nossa aplicação (veja o quadro “Parâmetros do JasperReports”);

5. Utilizar o método fillReport() da classe JasperFillManager para preencher o relatório. Esse método devolve um objeto do tipo JasperPrint que pode ser usado, entre outras coisas, para visualização no JasperViewer (utilitário nativo do JasperReports para apresentação de relatórios) ou para exportar o relatório em PDF, como veremos mais adiante;

6. No último passo vamos exibir o relatório na tela utilizando o JasperViewer.

A Listagem 7 mostra em código os seis passos citados acima.

Listagem 7. Código para gerar o relatório e exibir em modo desktop.

// Primeiro passo: Obter InputStream do arquivo jasper
  String caminhoRelJasper = "D:\\relatorios\\RelatorioPedidos.jasper";
      
  InputStream relJasper = EmissorRelatorios.class
    .getResourceAsStream(caminhoRelJasper);
   
  // Segundo passo: Obter lista com objetos Pedido. Para este exemplo foi criado um PedidoDAO
  List<Pedido> dadosRelatorio = emissorRelatorios.obterDadosRelatorio();
          
  // Terceiro passo: Criar o data source
  JRBeanCollectionDataSource ds = new JRBeanCollectionDataSource(dadosRelatorio);
   
  // Quarto passo: criar HashMap com os parâmetros dos relatórios
  Map<String, Object> parametros = new HashMap<String, Object>();
   
  // Caminho do diretório onde está o jasper do sub-relatório
  parametros.put("SUBREPORT_DIR", "D:\\relatorios\\");
  parametros.put("REPORT_RESOURCE_BUNDLE", ResourceBundle
    .getBundle("br.com.javamagazine.resource.Mensagens"));
  parametros.put("REPORT_LOCALE", Locale.US);
   
  JasperPrint impressao = null;
  try {
    // Quinto passo: preencher o relatório
    impressao = JasperFillManager.fillReport(caminhoRelJasper, parametros, ds);
    // Sexto passo: mostrar o relatório
    JasperViewer viewer = new JasperViewer(impressao, true);
    viewer.setVisible(true);
  }
  catch (JRException e) {
    System.out.println(e.getMessage());
  }

A Figura 8 mostra o resultado do nosso trabalho: o relatório devidamente preenchido com os dados da lista de objetos de pedido.

Visualização do relatório com JasperViewer
Figura 8. Visualização do relatório com JasperViewer.

Exibindo o relatório na web

Até esta etapa o relatório foi executado somente em modo desktop. Agora vamos executar esse mesmo relatório em uma aplicação web. Para isso, crie no Eclipse um projeto web acessando o menu File | New > Dynamic Web Project e informando o nome “RelatorioWeb”. Como este é um novo projeto, é necessário fazer os mesmos ajustes realizados no classpath do projeto desktop.

Feito isso, vamos criar um servlet pressionando Ctrl+N no Eclipse. Na janela que se abrir, selecione a opção Servlet e clique em Next. Informe no campo Java package br.com.javamagazine.servlet e no campo Class name EmissorRelatorioServlet.

Na Listagem 8 podemos observar o código do servlet que usaremos para gerar o relatório em PDF, e enviá-lo – através do HttpServletResponse – para o navegador.

A principal diferença do relatório web para o desktop é que ao invés de usar a classe JasperViewer, foi utilizada a classe JasperExportManager para escrever os bytes no OutputStream do HttpServletResponse.

Listagem 8. Servlet para gerar o relatório na web.

package br.com.javamagazine.servlet;
   
  // Imports da classes foram omitidos
   
  public class EmissorRelatorioServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
   
    public EmissorRelatorioServlet() {
      super();
    }
   
    protected void doProcess(HttpServletRequest request,
        HttpServletResponse response) throws ServletException, IOException {
      PedidoDAO pedidoDAO = new PedidoDAO();
   
      ServletOutputStream outputStream = response.getOutputStream();
      
      // Obtem o caminho para o diretório jasper
      String realPath = request.getSession().getServletContext().getRealPath(
        "/WEB-INF/jasper")
        + System.getProperty("file.separator");
      // Caminho do .jasper do relatorio
      String caminhoRelJasper = realPath + "RelatorioPedidos.jasper";
      // Lista com java beans    
      List<Pedido> dadosRelatorio = pedidoDAO.obterDadosRelatorio();
      
      // Stream com o .jasper
      InputStream relJasper = EmissorRelatorios.class
        .getResourceAsStream(caminhoRelJasper);
   
      // Cria o DataSource
      JRBeanCollectionDataSource ds = new JRBeanCollectionDataSource(
        dadosRelatorio);
   
      // Parâmetros dos relatórios
      Map<String, Object> parametros = new HashMap<String, Object>();
      // Caminho do diretório onde está o jasper do sub-relatório
      parametros.put("SUBREPORT_DIR", realPath);
      parametros.put("REPORT_RESOURCE_BUNDLE", ResourceBundle
        .getBundle("br.com.javamagazine.resource.Mensagens"));
      parametros.put("REPORT_LOCALE", request.getLocale());
   
      JasperPrint impressao = null;
      try {
        // Ajusta o response
        response.setHeader("Content-Disposition", "inline; filename=\""
          + "RelatorioPedidos.pdf" + "\"");
        response.setHeader("Cache-Control", "no-cache");
        response.setContentType("application/pdf");
        
        // Gera o relatório
        impressao = JasperFillManager
          .fillReport(caminhoRelJasper, parametros, ds);
        // Escreve no OutputStream do response
        JasperExportManager.exportReportToPdfStream(impressao, outputStream);
        outputStream.close();
      }
      catch (JRException e) {
        System.out.println(e.getMessage());
      }
    }
   
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      doProcess(request, response);
    }
   
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      doProcess(request, response);
    }
  }

Após a criação da servlet, localize no projeto RelatorioWeb o diretório WEB-INF e crie um diretório chamado jasper para armazenar os dois arquivos .jasper que criamos. Copie os arquivos RelatorioPedidos.jasper e RelatorioPedidos_ItensPedido.jasper para este diretório, desta forma a servlet conseguirá encontrá-los.

Para testarmos a servlet e visualizar o relatório, é necessário colocar o Tomcat no ar. Para isso, localize a aba Servers na parte inferior do Eclipse e clique no botão Start the Server (Start the Server). Em seguida, abra um navegador web, e acesse a URL http://localhost/RelatorioWeb/EmissorRelatorioServlet para chamar a servlet que gera o PDF. O resultado desta execução pode ser visto na Figura 9.

Visualização do relatório pela web
Figura 9. Visualização do relatório pela web.

Dependendo das configurações do navegador, o PDF poderá ser apresentado dentro dele. Se isso não ocorrer será solicitado que o usuário baixe o arquivo. Se o leitor desejar forçar o download do arquivo, substitua no parâmetro Content-Disposition do método setHeader() – da classe HttpServletResponse – a diretiva inline para attachment. Como não podemos garantir que o computador do usuário tenha um leitor de PDF instalado para que o browser apenas exiba o relatório, o efeito inverso não é possível.

Conclusão

Como vimos, o iReport é uma ferramenta que oferece ótima produtividade e flexibilidade para o desenvolvimento de relatórios bastante sofisticados. Ele aceita diversas fontes de dados para geração de relatórios que podem ser executados tanto em ambientes desktop quanto web.

O iReport é muito versátil, por isso não se limita apenas a criação de relatórios tradicionais como o do exemplo. Muitas empresas adotaram esta ferramenta para criação e emissão de segunda via de faturas, etiquetas, recibos e termos de contratos para que os próprios clientes possam obter estes documentos em PDF nos sites de autoatendimento.

Neste artigo exploramos apenas os dois recursos mais utilizados, o sub-relatório e a internacionalização. Fica como sugestão ao leitor aprender a criar relatórios de tabulação cruzada e relatórios com gráficos.

Links