quarta-feira, 28 de março de 2012

Entendendo o serialVersionUID - JAVA

O serialVersionUID é uma dúvida constante entre muitos desenvolvedores. Afinal, quando e para que exatamente usá-lo? Devo gerar um número aleatório bem grande, ou um número qualquer? Essas perguntas são comuns, e ao desenvolvedor experiente é necessário conhecer a fundo esse detalhe do processo de serialização do Java.
Quando um objeto é serializado no Java, essa sequência de bytes, além de conter seus atributos de instância não transientes, carrega consigo um número que indentifica a “versão” da classe que foi usada durante o processo. Esse é o chamado serialVersionUID, ou seja, o indentificador de versão de serialização de uma classe. Esse número é utilizado para saber se o objeto que estamos recuperando é de uma versão “compatível” com a versão da classe que foi utilizada quando serializamos o objeto: em outras palavras, os arquivos .class não precisam ser necessariamente os mesmos para que o processo de serialização ocorra com sucesso.
Por exemplo, considere a seguinte classe Usuario:
package br.com.caelum;
 
public class Usuario implements Serializable {
  private String login;
}
Essa classe possui o serialVersionUID igual a 2806421523585360625L. Esse número não é aleatório! Ele é um hash (SHA) calculado em cima dos nomes dos seus atributos, e assinaturas dos métodos em uma ordem bem definida pela especificação do processo de serialização. E como eu sei esse número? O JDK vem com a ferramenta serialver, que implementa esse mesmo hash:
serialver br.com.caelum.Usuario
Se o serialVersionUID utilizado durante a serialização não bater exatamente com oserialVersionUID da classe que está sendo usada para recuperar essa informação, uma exception é lançada: java.io.InvalidClassException.
Por exemplo, se adicionarmos um novo atributo na nossa classe Usuario:
public class Usuario implements Serializable {
  private String login;
  private String senha;
}
Agora teremos o serialVersionUID valendo 416295346730660862L. Caso você serialize umUsuario com a primeira classe aqui definida, e tentar recuperar essa informação usando essa nova versão de classe, receberemos a conhecida java.io.InvalidClassException. Esse é o comportamente que em muitos casos queremos, mas algumas vezes fazemos pequenas modificações na classe as quais percebemos que não impactarão no processo de serialização, e precisamos manter compatibilidade com a versão antiga daquela classe. Para isso, basta definirmos explicitamente qual é o nosso serialVersionUID, e no caso de querer manter compatibilidade com a classe Usuario anterior, vamos utilizar o valor de serialVersionUID que seria gerado pela JVM: 2806421523585360625L. O código ficaria:
public class Usuario implements Serializable {
  private static final long serialVersionUID = 2806421523585360625L;
  private String login;
  private String senha;
}
Às vezes recebemos um warning do Eclipse, e ele pede para que seja definido oserialVersionUID da classe em questão. Isso ocorre porque você implementa Serializable ou uma de suas mães a implementa. O Eclipse então te abre três opções: utilizar o@SurpressWarnings para você assumir o risco, usar um valor default, ou usar o valor gerado. O gerador de UIDs do Eclipse é exatamente o mesmo gerador utilizado pelo Java SE para criar os UIDs padrão! Reforçando, esse número não é um número aleatório!
Quando alguém esquece de manter o mesmo serialVersionUID para duas versões compatíveis de uma classe, podemos ter problemas em usar diferentes versões do software que são teoricamente compatíveis. Isso muitas vezes acontece em servidores de aplicação, e se seu cliente esta desatualizado em relação a versão dos jars necessários pelo servidor, podemos ter algunsInvalidClassExceptions que poderiam ser facilmente evitados se o serialVersionUID tivesse sido corretamente aplicado. Claro que algumas outras vezes as versões realmente não são compatíveis e a exception procede.
Esse grave problema pode acontecer mesmo usando classes do Java SE entre diferentes versões, como é o caso da classe java.text.AttributedCharacterIterator.Attribute (utilizada pelajava.awt.Font). Do Java 1.3 para o Java 1.4 essa classe foi levemente alterada, e oserialVersionUID gerado pelo algoritmo da JVM desta classe mudou de-1514471214376796190L para -9142742483513960612L. Quando alguém serializava umajava.awt.Font em uma versão não podia desserializa-la em outra, sendo que as versões tecnicamente são compatíveis: a não definição explícita do serialVersionUID gerou um bug no Java SE. Como isto foi resolvido? Definiram o serialVersionUID como -1514471214376796190L, que é o valor que seria gerado pela JVM na versão anterior da classe.
Como então devemos proceder para escolher um serialVersionUID apropriado? É muito simples: se essa classe está nascendo neste momento, você pode se dar ao luxo de utilizar umserialVersionUID, como por exemplo:
public class Usuario implements Serializable {
  private static final long serialVersionUID = 1L;
  private String login;
}
Porém se você está definindo o serialVersionUID de uma classe já em produção, e sabe que a mudança que está fazendo é compatível com a versão anterior, você deve utilizar oserialVersionUID que seria gerado pela JVM na primeira versão, como foi o caso aqui quando adicionamos o atributo senha na classe Usuario, e também foi o caso da correção do bug da classe java.text.AttributedCharacterIterator.Attribute. Quando você fizer uma alteração onde percebe que o cliente precisará de atualização das classes envolvidas, basta definir um serialVersionUID diferente dos anteriormente utilizados.
Para completar, implementar uma interface que não define métodos (Serializable) e ser forçado a escrever um atributo sem um contrato mais burocrático é um tanto estranho em uma linguagem como o Java. Sem dúvida, se esse mecanismo todo tivesse sido inventado já com a existência de anotações, Serializable seria uma anotação e version um atributo dela, talvez obrigatório, criando algo como @Serializable(version=12345L). Boas serializações e invocações remotas!

