segunda-feira, 31 de dezembro de 2012

[JPA] Melhorando a performance de aplicações JPA com cache de segundo nível.


O cache de segundo nível é um repositório local, gerenciado pelo mesmo mecanismo de persistencia responsável pelo ORM da aplicação, para melhorar a performance da mesma.

A intenção é melhorar a performance reduzindo as conversações entre aplicação e banco de dados.

É suportado pela especificação JPA e pode ser configurado para os seguintes modos de trabalho (Cache Mode):

ALL Todas as entidades declaradas na unidade de persistência irão para o cache.
NONE Nenhuma entidade da unidade de persistência irá para o cache.
ENABLE_SELECTIVE Habilita o cache para entidades que forem declaradas para tal com a anotação @Cacheable.
DISABLE_SELECTIVE Habilita o cache para todas as entidades da unidade de persistência, exceto para aquelas que estiverem anotadas com @Cacheable(false).
UNSPECIFIED Adota o comportamento default do mecanismo de persistência JPA utilizado.

Um problema comum do uso do cache de segundo nível é quando os dados mudam no banco mas não mudam no cache. É o que se chama de stale read. Stale reads podem ser resolvidos com a estratégia adequada de configuração do modo de trabalho do cache e outras configurações possíveis. Por isso, é muito importante conhecer profundamente o cache de segundo nível do mecanismo de persistência que você pretende adotar.

No modo ENEABLE_SELECTIVE, as subclasses das entidades anotadas com @Cacheable também serão cacheadas a menos que a anotação seja sobrescrita com @Cacheable(false).

Especificando as configurações de cache mode para melhorar a performance.

Para ajustar o cache mode para uma unidade de persistencia, especifique um dos modos possíveis como valor do elemento shared-cache-mode no persistence.xml.

<persistence-unit name="examplePU" transaction-type="JTA">
  <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
  <jta-data-source>jdbc/__default</jta-data-source>
  <shared-cache-mode>DISABLE_SELECTIVE</shared-cache-mode>
</persistence-unit>

Ou então, programaticamente:

EntityManagerFactor emf = 
    Persistence.createEntityManagerFactory(
        "myExamplePU", new Properties().add(
            "javax.persistence.sharedCache.mode", "ENABLE_SELECTIVE"));

OBS: Se o mecanismo de persistencia adotado não suportar cache de segundo nível, as configurações acima não produzirão qualquer efeito na aplicação.

Configurando cache retrieval e store modes.

Se o cache de segundo nível estiver habilitado na unidade de persistência pela configuração do shared cache mode, o comportamento do cache de segundo nível poderá ser modificado configurando as propriedades javax.persistence.cache.retrieveMode e javax.persistence.cache.stroreMode. Estas propriedades podem ser configuradas no nível do contexto de persistência, passando o nome e o valor da propriedade para o método EntityManager.setProperty, ou configurando uma operação per-EntityManager, ou seja, por entidade (EntityManager.find ou EntityManager.refresh) ou no nível per-query, ou seja, por query.

Cache Retrieval Mode

O modo de recuperação de dados a partir do cache, configurado pela propriedade javax.persistence.retrieveMode, controla como os dados são lidos do cache por chamadas ao método EntityManager.find e a partir de queries.

A propriedade retrieveMode pode ser configurada com uma das constantes definidas pelo enum javax.persistence.CacheRetrieveMode (USE que é o default ou BYPASS). Quando configurada para USE, os dados são recuperados a partir do cache, se disponíveis. Se o dado não estiver no cache, o mecanismo de persistência irá buscá-lo no banco. Quando configurada com BYPASS, o mecanismo de persistência ignora o cache e os dados são recuperados diretamente do banco de dados.

Cache Store Mode

O modo de salva dos dados do cache, configurado pela propriedade javax.presistence.storeMode, controla como os dados serão salvos no cache.

A propriedade storeMode pode ser configurada por uma das constantes definidas pelo enum javax.persistence.CacheStoreMode, podendo ser USE, que é o default, BYPASS ou REFRESH. Configurada como USE, o dado no cache é criado ou atualizado na primeira vez que for lido do banco de dados ou gravado (commited) no mesmo. Se o dado já estiver no cache, o modo USE não irá forçar um refresh quando o dado for lido do banco.

Quando configurada para BYPASS, os dados lidos ou gravados no banco não serão inseridos no cache. É isso! O cache será imutável.

Quando configurada para REFRESH o dado é salvo no cache toda vez que for lido ou gravado no banco. Se o dado já estiver no cache, será atualizado (refreshed) no cache.

Configurando o Cache Retrieval ou o Store Mode.

Para configurar o cache retrieval ou store mode, basta chamar o método EntityManager.setProperty do contexto de persistência e passar como parâmetro o par {nome, valor} da propriedade:

EntityManager em = ...;
em.setProperty("javax.persistence.cache.storeMode", "BYPASS");

Para configurar o cache retrieval ou store mode quando chamar os métodos EntityManager.find ou EntityManager.refresh, primeiro crie uma instância de Map<String, Object> e adicione os pares nome, valor conforme a seguir:

EntityManager em = ...;
Map<String, Object> props = new HashMap<String, Object>();
props.put("javax.persistence.cache.retrieveMode", "BYPASS");
String personPK = ...;
Person person = em.find(Person.class, personPK, props);

OBS: O cache retrieve mode será ignorado na chamada do método EntityManager.refresh, pois as chamadas para refresh resultam sempre em dados lidos do banco e nunca do cache.

Para configurar o retrieval ou store mode quando for usar queries, chame o método Query.setHint ou o método TypedQuerie.setHint, dependendo do tipo de query:

