Dando continuidade na postagem: JasperReports 4.0.1: Utilizando JavaBean datasource, agora vamos usar o .jasper gerado no iReport, em um projeto Java EE 6.
Começando...
Para começar, no NetBeans crie uma nova Aplicação Web com o nome de PrimeiroRelatorio (seguindo os passos já vistos em postagens anteriores)...
Feito isso, vamos criar a estrutura que usamos para gerar o .jasper (postagem do link), então em pacotes de código-fonte crie um novo pacote com o nome de primeirorelatorio e dentro dele um pacote model, dentro desse pacote crie a classe Produto (lembrando que tem que ser idêntica à aquela usada no .jar para gerar o relatório), ficando com esta estrutura:
Relembrando a classe Produto:
public class Produto { private int codigo; private String nome; private BigDecimal valor; //gerar getters e setters }
Agora dentro do WEB-INF, crie um novo diretório com o nome de relatorios, dentro dele coloque o arquivo .jasper, ficando assim:
Caso não tenha baixado as bibliotecas do JasperReports, baixe por aqui.
De todas as bibliotecas disponibilizadas no link, para este tipo de relatório iremos usar apenas algumas delas, acabei filtrando e chegando as que realmente precisavam, então adicione ao projeto as seguintes bibliotecas:
Depois disso assim como foi criado o pacote model, precisamos de um pacote controle, para armazenar nossos beans (nesse caso, teremos apenas um bean), então dentro do pacote primeirorelatorio, crie um novo pacote com o nome de controle, ficando assim: primeirorelatorio.controle, agora aqui dentro crie uma classe java com o nome de RelatorioBean.java, ela será assim (abaixo dela eu explico algumas coisas):
package primeirorelatorio.controle; import java.io.File; import java.io.FileInputStream; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.enterprise.context.RequestScoped; import javax.faces.context.ExternalContext; import javax.faces.context.FacesContext; import javax.inject.Named; import javax.servlet.ServletContext; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import net.sf.jasperreports.engine.JRDataSource; import net.sf.jasperreports.engine.JasperRunManager; import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource; import primeirorelatorio.model.Produto; /** * * @author andii */ @Named @RequestScoped public class RelatorioBean { public void gerarRelatorio() { ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext(); ServletContext context = (ServletContext) externalContext.getContext(); String arquivo = context.getRealPath("WEB-INF/relatorios/PrimeiroRelatorio.jasper"); JRDataSource jrds = new JRBeanCollectionDataSource(listarProdutos()); gerarRelatorioWeb(jrds, null, arquivo); } private void gerarRelatorioWeb(JRDataSource jrds, Map<Object, Object> parametros, String arquivo) { ServletOutputStream servletOutputStream = null; FacesContext context = FacesContext.getCurrentInstance(); HttpServletResponse response = (HttpServletResponse) context.getExternalContext().getResponse(); try { servletOutputStream = response.getOutputStream(); JasperRunManager.runReportToPdfStream(new FileInputStream(new File(arquivo)), response.getOutputStream(), parametros, jrds); response.setContentType("application/pdf"); servletOutputStream.flush(); servletOutputStream.close(); context.renderResponse(); context.responseComplete(); } catch (Exception e) { e.printStackTrace(); } } private List<Produto> listarProdutos() { List<Produto> produtos = new ArrayList<Produto>(); Produto p1 = new Produto(); p1.setCodigo(1); p1.setNome("Produto 1"); p1.setValor(new BigDecimal(1.99)); produtos.add(p1); Produto p2 = new Produto(); p2.setCodigo(2); p2.setNome("Produto 2"); p2.setValor(new BigDecimal(3000.50)); produtos.add(p2); Produto p3 = new Produto(); p3.setCodigo(3); p3.setNome("Produto 3"); p3.setValor(new BigDecimal(500.00)); produtos.add(p3); return produtos; } }
Vamos ver o que esses métodos fazem:
public void gerarRelatorio()
Inicialmente este método será chamado pela nossa página JSF, nele nós precisamos pegar o caminho exato do nosso arquivo .jasper, que lá no começo da postagem colocamos dentro de WEB-INF/relatorios, para isso usamos o context.getRealPath.
Quanto ao jrds: por termos utilizado uma classe Java para gerar nosso .jasper, nos precisamos passar para nosso relatório um JRDataSource, que é formado por uma lista de Produto (pois o método listarProdutos(), retorna uma lista de produtos que poderia muito bem estar vindo de um banco de dados).
Ao chamar o gerarRelatorioWeb, estamos passando um parâmetro null, esse corresponderia a uma Map, que serviria para passarmos parâmetros(que vai ficar pra outra postagem) para o nosso relatório...
Agora vamos ver o que acontece no:
gerarRelatorioWeb(JRDataSource jrds, Map<Object, Object> parametros, String arquivo)
Neste método nós dependemos do response para mostrar o nosso relatório no navegador, quanto a essa parte não vou dar muitas explicações, pois sairia do nosso foco que é o JasperReports...
Bom, no final o responsável por gerar o que vai ser o nosso relatório, é essa linha aqui:
JasperRunManager.runReportToPdfStream...
esse método não cria um arquivo .pdf fisicamente no computador, apenas gera ele como se fosse um arquivo temporário, e quem decide se quer salvar o relatório ou não é o usuário, depois disso, o response entra em campo novamente dizendo ao navegador que o que será enviado é do tipo application/pdf, cada navegador reage de uma forma quando se trata de PDF, no caso do Google Chrome, mostra nele mesmo, se não me engano o Firefox, já dá a opção de fazer download do PDF...
e por fim o servletOutputStream, é quem manda o relatório para o navegador!
Depois de entendermos como funciona essa classe, vamos alterar nossa página index.xhtml, para que ela chame o método e mostre o relatório:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"> <h:head> <title>Facelet Title</title> </h:head> <h:body> <h:form> <h:commandButton value="Ver relatorio" actionListener="#{relatorioBean.gerarRelatorio}" onclick="this.form.target='_blank'" /> </h:form> </h:body> </html
Quanto ao onclick do commandButton, eu uso para poder abrir o relatório em outra aba do navegador. Visualmente a página index, fica assim:
Quem estiver usando o Google Chrome para fazer estes testes, terá o seguinte resultado ao clicar no botão:
E por fim... a estrutura completa do projeto é esta:
E por aqui finalizamos a postagem de hoje, logo logo posto como usar este mesmo relatório em um projeto desktop! =)
Estou tentando adaptar seu codigo para mostrar relatorios no meu projeto de TCC, mas quando eu clico no botão Ver relatorio ele abre a mesma página em outra aba e não mostra o relatorio. O que pode ser? Você poderia me ajudar?
ResponderExcluirBoa tarde!!
ExcluirClaudinei, você conseguiu resolver o seu problema?
estou com o mesmo problema aqui, e não sei como resolver.
Claudinei, olha só vendo o codigo fonte para saber o que pode estar acontecendo, não tive nada parecido com isto.
ResponderExcluirOla, tudo bem ?
ResponderExcluirApenas uma dúvida, msm usando um projeto web preciso colocar o .jar no classpath ?
Olá Naldo! Vc quer dizer colocar no classpath do windows? se for isso não precisa não, já é suficiente adicioná-las no projeto apenas.
ResponderExcluirOlá, parabéns pela série de tutoriais, tentei este relatório e tive o seguinte erro
ResponderExcluirServlet.service() for servlet Faces Servlet threw exception...
java.lang.IllegalStateException: PWC3991: getOutputStream() has already been called for this response
Sabe o que pode ser?
Obrigado,
Diego
Oi, vc sabe como nao abrir em uma nova aba caso o relatorio nao seja gerado. Eu quero exibir uma mensagem de erro, mas com target blank, acaba gerando a mensagem na nova aba.
ResponderExcluirOlá Antonio! Para mostrar o relatório na mesma página basta tirar o onclick="this.form.target='_blank'"
ResponderExcluirDiego Moura, quando acontecem erros dessa forma, basta vc olhar o que diz no log do servidor de aplicação, ele geralmente diz exatamente o que está acontecendo
ResponderExcluirmeu deu esse erro:
ResponderExcluirjava.lang.IllegalStateException: getOutputStream() has already been called for this response.
o que pode ser??
Os problemas podem ser diversos, vc tem que ler o que erro aparece no console do seu servidor de aplicação.
ExcluirOlá andii.brunetta
ResponderExcluirEu também estou com o erro "java.lang.IllegalStateException: getOutputStream() has already been called for this response".
Pesquisando na internet parece ser porque o org.apache.catalina.connector.Response.getWriter é executado antes do getOutputStream(). Em páginas JSP, parece ser problema com espaços em branco no código java entre <% %>
Alguma dica sobre a solução desse problema?
Ketellin, qual o erro que aparece no glassfish???
ExcluirPor favor, pode me ajudar?
ResponderExcluirO unico tutorial que consegui fazer foi o seu, deu certinho.
Mas preciso de uma ajuda simples, acredito eu!
Eu monto meu DataSource desta forma: JRDataSource jrds = new JRBeanCollectionDataSource(lista);
Mas acontece que eu quero montar sem "LISTA", ou seja quero enviar:
String nome;
String cpf;
String telefone;
Sem que se repita através de uma lista.
Eu consigo enviar no DataSource somente o meu DTO?
Por exemplo:
JRDataSource jrds = new JRBeanCollectionDataSource(DTOCONTATOS);
Pode me ajudar?
Olá, já pensou em passar esses valores por parametro? veja essa postagem http://javasemcafe.blogspot.com.br/2011/06/jasperreports-401-utilizando-subreports.html talvez ela possa te ajudar.
ExcluirPoxa, show, tinha perdido a esperança da resposta.
ResponderExcluirIrei seguir este tutorial, muito obrigado!
Que bom que te ajudou :)
ExcluirObtive o seguinte erro:
ResponderExcluirThe method runReportToPdfStream(InputStream, OutputStream, Map, JRDataSource) in the type JasperRunManager is not applicable for the arguments (FileInputStream, ServletOutputStream, Map, JRDataSource)
Valeu! Ajudou muito!
ResponderExcluir