segunda-feira, 27 de fevereiro de 2012

Instalar APK a partir da sua aplicação - Android


File apk = new File( Environment.getExternalStorageDirectory() + "/Copas.apk"); 
Intent it = new Intent(Intent.ACTION_VIEW);  
it.setDataAndType(Uri.fromFile(apk),  
  "application/vnd.android.package-archive");  
startActivity(it);  

Fonte: http://nglauber.blogspot.com/2012/02/android-dicas-6.html

sexta-feira, 27 de janeiro de 2012

Capturar memória do PC (RAM) - Java

1º post do ano de 2012!  :D

import java.lang.management.ManagementFactory; 
import com.sun.management.OperatingSystemMXBean; 
 

public class CapturaRAM { 
 
    public static void main(String[] args) { 
        OperatingSystemMXBean mxbean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); 
        System.out.println("Memoria total : " + (((new Double(mxbean.getTotalPhysicalMemorySize()) /  1024) / 1024) /1024) + " GB ");         
    }   




Mais sobres as funções utilizadas:
ManagementFactory 
OperatingSystemMXBean

quinta-feira, 29 de dezembro de 2011

Configurar Hibernate via (xml) e (programação) - JAVA


xml:


hibernate.cfg.xml

""<""?xml version="1.0" encoding="UTF-8"?>
""<""!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
""<""hibernate-configuration>
  ""<""session-factory>
    "<"property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect
    "<"property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver
    "<"property name="hibernate.connection.url">jdbc:mysql://localhost:3306/nomeDoBancoDados
    "<"property name="hibernate.connection.username">root
    "<"property name="hibernate.connection.password">root
    "<"mapping class="br.com.umcastec.bean.Cliente"/>
  "<"/session-factory>
"<"/hibernate-configuration>


programação:
HibernateUtil.java


package br.com.umcastec.util;


import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration;


public class HibernateUtil {


    private static final SessionFactory sessionFactory;


    static {


        AnnotationConfiguration annotationConfig = new AnnotationConfiguration();


        try {


            annotationConfig.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
            annotationConfig.setProperty("hibernate.connection.driver_class", "com.mysql.jdbc.Driver");
            annotationConfig.setProperty("hibernate.connection.url", "jdbc:mysql://localhost:3306/nomeDoBancoDados");
            annotationConfig.setProperty("hibernate.connection.username", "root");
            annotationConfig.setProperty("hibernate.connection.password", "root");


            annotationConfig.addAnnotatedClass(br.com.umcastec.bean.Cliente.class); //add todas as classes "JPA annotated"


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


        sessionFactory = annotationConfig.buildSessionFactory();


    }


    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
}


obs: no trecho de código do xml, coloquei aspas no símbolo "<".