EntityManager em = ...;
CriteriaQuery<Person> cq = ...;
TypedQuery<Person> q = em.createQuery(cq);
q.setHint("javax.persistence.cache.storeMode", "REFRESH");
...

OBS: Configurar o store ou o retrieval mode em uma query ou chamando os métodos EntityManager.find ou EntityManager.refresh sobrescreve as configurações do entity manager.


Controlando o cache de segundo nível programaticamente.

A interface javax.presistence.Cache define métodos para interagir com o cache de segundo nível programaticamente. A interface Cache define métodos para checar se uma entidade particular tem dados salvos no cache, para remover uma entidade particular do cache, para remover todas as instâncias (inclusive de subclasses) de qualquer classe de entidade do cache e para limpar o cache de todos os dados das entidades.

OBS: Se o cache de segundo nível for desabilitado, chamadas aos métodos da interface Cache não terão qualquer efeito sobre a aplicação, exceto para o método contains, que sempre irá retornar false.

Checando se os dados de uma entidade estão no cache.

Chame o método Cache.contains para achar ou simplesmente para verificar se uma entidade está no cache de segundo nível. Este método retornará true se os dados da entidade estiverem em cache e false em caso contrário.

EntityManager em = ...;
Cache cache = em.getEntityManagerFactory().getCache();
String personPK = ...;
if (cache.contains(Person.class, personPK)) {
  // the data is cached
} else {
  // the data is NOT cached
}

Removendo uma entidade do cache.


Chame um dos métodos Cache.evict para remover uma entidade em particular ou todas as entidades de um dado tipo do cache de segundo nível.

Para remover uma entidade em particular chame Cache.evict passando a classe da entidade e o Id (chave primária).


EntityManager em = ...;
Cache cache = em.getEntityManagerFactory().getCache();
String personPK = ...;
cache.evict(Person.class, personPK);

Para remover todas as instâncias de uma classe de entidade, incluindo suas subclasses, chame evict e especifique a classe:

EntityManager em = ...;
Cache cache = em.getEntityManagerFactory().getCache();
cache.evict(Person.class);

Todas as instâncias da classe Person serão removidas do cache. Se Person tiver subclasse, chame o método evictAll para remover suas instâncias também.


Removendo todos os dados do cache.


Chamar o método Cache.evictAll irá limpar completamente o cache de segundo nível:


EntityManager em = ...;
Cache cache = em.getEntityManagerFactory().getCache();
cache.evictAll();

E é isso! Este post é o resultado da tradução, com poucas adaptações, do The Java EE 6 Tutorial, Part VI Persistence, Chapter 38 Improving the Performance of Java Persistence APIApplications by Setting a Second-Level Cache.


Fonte: http://docs.oracle.com/javaee/6/tutorial/doc/gkjjj.html
Tradução: Mauricio da Silva Marinho
Ao copiar, dê crédito ao autor.

terça-feira, 11 de dezembro de 2012

Manifesto for Software Craftsmanship

Raising the bar! Sign it today!

As aspiring Software Craftsmen we are raising the bar of professional software development by practicing it and helping others learn the craft. Through this work we have come to value:

Not only working software,
       but also well-crafted software

Not only responding to change,
       but also steadily adding value

Not only individuals and interactions,
       but also a community of professionals

Not only customer collaboration,
       but also productive partnerships

That is, in pursuit of the items on the left we have found the items on the right to be indispensable.

© 2009, the undersigned.
this statement may be freely copied in any form,
but only in its entirety through this notice.

segunda-feira, 10 de dezembro de 2012

A Importância dos Testes de Software

Testar ajuda no entendimento do código:

Normalmente quando começamos a trabalhar num projeto que já está em andamento, não temos idéia do como as coisas funcionam. Às vezes o projeto já tem vários meses de vida e o novo time precisa dar continuidade. Sem os testes unitários e/ou aceitação o tempo para entender como cada parte funciona seria bem maior.

Testar elimina suposições falsas

Quando aprendemos algo novo, suposições são naturalmente feitas. Contudo quando não há entendimento completo do que algo faz, fazemos suposições, quais podem custar muito tempo. Quando nos deparamos com um problema, validamos todas as suposições.

Escrever algumas linhas de código não sai caro e pode de grande utilidade para aumentar a confiança. Grandes problemas pelos quais passamos são causados por coisas pequenas, geralmente, por conta de suposições falsas.

É importante ter segurança

Para cada teste escrito, um pouco mais de segurança é obtida. Se não temos certeza do que um método faz, escrevemos um teste: algumas linhas de código e muitas dores de cabeça serão evitadas no futuro.

"Nada é tão simples que não mereça ser testado". Escreva testes unitários até que você tenha segurança sobre o entendimento o problema.

Testes podem ser utilizados como exemplo para outras pessoas

Testar se mostra ainda mais benéfico quando estamos trabalhando em time. Cada teste é um exemplo prático de como o código deve ser utilizado. Testes servem como exemplos e são de fácil entendimento.

Ao testar o código que você está escrevendo, você não apenas ajuda a si mesmo, mas, poupa muito tempo das pessoas que vão mantê-lo no futuro.

Até a próxima! =]

segunda-feira, 3 de dezembro de 2012

Objetos imutáveis

Objetos imutáveis são aqueles que não permitem modificação no seu estado após sua construção. Joshua Bloch, no seu livro Effective Java, faz a seguinte recomendação: 

"Classes deveriam ser imutáveis a menos que tenhamos uma ótima razão para que seja ao contrário. Se a classe não pode ser imutável, limite a mutabilidade.”  

