[RESOLVIDO] Erro Relatório Linux

Bom dia,

Não sei se estou postando o tópico no lugar certo, mas…

Estou com um problema na recuperação de uma relatório.

Estou com um ambiente Java EE (jsf, ejb, jpa, etc) + glassfish 3.1.1 + postgresql 9.0

Foi necessário a criação de um relatório e no momento da sua geração era preciso que fosse salvo no banco de dados para que seja possível rever aquele mesmo relatório gerado…

Salvei então os bytes do relatório.

Testei a funcionalidade de geração do relatório, download do mesmo, a persistência da entidade no banco e a posterior consulta e recriação do relatório através dos bytes salvos no banco.
Tudo funcionou perfeitamente na minha Applicação Local. Ela roda em um Windows 7.

No entando quando a funcionalidade foi para o servidor de produção (Linux) ocorreu um problema.
Consigo gerar o relatório, fazer o download do mesmo, persistir a entidade no banco e consultar as entidades salvas, mas quando faço o download do relatório gerado pela recuperação do mesmo no banco, o download acontece, mas quando abro o arquivo ele está corrompido.
E o que mais estou batendo a cabeça, quando eu recupero e faço o download do relatório gerado e salvo pelo servidor de produção pela minha aplicação local (que aponta para o mesmo banco de produção) o download acontece e o arquivo abre normalmente. Ou seja não é problema no ato de salvar os bytes do relatório…

O relatório gerado no servidor não consegue ser recuperado depois pelo próprio servidor… E o meu servidor consegue recuperar o gerado pelo servidor de produção…
Sendo que os servidores são da mesma versão, a aplicação implantada no servidor de produção é enconding UTF-8.

Alguem já passou por isso?

Desde já agradeço.

Olá Jonny, Apesar de java ter compatibilidade entre os diversos sistemas, fazer uma sistema no windows para ele rodar em servidor linux tem que ser um projeto bem pensando, por causa de pequenos detalhes.

exemplo:

Windows usa c:\Arquivos de Programas\algum arquivo da aplicação
linux usa: /usr/local/algum arquivo da aplicação

entre outras coisa.

Para que voce não perca tempo, consiga um linux desktop que rode uma distribuiçao grafica de seu servidor linux, installe sua mesma IDE.(se sua IDE for visual studio ou outra da MS desconsidere a minha resposta )
transfira seu projeto e rode no linux que vai lhe dá o erro. Acredito que este é a forma mais rapida de voce resolver esse problema.

[quote=linsspinho]Olá Jonny, Apesar de java ter compatibilidade entre os diversos sistemas, fazer uma sistema no windows para ele rodar em servidor linux tem que ser um projeto bem pensando, por causa de pequenos detalhes.

exemplo:

Windows usa c:\Arquivos de Programas\algum arquivo da aplicação
linux usa: /usr/local/algum arquivo da aplicação

entre outras coisa.

Para que voce não perca tempo, consiga um linux desktop que rode uma distribuiçao grafica de seu servidor linux, installe sua mesma IDE.(se sua IDE for visual studio ou outra da MS desconsidere a minha resposta )
transfira seu projeto e rode no linux que vai lhe dá o erro. Acredito que este é a forma mais rapida de voce resolver esse problema.

[/quote]

Fiz como você disse, porém quando vou recuperar a entidade salva que contem os bytes do relatorio é feito o download normalmente e no servidor não dá nenhum erro…

Temn cara de ser problema de charset. Mostre o código de como voce esta salvando este relatorio.

Aqui gero o relatório pela primeira vez:

