Ué, catch não captura a Exception?

EDITADO

Sinistro o bagulho pessoal. Tô fazendo uma classe pra facilitar serialização. Tem uma classe abstrata Serializer e duas implementações, uma delas é XMLSerializer.

O problema é o seguinte: eu serializei um objeto para XML, depois abri o arquivo e mudei o nome da classe, depois tentei desserializar. O objetivo era testar se a classe ia fazer o comportamento certo, que seria retornar null e guardar a exception lançada. Mas isso não acontece. Nessa implementação que usa java.beans.XMLDecoder, ao chamar o readObject() desta classe é lançada uma ClassNotFoundException.

Até aí tudo bem, mas o método não tem a exceção na assinatura (se faz parte), e o catch simplesmente não funciona!!!

Arguém majuda faizfavôr!!!

Classe probemárdiga XMLSerializer:

package common.serialize;

import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class XMLSerializer<T> extends Serializer<T> {
	
	public XMLSerializer(String fileName) {
		super(fileName); 
	}
	
	public XMLSerializer(File file) {
		super(file); 
	}
	
	@Override
	protected void serialize(T obj, OutputStream os) {
		XMLEncoder xe = new XMLEncoder(os);
		xe.writeObject(obj);
		xe.close();		
	}
	
	@SuppressWarnings("unchecked")
	@Override
	protected T deserialize(InputStream is) 
		throws IOException, ClassNotFoundException {
		T result = null;
		XMLDecoder xd = new XMLDecoder(is);
		// DÁ PAU AQUI!!!!!!! GERA ClassNotFoundException
		// NEM ADIANTA TRATAR, DIZ QUE NUNCA LANÇA, MAS LANÇA!!!
		result = (T) xd.readObject();
		xd.close();			
		return result; 
	}	
	
}

Essa classe aqui é a classe abstrata Serializer, não sei se ela teria algo errado:

package common.serialize;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

// This class uses Generics for avoiding unsafe casts on the read() calls. 

public abstract class Serializer<T> {
	
	private File file;
	private Exception lastError;
	
	public Serializer(String fileName) { 
		this(new File(fileName)); 
	}
	
	public Serializer(File file) { 
		this.file = file; 
	}
	
	public boolean succeeded() {
		return lastError == null;
	}
	
	public Exception lastError() {
		return lastError;
	}
	
	public String lastErrorMessage() {
		return succeeded()? null : lastError.getMessage();
	}
	
	// lastError will always be null whenever the (de)serialization 
	// executes successfully, and always non-null otherwise.
	
	// This method should be synchronized?
		
	public boolean write(T obj) {		
    	try {
    		FileOutputStream fos = new FileOutputStream(file);
			serialize(obj, fos);
			fos.close();
			lastError = null;
			return true;
		} catch (IOException e) {
			lastError = e;
			return false; 
		}
	}
	
	public T read() {
		T result = null;
		try {
			FileInputStream fis = new FileInputStream(file);
			result = (T) deserialize(fis);
			fis.close();
			lastError = null;
		// APELEI PARA THROWABLE E NADA!
		} catch (Throwable  e) {
			lastError = new Exception(e);
		}
		return result;
	}
	
	protected abstract void serialize(T obj, OutputStream os) 
		throws IOException;
	
	protected abstract T deserialize(InputStream is)
		throws IOException, ClassNotFoundException;
	
}

Essa é a classe de teste:

package avancado.serializacao;

import common.Console;
import common.FileUtil;
import common.OptionParser;
import common.serialize.DefaultSerializer;
import common.serialize.Serializer;
import common.serialize.XMLSerializer;

public class Serializando {	
	
	private static final String ARQUIVO = "Serializando.txt";

	public static void main(String[] args) {		
		Pessoa p;
		OptionParser op = new OptionParser(args, "rx");	
		
		Serializer<Pessoa> serializer = op.optionGiven('x')?
			new XMLSerializer<Pessoa>(ARQUIVO):
			new DefaultSerializer<Pessoa>(ARQUIVO);

		if (op.optionGiven('w')) {
			p = new Pessoa("Renato", 22, 58.0f);
			Console.puts(serializer.write(p)? 
				"Escrevi pessoa: " + p :				
			    "Erro ao escrever: " + serializer.lastErrorMessage());			
		}	
		if (op.optionGiven('r')) { 
			p = serializer.read();
			// read() == null não quer dizer erro!
			Console.putss(serializer.succeeded()?
				"Li pessoa: " + p :
				"Erro ao ler: " + serializer.lastErrorMessage());
		}		
		Console.puts(FileUtil.read(ARQUIVO));
	}
	
}

