Gerar PDF através de JSF + Jasper

Pessoal,

Estou com um problema ao tentar gerar pdf com JSF, eu já procurei nos tópicos, mas mesmo assim não consegui resolver,

O que acontece é o seguinte, antes eu trabalhava com JSP & Servlet e eu conseguia gerar os pdf’s através do Jasper tranquilamente.

Agora eu peguei a mesma função que eu usava e adaptei para JSF, porém quando tem gerar o PDF quando uso o JasperFillManager.fillReport

Gera um nullPointer.

Alguém já passou por isso?

Tem alguma sugestão melhor de como gerar pdf com JSF e Jasper?

Desde já obrigado .

Segue o código

[code]
public String executarRelatorio()
throws ParseException {

    FacesContext context = FacesContext.getCurrentInstance();
    ServletContext sc = (ServletContext) context.getExternalContext().getContext();
    String nomeArquivo = sc.getRealPath("/") + "relatorios/" + "teste.pdf";

    File file = new File(nomeArquivo);
    if (file.isFile()) {
        file.delete();
    }

    Connection connection = null;
    try {
        connection = ConexaoBD.getConnection();

        Map<String, String> parametros = new HashMap<String, String>();

        String jasper = sc.getRealPath("/relatorios/teste.jasper");
        FacesUtils.msgInfo(jasper);
        
        JasperPrint jp = JasperFillManager.fillReport(
                jasper,
                parametros,
                connection);
        JasperExportManager.exportReportToPdfFile(jp, nomeArquivo); 
    } catch (Exception e) {
        e.printStackTrace();
        FacesUtils.msgErro("Other" + e.getMessage());
    } finally {
        ConexaoBD.closeConnection(connection, null, null);
        return "mostrarServicos";
    }
}[/code]

[quote=davidavpereira]Pessoal,

Estou com um problema ao tentar gerar pdf com JSF, eu já procurei nos tópicos, mas mesmo assim não consegui resolver,

O que acontece é o seguinte, antes eu trabalhava com JSP & Servlet e eu conseguia gerar os pdf’s através do Jasper tranquilamente.

Agora quando eu vou gerar o PDF, o navegador fica parado, não carrega o PDF, já procurei em tudo quanto é canto, já modifiquei meu código várias vezes,
e não conseguia achar a solução.

Alguém já passou por isso?

Tem alguma sugestão melhor de como gerar pdf com JSF e Jasper?

Desde já obrigado .

Segue o código

[code]
public void executarRelatorio(ActionEvent event) {

    FacesContext context = FacesContext.getCurrentInstance();
    HttpServletResponse response = (HttpServletResponse) context.getExternalContext().getResponse();

    //pega o caminho do arquivo .jasper da aplicação
    InputStream reportStream = context.getExternalContext().getResourceAsStream("/relatorios/teste.jasper");

    //envia a resposta com o MIME Type PDF
    response.setContentType("application/pdf");

    /*força a abertura de download
    response.setHeader("Content-disposition",
    "attachment;filename=relatorio.pdf");
     */

    Connection connection = null;

    try {
        connection = ConexaoBD.getConnection();
        ServletOutputStream servletOutputStream = response.getOutputStream();

        Map<String, String> parametros = new HashMap<String, String>();

        //envia para o navegador o PDF gerado
        JasperRunManager.runReportToPdfStream(reportStream,
                servletOutputStream, parametros, connection);

        servletOutputStream.flush();
        servletOutputStream.close();

    } catch (JRException e) {
        e.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        //evita erro do JSF após completar
        //a geração do relatório
        //avisando o FacesContext que a resposta está completa
        context.responseComplete();
        ConexaoBD.closeConnection(connection, null, null);

    }

}
[/code][/quote]

Bom acho que nunca postei no guj

Mas enfim vamo lá:

Tenho esse método que pega um jasperPrint e joga num array de bytes e joga esse array no ServletOutputStream

private void generateReportPdf(JasperPrint jp) throws Exception {
	byte[] bytes = JasperExportManager.exportReportToPdf(jp);
	HttpServletResponse res = FacesContextUtil.getInstance().getResponse();

	res.setContentType(APP_PDF);
	res.setContentLength(bytes.length);
		
	ServletOutputStream os = res.getOutputStream();
        os.write(bytes, 0, bytes.length);  
        os.flush();  
        os.close();
        
        os = null;
        res = null;
        bytes = null;
}

Com esse método eu gero o DataSource e mando para impressão, chamando o método anterior /.