[code]public static byte[] gerarRelatorioJasper(List dataSource, Map parametros, String caminho, String nomeArquivo) throws Exception{
byte[] arquivo;
try {

		String layout = FacesContext.getCurrentInstance().getExternalContext().getRealPath(caminho);
		JRBeanCollectionDataSource ds = new JRBeanCollectionDataSource(dataSource);
		JasperPrint impressao = JasperFillManager.fillReport(layout, parametros, ds);
		arquivo = JasperExportManager.exportReportToPdf(impressao);
		gerarDownloadArquivo(arquivo, "application/pdf", nomeArquivo);
		
	} catch (Exception e) {
		e.printStackTrace();
		throw new Exception(MensagemErro.ERRO);
		
	}
	return arquivo;
}[/code]
public static void gerarDownloadArquivo(byte[] bytes, String contentType, String nomeArquivo) {
		FacesContext.getCurrentInstance().responseComplete();
		ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
		HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();
		
		try {
			response.setContentType(contentType);
			response.setHeader("Content-Disposition", "attachment; filename="+nomeArquivo);
			response.setContentLength(bytes.length);
			response.getOutputStream().write(bytes);

		} catch (IOException ex) {
			ex.printStackTrace();
		} 
	
	}

Aqui salvo a entidade certidão.

byte[] arquivo = FacesUtil.gerarRelatorioJasper(dataSource, parametros, layout, "relatorio.pdf");
Certidao certidao = new Certidao(Calendar.getInstance(), chaveAutenticacao, arquivo, 
usuario.getCodigoCadastroUnico(), 
daoFactory.getTipoCertidaoDAO().recuperarPorId(Constantes.TIPO_CERTIDAO_NADA_CONSTA_MULTAS));
daoFactory.getCertidaoDAO().merge(certidao);

Aqui faço o download dos bytes do relatório que foi salvo.

public void gerarRelatorio(){
		if(certidao!=null && certidao.getId()!=null){
			FacesUtil.gerarDownloadArquivo(certidao.getDados(), "application/pdf", "relatorio.pdf");
		}
	}

Um detalhe, o arquivo corrompido tem o dobro do tamanho do arquivo normal.
O relatório que estou testando tem 71kb e o que vem corrompido tem 142kb.

Ola jonny, provavelmente o arquivo esta sendo duplicado. e voce deve está estressado e não consegue ver o error, que provavelmente está no metodo gerarDownloadArquivo().

                JasperPrint impressao = JasperFillManager.fillReport(layout, parametros, ds);  //ENTÃO AQUI O ARQUIVO TA SAINDO COM 71kb?

esse metodo merge()? ele está juntando ou update? Array or Sort?

Acho que vc deve dá um print no byte.length() a partir da linha de codigo acima até o merge(Certidão).

[quote=linsspinho]Ola jonny, provavelmente o arquivo esta sendo duplicado. e voce deve está estressado e não consegue ver o error, que provavelmente está no metodo gerarDownloadArquivo().

                JasperPrint impressao = JasperFillManager.fillReport(layout, parametros, ds);  //ENTÃO AQUI O ARQUIVO TA SAINDO COM 71kb?

esse metodo merge()? ele está juntando ou update?[/quote]

Esse trecho

JasperPrint impressao = JasperFillManager.fillReport(layout, parametros, ds); arquivo = JasperExportManager.exportReportToPdf(impressao);

É chamado apenas na primeira vez que crio o relatório quando ainda possuo os parametros e o datasource.
Esse trecho saí sim com 71kb, mas na minha máquinha independente do momento o arquivo sai sempre com 71kb.
Esse método merge é para salvar a entidade certidao no BD.
A entidade certidao tem esse array de bytes que é o retorno de JasperExportManager.exportReportToPdf(impressao);

Acredito que tem-se que identificar onde ele está duplicando ou aumentando de tamanho.

Acho que vc deve dá print no byte.length() a partir da linha de codigo acima até o merge(Certidão).

Ainda continuo desconfiando de charset: qual o tipo da coluna onde você está salvando o byte array do PDF? BLOB? Você está fazendo alguma conversão para String deste seu byte array?

Verifique também qual o charset definido para glassfish e qual esta definido para o postgresql.

No banco o tipo de dados é bytea
E tanto no glassfish quanto no postgresql estão UTF-8.
Não, do banco eu ja mando direto os bytes, não converto para String.

oyama, se esse fosse o problema, seria um problema generalizado,digo, aconteceria no windows tb, ou não?