A utilização deste recurso simplifica a codificação, caso as seguintes premissas sejam observadas:
  • Tenha certeza a classe não possa ser estendida ou tenha seus métodos sobrescritos: utilize o modificador "final" na definição da classe ou utilize fábricas estáticas e esconda os construtores com o modificador de acesso "private";
  • Todos os atributos da classe devem ter ser "private" e/ou "final".
  • Não devem estar disponíveis quaisquer métodos que possam modificar o estado do objeto, nem mesmo assessores de atribuição (sets);
  • Deve proporcionar a construção em uma única chamada, ao invés de disponibilizar um construtor sem argumentos, e sucessivas chamadas a assessores ou quaisquer outros métodos;
  • Se a classe precisa que quaisquer campos sejam mutáveis, estes terão que disponibilizar cópias defensivas.
Objetos imutáveis possuem uma grande lista de vantagens. Sem dúvidas, serão os objetos mais simples e robustos que você pode utilizar. Suas principais características são:
  • São de simples construção, teste e utilização;
  • São automaticamente "thread-safe" e não possuem problemas de sincronização;
  • Não necessitam de construtores de cópia, nem da interface “clone”;
  • Possibilitam seu uso em caches de objetos por utilizar hashCode em "lazy initializations";
  • Não necessitam serem copiados de forma defensiva quando utilizados como atributos em outros objetos (String, Integer, etc);
  • São perfeitos para utilizar como índices em Maps e elementos de um Set por não mudar de estado enquanto estão atribuídos a uma coleção;
  • Possuem o que Joshua Bloch chama de "failure atomicity": um objeto imutável quando lança uma exceção, esta nunca será deixada num estado indeterminado ou não desejado.
Exemplos de objetos imutáveis: String, Integer, Boolean, Datetime (Joda Time API).

Até a próxima! =] 

quarta-feira, 28 de novembro de 2012

Dicas para melhorar a qualidade do seu código

1. Mantenha o nome dos métodos o mais curto possível;

Os métodos quais estejam longos demais ao ponto que seja necessário utilizar a rolagem da tela necessitam de maior concentração e provavelmente o programador tenha dificuldades em ver o todo. De 5 a 20 linhas é um bom tamanho para na maioria dos casos.

2. Nem cogite a possibilidade de utilizar a mesma variável para mais de um propósito;

uma variável deve ser utilizar para apenas um propósito. Ao transformar seu comportamento com o modificador "final", podemos ajudar o compilador a otimizar o código, bem como deixar claro que o seu valor não vai mudar. É uma boa prática que deixa o código mais legível.

3. Procure nomear métodos e variáveis de forma clara e objetiva (anti-burro);

Qualquer um deve ser capaz de entender o código enquanto estiver olhando para ele. Evite ao máximo usar abreviações.

4. Declare a variável o mais próximo possível de onde serão utilizadas;

Normalmente ao fazer algum trabalho manual, posicionamos as ferramentas o mais próximo possível de nós! O mesmo deve acontecer com uma variável, para que não se perca o contexto de sua utilização em mais longos (com aproximadamente 20 linhas).

5. Não utilize números mágicos;

prefira sempre criar constantes ao comparar um valor variável com outro constante. Nada pior do que se deparar com o seguinte código durante a resolução de um bug:
va < 100
Acredito que seria mais fácil caso a linha acima fosse escrita da seguinte forma:
velocidadeAtual < VELOCIDADE_MAXIMA_PERMITIDA
6. Respeite as características da linguagem que esteja utilizando;

Digamos que você seja um programador Java que esteja aprendendo Ruby. Procure entender como fazer algo na nova linguagem, ao invés de tentar "portar" seu código escrito em Java. Normalmente a mesma coisa é feita de forma diferente em cada linguagem, conforme o exemplo a abaixo

em java:
for (int i = 0; i &lt; 5; i++) {

   System.out.println("Hello world!");

}

portado para o Ruby:
for i in (0..5)

   puts "Hello world!"

end
Como deveria ser em Ruby:
5.times { puts "Hello world!"}
7. Não vá contra os padrões de codificação;

Muitas linguagens possuem padrões de codificação, sendo a mais conhecida, a do Java. Quebrar quaisquer regras deste padrão só pode ser feita caso exista, realmente, uma boa razão para tal. Não o faça por simplesmente não gostar de algo.

8. Não otimize código antes da hora;

Em primeiro lugar, escreva código bem legível e totalmente coberto por testes unitários. Não se preocupe caso não seja executado na velocidade desejada. Faça o "refactory" a medida for necessário. Se realmente for necessária qualquer otimização, em primeiro lugar ache o ponto correto, para tanto, use ferramentas "Profiler". Evite resolver problemas que não existem.

9. Sempre faça o "refactory" seu código após testá-lo;

Uma forma de melhorar a qualidade do código e pelo "refactoring", após a bateria de testes unitários. Utilize as melhores práticas de TDD (Desenvolvimento Orientado a Testes) durante o ciclo de desenvolvimento de uma funcionalidade. Assim que o código passe em todos os testes, melhore seu código e o deixe o mais legível possível, sempre executado toda a bateria de testes para assegurar que nada quebrou durante o processo.

10. não seja bloqueado pelo "overdesign";

Padrões de Desenho (Design Patterns) devem ser utilizados para simplificar o desenho da solução, não significa que podem ser utilizados em todos os lugares e em todas as situações. Entenda primeiro a necessidade, construa a solução utilizando as melhores práticas de TDD e aí sim, melhorar aplicando o padrão correto, quando possível.

11. aprenda algo novo com prototipação.

Programação envolve o aprendizado de coisas novas. Quando se aprende uma nova biblioteca ou linguagem, geralmente surge a vontade e jogar fora todo o código antigo e aí sim, escrever tudo novo do zero. Definitivamente não é uma bom a fazer.

Adicionar a nova biblioteca ou linguagem a uma aplicação já existente pode causar o mesmo problema. Normalmente não é bom trocar todas as funções escritas em Javascript pelo JQuery, enquanto você ainda está aprendendo como ele funciona.