Essa é a saída da classe de teste. Vejam que o XML aparece às vezes no meio das exceptions, isso parece código em multi-thread:

java.lang.ClassNotFoundException: avancado.serializacao.Pesdssdoa
Continuing ...
java.lang.NullPointerException: target should not be null
Continuing ...
java.lang.NullPointerException: target should not be null
Continuing ...
java.lang.NullPointerException: target should not be null
Continuing ...
java.lang.NullPointerException: target should not be null
Continuing ...
java.lang.NullPointerException: target should not be null
Continuing ...
java.lang.NullPointerException: target should not be null
Continuing ...
java.lang.NullPointerException: target should not be null
Continuing ...
Li pessoa: null

<?xml version="1.0" encoding="UTF-8"?> 
<java version="1.5.0_06" class="java.beans.XMLDecoder"> 
 <object class="avancado.serializacao.Pesdssdoa"> 
  <void property="idade"> 
   <int>22</int> 
  </void> 
  <void property="nome"> 
   <string>Renato</string> 
  </void> 
  <void property="peso"> 
   <float>58.0</float> 
  </void> 
 </object> 
</java> 

Helpem me plis!

sem o printStackTrace fica sombrio …

Pois é, como vou printar o stack trace se nem catcheia!!! :smiley:

Não lembro como mas tinha conseguido ver, a exceção emerge no XMLDecoder.readObject() e origina-se das suas profundezas, é tudo lá dentro. Só que esse método não lança exceção segundo o código, por isso não consigo tratar pois o compilador não deixa, mas quando roda esse método lança sim!!!

Será que não é um Error não?

Meu amigo, tenta colocar throw new Exception (blablablabla) por exemplo…
Acho que deve resolver seu problema… :stuck_out_tongue:

A uns tempos eu tive um problema desses, com essa mesma ClassNotFoundException, e o que acontecia era que a exceção estava sendo lançada de outra Thread, porque o classloader era chamado assim… sei lá, não me pergunte por que…

Dei uma procurada na internet e só o que encontrei foi isso aqui:
http://dev.eclipse.org/mhonarc/lists/gef-dev/msg00469.html

A sugestão (segue abaixo) não parece fazer muito sentido, mas pelo jeito funcionou:

You can add the following expression: Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
before using the nofouned class

try{
// make a bomb
} catch (Throwable t){
t.printStackTrace();
}

Esse catch é pegador…

VELO

Não, ClassNotFoundException é exatamente uma Exception (checked).

Onde?