protected void generateReport(Map<String, String> parameters) {
		
		GenericDataSource<GenericReportQuery<B>, B> ds = new GenericDataSource<GenericReportQuery<B>, B>();
		
		if ( this.getBean() != null && this.getBean().getId() != null ) {
			this.afterOperation();
			ds.setQuery(new GenericReportQuery<B>(this.clazz, this.session));
			ds.setFilter(this.getBean());
			ds.doConsultByCriteria();
		} else {
			List <B> f = new ArrayList<B>();
			f.add(this.newBean());
			ds.setBeans(f);
			f = null;
		}
		
		GenericReportDataSource<GenericDataSource<GenericReportQuery<B>, B>, B> rs = 
			new GenericReportDataSource<GenericDataSource<GenericReportQuery<B>, B>, B>(ds);
		
		
		try {
			JasperPrint jp = JasperFillManager.fillReport(FacesContextUtil.getInstance().getContext().getRealPath(this.getPathReport()), parameters, rs);
			
			if ( this.getTypeReport().equals(EXT_PDF) )
				this.generateReportPdf(jp);
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		parameters.clear();
		rs = null;
		
		FacesContextUtil.getInstance().getFacesContext().responseComplete();

	}

Este método é public e é utilizado para chama o responsável por gerar o relatório, ou seja o método anterior /, ele fica em um managed bean.

public void generateReport() {

		Map<String, String> parameters = new HashMap<String, String>();

		ServletContext sc = FacesContextUtil.getInstance().getContext();
		
		parameters.put("FichaSistema", sc.getRealPath("WEB-INF/classes/br/com/S177/report/FichaSistema.jasper"));
		parameters.put("FichaFuncionario", sc.getRealPath("WEB-INF/classes/br/com/S177/report/FichaFuncionario.jasper"));
		parameters.put("ImageFolder", sc.getRealPath("WEB-INF/classes/br/com/S177/report/images"));
		
		this.generateReport(parameters);

	}

Para visualizar o relatório eu criei uma view, responsável por mostrar o mesmo.

<?xml version="1.0" encoding="ISO-8859-1"?>
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      >
      
	<html>
		<head>
			<script type="text/javascript" src="../js/jquery.js"></script>
		</head>
		<body onload="jQuery('#generate_button').trigger('click')">
			<h:form rendered="#{applicationController.logged}" id="generate_form" prependId="false">
				<h:commandButton id="generate_button" action="#{applicationController.controller.generateReport}" value="x" style="display:none;" />
			</h:form>
		</body>
	</html>
      	
</ui:composition>

Aqui tive que fazer uma pequena gambiarra, pelo seguinte em JSF nativamente, não existe como fazer uma página carregar um action no onload dela, ATÉ ONDE EU SEI, eu sei que deve exister algum projeto open source por ai que se proponha a fazer isso para o JSF, só que eu estava sem saco para isso, então não me venha “A porque não utilizo XYZ componente”.

bom o eu fiz é o seguinte, coloquei um form ai, como você pode ver, com um botão escondido, com jQuery eu emulo um onclick nele ao final do load da página, o action que eu chamo no botão como você pode ver é o de gerar o relátorio.

Bom é isso
se ficar confuso posta de novo que eu tento explicar
Vlw []´s

Amigo,

Estou fazendo a minha classe para gerar pdf seguindo a sua linha de raciocinio,

porém na sua classe tem alguns métodos que você chama e não sei como você os fez,

você poderia postá-los ? por favor.

Obrigado.

Basicamente faça isso:

HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();   
    ServletOutputStream servletOutputStream = response.getOutputStream();

    byte[] bytes = JasperRunManager.runReportToPdf(jasperReport, parameters, ds); //aqui vc substitui pelo seu gerador de relatorios, ao seu modo

        response.setContentType("application/pdf");
        response.setHeader("Content-disposition", "filename=\"Pedido.pdf\""); //nome que vc quer dar ao arquivo
        response.setContentLength(bytes.length);

        //sem essas linhas abaixo não funciona, não roda,  da pau hehehe
        servletOutputStream.write(bytes, 0, bytes.length);
        servletOutputStream.flush();
        servletOutputStream.close();
        FacesContext.getCurrentInstance().renderResponse();
        FacesContext.getCurrentInstance().responseComplete();

Foi difícil encontrar isso pelo google, juntei um monte de links que encontrei pra chegar nessa solução.

Abraços.

1 curtida

Você poderia dizer quais métodos você gostaria de saber?

Vlw

Rafek

 if ( this.getTypeReport().equals(EXT_PDF) )  
                 this.generateReportPdf(jp);  

Eu fiz os métodos que você me passou mas mesmo assim ficou em branco,

Assim vou te explicar certinho o que estou tentando faze,

Eu tenho os relatórios prontos em jasper, eles precisam receber uma conexao porque eu executo a consulta por eles.

O que quero fazer, é uma maneira do usuário poder gerar esses relatórios pelo JSF.

Obrigado.

[quote=davidavpereira]Rafek

 if ( this.getTypeReport().equals(EXT_PDF) )  
                 this.generateReportPdf(jp);  

Eu fiz os métodos que você me passou mas mesmo assim ficou em branco,

Assim vou te explicar certinho o que estou tentando faze,

Eu tenho os relatórios prontos em jasper, eles precisam receber uma conexao porque eu executo a consulta por eles.

O que quero fazer, é uma maneira do usuário poder gerar esses relatórios pelo JSF.

Obrigado.[/quote]
Já tentou a minha sugestão?
Aqui funciona!

Tchello

Estou tentando a sua sugestão,

Mas não estou conseguindo…

Não sei o que acontece…

[code]
public void pdf(ActionEvent event) throws Exception {

    Connection con = null;

    try {
        con = ConexaoBD.getConnection();
        HttpServletResponse response = (HttpServletResponse) 
                FacesContext.getCurrentInstance().getExternalContext().getResponse();

        ServletOutputStream servletOutputStream = response.getOutputStream();

        Map parameters = new HashMap();
        ServletContext sc = (ServletContext)
                FacesContext.getCurrentInstance().getExternalContext().getContext();
        String jasper = sc.getRealPath("/relatorios/obras_funcionarios.jasper");

        byte[] bytes = JasperRunManager.runReportToPdf(jasper, parameters, con);
        //aqui vc substitui pelo seu gerador de relatorios, ao seu modo

        response.setContentType("application/pdf");
        response.setHeader("Content-disposition", "filename=\"teste.pdf\""); //nome que vc quer dar ao arquivo
        response.setContentLength(bytes.length);

//sem essas linhas abaixo não funciona, não roda da pau hehehe
servletOutputStream.write(bytes, 0, bytes.length);
servletOutputStream.flush();
servletOutputStream.close();
FacesContext.getCurrentInstance().renderResponse();
FacesContext.getCurrentInstance().responseComplete();
} catch (Exception e) {
e.printStackTrace();
} finally {
ConexaoBD.closeConnection(con, null, null);
}
}[/code]

Eu modifiquei e estou testando…

e puts cara não vai…

estou com todos relatórios prontos… e a aplicação também …, só falta gerar os pdf…

tem alguma dica pra mim?

Obrigado

Mas que erro ta dando agora?
NPE ainda?

Sua conexão com o BD ta sendo realmente recuperada?
Printa ai o StackTrace da sua Exception.

está dando erro no getOutPutStream() :shock:

Putsssss

What is happen?

Quando eu usava Servlet era bico… rsrs

org.apache.jasper.JasperException: java.lang.IllegalStateException: getOutputStream() has already been called for this response
org.apache.jasper.servlet.JspServletWrapper.handleJspException(JspServletWrapper.java:522)
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:410)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:342)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:267)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
com.sun.faces.context.ExternalContextImpl.dispatch(ExternalContextImpl.java:408)
com.sun.faces.application.ViewHandlerImpl.executePageToBuildView(ViewHandlerImpl.java:442)
com.sun.faces.application.ViewHandlerImpl.renderView(ViewHandlerImpl.java:115)
com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:106)
com.sun.faces.lifecycle.LifecycleImpl.phase(LifecycleImpl.java:251)
com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:144)
javax.faces.webapp.FacesServlet.service(FacesServlet.java:245)