A melhor forma é criar projetos de exemplo, onde serão resolvidos os mesmos problemas com as novas bibliotecas ou linguagens. Estude, crie um protótipo e quando tiver a real noção do que pode ou o que não pode ser feito com elas. Daí sim, as utilize nos seus projetos.

Recomendo fortemente a leitura dos livros Code Complete, e Clean Code, cujos autores Steve McConnell do Robert C. Martin respectivamente.

Até a próxima! =]

sexta-feira, 9 de novembro de 2012

[VRaptor] Melhorando Hash MD5 com Salt

Vamos utilizar uma técnica chamada Salt, para adicionar melhor segurança à estratégia de guardar o hash das senhas no banco.

Esta técnica consiste em atribuir um valor aleatório, por cadastro, para que somado a senha, gere um hash.

O objetivo é impedir que existam senhas fracas no banco, suscetíveis a ataques de força bruta baseados em dicionários.

Alguns sites undergrounds oferecem dicionários com mais de 8 bilhões de entradas. O Linkedin sofreu um ataque desta natureza há algum tempo e como resultado, vazaram cerca de 6 milhões de senhas.

Espero que você goste:



O salt pode ser modificado de tempos em tempos, automaticamente, atualizando os hashes. E pode ser mantido em um local diferente. Você ainda pode guardar o salt cifrado em um algoritmo diferente.

Outras estratégias podem auxiliar a sofisticar mais ainda o processo, como gerar o hash do hash com diferentes algoritmos em várias iterações e por aí vai...

OBS: Esta técnica ajuda defesas contra ataques no banco de dados. Não elimina a necessidade da adoção de senhas fortes, uma vez que existem ataques direto a formulários e que exploram as fragilidades do protocolo http.

Espero ter te ajudado, apreciarei todas as críticas, dúvidas e sugestões.

Forte abraço,

Mauricio da Silva Marinho.

quinta-feira, 8 de novembro de 2012

[Java] Thread X Runnable

Threads está entre os assuntos mais difíceis do exame SCJP/OCJP. O que podemos dizer sobre elas...

Basicamente, temos dois tipos de ambiente de execução (em se tratando de execução):

  • Execução de segmentação única. Onde as ações são executadas em fila, uma após a outra, necessariamente. 
  • Execução concorrente. Onde várias ações disputam a atenção dos recursos de forma independente.

Neste último caso, em Java, as ações concorrentes são threads concorrentes. É o que chamamos de threads de execução. Dispomos deste recurso quando precisamos que alguma ação ocorra independente de outra(s), quando não podemos ficar esperando uma ação terminar para continuar executando uma tarefa ou uma sequencia de tarefas.

Em Java, uma thread é uma instância de java.lang.Thread. As execuções de aplicações Java, ocorrem em pilhas de chamadas. Cada pilha de chamadas reside em uma única Thread. Logo, ao criar uma Thread, estamos criando uma nova pilha de chamadas, no mesmo processo.

Um processo é um conjunto de tarefas, que em um SO multitarefa, pode executá-las concorrencialmente. Exatamente, as Threads!

Como a VM tratará a execução das threads dependerá da implementação da VM. Isso mesmo! Não espere garantir que uma aplicação que crie várias instâncias de Thread tenha o mesmo comportamento em VMs diferentes. Então, se este for o caso, não se pode basear a implementação em uma VM específica, a menos que saiba que a aplicação só será executada naquela VM.

A classe java.lang.Thread expõe alguns métodos para gerenciar as threads:

  • start()
  • yield()
  • sleep()
  • run() - chame aqui o código que será executado pela thread.
Cria-se uma Thread de duas formas:
  • Estendendo java.lang.Thread, ou
  • implementando java.lang.Runnable.

Estender Thread pode parecer mais fácil, mas certamente não é a solução mais elegante!

Por que? Simplesmente pelo fato de que a herança, em OO, é um recurso para se especializar uma classe, para que se faça algo novo.

Então se você precisa apenas criar uma execução em uma thread separada, não há necessidade de especializar Thread. Lembre-se que ao estender Thread, não poderá estender mais nada! Neste caso, o mais lógico, seguindo princípios de OO, seria implementar Runnable.

Implementar Runnable vai permitir que sua classe estenda outra classe de negócio, além de dispor dos comportamentos de Thread (ou de Runnable. Thread implementa Runnable, sabia?!).

Dizemos que estender Thread é mais simples porque por este caminho, para criar uma nova thread, basta instanciar a subclasse de Thread que você criou:

public class minhaThread extends Thread{
  public void run(){
    //Chama a execucao da nova thread aqui.
  }
}

MinhaThread novaThread = new MinhaThread();

Implementando Runnable (recomendável na esmagadora maioria dos casos), ficaria assim:

//Cria a classe que implementa Runnable
public class MinhaRunnable implements Runnable{
  public void run(){
    //Chama a execucao da nova thread aqui.
  }
}

//Instancia MinhaRunnable
MinhaRunnable minhaRunnable = new MinhaRunnable();

//Cria uma instancia de Thread, passando sua Runnable para executar
Thread novaThread = new Thread(minhaRunnable);

A classe Runnable que passamos para a Thread, tecnicamente chama-se destino (target).

Até o momento, a thread foi criada, mas não foi executada, dizemos que o estado dela é new. Para executar a thread, basta chamar .start() e a thread passará de new para running:

//Para iniciar a thread
novaThread.start();

Sacou?

Espero ter te ajudado. Críticas, dúvidas e sugestões são bem vindas.

Forte abraço e até o próximo post.

Mauricio da Silva Marinho

terça-feira, 6 de novembro de 2012

[VRaptor] Configurando o Hibernate

Olá, mais uma vez.

