Home > Artigos > Frameworks, APIs, Instalação e Configuração >
Prevayler: Serialização e Snapshot
Publicado por Tutoriais Admin em 17/08/2009 - 1.879 visualizações
Ja tentou alterar a classe Pessoa que construímos? Não? Tente! Você verá que pode alterar a classe sem que o Prevayler perca as suas informações. É claro que se você remover o atributo nome o Prevayler não conseguirá recuperar este atributo e, portanto ele não será carregado. Mas não irá ocorrer erros.
Como explicar isso? Simples, os objetos armazenados não são instâncias de Pessoa . Isso mesmo, o arquivo de log com o qual estamos trabalhando não grava objetos Pessoa , mas grava objetos de AdicionaPessoa . Uma vez que você implementa um objeto da classe Command do Prevayler o seu objeto já é serializável, visto que a classe Command herda de Serializable . Prova disso? Tente alterar a nossa classe AdicionaPessoa adicionando o seguinte atributo:
private long teste;Agora execute novamente o programa. Você verá o erro:
Reading PrevalenceBase / 000000000000000000001.commandLog...Esta simples mudança na classe fará com que o Java não consiga buscar os dados serializados para o retorno do Log. Isso é sinônimo de problemas? Não, fique tranquilo.
java.io.InvalidClassException: AdicionaPessoa; local class incompatible: stream classdesc serialVersionUID =
1273287439490966696, local class serialVersionUID = - 4265660114243414788
Some commands might have been lost. Looking for the next file...
Primeiramente permita-me explicar o que é Serialização . Quando você tem um objeto em memória, ele pode não estar armazenado sequencialmente na memória. Por esse motivo existe a serialização, que nada mais é do que colocar os valores que o objeto está utilizando juntamente com suas propriedades de uma forma que fique sequencial. Tornando um objeto Serializable , estamos atribuindo essa qualidade a ele, e dando privilégios para que o mesmo possa ser gravado em disco ou enviado por rede (Serial - > Bit a Bit).
Mas se a serialização é tão sensível assim, como podemos implementar um grande sistema com isso? Não se preocupe, a serialização também tem seus truques. O trunfo é um atributo chamado serialVersionUID, isso mesmo, aquele que aparece na mensagem de erro acima. É com esse atributo que o java mantém um versionamento das classes. É com ele que o mesmo detecta se uma classe foi alterada e por isso não é mais compatível. Sendo assim, o que podemos fazer é passar o controle desse atributo para o desenvolvedor.
Adicionando o seguinte atributo nas classes AdicionaPessoa e Pessoa :
private static final long serialVersionUID = 1L;De agora em diante é o desenvolvedor que decide quando uma classe não é mais compatível com a versão anterior.
Feito isso, você pode apagar os dados salvos pelo prevayler, executar a aplicação, salvar algumas informações e tentar modificar ambas as classes. Não irá mais ocorrer erros de incompatibilidade de classes.
Regras para a recuperação de dados serializados
As regras são simples, e muito intuitivas:
- Se o java tentar recuperar um objeto, ele irá verificar quais os atributos ainda existem, e estes serão carregados. O resto é jogado fora.
- A ordem dos atributos não interessa, portanto você pode alterar a ordem que não haverá nenhum problema. A serialização salva e busca com o nome do atributo. Por isso mesmo que o nome não pode ser alterado.
- Sempre é uma boa prática manter, em todas as classes serializáveis o atributo serialVersionUID .
Log do Prevayler
Já descobrimos que o log do Prevayler armazena simplesmente os objetos Command , tendo isso ele pode re-executar todos os objetos encontrados para voltar os dados a situação atual.
Se quizer fazer um teste, coloque um System.out.println no método public Serializable execute (PrevalentSystem system) da classe AdicionaPessoa . Quando você executar a aplicação poderá ver os objetos sendo carregados.
Snapshot: Um Print Screen nos dados
Um bom banco de dados não fica armazenando Logs a vida inteira, fica? Por esse motivo que o Prevayler implementou um Snapshot dos dados. O Snapshot busca os objetos em memória e salva em disco, este será o nosso sistema de backup.
Se você notar bem, a nossa classe Main, já está utilizando o SnapshotPrevayler no seguinte bloco:
SnapshotPrevayler prevayler = null;
try {
prevayler = new SnapshotPrevayler(new ListaPessoas());
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Isso quer dizer que já estamos prontos para gravar o nosso backup. Para fazer isso é só invocar o método prevayler.takeSnapshot (); Este método varre os objetos associados a instância prevayler e salva-os em disco, no nosso caso, salvará todos os integrantes da ListaPessoas incluindo o ArrayList de Pessoa e seus objetos internos.
Para recuperar os dados, o Prevayler busca o último snaptshot retirado dos dados e a partir daí começa a executar os logs de dados. Não tem erro. Todos os dados voltam para a aplicação.
Para fazer um melhor uso do Snapshot pode-se implementar uma Thread para fazer os backups:
public class SnapshotTimer extends Thread {
SnapshotPrevayler prevayler;
public SnapshotTimer(SnapshotPrevayler prevayler) {
this.prevayler = prevayler;
}
public void run() {
super.run();
try {
while (true) {
Thread.sleep(1000 * 5); // 5 segundos de espera.
prevayler.takeSnapshot();
System.out.println("Snapshot disparado as " + new java.util.Date() + "...");
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
E com algumas mudanças já temos a nossa classe Main de teste:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import org.prevayler.implementation.SnapshotPrevayler;
public class Main {
public static void main(String[] args){
System.out.println("Iniciando Prevayler...");
SnapshotPrevayler prevayler = null;
SnapshotTimer snapshot = null;
try {
prevayler = new SnapshotPrevayler(new ListaPessoas());
snapshot = new SnapshotTimer(prevayler);
snapshot.start();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.print("Digite o nome da pessoa ou FIM para sair: ");
String nome = lerTeclado();
while (!nome.equals("FIM")) {
try {
prevayler.executeCommand(new AdicionaPessoa(nome));
} catch (Exception e1) {
e1.printStackTrace();
}
System.out.println("Pessoa armazenada.");
System.out.print("Digite o nome da pessoa ou FIM para sair: ");
nome = lerTeclado();
}
snapshot.interrupt();
System.out.println("Imprimindo pessoas persistidas.");
ListaPessoas lista = ((ListaPessoas)prevayler.system());
for (int i=0; i<lista.size(); i++) {
System.out.println(lista.get(i).getNome());
}
}
public static String lerTeclado() {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
try {
return reader.readLine();
} catch (IOException e1) {
return "FIM";
}
}
}
É isso aí pessoal. Espero que tenham gostado! Aguardem os próximos artigos sobre o Prevayler.
- Prevayler - Promessa ou Ilusão?
- Prevayler - controlando os arquivos salvos no disco
- Prevayler && snapshot
- Prevayler 2.0 - Migrando a persistência
- Prevayler Parte 2: Serialização e Snapshot.
- Prevayler
- Arquivo Binário
- prevayler - XmlSnapshotManager
- Tamanho dos links do Fórum na página inicial...