root cause

java.lang.IllegalStateException: getOutputStream() has already been called for this response
org.apache.catalina.connector.Response.getWriter(Response.java:610)
org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:198)
com.sun.faces.application.ViewHandlerResponseWrapper.flushContentToWrappedResponse(ViewHandlerResponseWrapper.java:130)
com.sun.faces.taglib.jsf_core.ViewTag.doStartTag(ViewTag.java:173)
org.apache.jsp.mostrarServicos_jsp._jspx_meth_f_005fview_005f0(mostrarServicos_jsp.java:148)
org.apache.jsp.mostrarServicos_jsp._jspService(mostrarServicos_jsp.java:122)
org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:374)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:342)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:267)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
com.sun.faces.context.ExternalContextImpl.dispatch(ExternalContextImpl.java:408)
com.sun.faces.application.ViewHandlerImpl.executePageToBuildView(ViewHandlerImpl.java:442)
com.sun.faces.application.ViewHandlerImpl.renderView(ViewHandlerImpl.java:115)
com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:106)
com.sun.faces.lifecycle.LifecycleImpl.phase(LifecycleImpl.java:251)
com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:144)
javax.faces.webapp.FacesServlet.service(FacesServlet.java:245)

Help Me.

Obrigado.

Essa é a parte da Aplicação que chama o relatorio…

<h:commandLink
                            value="Gerar relatório PDF" actionListener="#{servicoController.pdf}" />

Alguém pode me mostrar um exemplo completo? Se nao for pedir muito.

Obrigado.

[quote=davidavpereira]

Tchello

O último código que você mandou aí funcionou corretamente,

cara eu criei outra aplicação e rodou belezinha…

VALEW