Neste vídeo tento explicar como configurar o Hibernate no VRaptor, para que ele (o VRaptor) gerencie a injeção das sessões do Hibernate nos DAOs. O projeto utilizado é o kanban, projeto criado no post anterior.
 
 Espero que este tutorial e suas explicações lhe sejam úteis de alguma forma. Apreciarei qualquer crítica e/ou sugestão.

 Forte abraço, Mauricio da Silva Marinho

sábado, 3 de novembro de 2012

[VRaptor] vraptor-blank-project no NetBeans

Importar o projeto em branco do vraptor (vraptor_blank_project) pode ser um pouquinho problemático pelo fato de os arquivos de propriedades do projeto para o NetBeans não estarem atualizados com o projeto. Veja neste vídeo explicativo como fazê-lo, inclusive, mudando o nome do projeto sem muitos problemas.


Espero que gostem, pois é o primeiro vídeo de muitos. Estou aprendendo a editar, os próximos serão melhores.

Forte abraço,

Mauricio da Silva Marinho

sexta-feira, 2 de novembro de 2012

[Java] Conversão de Tipos (Cast)

Pode parecer um assunto batido, mas será que não tem uns "peguinhas" para quem está de olho no exame para certificação?

Convertendo variáveis de referência.

Este assunto tem uma relação com polimorfismo. A mágica do polimorfismo está em você poder referenciar um tipo mais específico com uma variável de um tipo mais genérico.

Isto é possível porque o compilador verifica a árvore de herança das classes envolvidas neste processo, veja:

public class Veiculo{
  public void informaLotacao(){
     System.out.println("Um motorista.");
  }
}

public class Ferrari extends Veiculo{
  public void informaLotacao(){
    System.out.println("Um motorista e um passageiro.");
  }
  public void informaPotencia(){
    System.out.println("460 cavalos.");
  }
}

public class Onibus extends Veiculo{
  public void informaLotacao(){
    System.out.println("Um motorista e trinta e dois passageiros.");
  }
  public void informaCombustivel(){
    System.out.println("Diesel.");
  }
}

public class TestaVeiculo{
  public static void main(String[] args){
    Veiculo ferrari = new Ferrari();
    Veiculo onibus = new Onibus();
    System.out.println("Uma ferrari tem espaço para " + ferrari.informaLotacao() + " e possui " + ferrari.informaPotencia());
    System.out.println("Um ônibus tem espaco para " + onibus.informaLotacao() + " e funciona com " + onibus.informaCombustivel());
  }
}

Ao executar TestaVeiculo que saída você espera receber?

Na verdade, vai acontecer um erro de compilação dizendo cannot find symbol. Isso porque as referências são do tipo Veiculo, que não possui o metodo informaPotencia() e nem informaCombustivel().

Entenda que você só pode chamar os métodos que o tipo da sua variável de referência expõe para você, mesmo que seja uma referência para uma instância de um tipo mais especializado, com mais métodos.

Para acessar os métodos do tipo especializado temos primeiro que converter os tipos, veja:

public class TestaVeiculo{
  public static void main(String[] args){
    Veiculo ferrari = new Ferrari();
    Veiculo onibus = new Onibus();
    Ferrari ferrariConvertida = (Ferrari) ferrari;
    Onibus onibusConvertido = (Onibus) onibus;
    System.out.println("Uma ferrari tem espaço para " + ferrariConvertida.informaLotacao() + " e possui " + ferrariConvertida.informaPotencia());
    System.out.println("Um ônibus tem espaco para " + onibusConvertido.informaLotacao() + " e funciona com " + onibusConvertido.informaCombustivel());
  }
}

Agora você fez as pazes com o compilador, realizando o cast (conversão) de sua referência generica para um tipo mais específico, que expõe os métodos que você quer usar. Tudo certo! Mas pense comigo: já que o compilador olha apenas para a árvore de herança, o que aconteçe em uma situação assim:

public class TestaVeiculo{
  public static void main(String[] args){
    Veiculo ferrari = new Veiculo();
    Veiculo onibus = new Veiculo();
    Ferrari ferrariConvertida = (Ferrari) ferrari;
    Onibus onibusConvertido = (Onibus) onibus;
    System.out.println("Uma ferrari tem espaço para " + ferrariConvertida.informaLotacao() + " e possui " + ferrariConvertida.informaPotencia());
    System.out.println("Um ônibus tem espaco para " + onibusConvertido.informaLotacao() + " e funciona com " + onibusConvertido.informaCombustivel());
  }
}

Bom neste caso, você prova que o compilador só olha para a árvore de herança, porque ele não vai reclamar de nada. Entretanto, ao executar o código você vai receber um java.lang.ClassCastException, porque as variáveis de referência ferrari e onibus agora são do tipo Veiculo e um tipo generalizado não cabe em um tipo especializado, ou seja, Ferrari é um Veiculo, mas Veiculo, não necessariamente, é uma Ferrari. Ou ainda, Veiculo pode ser um Onibus, que é bem diferente de uma Ferrari, concorda?

Saber o que não compila e o que compila e não roda é fundamental para o exame.

Forte abraço e até a próxima.

Mauricio da Silva Marinho.

[Java] Overloading ou Overriding? (Parte 2/2)

Uns chamam de sobrescrita outros de subscrita, eu acho subscrita feio, mas sobrescrita está errado, já que overriding não quer dizer isto. Bem vou adotar subscrita e os que tiverem outras idéias podem postar como resposta a este post. Ficarei muito grato!

Overriding (Subscrita).

Subscrita é dar outra implementação a um método que já existe na classe herdada, exceto, é claro, se o método em questão for declarado, na classe mãe, como final ou static.

