VRaptor3 x relatórios - alguem já fez? [RESOLVIDO]

Alguém já iniciou a confecção de relatórios PDF/HTML ou outro tipo com Jasper ou JFree e VRaptor 3?
Deixe uma dica além do que consta na documentação. Vai ajudar os menos experientes como eu :wink:

Ola Cinei!

Basta você devolver um InputStream com seu método, que o vraptor providenciará o resultado como um download.

Caso você queira fazer algo mais rebuscado, para setar alguns valores do header de resposta, receba HttpServletResponse no seu construtor, para que essa seja injetada, e lá você configura o tipo, tamanho e nome do arquivo, para que o download seja feito de maneira mais elegante!

Silveira,
obrigado pelas dicas. Devo iniciar logo o primeiro.
Na maioria das vezes a nova versão possui mais facilidades.
Parabens a todos envolvidos.

Você pode retornar na sua lógica um InputStream, ou um Download:

@Resource
public class RelatorioController {
public Download geraRelatorio() {
      InputStream pdf = //gera o pdf;
      String contentType = "application/pdf";
      String fileName = "relatorio.pdf";
      return new InputStreamDownload(pdf, contentType, fileName);
}
}

Oi Lucas!

Seria legal tambem poder passar como argumento o tamanho do arquivo, assim os browsers conseguem mostrar a estimativa de quanto falta!

Que tal uma issue pro 3.01?

paulo

Por mim tudo bem =)

Como eu quero deixar bem flexivel, peguei o seguinte caminho que ficou meio longo:

Intercept

@Intercepts
@RequestScoped
public class DownloadInterceptor implements Interceptor {


    private ClientOutput clientOutput;
	private HttpServletResponse response;
	private ServletContext sc;

	public DownloadInterceptor(HttpServletResponse response, ServletContext sc, ClientOutput clientOutput) {
        this.response = response;
        this.sc = sc;
        this.clientOutput = clientOutput;
	}
	
	public void intercept(InterceptorStack stack, ResourceMethod method, Object resourceInstance)
	throws InterceptionException {
		clientOutput.setResponse(response);
		clientOutput.setServletContext(sc);

		stack.next(method, resourceInstance);
	}

	public boolean accepts(ResourceMethod method) {
        return true;
    }
}

Componete com HttpServletResponse isolado e que será injetado no Resource

@Component
public class ClientOutput {
	private HttpServletResponse response;
	private ServletContext sc;

	public void setResponse(HttpServletResponse response){
        this.response = response;
    }
    
    public void setServletContext(ServletContext sc){
        this.sc = sc;
    }
    
    public void setNoCache (){
        this.response.setHeader ("Pragma", "public");  
        this.response.setHeader("Cache-control", "must-revalidate");  
    }

    public ServletOutputStream getOutputStream() throws IOException {
            return response.getOutputStream();
    }
    
    public HttpServletResponse getResponse(){
    	return this.response;
    }

    public File getFile(String relatorio) throws IOException {
		File reportFile = new File(this.sc.getRealPath(relatorio));  
        return reportFile;
}

    public String getPath(String caminho) throws IOException {
		return sc.getRealPath("/WEB-INF/relatorios/")+File.separator;
}

    public void setContentType(String type) {
            response.setContentType(type);
    }
}

Método da classe que gera o Relatório quando for usar JRBeanCollectionDataSource

	@SuppressWarnings("unchecked")
	public void relatorioJrds (ClientOutput clientOutput, List dados, String relatorio, HashMap<Object, Object> parameters) {
		this.clientOutput = clientOutput;
		this.clientOutput.setNoCache();
		
		try {  
			JRDataSource jrds = new JRBeanCollectionDataSource(dados);  

			byte[] bytes = null;  
			try {
				bytes = JasperRunManager.runReportToPdf(this.clientOutput.getFile(relatorio).getPath(), parameters, jrds);  
			} catch (Exception e) {                  
				e.printStackTrace();  
				return;  
			}                                
			if (bytes != null && bytes.length > 0) {  
				clientOutput.getResponse().setContentLength(bytes.length);
				this.clientOutput.getOutputStream().write(bytes, 0, bytes.length);  
				this.clientOutput.getOutputStream().flush();  
				this.clientOutput.getOutputStream().close();  
			}  
		} catch (Exception e) {  
			e.printStackTrace();  
		}
	}

Resource

public CompradorController(DaoFactory daoFactory, Result result, ClientOutput clientOutput) {
.
.
.

@SuppressWarnings("unchecked")
public void geraRelatorio(String coluna, String texto) {
	this.compradorRegra.listar(coluna, texto, 1, 99, coluna);
	List dados = this.compradorRegra.getCompradors();
		Relatorios relat = new Relatorios(this.clientOutput, "application/pdf");
		relat.setTituloRelatorio("Comprador");
		relat.setCabecalho(this.daoFactory);
		relat.setNomeRelatorio("Comprador.jasper");
		relat.relatorioJrds(dados);
	
}

Método utilizado acima (da Classe Relaorios):

	@SuppressWarnings("unchecked")
	public void relatorioJrds(List dados) {
			new ReportGenerator().relatorioJrds(this.clientOutput, dados, this.nomeRelatorio, this.parameters);
	}

Complexo demais?
Mas o problema é que vem a mensagem:

O VRaptor pode te ajudar em muitas coisas que você fez:

Você não precisa do DownloadInterceptor, pode apagá-lo, basta receber o Response e o ServletContext no construtor do
ClientOutput:

@Component
public class ClientOutput {
	private final HttpServletResponse response;
	private final ServletContext sc;
        public ClientOutput(HttpServletResponse response, ServletContext context) {
             this.response = response;
             this.sc = context;
        }
       //...
}

Você pode apenas retornar um InputStream com o Pdf gerado, daí não precisa escrever o OutputStream na mão:

	@SuppressWarnings("unchecked")
	public InputStream relatorioJrds (ClientOutput clientOutput, List dados, String relatorio, HashMap<Object, Object> parameters) {
		this.clientOutput = clientOutput;
		this.clientOutput.setNoCache();
		
		try {  
			JRDataSource jrds = new JRBeanCollectionDataSource(dados);  

			byte[] bytes = null;  
			try {
				bytes = JasperRunManager.runReportToPdf(this.clientOutput.getFile(relatorio).getPath(), parameters, jrds);  
			} catch (Exception e) {                  
				e.printStackTrace();  
				return;  
			}                                
			if (bytes != null && bytes.length > 0) {  
				clientOutput.getResponse().setContentLength(bytes.length);
				return new ByteArrayInputStream(bytes);
			}  
		} catch (Exception e) {  
			e.printStackTrace();  
		}
	}

o erro que tá dando é que você não falou pro VRaptor que você já escreveu a resposta da requisição, e então ele tentou
fazer o forward pra página padrão (…/relatorioJrds.jsp). Quando você mexe no output Response você tem que fazer a chamada:
result.use(Results.nothing()); pro vraptor não redirecionar pra página padrão…

e outra coisa: receba o ClientOutput no construtor do controller…

[]'s

Muito bom. Obrigado.
Acredito que agora conseguirei chegar onde preciso.
Valeu …ainda não me acostumei com a possibilidade de injetar quase tudo e com mais facilidades.

Resolvido da forma que perguntei. Estou enviando um objeto já populado
Agradeço a todos que ajudou.
Meu próximo desafio e conseguir utilizar conexão com Jasper via JNDI, se é que é possível.