[quote=velo] try{ // make a bomb } catch (Throwable t){ t.printStackTrace(); }

Esse catch é pegador…

VELO[/quote]

Dá uma olhada no meu post, eu coloquei isso e mesmo assim nada… :frowning:

[quote=ciczan]A uns tempos eu tive um problema desses, com essa mesma ClassNotFoundException, e o que acontecia era que a exceção estava sendo lançada de outra Thread, porque o classloader era chamado assim… sei lá, não me pergunte por que…

Dei uma procurada na internet e só o que encontrei foi isso aqui:
http://dev.eclipse.org/mhonarc/lists/gef-dev/msg00469.html

A sugestão (segue abaixo) não parece fazer muito sentido, mas pelo jeito funcionou:

You can add the following expression: Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
before using the nofouned class[/quote]

Pô cara, minha cabeça é meio travada com esses lances de classloader. Mas li oi link, o que seria “classe não encontrada”? Seria na chamada XMLDecoder.readObject()? Assim que puder vou testar isso…

Isso parece até bug, onde já se viu, catch que não catcheia o catcheável :x :x :x

Pessoal, eu andei testando e a única conclusão que consegui chegar é que isso é um bug, se é que podemos chamar assim.

Eu cheguei a testar a dica do Ciczan antes da chamada XMLDecoder.readObject(), mas não rolou.

O que eu acho que acontece é que exception nenhuma é lançada, apenas uma mensagem é exibida na tela com o seu nome.

A solução gambiarra que achei foi manter System.err como null enquanto o método problemático roda. Não pude debugar a java.beans.XMLDecoder também, parece que algum parser SAX surge das profundezas e usa o System.err…

Alterei o método deserialize da XMLSerializer assim:

@SuppressWarnings("unchecked")
@Override
protected T deserialize(InputStream is) 
	throws IOException, ClassNotFoundException {
	
	T result = null;
	XMLDecoder xd = new XMLDecoder(is);
		
	// This ensures System.err being null while XMLDecoder.readObject()
	// method executes, and having the old value after this, 
	// through a "backup".
		
	// XMLDecoder.readObject() doesn't throws an exception to indicate 
	// class loading error, as ObjectInputStream.readObject() does. 
	// It uses the System.err instead.
		 
	// Setting System.err as null lead to a NullPointerException that 
	// makes the Serializer.read() method having the expected behavior.
		
	PrintStream backup = System.err;
	System.setErr(null);
		
	// I hope System.err is called just for class loading errors
	// and nothing more. I coundn't debug this class :(
		
	try {
		result = (T) xd.readObject();
	} finally {
		// Restoring the System.err backup...
		System.setErr(backup);
		xd.close();
	}					
	return result;		
}	

O que acham?? É meio gambiarra, o mais certo acho que era não usar System.err e passar a exception prea frente (pra mim)…

E ae Renato3110!!

Cara, uma pergunta. O q tu queres fazer neste caso de exceção?

Na verdade você precisa usar um java.beans.ExceptionListener para pegar os erros durante o parse. O ExceptionListener default só imprime aquelas mensagens no System.err, mas vc pode implementar o seu:

XMLDecoder xd = new XMLDecoder(is, null, new ExceptionListener() {
            public void exceptionThrown(Exception e) {
                System.out.println("Deu erro  - " + e.getMessage());
            }
        });

Vê se isso te ajuda!

Falou

Pô Rodrigo, valeu pela dica. Muito esquisito isso ai, não acha? Vou testar depois, valeuz…

Kra, c tem eclipse aí?

VELO

Quem será que teve essa grande idéia hein?

[quote=velo]Kra, c tem eclipse aí?

VELO[/quote]

É o Eclipse que tô usando…

Quem será que teve essa grande idéia hein?[/quote]

Também não achei lá muito legal não, pelo menos se esse argumento fosse obrigatório… Se não fosse o fórum eu nem ia ficar sabendo, nem tive como testar ainda…

Aliás, por falar nisso queria saber a opinião de vocês porque eu também nessas classes procuro fugir do esquema convencional de exceções.

Acho muito absurdo obrigar a tratar a exceção em cada chamada de read() ou write(). Também não acho legal uma runtime exception, simplesmente porque em caso de erro não há tratamento e o programa termina!

O cenário que imagino é que em alguns casos não será importante tratar o erro, mas em outros sim. Eu pensei numa forma que não obriga nem uma coisa, nem outra, deixando que a situação determine. Na prática seria o mesmo efeito de uma RuntimeException, só que silenciosa caso não seja tratada.

É o seguinte, o write(T) retorna um booleano que indica sucesso ou falha na serialização. Se você se interessar em tratar o erro você pode, em caso de falha, obter a exceção gerada.

No read() o retorno null não significa erro, você pode usar succeeded() ou failed() para verificar erro e depois obter a exceção gerada, ou mesmo apenas a sua mesnagem que é mais prático.

Ficaria assim:

Pessoa pessoa = new Pessoa();
Serializer<Pessoa> serializer = new DefaultSerializer<Pessoa>("objeto.txt");

if (!serializer.write(pessoa))
    serializer.lastError().printStackTrace();

pessoa = serializer.read();
if (serializer.falied())
    serializer.lastError().printStackTrace();

Feio? Bonito?

Pera, você acha ruim tratar exceção mas não acha ruim tratar um boolean? hehe é a mesma coisa cara.

Pessoa pessoa = new Pessoa(); Serializer<Pessoa> serializer = new DefaultSerializer<Pessoa>("objeto"); try { serializer.write(pessoa); pessoa = serializer.read(); } catch( Exception e ) { e.printStackTrace(); }

Nesse caso não acho a mesma coisa porque você pode ignorar o boolean se quiser, mas não ecxeções, mesmo as de runtime, que me parece nem seriam indicadas nesse caso.

Hum, não consigo enxergar uma situação onde ignorar a falha em um processamento é ok, ou seja, onde não seria necessário tratar uma exceção ou um boolean.

Mas se há, tens razão no que diz.

Realmente em uma aplicação real é meio estranho…

Seria mais pra testes, protótipos ou qualquer situação que você queira ser mais pragmático…

Acho que vou fazer uma versão que lança exceção…

É par aisso que existem Checked Exceptions. Uma Checked Exception é uma cláusula no contrato do método que diz que as coisas podem dar errado e que você deve pensar neste caso sme xingar o método, ele te avisou.

Uma Unchecked Exception é uma quebra de contrato, aí sim você pode xingar o método.