É o que ocorre com os métodos abstratos herdados de uma superclasse abstrata. Neste caso, a menos que a subclasse também seja abstrata, é obrigatório prover a implementação do método.

Então podemos dizer sem medo de errar, que a subclasse concreta subscreve os métodos abstratos herdados de uma superclasse abstrata.

Nestes casos, você concorda que poderemos ter várias implementações para cada subclasse que subscrever um método abstrato herdado de uma mesma subclasse?

Então podemos dizer que teremos, neste caso, várias formas (implementações) do mesmo método?

É claro que sim, concordo com você, então estamos diante de um polimorfismo, correto?

Correto, você acertou novamente.

Veja um exemplo para prosseguirmos em nosso raciocínio:

public class Animal{
  public void moveSe(){
    System.out.println("Sai de um lugar para outro.");
  }
}

public class Cobra extends Animal{
  public void moveSe(){
    System.out.println("Sai de um lugar para outro, rastejando.");
  }
}

public class Canguru extends Animal{
  public void moveSe(){
    System.out.println("Sai de um lugar para outro, saltando.");
  }
}

public class TesteAnimal(){
  public static void main(String[] args){
    Animal cobra = new Cobra();
    Animal canguru = new Canguru();
    cobra.moveSe();
    canguru.moveSe();
  }
}

O exemplo anterior, compilando sem problemas, produzirá que saída ao executarmos a classe TestAnimal?

Exatamente, a saída será:

Sai de um lugar para o outro, rastejando.
Sai de um lugar para o outro, saltando.

Isto porque, apesar de cobra e canguru serem do tipo Animal, cada uma destas referências, aponta para uma instância de Cobra e Canguru, respectivamente. Portanto, o método subscrito será invocado na execução.

Lembre-se que isto só é possível porque Animal também tem um método moveSe(). Caso contrário, o método moveSe() não teria visibilidade para uma referência do tipo Animal. E mesmo porque, se Animal não tivesse moveSe(), não estaríamos falando de subscrita (e o animal não estaria no código).

Veja outras restrições para um método subscrito:
  • Não pode ter um modificador de acesso mais restritivo.
  • Não pode ter uma assinatura diferente.
  • Não pode ter um tipo de retorno diferente.
  • Não pode ter nível de acesso mais restritivo.
  • Apenas métodos herdados podem ser subscritos. Lembrando, se o método original for final ou static, não poderá ser subscrito, e se for private, só poderá ser subscrito por subclasses no mesmo pacote da superclasse.
  • Não pode lançar exceções verificadas novas ou mais abrangentes que as declaradas pelo método original da superclasse.


É isso! Espero ter-lhe ajudado de alguma forma com estas informações. Qualquer dúvida ou contribuição, fique a vontade para postar. Terei imenso prazer em responder todas.

Mauricio da Silva Marinho.


[Java] Overloading ou Overriding? (Parte 1/2)

Qual a diferença entre estas duas técnicas de programação OO? O que significam?

Overloading (Sobrecarga).

Um método sobrecarregado é aquele que aparece mais de uma vez em uma classe (ou subclasse) com assinatura (lista de argumentos) distinta.

O tipo de retorno e a implementação, podem ser, opcionalmente diferentes. Então podemos dizer que métodos sobrecarregados:

  • devem possuir assinaturas distintas.
  • podem possuir tipos de retorno distintos.
  • podem possuir modificadores de acesso distintos.
  • podem declarar exceções verificadas novas ou mais abrangentes.

Veja o seguinte exemplo de método sobrecarregado:

class Aritmetica{
  public int soma(int a, int b){
    return a + b;
  }
  public double soma(double a, double b){
    return a + b;
  }
}

//Chamando o metodo soma sobrecarregado.
public class TestaAritmetica{
  public static void main(String[] args){
    Aritmetica calculadora = new Aritmetica();
    int a = 10;
    int b = 20;
    int somaDosInts = calculadora.soma(a, b);
    int somaDosDoubles = calculadora.soma(10.5 + 20.5);
  }
}

No exemplo anterior, na classe TestaAritmetica, somaDosInts recebe o retorno da chamada a primeira forma do método sobrecarregado Aritmetica.soma que é soma(int, int). E somaDosDoubles recebe o retorno da chamada a segunda forma do método sobrecarregado Aritmetica.soma, que é soma(double, double). É um método polimórfico, pois tem mais de uma forma.

É o tipo de argumento que irá acionar a forma chamada de um método sobrecarregado, pois a identidade do método é dada pela sua assinatura. Veja este outro exemplo:

class Pet{}

class Cao extends Pet{}

class Lar{
  public void maisUmNaFamilia(Pet pet){
    System.out.println("Chegou um pet na casa!");
  }
  public void maisUmNaFamilia(Cao cao){
    System.out.println("Chegou um cão na casa!");
  }
  public static void main(String[] args){
    Lar lar = new Lar();
    Pet pet = new pet();
    Cao cao = new Cao();
    Pet refParaCao = new Cao();
    lar.maisUmNaFamilia(pet);
    lar.maisUmNaFamilia(cao);
    lar.maisUmNaFamilia(refParaCao);
  }
}

E no caso do exemplo anterior, qual seria a saída?

Lembre-se: a forma do método sobrecarregado será identificada pelos tipos da lista de parâmetros, pela assinatura do método. Logo, independente de refParaCao guardar uma referência de Cao, seu tipo é Pet e por isso o método forma do método sobregarregado Lar.maisUmNaFamilia que será invocada é: Lar.maisUmNaFamilia(Pet). Potanto a saída será:

Chegou um pet na casa!
Chegou um cao na casa!
Chegou um pet na casa!

É isso aí! Estes conhecimentos são cobrados na prova SCJP/OCJP e espero que lhe ajudem a esclarecer algumas questões importantes. No próximo post discutiremos Overriding, as confusões entre os dois conceitos e como o examinador pode se aproveitar disso para lhe confundir no exame.

Forte abraço,

Mauricio da Silva Marinho


quarta-feira, 31 de outubro de 2012

[JavaScript] JavaScript SideServer!? Isso pode, Arnaldo?

Ainda há pouco estava passeando na Internet, buscando material sobre JavaScript side server quando lembrei, que há muito tempo atrás, entrei em contato com uma iniciativa da antiga Borland, em produzir algo do tipo e vejam o que achei:

Isso deve arrancar nostalgia de alguns. Foi uma excelente ferramenta para o desenvolvimento de páginas dinâmicas para a web, nos idos de 1997/98. Permitia o desenvolvimento de formulários no melhor estilo drag and drop, com um editor muito parecido com o do Delphi. Muito bem feita. Curiosamente teve poucas versões e vida curtíssima. Na Amazon ainda restam 4 unidades do software original para venda. Veja que esta versão é para Windows 95 ou NT e que vinha com o navegador Netscape. Cara!!! Pra quem quiser matar a saudade: 
  • http://www.theopensourcery.com/keepopen/2006/blast-from-the-past-2-borland-intrabuilder/
  • http://alamopc.org/pcalamode/reviews/archive1/rev187.htm
  • http://www.drdobbs.com/borlands-intrabuilder-10/184415559
Bom, recentemente tivemos uma iniciativa da Aptana com o Jaxer, um servidor ajax rodando sobre a máquina do Mozilla. Possuía recursos para escalonamento elástico, preparado para cloud. Com ele era possível marcar um bloco <script> para rodar no cliente, no servidor ou em ambos. Como ele rodava em qualquer container Servlet/JSP, aproveitava recursos da VM para conexão com bancos de dados com prepared statement e tudo mais.

Bem, infelizmente, pouco se encontra do Jaxer hoje na Internet. Muitas outras iniciativas entre o IntraBuilder e o Jaxer aconteceram e curiosamente morreram antes de atingir a maturidade.

Por que alguém anteciparia a aposentadoria de um bom produto a ponto de explodir na indústria e no mercado antes da hora? Por que ninguém quer usar JavaScript fora do navegador?

A bola da vez é o Node.js. Tecnologia JavaScript side server que promete. Já nasce quebrando paradigmas. A idéia de que uma linguagem de programação tem seu sucesso dependendo de sua interação com bancos relacionais é uma delas. Node.js não se propõe a isso. É uma tecnologia para requisições massivas, mais próximo dos No SQL.

No Node.js, do lado do server, para criar uma API RESTful, você importa o módulo http com a instrução require, atribuindo-o a uma variável. Utiliza o método createServer do http para passar uma função anônima que recebe dois parâmetros request e response - há agora sim - e que tem uma propriedade listen, que recebe uma porta. Fica algo assim:

var http = require("http");

http.createServer(function(request, response){
    //Aqui eh a hora do show me the code...
}).listen(80);

Este exemplo é uma API RESTful. O código fica em um arquivo .js e executamos ele chamando o node na linha de comando passando o arquivo como parâmetro. Um servidor será criado ouvindo na porta 80.

O exemplo completo você vê aqui: http://www.ibm.com/developerworks/br/library/os-nodejs/

O Node.js é um paradigma novo, a idéia é um recurso do lado do servidor, rápido, assíncrono e sem bloqueio. Bancos relacionais são síncronos e com bloqueio, logo, Node.js não foi feito para bancos relacionais. O mundo do Node.js é Cloud + No SQL. Sacou?

Forte abraço, até o próximo post.

Mauricio da Silva Marinho

terça-feira, 30 de outubro de 2012

[JavaScript] Onde colocar o script?

Salve salve. Conforme prometido, segue o post (que já estava preparado, mas faz de conta que não), sobre onde colocar o bloco <script> na sua página.

Alguns podem estar se perguntando, mas que importância tem a localização do bloco <script>. Respondo: Muita!

É verdade. Muita coisa pode melhorar ou piorar quanto a performance de execução da sua página a depender da localização do seu javascript, acredite.

Para entender melhor, vamos falar um pouco sobre carga e execução da página.

Como você já sabe, sua página HTML é interpretada e executada pelo seu navegador (sério!?). Ocorre que - regra geral - ao encontrar blocos <script> a execução de HTML é interrompida para que o script seja carregado, interpretado e executado e só então a execução do HTML continua.

Como o processo que executa a página é o mesmo (HTML e JavaScript) - regra geral - não dá pra fazer as duas coisas ao mesmo tempo.

Ainda existe o problema de você carregar um script de manipulação de um elemento DOM antes da declaração do elemento em HTML. O resultado disto é que seu script não poderá manipular o objeto, pois ele ainda não existe, não foi interpretado e muito menos executado pelo browser.

Neste momento, é irrelevante se o código está declarado dentro da tag <script> ou em um arquivo externo .js referenciado no atributo src daquela tag. A página vai ter que esperar até que o código seja baixado, lido e executado.

A especificação do HTML 4 indica que <script> deve ficar em <head> ou <body> e pode aparecer várias vezes dentro destes dois elementos.

Tradicionalmente, as tags <script> são utilizadas para carregar arquivos .js e são posicionadas no <head> junto com tags <link> que carregam arquivos .css.

Pense no seguinte: O HTML só vai começar a ser renderizado, após todos os scripts referenciados serem baixados e executados. Se você permitisse que todo o conteúdo HTML fosse carregado antes dos scripts, talvez pudesse melhorar a percepção de performance da página. Lembre-se que os navegadores não começam a mostrar nada na página (fica em branco) até que cheguem na tag <body>. Se os scripts forem pesados, o delay da página em branco será maior e consequentemente a percepção de lentidão vai aumentar. Observe um exemplo hipotético de página com 3 scripts referenciados em seu <head> e o tempo de carga/execução de cada um deles:

  • arquivo1.js (500ms).
  • arquivo2.js (350ms).
  • arquivo3.js (400ms).

Movendo estas referências para o final da página, você irá ganhar mais de 1s no início da exibição do documento, o suficiente para melhorar a percepção de performance.

IE 8+, Firefox 3.5+, Safari 4+ e Chrome 2+ já permitem o download paralelo dos recursos externos, incluindo outros scripts. Mesmo assim, o download dos scripts irá concorrer com o download de outros recursos visuais da página e o cliclo de vida irá interromper a execução da página até que todos os scripts do <head> sejam executados.

Conclusão: Colocar os scripts mais próximos do rodapé da página, antes de </body>, pode melhorar a performance, este é o posicionamento recomendado, respeitando a ordem de dependência dos scripts. Ah! Muito importante: Mantenha os blocos <script> agrupados e compactados. Várias técnicas de compactação e agrupamento de scripts estão disponíveis, dentre elas a minificaçao e a concatenação de scripts por uma ferramenta de build como o Yahoo combo handler, que permite que você referencia mais de um script em uma mesma tag <script>, mas estes são assuntos para serem explorados em outra oportunidade.

Espero que estes conhecimentos lhe sejam úteis de alguma forma.

Forte abraço,

Mauricio da Silva Marinho.

[JavaScript] Scripts Dinâmicos

[JavaScript] Scripts Dinâmicos.

Com conhecimentos de DOM (Document Object Model) você pode criar praticamente qualquer parte do seu HTML dinamicamente, usando apenas JavaScript. O elemento <script> não é diferente. Referências podem ser recuperadas do DOM, modificadas, removidas ou até mesmo criadas. Um novo elemento <script> pode ser facilmente criado usando métodos do DOM da seguinte forma:

var script = document.createElement("script");
script.type = "text/javascript"; //desnecessario no HTML5
script.src = arquivo1.js;
document.getElementsByTagName("head")[0].appendChild(script);

Este novo elemento <script> carrega o arquivo arquivo1.js. O arquivo começa a ser transferido para o browser tão logo o elemento seja adicionado na página. Vale lembrar que a interpretação do HTML é interrompida cada vez que um elemento <script> é encontrado e retomada após a execução do código em <script>.

O interessante nesta técnica, é que o arquivo é transferido e executado sem bloquear outros processos na página, não importando onde o download tenha sido iniciado. Você pode colocar este código no <head> do documento sem afetar o resto da página.

OBS: Trataremos do melhor lugar para colocar o <script> no próximo post. Por enquanto, é mais seguro colocá-lo no <head> e não no <body>, especialmente se tratamos de um elemento <script> executado durante o carregamento da página. No IE, se o <script> estiver no <body> é possível receber um erro do tipo "operation aborted" se todo o conteúdo do <body> não estiver completamente carregado.

Quando um arquivo é carregado pela criação dinâmica de um nó <script> no DOM, o código transferido, geralmente, é executado imediatamente (ressalvas para o Firefox e Opera, que irão aguardar até que <script>'s anteriores sejam executados.

Isto funciona bem com scripts auto suficientes (sefl-executing), mas podem ser problemáticos se o código contém interfaces para serem usadas por outros scripts na página. Neste caso você precisa rastrear quando o código foi completamente carregado e está disponível para uso. Se não estiver utilizando JQuery, teremos que fazê-lo pelos eventos que podem ser disparados pelo <script>.

Firefox, Opera, Chrome e Safari 3+ disparam um load quando o src de um <script> for recuperado. Você pode programar uma notificação, atribuindo a este evento uma função anônima para ficar ouvindo-o:

var script = document.createElement("script");
script.type = "text/javascript";

//Firefox, Opera, Chrome, Safari 3+
script.onload = function(){
    alert("Script carregado!");
};

script.src = "arquivo1.js";
document.getElementsByTagName("head")[0].appendChild(script);

No IE, bom... no caso do IE, pra variar, é diferente. Este navegador suporta uma implementação alternativa que dispara um evento readystatechange. Existe uma propriedade readyState no <script> que é carregada várias vezes durante a transferência do arquivo referenciado em seu src. Existem cinco possíveis valores para esta propriedade, são eles:

  • uninitialized, que é o default.
  • loading, quando o download do arquivo começa.
  • loaded, quando o download do arquivo termina.
  • interactive, quando os dados já foram baixados mas ainda não estão completamente disponíveis.
  • complete, quando os dados baixados estão prontos para serem utilizados.

O IE mostra-se inconsistente com os valores loaded e complete. Por vezes ele irá atingir o estado loaded e vai parar, sem disparar o complete. Calma que ainda pode piorar: outras vezes ele vai atingir o complete sem passar pelo loaded.

Sendo assim, é preciso tratar os dois estados relevantes para o carregamento loaded e complete, removendo o event handler (a função anônima atribuída ao evento) quando qualquer um deles ocorrer, para evitar de tratarmos duas vezes a mesma coisa (Ê laiá):

var script = document.createElement("script);
script.type = "text/javascript";

//Internet Explorer
script.onreadystatechange = function(){
    if(script.readyState == "loaded" || script.readyState == "complete"){
        script.onreadystatechange = null; //remove o event handler
        alert("Script carregado, eu acho!");
    }
};

Espero que os conhecimentos deste post lhe sejam úteis. No próximo post veremos o melhor lugar para colocar nossos scripts na página. Forte abraço.

Fonte: High Performance Javascript (Build Faster Web Application Interfaces) - Kindle Version, Location 603. Adaptações e sarcasmos de Mauricio da Silva Marinho.