Blog sobre desenvolvimento de software (Java, muito Java!), inovação tecnológica e cotidiano do Universo de TI. Acesse notícias, tutoriais, material de cursos e eventos, código, desafios, soluções, opiniões, pensamentos, divagações, balbuciações e abobrinhas diversas. Deixe seu comentário!

sábado, 29 de dezembro de 2012

JAR Hell - Um guia prático

O que é JAR Hell?


JAR Hell é um termo cunhado para descrever de forma genéricas problemas relacionados ao ClassLoader. Suas variações mais comuns acontecem quando:
    1. Duas ou mais classes diferentes, com o exato mesmo nome qualificado acabam sendo incluídas no projeto (e.g, duas versões diferentes da biblioteca ASM no Classpath).
    2. Duas ou mais bibliotecas no Classpath dependem de versões diferentes das mesmas classes (e.g., Hibernate e Spring dependendo de versões diferentes da biblioteca ASM).

      Sintomas do JAR Hell


      JAR Hells possuem algumas peculiaridades marcantes, mesmo assim nem sempre seu diagnóstico é óbvio; várias vezes uma situação de JAR Hell vem mixada com outros tipos de problemas como ausência de dependências transitivas no Classpath e omissões em arquivos de configuração.
      Eis alguns sintomas quem indicam um problema de JAR Hell:
      • Estão sendo disparados NoSuchMethodError, NoSuchFieldError ou IllegalAccessError.
      • ClassCastException entre classes iguais ou compatíveis.
      • Problemas de serialização / deserialização.
      Outra pista que aponta para uma situação de JAR Hell acontece quando um ou mais dos problemas mencionados acontecem de forma intermitente, e particularmente quando dão a impressão de estarem relacionados a questões de build / ambiente (e.g., aparecem no ambiente de homologação mas não no de desenvolvimento; builds pelo IDE funcionam / builds pela ferramenta de Integração Contínua não; com o servidor em modo de desenvolvimento tudo funciona mas em modo de produção não; com JVMs da Sun tudo funciona e com JRockit não).
      Isso ocorre pois quando há duas ou mais classes com o mesmo nome qualificado no Classpath, a lógica que determina qual delas será carregada fica codificada no ClassLoader utilizado, ou em última instância, na lógica dos ClassLoaders de base da própria JVM. 
      Prever qual versão de uma classe será carregada é uma tarefa difícil devido a cadeia complexa de ClassLoaders tipicamente presente em soluções enterprise. O próprio ClassLoader escolhido para abrir determinada classe pode variar conforme o estado do container e código da aplicação. 
      Em geral o ClassLoader que carrega uma classe também fica responsável por carregar todas as outras classes utilizadas por ela; essas classes e o valor de suas variáveis estáticas são então cacheados em memória (e.g., na famigerada PermGen). Isso significa que - salvo exceções em que o ClassLoader se torna elegível para Garbage Collection e a JVM é otimizada para fazer unloading de classes - uma vez que determinada versão de uma classe seja carregada ela permanece em memória, em um namespace relacionado ao ClassLoader, até fim do processo.

      Resolvendo Problemas de JAR Hell


      Os passos lógicos para resolver JAR Hells são:
      • No caso de classes duplicadas dentro do domínio da sua aplicação: Detectar e excluir versões antigas da classe duplicada. Preferencialmente deixar somente uma classe de cada tipo no Classpath, i.e., não há necessidade da mesma classe estar presente em vários JARS da aplicação, sendo recomendável refatorar as classes repetidas para um JAR a parte.
      • No caso de incompatibilidade entre bibliotecas de terceiros: Fazer upgrades e / ou downgrades das bibliotecas para uma combinação com dependências transitivas compatíveis. Uma ferramenta para gerenciar dependências ajuda muito.
      Se por algum motivo não for possível eliminar versões duplicadas de uma classe ou encontrar bibliotecas compatíveis, como último recurso é possível escrever ClassLoaders personalizados. Esse é um tópico avançado que fico devendo para um próximo post. Classifico o uso de ClassLoaders customizados como um último recurso pois eles aumentam a Entropia do Software, são de difícil manutenção e, se não forem bem arquitetados, podem gerar todo tipo de problemas (NoClassDefFoundError, java.lang.OutOfMemoryError: PermGen space, etc).

      Encontrando classes duplicadas


      Geralmente essas classes estão em bibliotecas do EAR (APP-INF/lib), WAR (/WEB-INF/lib), ou do Servidor de Aplicação.
      Para saber de que JAR / diretório determinada classe está sendo lida utilize os seguintes métodos:
      public static URL getLocation(Class clazz) {
          return clazz.getProtectionDomain().getCodeSource().getLocation();
      }
      public static URL getLocation(String className) 
          throws ClassNotFoundException {
          return getLocation(Class.forName(className));
      }
      Exemplos de chamada e resposta (para um projeto mavenized):
      System.out.println(getLocation(EntityManager.class)); 
      /* 
       * file:/home/anthony/.m2/repository/javax/persistence/
       * javax.persistence/2.0-SNAPSHOT/javax.persistence-2.0-SNAPSHOT.jar 
       */
      System.out.println(getLocation(
          "org.apache.commons.lang.builder.ReflectionToStringBuilder")); 
      /* 
       * file:/home/anthony/.m2/repository/commons-lang/commons-lang/
       * 2.6/commons-lang-2.6.jar 
       */
      Um bom lugar a ser analisado são clientes de Web services gerados automaticamente, principalmente quando mais de um serviço depende do mesmo XSD. Nesse cenário é comum que vários clientes sejam gerados a partir de WSDLs diferentes, todos contendo suas próprias versões da mesma classe. 
      Na verdade todo tipo de código gerado automaticamente merece atenção: Classes JAXB geradas com xjc, entidades JPA repetidas em diferentes projetos EJB, etc. 

      Problemas de compatibilidade entre bibliotecas de terceiros


      Para resolver esse tipo de JAR Hell é necessário encontrar versões compatíveis das bibliotecas (e.g., uma versão do Spring e do Hibernate que possam funcionar com a mesma versão da biblioteca ASM), ou, em casos de total incompatibilidade, escrever ClassLoaders customizados.
      Geralmente esse tipo de erro acontece com bibliotecas populares como como Apache Commons, Javassist e ASM que são dependências para uma grande quantidade de frameworks e bibliotecas.
      Devido a dependências transitivas (e.g., seu projeto depende da biblioteca A que por sua vez depende da biblioteca B que depende da biblioteca C e assim sucessivamente), encontrar versões compatíveis de bibliotecas pode se tornar uma tarefa enfadonha.  
      Para aliviar o trabalho do programador ferramentas poderosas como Maven, Ivy e Buildr estão disponíveis. Com essas ferramentas é possível especificar as bibliotecas (e versões / intervalos de versões) que seu projeto necessita em arquivos de configurações; essas ferramentas fazem então o download dos artefatos - e suas dependências transitivas - a partir de uma hierarquia de repositórios de software (como o gigantesco Repositório Central), ajustam o Classpath da aplicação para compilação / execução / testes e ainda empacotam bibliotecas dentro dos EARs, WARs, etc para distribuição quando cabível. 
      Caso ocorra algum conflito o desenvolvedor pode trocar facilmente a versão de determinada biblioteca no arquivo de configuração e deixar que a ferramenta faça o trabalho duro por ele.
      Enquanto uma ferramenta como o Maven não se aplica a todo tipo de projeto (pois introduz suas próprias complexidades), eu costumo usá-lo pelo menos para fazer download das dependências do projeto e exportá-las com o Dependency plugin. Fico devendo um tutorial sobre esse assunto para um próximo post.

      Bibliotecas do Servidor de Aplicação vs Bibliotecas do Projeto


      Pessoalmente tento, sempre que possível, usar bibliotecas do próprio servidor (que passaram por todo o processo de homologação do Java EE, e, por via de regra - mas não como verdade absoluta - são compatíveis). Para esclarecer, não estou pedindo para ninguém abrir mão das bibliotecas (por favor, reuso na cabeça!), apenas recomendo pesquisar se o próprio servidor de aplicação já não fornece uma versão da biblioteca desejada ou algo equivalente (e.g., Hibernate x Eclipselink para JPA).
      Não se preocupe em sempre incluir a versão bleedin edge de uma biblioteca no projeto. Geralmente as versões disponibilizadas pelo Application Server são mais do que suficientes (salvo exceções em lugares que ainda estão rodando Application Servers homologados para versões anteriores ao Java EE 5). Minha experiência nesse ponto é que quando você coloca uma biblioteca bleedin edge no projeto ela acaba permanecendo lá por mutos anos e eventualmente se torna legada (ninguém quer mexer em bibliotecas da aplicação por motivos óbvios, incluindo JAR Hells), enquanto que ao fazer referência as bibliotecas do App Server, a evolução vem lenta porém certamente conforme upgrades planejados para versões mais novas do container.
      Enquanto há bons argumentos contra depender do container (e.g., reduzir o trabalho de infraestrutura ao fazer deploy das bibliotecas; ter menos problemas ao fazer upgrade do servidor de aplicação e ter mais controle sobre o que está sendo utilizado no projeto), eu pessoalmente acho que é um trade-off válido.
      Eis um exemplo de como referenciar uma biblioteca no weblogic-application.xml (EAR).
      <wls:library-ref>
          <wls:library-name>jersey-bundle</wls:library-name>
          <wls:specification-version>1.1.1</wls:specification-version>
          <wls:implementation-version>1.1.5.1</wls:implementation-version>
      </wls:library-ref>
      Fico devendo exemplos de sobre como referenciar bibliotecas no JBoss AS, GlassFish, WebSphere, etc.

      Na verdade, como deixei muitos pontos em aberto nesse post (como escrever um ClassLoader, como exportar dependências do Maven e como referenciar bibliotecas nos principais servidores de aplicação), aceito sugestões sobre o tema de posts futuros. Por favor deixem seu comentário ou enviem uma mensagem através do formulário de contato.

      terça-feira, 21 de fevereiro de 2012

      SCMs e Pastebins - O que fazer com o seu código “pessoal”

      Para quem está em busca de ferramentas para lidar com aquele código extra que acabamos escrevendo fora do trabalho e / ou do escopo do sistema de controle de versão da empresa como:
      • Coleção de scripts diversos para automação (i.e., scripts de do programador vagabundo... Aqueles que quebram um galhão mas nunca ficam apresentáveis o suficiente para você mostrar para os outros).
      • Testes com JavaScript e JQuery.
      • Snippets de código que fazem as coisas acontecerem em determinada linguagem / framework e na hora “h” sempre esquecemos.
      • Código de exercícios e tutoriais.
      • Projetinhos Open source.
      Eu recomendo fortemente o Bitbucket da Atlassian (empresa que criou o JIRA).

      Endereço: http://bitbucket.org/

      O Bitbutcket disponibiliza gratuitamente repositórios para controle de código fonte (Mercurial, e mais recentemente Git) com interface de administração / navegação web... Além de Wikis, Issue Trackers e toda a parafernalha típica de sites do género.
      O grande diferencial em relação a serviços semelhantes (SourceForge, Google Code, GitHub, etc) é que você pode criar uma quantidade ilimitada de repositórios privados (pessoais ou compartilhados com até cinco pessoas). O que faz do Bitbucket o lugar perfeito para colocar todo aquele código que fica jogado no seu HD e eventualmente é perdido.

      Para quem não conhece, também sou fã de dois outros serviços:

      http://jsfiddle.net/ - Ferramenta do tipo pastebin em que você digita o código JavaScript / HTML / CSS em suas respectiva seções e visualiza o resultado final no iframe inferior direito. Ótimo para aqueles testes rápidos, perfeito para JQuery. Se quiser salvar o código, o jsfiddle gera uma URL para você (não requer cadastro).

      http://ideone.com/ - Mesmo princípio do jsfiddle, porém mais voltado para server side. Possui compiladores e interpretadores de mais de 40 linguagens (como Java, C#, C++, VB.NET, Groovy, Ruby, Scala, etc). Você digita o código - opcionalmente também os valores de entrada - e a ferramenta compila / interpreta o programa mostrando a saída (ou erros) em uma interface web. Muito bom para SQL.

      quinta-feira, 29 de dezembro de 2011

      Planilha para economizar com o Skype

      Usuários de longa data do Skype sabem que o aplicativo pode diminuir significativamente sua conta de telefone. Alguns exemplos:
      • Qualquer tel. no Canadá R$0,06 por minuto, mais taxa de conexão de R$0,09 [1].
      • Tel. fixo no Rio de Janeiro → R$0,09 por minuto, mais taxa de conexão de R$0,189 [1].
      • Celular no Brasil R$0,54 por minuto, mais taxa de conexão de R$0,189 [1].
      Bem melhor do que as tarifas do meu Net Fone (R$1,09, R$0,27 e R$1,53 respectivamente).
      Outro ponto a ser considerado é que a qualidade da ligação é ótima: Com uma banda larga veloz e um bom microfone o uso do Skype é praticamente imperceptível para a outra ponta. Inclusive é possível configurar o Skype para exibir seu número de celular no identificador de chamadas do destinatário da ligação.

      Gostou? Então que tal economizar ainda mais?
      O Skype permite a compra de créditos em 15 moedas diferentes, cada qual com sua tabela de preços e taxas de conexão. Como o Skype não atualiza seus preços frequentemente e a taxa de câmbio sofre variação, é possível economizar bastante escolhendo a melhor moeda para comprar seus créditos. A diferença de preço é tão grande que a manobra vale a pena mesmo considerando a taxa de IOF de 6.38% incidente sobre compras internacionais no cartão de crédito.
      Pensando nisso criei uma planilha no Google Spreadsheet que extrai informações sobre tarifas diretamente do site do Skype, bem como as cotações das moedas através do Google Finance. A planilha permite saber quanto custa sua ligação via Skype para o destino desejado e qual a melhor moeda para comprar seu crédito de acordo com a taxa de câmbio do momento (cotações sujeitas a atraso de 20 minutos de acordo com o disclaimer do Google).

      Visão em tempo real da planilha para ligações para celulares do Brasil:

      Planilha em tela cheia 

      Com a cotação da Libra Esterlina em R$2,8945 como no momento da escrita desse post, é possível economizar mais R$0,15 por minuto ao ligar para celulares no brasil (12.6p = R$0,39 já acrescentando IOF).
      A planilha serve como um "aplicativo web" permitindo que você preencha os dados da ligação e descubra a melhor moeda a ser utilizada de acordo com as características da sua chamada:


      Para obter uma cópia editável da planilha siga os seguintes passos:
      1. Entre no Google Docs com sua conta do Google (é gratuito).
      2. Clique no link da planilha para abri-la em uma nova janela.
      3. Faça uma cópia do planilha clicando no menu File Make a Copy:

      Pronto, sua cópia da planilha é editável e privada. Desse momento em diante você pode acessar o Google Docs, abrir sua cópia da planilha, preencher os dados da ligação (Call Info) e receber informações atualizadas sobre preços do Skype. Não é necessário fechar e abrir a planilha para obter novas cotações, isso é feito automaticamente em background.

      Algumas dicas:
      • Na primeira vez que você abrir a planilha pode demorar um pouco para que os preços sejam exibidos. Da mesma forma, na primeira vez que você clicar no combo box Destination pode demorar um pouco para que os países sejam carregados.
      • Se ocorrer algum erro para obter uma cotação ou preço do site da Skype espere meia hora e tente de novo (lembre-se que a internet segue o princípio do melhor esforço :D).
      • Para mudar sua moeda clique em cima dos seus créditos e, no pop-up que irá abrir, clique no link Change indicado pela figura abaixo:

      • De acordo com documentação do Skype uma taxa extra de conversão pode incidir sobre seus créditos durante a operação de troca de moeda. Dessa forma, a melhor estratégia para economizar é comprar créditos de acordo com as ligações pretendidas e esgotá-los antes de mudar de moeda. Trocar de moeda várias vezes para economizar alguns centavos por ligação não vale a pena pois a taxa extra de conversão incidirá sobre todo o seu crédito restante.
      • Lembre-se que em compras por cartão de crédito a taxa de câmbio aplicada será determinada pela operadora do cartão próximo a data de fechamento da sua fatura. Logo, como em toda transação financeira, há riscos envolvidos - no caso, o da moeda escolhida super valorizar frente ao real. Eu venho comprando créditos em Libras Esterlinas e Wons sul-coreanos já faz algum tempo (mais de 2 anos) e não passei ainda por nenhuma situação em que essas moedas valorizassem o suficiente para fazer a compra em real valer a pena. Pelo contrário, em boa parte das vezes o real valoriza frente as outras moedas fazendo com que a operadora de cartão de crédito me debite a diferença. Tenha em mente porém que a taxa de câmbio é dependente do cenário econômico, e que o governo brasileiro tem um longo histórico de uso de manobras para controlar o câmbio (lembrando que manter o real desvalorizado frente ao dólar é um dos objetivos do Ministro Guido Mantega). Use a planilha por sua conta e risco.
      • Uma boa prática é comprar o crédito um pouco antes da sua fatura vencer, de forma a correr menos riscos relacionados a flutuação cambial.
      • Uma alternativa para não assumir o risco da flutuação cambial é usar o serviço de pagamentos PayPal que permite a conversão da moeda estrangeira em real através de uma cotação pré-fixada. Dessa forma você sabe exatamente o quanto será debitado do seu cartão de crédito na hora da compra. O trade-off é que as taxas de câmbio pré-fixadas não são tão competitivas quanto as pós. Novamente, no cenário econômico dos últimos dois anos, em nenhuma das compras que fiz a taxa pré-fixada valeria a pena.
      Para encerrar o post com um conteúdo um pouco mais voltado para desenvolvedores, veja que essa planilha somente foi possível graças às funcionalidades fantásticas do Google Spreadsheet, em especial ImportHtml, ImportXML, e Google Finance. O recurso de listas suspensas também é muito interessante. Estou oficialmente distribuindo a planilha sob Licença Creative Commons. Sinta-se livre para estudar as fórmulas e criar trabalhos derivados.


      1. Taxa de conexão é aplicada somente uma vez por ligação. Preços da modalidade Pré-pago. É possível obter tarifas menores e isenção da taxa de conexão assinando um pacote mensal.

      domingo, 8 de maio de 2011

      Java: == vs equals()

      Operadores e métodos de comparação são fonte de muitas dúvidas para programadores que estão iniciando em Java. De fato, o operador  ==, os métodos equals e hashCode são tópicos do Java Standard Edition 6 Programmer Certified Professional Exam para quem busca o título Oracle Certified Professional Java Programmer (antigo SCJP). Várias questões cobram, direta ou indiretamente, conhecimento sobre o assunto.
      O operador == é usado para comparação entre tipos primitivos:
      int x = 10;
      if (x == 10) {
        System.out.println("x é 10!");
      }
      Já para tipos complexos o programador pode se surpreender com o resultado. O operador == só "funciona" com referências para o mesmo objeto:
      public class Pessoa {
          
          private String cpf;
      
          public Pessoa(String cpf) {
              this.cpf = cpf;
          }
          
          public static void main(String[] args) {
              Pessoa p1 = new Pessoa("111.111.111-11");
              Pessoa p2 = new Pessoa("111.111.111-11");
              Pessoa p3 = p1;
              
              System.out.println(p1 == p2); // false
              System.out.println(p1 == p3); // true
          }
          
      }
      Esse resultado remete a uma decisão de design da linguagem. A regra de negócios para determinar se dois objetos são iguais é específica para cada aplicação. Enquanto o compilador pode assumir que duas referências para o mesmo objeto são iguais, assumir que dois objetos diferentes com os mesmos valores são iguais seria perigoso. Só porque uma pessoa tem um carro do mesmo modelo e cor do que o seu, isso não significa que os carros são iguais e você pode pegar o carro dela.
      Eis o motivo de existência do método equals na classe Object. Para quem não está familiarizado com essa classe, ela serve de superclasse para todas as classes em Java, logo, seus métodos são universais, podendo ser chamados a partir de qualquer instância de qualquer objeto disponível em tempo de execução. A fim de definir a regra de comparação para objetos de uma classe deve-se sobrescrever o método equals no corpo da mesma: 
      public class Pessoa {
          
          private String cpf;
      
          public Pessoa(String cpf) {
              this.cpf = cpf;
          }
      
          // Adaptado do código gerado pelo Netbeans
          @Override
          public boolean equals(Object obj) {
              // Um objeto nunca deve ser igual a null
              if (obj == null) {
                  return false;
              }
              // Uma pessoa só pode ser igual a outra pessoa
              if (getClass() != obj.getClass()) {
                  return false;
              }
              // Converte a referencia para uma pessoa
              final Pessoa other = (Pessoa) obj;
              // Duas pessoas só podem ser comparadas se possuem CPF
              if (this.cpf == null || other.cpf == null) {
                  return false;
              }        
              // Duas pessoas são iguais se possuem o mesmo CPF
              return this.cpf.equals(other.cpf);
          }
          
          public static void main(String[] args) {
              Pessoa p1 = new Pessoa("111.111.111-11");
              Pessoa p2 = new Pessoa("111.111.111-11");
              Pessoa p3 = p1;
              
              System.out.println(p1.equals(p2)); // true
              System.out.println(p1.equals(p3)); // true
          }
          
      }
      Agora sim... Veja que no meu exemplo duas pessoas só são iguais se possuem o mesmo CPF. Outros sistemas poderiam adotar critérios diferentes para comparar duas pessoas, ou mesmo comparar pessoas com outros objetos (do tipo Batatinha por exemplo). Observe que a comparação entre CPFs também foi feita através de equals; isso é possível pois a classe String (API padrão do Java) sobrescreve o método equals (o mesmo é valido para várias outras classes como Integer, Long, Boolean, etc).
      Quem acompanhou até aqui deve estar se perguntando o que acontece se houver uma chamada para equals sem que seja feita a sobrescrita do método. Não passe vontade! Vá lá, comente o método equals e rode a aplicação. Eu espero...
      Pronto? Eis o resultado:
      System.out.println(p1.equals(p2)); // false
      System.out.println(p1.equals(p3)); // true
      Acontece a mesma coisa do que se utilizássemos o operador ==, ou seja, os objetos são comparados por referência. Para os curiosos, segue a implementação no método equals na classe Object que justifica o porquê:
      public boolean equals(Object obj) {
         return (this == obj);
      }
      Uma regra que exige certo grau de disciplina do desenvolvedor é sempre sobrescrever o método hashCode junto com o método equals. Se essa regra não for respeitada, várias classes do Java que dependem da função hash do objeto apresentarão comportamento inconsistente para o mesmo (exemplos dessas classes são as collections HashSet e HashMap). 
      O que é uma função hash e para que ela é utilizada fico devendo para um próximo post. Por enquanto assuma que o método hashCode deve mapear um objeto qualquer para um número inteiro para fins de comparação.
      Lendo o javadoc do método hashCode, um ponto importante do contrato que deve ser respeitado é: Se a comparação entre dois objetos com equals retornar true os objetos devem possuir o mesmo hashCode (o inverso não é verdadeiro, ou seja, dois objetos com o mesmo hashCode não necessariamente precisam retornar true para a função equals). Segue uma implementação gerada pelo Netbeans que atende a esse contrato para a classe Pessoa:  
      @Override
      public int hashCode() {
         int hash = 5;
         hash = 37 * hash + (this.cpf != null ? this.cpf.hashCode() : 0);
         return hash;
      }
      Observe que:
      1. Toda pessoa com o mesmo CPF possui o mesmo hashCode.
      2. Como para o método equals, String e várias outras classes do Java sobrescrevem o método hashCode, sendo possível reaproveitá-lo em nossas implementações.
      Cuidado: O uso incorreto do operador == com objetos complexos pode introduzir bugs difíceis de serem rastreados. Veja o seguinte exemplo com strings:
      String s1 = "abacate";
      String s2 = "abacate";
      String s3 = new String("abacate");
      
      System.out.println(s1 == "abacate"); // true
      System.out.println(s1 == s2); // true 
      System.out.println(s1 == s3); // false
      Nesse caso s1 e s2 foram criados a partir do mesmo literal “abacate”. A JVM, por motivos de performance / eficiência no consumo de memória fez com que somente uma String contendo o valor “abacate” fosse de fato criada e ambas as referências apontassem para ela (através de um pool de strings). Já em s3 houve a criação de um objeto diferente em memória (com new), logo, o operador == irá retornar false.
      Sempre é possível usar o método intern para obter o objeto String presente no pool, apto a ser comparado com o operador ==. Porém, esse é o típico caso em que você deve se perguntar qual a vantagem de fazer isso (e se o motivo não for convincente, simplesmente não faça).

      Resumindo: 
      • Use == para primitivos.
      • Use equals para tipos complexos.
      • Verifique sempre se as classes dos objetos que você está tentando comparar sobrescrevem equals corretamente.
      • Se não sobrescreverem, sobrescreva equals de acordo com sua regra de negócio.
      • Se você sobrescrever equals deverá também sobrescrever hashCode.
      Dica: Gerando os métodos equals e hashCode com seu IDE favorito:
      • Netbeans: Alt + Insert  equals() and hashCode() 
      • Eclipse: Menu Superior  Source  Generate hashCode() and equals()
      • IntelliJ: Alt + Insert  equals() and hashCode()

      Curiosidade: Veja que os IDES produzem implementações diferentes, porém válidas para ambos os métodos.

      quinta-feira, 5 de maio de 2011

      Netbeans 7.0

      Uma nota rápida sobre o Netbeans 7.0.
      Brinquei com o IDE nas últimas semanas e tive uma experiência excelente.
      Segue uma lista das minhas features favoritas:
      • Melhorias no suporte ao Weblogic. Bingo! Estrelinha dourada para a Oracle. Não nego que o o Weblogic é meu Application Server predileto. A combinação Weblogic + Netbeans tem tudo para no futuro se tornar o melhor ambiente de desenvolvimento Java EE do mercado. 
      • Suporte ao Tomcat 7 e melhorias no suporte ao GlassFish. Subi alguns projetos de médio porte no Tomcat e fiz redeploy várias vezes sem esbarrar em nenhum OutOfMemoryError: PermGen space (outros programadores reportaram a mesma coisa; parabéns à equipe do Tomcat). O tempo de turnaround para deploy em ambos os containers também está menor no meu computador. Na minha opinião o Netbeans atualmente possui o melhor suporte para Java EE 6 entre todos os IDES do mercado (seguido de perto pelo IntelliJ).
      • Suporte ao JDK 7 não poderia faltar. As sugestões automáticas são bem legais para ajudar programadores a se acostumarem com o operador diamante, try-with-resources, multi-catch, switch sobre Strings, etc.
      Ao contrário do que muita gente desconfiada (inclusive eu) acreditava, parece que há esperança para o Netbeans dentro da Oracle. Quanto mais suporte às tecnologias estratégicas da companhia for implementado no Netbeans (mesmo que através de plugins) melhor. Além do suporte ao banco de dados Oracle e ao Weblogic, aguardo melhorias no suporte à BPEL e Hudson. Ficaria muito feliz também se um dia a Oracle implementasse suporte para Weblogic Portal e SOA Suite no IDE. O Netbeans seria com certeza uma alternativa interessante ao JDeveloper e ao OEPE.

      Faça o download da versão atual aqui.

      terça-feira, 29 de março de 2011

      Satisfação imediata com o banco de dados H2

      Até hoje lembro do meu primeiro contato com bancos de dados (aproveito para avisar que os próximos parágrafos terão pouco a ver com a tecnologia do título. Se a idéia é aprender sobre o uso de H2, pule direto para ). Eu estava no segundo ano da faculdade, era bolsista e tinha todo o tempo livre do mundo, então decidi instalar e configurar um ambiente de desenvolvimento com uma versão antiga do Ubuntu e uma versão mais antiga ainda do PostgreSQL.
      Foi uma experiência bastante enlouquecedora enriquecedora. Em um único sprint aprendi como trabalhar com autenticação e autorização no Linux e no PostgreSQL. Eu lembro o quanto penei para trocar a senha do usuário postgres, executar o comando psql com esse usuário, criar um novo usuário que coincidisse com o meu login do Linux no banco, criar um novo banco com o mesmo nome do usuário, criar um novo schema nesse banco para testes e assim por diante. Depois que consegui conectar no banco com o meu usuário usando o comando psql levei mais pelo menos um dia para descobrir como conectar via JDBC.
      Não satisfeito com todo esse trabalho, alguns meses depois resolvi compilar e instalar na unha uma versão mais nova do banco com uma série de extensões... Foram dias brigando com o comando make (e as dependências da instalação), switches do SGBD, arquivos de configuração e coisas de dia a dia do Linux como chown, chmod, ln, configuração da variável PATH, Sys-V, etc. Quando finalmente consegui fazer tudo rodar direito eu já estava me sentindo bem familiarizado com o Linux (bem... vivendo uma ilusão na verdade, pois eu poderia ter adicionado meia dúzia de repositórios com binários que facilitariam bastante o processo).
      Não preciso nem dizer que graças a minha falta de experiência tachei o PostgreSQL de complicado, acadêmico e burocrático antes mesmo de começar a usá-lo.
      Um ano depois, já no mercado de trabalho, tive um contato inicial bem menos traumático com o MySQL. Um apt-get e meia dúzia de pequenas edições em arquivos de configuração depois e tudo já estava rodando em um ambiente com  Apache + PHP + phpMyAdmin + MySQL. Criei os bancos e tudo que precisava com o phpMyAdmin (tomando o cuidado de criticar injustamente o pgAdmin em toda oportunidade de comparação que tive), modelei o banco com o DBDesigner (depois conheci o MySQLWorkbench que uso até hoje) e consegui conectar via JDBC na primeira tentativa. Foi rápido e satisfatório.
      Semanas depois quando descobri que as foreign keys não funcionavam (devido a engine default MyISAM) e que as constraints do tipo CHECK eram silenciosamente ignoradas, não me importei muito com o trabalho extra de alterar a engine de trocentas tabelas e reescrever o código das constraints CHECK do lado da aplicação. Em contraste passei meses implicando com coisas menores do PostgreSQL como o fato de ter que especificar mais colunas do que realmente precisava na cláusula GROUP BY, bem como o uso de DISTINCT ON, ILIKEetc.
      Moral da história: Eu admito ter um peso e duas medidas para os dois mais populares SGBDs Open Source do mercado. Depois de ter desenvolvido muitas aplicações e participado da instalação e configuração de diversos outros bancos (como Oracle, DB2, SQL Server, Sybase, etc) nos mais diversos ambientes - com diferentes requisitos (e camadas de complexidade) - posso dizer que ambos os bancos são ótimos. Mesmo assim, quando a escolha cabe a mim, eu acabo usando o MySQL. E por que? Porque em um período crucial do meu aprendizado acabei tendo uma experiência mais satisfatória com ele.

      Hoje tenho a grata oportunidade de treinar outros programadores. Volta e meia isso significa ajudá-los em seus primeiros passos com SQL. Por experiência própria, sei que esses primeiros contatos com o SGBD podem fazer toda a diferença na formação do aluno. Só aprenderão aqueles que fizerem exercícios, e só farão exercícios aqueles que conseguirem quebrar a barreira da instalação e configuração do ambiente.
      A situação ideal seria que meus alunos pudessem criar bancos e executar todo tipo de comando SQL sem burocracia. Também é importante que usem um banco de dados confiável, com bom suporte aos padrões de SQL e que rode nos mais diversos sistemas operacionais. Se o banco puder rodar embbeded ajuda bastante, pois dessa forma é possível criar exercícios que vão direto ao ponto em projetos pré-configurados.

      Conheça o H2, um banco de dados puramente implementado em Java que faz tudo isso. Em um único jar com pouco mais de 1Mb ele traz: um poderoso SGBD que pode rodar tanto em modo embbeded quanto client-server, um driver JDBC tipo 4 e várias ferramentas como uma interface de administração web com Auto Completion para SQL.
      É possível fazer o download do instalador para Windows, do zip multi-plataforma, ou ainda apenas do jar do SGBD. Com o jar ou a versão zipada nenhuma instalação é necessária. Basta clicar duas vezes em h2-[versão].jar que o console do banco aparecerá no seu navegador padrão. Se o seu sistema suportar o conceito de “Área de notificação” (classe SystemTray do Java) um ícone amarelo também deve aparecer; a partir deste você pode, a qualquer momento, acessar o console.


      Na tela de login você deve indicar uma URL JDBC para o banco. A parcela da URL depois do jdbc:h2: aponta para um arquivo no sistema operacional - o caminho pode ser absoluto (começando com “/”), relativo ao diretório atual (começando com “./”) ou ao diretório do usuário no SO (começando com “~/”). Também será necessário entrar com o login e senha do administrador.
      Se a URL apontar para um arquivo não existente o banco será criado (verifique que um arquivo [nome do banco].db apareceu no local indicado); também será criado um usuário administrador com o login e senha informados.


      Pronto, isso é tudo que precisa ser feito para criar um banco e acessá-lo em modo embbeded. Para que múltiplos processos possam acessar o mesmo banco concorrentemente basta adicionar o parâmetro AUTO_SERVER=TRUE no fim da URL. Por exemplo:
      jdbc:h2:~/Projetos/DBs/h2/RH/RH;AUTO_SERVER=TRUE
      É possível ainda fazer com que um dos processos aja exclusivamente como servidor e os outros exclusivamente como clientes. Clientes podem conectar em servidores remotos via TCP/IP ou SSL (veja a documentação aqui).
      Uma vez conectado, a tela de administração aparecerá. No lado esquerdo você verá todos os objetos (schemas, tabelas, usuários, constraints, etc) do banco em uma Tree View. No lado direito você pode entrar com comandos SQL (contando com a funcionalidade de Auto Completion para te ajudar). O resultado dos comandos é exibido na parte inferior da tela.


      Que tal um Test Drive do banco? Vamos criar um pequeno schema definido pela figura abaixo, populá-lo, e executar algumas queries de complexidade média para ver como a engine responde.


      Começando pelo DDL, vamos criar as tabelas e constraints:
      -- ----------------------------------------------------
      -- Tabela departamento
      -- ----------------------------------------------------
      CREATE TABLE departamento 
      (
        -- IDENTITY = BIGINT + auto incremento + PRIMARY KEY
        id IDENTITY ,
        nome VARCHAR(45) NOT NULL ,
      );
      
      -- Constraint nomeada
      ALTER TABLE departamento 
      ADD CONSTRAINT departamento_nome_unq UNIQUE(nome);
      
      -- ----------------------------------------------------
      -- Tabela funcionario
      -- ----------------------------------------------------
      CREATE TABLE funcionario 
      (
        id IDENTITY ,
        nome VARCHAR(255) NOT NULL ,
        -- Valor default
        salario DECIMAL(10,2) NOT NULL DEFAULT 545.00,
        departamento_id INT NOT NULL ,
      );
      
      -- Foreign Keys não poderiam faltar 
      ALTER TABLE funcionario 
      ADD CONSTRAINT funcionario_departamento_fk 
      FOREIGN KEY (departamento_id) REFERENCES departamento (id);
      
      -- Funciona!
      ALTER TABLE funcionario
      ADD CONSTRAINT funcionario_salario_chk CHECK (salario >= 545.00);
      
      -- ----------------------------------------------------
      -- Tabela bonus
      -- ----------------------------------------------------
      CREATE TABLE bonus 
      (
        id IDENTITY ,
        valor DECIMAL(10,2) NOT NULL DEFAULT 0.00 ,
        -- Que tal usar uma função?
        data DATE NOT NULL DEFAULT CURRENT_DATE ,
        funcionario_id BIGINT NOT NULL ,
      );
      
      ALTER TABLE bonus 
      ADD CONSTRAINT bonus_funcionario_fk
      FOREIGN KEY (funcionario_id) REFERENCES funcionario (id);
       
      -- Precisa de índices? Sem problemas
      CREATE INDEX bonus_data_idx ON bonus(data);
      
      Bem direto né? E agora inserts de múltiplas linhas a la MySQL:
      INSERT INTO departamento (nome) 
      VALUES ('Administrativo'),  
             ('TI'), 
             ('Vendas');     
      
      INSERT INTO funcionario (nome, salario, departamento_id)  
      VALUES ('Auxiliar Administrativo', 2000.00, 1),
             ('GP I', 5500.00, 1),
             ('GP II', 7000.00, 1),
             ('Programador I', 3000.00, 2),
             ('Programador II', 6000.00, 2),
             ('Programador III', 10000.00, 2),
             ('Vendedor I', 1500.00, 3),
             ('Vendedor II', 4500.00, 3);
         
      INSERT INTO bonus (valor, data, funcionario_id)
      VALUES (15000.00, '2010-03-20', 7),
             (15000.00, '2010-07-03', 7),
             (20000.00, '2009-09-09', 8),
             (60000.00, '2010-10-06', 8),
             (45000.00, '2010-11-15', 8);
      Veja que todas as constraints estão sendo respeitadas (tente inserir um funcionário que ganhe menos de R$545,00 ou atribuir um valor inválido em um campo que tenha uma foreign key e você se deparará com um exceção).
      Mas e as queries? Vamos resolver um primeiro exercício: Determinar, para cada departamento, a quantidade de funcionários e o salário médio.
         SELECT 
               d.nome AS `Departamento`, 
               COUNT(f.id) AS `Numero de Funcionários`, 
               ROUND(AVG(f.salario),2) AS `Média Salarial`
          FROM  
               departamento d 
               LEFT OUTER JOIN funcionario f ON (d.id = f.departamento_id)
      GROUP BY
               d.id
      ORDER BY
               d.nome;
      
      Perfeito. Essa query usa de tudo um pouco, alias, group by, order by, joins e funções (veja a documentação). Repare que temos o melhor de dois mundos: um banco que não exige a repetição de colunas funcionalmente dependentes na cláusula group by e respeita as constraints do tipo CHECK.
      Mas que tal complicar um pouco mais? A definição do exercício número dois é: Encontrar todos os vendedores que ganharam mais de R$10.000,00 em bônus nos últimos doze meses; determinar o total de bônus recebidos por cada vendedor no perído e ordená-los de acordo (começando com o vendedor com a maior soma de bônus e terminando com o vendedor com a menor).
      SET @MESES = 12; 
      SET @BONUS = 10000.00;
      
        SELECT 
               f.id,
               f.nome AS `Vendedor`,
               SUM(b.valor) AS `Bonus` 
          FROM
               funcionario f 
               INNER JOIN departamento d ON (f.departamento_id = d.id)  
               INNER JOIN bonus b ON (f.id = b.funcionario_id)
         WHERE
               d.nome = 'Vendas'
               AND b.data >= DATEADD('MONTH', -@MESES, CURRENT_DATE)
      GROUP BY
               f.id 
        HAVING
               `Bonus` >= @BONUS
      ORDER BY
               `Bonus` DESC; 
      
      Novamente tudo no lugar. Aí está nossa cláusula having, múltiplos joins e até mesmo o uso de variáveis. Deixo para o leitor brincar com subqueries, UNION, MINUS, EXCEPT, INTERSECT, etc.
      O H2 têm uma quantidade de funcionalidades que deixa muitos outros bancos conhecidos no chinelo. Saindo do básico (views, sequences, triggers, stored procedures, controle de transações, controle de usuários, roles, análise de planos de execução , etc) algumas features interessantes que lembro de cabeça são o modo in memory (com ótima performance, excelente para caches), a habilidade de emular outros databases, queries recursivas, suporte a índices do tipo full text (nativos ou integrados ao Lucene) e suporte básico a clusterização.
      Os veteranos de Java que sentem calafrios quando escutam a combinação de palavras embedded database podem ficar tranquilos. Uso o H2 já há alguns anos e nunca tive sequer um banco corrompido. Cheguei a fazer testes em que bati forte no banco com o JMeter para estressar um aplicativo web e observei um curva de degradação de performance bem saudável. O controle transacional é confiável (inclusive quando operações do H2 participam de transações XA), e, no geral, o otimizador de queries faz um bom trabalho (apesar de algumas limitações).
      Bom para não escrever um artigo muito tendencioso, informo que o H2  não deve ser considerado um substituto para databases tradicionais em ambiente de produção. Segue um link do site oficial que fala sobre algumas de suas limitações (diga-se de passagem, 4TB de armazenamento é um limite bastante generoso). Também é necessário dizer que por padrão o banco usa table locks, o que reduz bastante a performance quando uma tabela sofre muitos comandos de escrita concorrentes (porém, já existe suporte experimental a MVCC).
      Comprou a idéia? Não custa experimentar, é grátis.
      Ah! Já que estamos falando de dinheiro, quem está por trás desse banco é o Thomas Müller, o mesmo criador do HSQLDB. Esse segundo banco você já deve ter usado mesmo que não saiba... Por padrão ele é incluído nos softwares Mathematica, Open Office Base, Liferay, JBoss, Jira, Hibernate e JForum, além, é claro, do super renomado software de contabilidade Open Source que escrevi na faculdade, o AAS hehehe). Você encontra um dotão “Donate” do PayPal na página do H2.

      terça-feira, 23 de novembro de 2010

      Exame - POSCOMP 2010

      O POSCOMP - Exame Nacional para Ingresso na Pós-Graduação em Computação - foi aplicado no dia 17 de Outubro de 2010 em diversos locais do país (bem como do Peru).
      Apenas um bom desempenho na prova não garante vaga em nenhum programa de pós-graduação de primeira linha; estes utilizam também critérios como cartas de recomendação, publicações, participação em programas de iniciação-científica e congressos, atividades extracurriculares, bolsas, prêmios, desempenho na graduação, etc. Todavia, o exame caracteriza uma etapa importante (e muitas vezes decisiva) dos processos seletivos.
      A prova unificada é conveniente para o candidato que pode pleitear vagas em diferentes Universidades sem se deslocar. Na maioria dos casos o único exame necessário para ingressar em programas de Pós-Graduação em Computação é o POSCOMP (em São Paulo, por exemplo, isso vale para programas como os da USP, Unesp, Unicamp, ITA e UFScar). Entretanto cabe ao candidato se inscrever separadamente nos processos seletivos de cada instituição.
      Mas não pense que o POSCOMP é moleza. Esse ano os candidatos conseguiram, em média, 29.3 acertos nas 70 questões de múltipla escolha. Históricamente a média de acertos é menor do que 50%.
      As Universidades por padrão não estabelecem "notas de corte" para seus programas de Pós-Graduação, porém, utilizam as notas como critério de comparação entre candidatos.
      A SBC publicou em seu site os exames e gabaritos das provas de 2002 a 2007. Candidatos que prestaram o exame nos últimos 3 anos também tiveram acesso aos gabaritos através de uma área restrita do site, mas por algum motivo a SBC ainda não divulgou links abertos para os demais interessados.
      Como prestei POSCOMP esse ano no IME/USP, obtive acesso ao exame e gabarito oficial que divulgo abaixo:
      Prova POSCOMP - 2010 no Scribd


      Dica: Resolver provas anteriores é uma maneira excelente de se preparar para os exames que estão por vir.