Por isso eu digo que é necessário identificar primeiramente o metodo onde ocorre.

Jonny isso so pode ser feito se voce estiver realmente usando o seu projeto dentro da IDE no linux. Ou nunca se encontrará o problema. A não ser que encontremos alguém que ja tenha tido o problema.

[quote=linsspinho]Por isso eu digo que é necessário identificar primeiramente o metodo onde ocorre.

Jonny isso so pode ser feito se voce estiver realmente usando o seu projeto dentro da IDE no linux. Ou nunca se encontrará o problema. A não ser que encontremos alguém que ja tenha tido o problema.[/quote]

Eu instalei uma VM aqui e fico como no Windows, mas no log do servidor mostrado na IDE não dá nenhum erro… =\

Não. Já vi vários sistemas em Java desenvolvidos em ambiente Windows e migrado para ambiente Linux darem problemas, principalmente quando usam conversão de byte array para String, pois o charset dos ambientes é diferente. O mesmo ocorre quando não se configura o charset da JVM e do banco de dados de maneira compatível.

Bem, olhando o código eu não achei nenhum lugar que é feito nenhuma conversão explicitamente. Poste o mapeamento JPA da classe Certidao e do CertidaoDAO. Nunca trabalhei com PostgreSQL, e não conheço o type bytea. Verifique se ele não faz conversões para caracter internamente.

So lembrando que ele está provavelmente duplicando os dados.

Como so agora estou começando a estudar java Web, não posso ajudar muito, Espero que alguem mais, com experiencia em java web te ajude, alem de oyama.

[code]package br.gov.pi.detran.ejb.modelo;

import java.io.Serializable;
import java.util.Calendar;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Transient;

import br.gov.pi.detran.ejb.cadastrounico.modelo.Pessoa;

@Entity
@SequenceGenerator(name=“Certidao”, sequenceName=“certidao_id_seq”)
public class Certidao implements Serializable{

private static final long serialVersionUID = 355911567316243680L;
@Id
@GeneratedValue(generator="Certidao")
private Long id;
private Calendar dataHoraGeracao;
private String chaveAutenticacao;
private byte[] dados;
private Long codigoCadastroUnico;
@ManyToOne(cascade={CascadeType.MERGE, CascadeType.PERSIST}, fetch=FetchType.LAZY)
private TipoCertidao tipoCertidao;
@Transient
private Pessoa pessoa;

public Certidao() {
	
}

public Certidao(Calendar dataHoraGeracao, String chaveAutenticacao,
		byte[] dados, Long codigoCadastroUnico, TipoCertidao tipoCertidao) {
	this.dataHoraGeracao = dataHoraGeracao;
	this.chaveAutenticacao = chaveAutenticacao;
	this.dados = dados;
	this.codigoCadastroUnico = codigoCadastroUnico;
	this.tipoCertidao = tipoCertidao;
}

//getters and setters

@Override
public int hashCode() {
	final int prime = 31;
	int result = 1;
	result = prime * result + ((id == null) ? 0 : id.hashCode());
	return result;
}

@Override
public boolean equals(Object obj) {
	if (this == obj)
		return true;
	if (obj == null)
		return false;
	if (getClass() != obj.getClass())
		return false;
	Certidao other = (Certidao) obj;
	if (id == null) {
		if (other.id != null)
			return false;
	} else if (!id.equals(other.id))
		return false;
	return true;
}

}
[/code]

Faz conversões internas não. No banco é bytea mesmo, bytea é equivalente ao byte[].

Tentei agora a pouco usando o fileDownload do primefaces
funcionou na minha máquina, mas continuo sem sucesso no linux.

@Lob @Type(type="org.hibernate.type.PrimitiveByteArrayBlobType") private byte[] dados;

Aparentemente eu consegui resolver… Testei no meu servidor linux e deve ser colocada a nova versão hoje no servidor de produção. Só aí vou ter certeza :slight_smile:
Coloquei essa anotações no array de bytes de certidão.
Valeus :slight_smile: