março 31, 2011

Configurando um pool de conexões no GlassFish



Um pool de conexões é uma espécie de cache de conexões com o banco de dados, que tem por objetivo, torná-las reutilizáveis. Assim, a comunicação com o banco torna-se mais eficiente.

Uma das grandes vantagens, quando se está trabalhando em um ambiente JAVA EE, é o fato de podermos contar com pools de conexões gerenciados pelo próprio container.

Esta característica, permite que aplicações se conectem ao banco de dados, sem que o desenvolvedor precise se preocupar com o ciclo de vida das conexões realizadas. Pois em um ambiente JAVA EE, esta tarefa é responsabilidade do container.

Contudo, para que o desenvolvedor possa contar com essa facilidade, é preciso configurar um pool de conexões. Sendo assim, vou mostrar como isto pode ser feito, utilizando o servidor de aplicações GlassFish e o banco de dados PostgreSQL.
  1. Copie o driver do PostgreSQL para a pasta [GLASSFISH_HOME]/glassfish/domains/domain1/lib/ (domain default). Para isso, basta baixar o driver jdbc4, correspondente à sua versão do PostgreSQL, em http://jdbc.postgresql.org. Desta forma, não será mais preciso utilizar o driver diretamente no classpath (pasta 'lib') de suas aplicações.
  2. Reinicie o servidor para que ele passe a utilizar o driver.
  3. Após a reinicialização, vá ao console de administração (normalmente porta 4848 - ex.: http://localhost:4848/) e selecione a opção Resources/JDBC/Connection Pools, no menu à esquerda.
  4. Clique no botão 'New...'. Defina o nome que achar melhor para o pool de conexões (ex.: meuBancoPool). Selecione a opção  javax.sql.ConnectionPoolDataSource na combo 'Resource Type' e selecione PostgreSQL em 'Database Vendor'. Clique no botão 'Next'.
  5. Selecione o 'Datasource Classnameorg.portgresql.ds.PGConnectionPoolDataSource, na combo. Em seguida, vá ao final da página e informe os seguintes dadosDatabaseName=[nome-do-banco], Password=[senha-do-banco], PortNumber=5432 (porta default), ServerName=[nome-do-servidor ou ip], User=[usuario-do-banco].
  6. Clique em 'Finish' para salvar o pool.
  7. Agora que o pool de conexões foi criado, é hora testá-lo. Para isso, selecione o pool recém criado e clique em 'Ping'. A mensagem "Ping Succeeded", indica que tudo está funcionando corretamente.
  8. Com o pool criado e testado, falta apenas permitir que suas aplicações o utilizem. Sendo assim,  é preciso criar um nome JNDI para ele. Vá ao menu Resources/JDBC/JDBC Resources, especifique o nome no formato jdbc/[nome-do-banco], selecione o pool de conexões criado nos passos anteriores e clique em 'OK', para finalizar. Este nome deverá ser utilizado nas aplicações, para permitir a comunicação com o banco de dados através do pool de conexões.

março 15, 2011

TDD na prática

Java Magazine 89

Escrevi um artigo sobre TDD, para a Java Magazine deste mês.

Nele, eu mostro o desenvolvimento de uma aplicação utilizando a técnica de Test-Driven Development (TDD).

Para conferir, basta acessar:

http://www.devmedia.com.br/post-20154-TDD-na-pratica.html

janeiro 06, 2011

Java e Domain-Driven Design com JSF, EJB e JPA

Java Magazine 87

Saiu um artigo meu, na capa da Java Magazine deste mês. Nele eu mostro a criação de uma aplicação baseada na metodologia de Domain-Driven Design, utilizando os frameworks JSF, EJB e JPA.

Vale a pena conferir!

Link para o artigo:

http://www.devmedia.com.br/post-19019-Java-e-Domain-Driven-Design-na-pratica.html

Link para a revista:

http://www.devmedia.com.br/post-19022-Revista-Java-Magazine-87.html

novembro 18, 2010

Mais polimorfismo, menos estruturas de controle...

Programadores OO estão acostumados com polimorfismo, porém, programadores da escola procedural ou programadores que estão iniciando no mundo OO, não conhecem ou não enxergam a utilidade prática deste recurso.

A definição do Deitel para polimorfismo é:

"O polimorfismo, permite 'programar no geral' em vez de 'programar no específico'. Em particular, o polimorfismo permite escrever programas que processam objetos que compartilham a mesma superclasse em uma hierarquia de classes como se todas fossem objetos da superclasse."

Mas como isso funciona na prática?

Mesmo com uma linguagem orientada a objetos como o JAVA, é possível escrever programas procedurais. Orientação a objetos está muito mais ligada à decisões de design de software, do que simplesmente à utilização de uma linguagem orientada a objetos.

Por exemplo, usando JAVA, poderíamos escrever o seguinte código:

public class TestAnimais {
 
     enum TipoAnimal{CACHORRO, GATO, GALINHA};
 
     public static void main(String[] args) {
  
          TipoAnimal tipo = TipoAnimal.CACHORRO;
          Animal a = new Animal(tipo);
  
          switch (a.getTipo()) {
               case CACHORRO:
                    System.out.println("ROOF");
                    break;
               case GATO:
                    System.out.println("MEOW");
                    break;
               case GALINHA:
                    System.out.println("CÓ-CÓ-CÓ");
                    break; 
               default:
                    break;
          }
    }
}

Este trecho de código é tipicamente procedural. Programas desenvolvidos utilizando este paradigma, costumam ter muitas instruções de controle do tipo if e switch, para tomada de decisões em tempo de execução.

Podemos refatorar o código acima utilizando uma abordagem polimórfica, orientada a objetos, para reduzir o uso de estruturas de controle (menos 'código espaguete') e tornar o design mais extensível.

Primeiro, criamos uma classe abstrata Animal:

public abstract class Animal { 
     public abstract void emitirSom();
}

Em seguida, criamos uma classe concreta Cachorro, que estende Animal.

public class Cachorro extends Animal {

     public void emitirSom() {
          System.out.println("ROOF");
     }
}

Com este design, nossa 'classe cliente' poderia ficar assim:

public class TestAnimais {
 
     public static void main(String[] args) {
          Animal a = new Cachorro();
          new TestAnimais().digaAlgo(a);
     }
 
     // método ilustrativo
     public void digaAlgo(Animal animal){
          // método polimórfico (Qual animal?)
          animal.emitirSom();
     }
}

O polimorfismo se manifesta no momento em que fazemos uma variável de referência da superclasse (Animal), apontar para um objeto da subclasse (Cachorro) na heap. Desta forma, quando ordenamos que um animal emita som, o método emitirSom() utilizado, é resolvido em tempo de execução baseado no tipo do objeto na heap, sem a necessidade de testes condicionais.

Muitos argumentam que programar orientado a objetos é mais lento. De fato, a curto prazo, esta afirmação muitas vezes se mostra verdadeira.

Os grandes benefícios do paradigma aparecem a médio e longo prazo, principalmente na fase de manutenção do projeto (onde se gasta aproximadamente 80% do tempo).

Por exemplo, se quisermos adicionar um novo animal ao projeto, basta criar um classe concreta (ex.: Gato) que estenda Animal, pois não há risco de comprometer a integridade do que já funciona (design extensível), além do fato de que qualquer método que receba ou retorne um objeto do tipo Animal (ex.: public void digaAlgo(Animal animal)), pode trabalhar normalmente com a nova classe concreta, sem a necessidade de adição de código extra, graças ao polimorfismo.

setembro 25, 2010

Trabalhando com JPA, fora de um container Java EE.

Resolvi escrever este post, para compartilhar a minha experiência com a utilização da API de Persistência do JAVA (JPA), fora de um container Java EE (ex.: TOMCAT). No meu caso, o ambiente em questão é composto por: Tomcat, Hibernate e PostgreSQL.

Ao utilizar a JPA em um ambiente "não Java EE", o desenvolvedor não pode contar com o recurso de "Dependency Injection" (pelo menos não, na camada de modelo) e deve, por conta própria, gerenciar o ciclo de vida dos Entity Managers no domain model. Não realizar esta tarefa de forma eficiente, pode resultar em situações desagradáveis, como o travamento do banco de dados por conexões abertas em excesso.

A interface EntityManager, faz a "ponte" entre o mundo orientado a objetos e o mundo relacional,  ela oferece serviços de persistência para objetos, como não é possível contar com a Dependency Injection,  é necessário utilizar a interface EntityManagerFactory, que também faz parte da API, para obter um EntityManager

Entity Managers obtidos através de uma Entity Manager Factory, são conhecidos como Applicaton-Managed Entity Managers, é de responsabilidade do desenvolvedor gerenciar seu ciclo de vida, os que são gerenciados pelo container, são conhecidos como Container-managed Entity Managers.

Como EntityManagerFactory é um objeto de carga pesada, uma idéia básica é, criar uma única factory, através de um Singleton e permitir acesso à ela de forma global. Assim, as classes responsáveis por realizar a comunicação com o banco de dados, podem obter objetos do tipo EntityManager, através desta factory compartilhada, sempre que necessário.

Uma abordagem para alcançar este objetivo envolve: 
  1. A Criação de um Listener que verifica se a aplicação foi iniciada ou finalizada;
  2. A Criação de um singleton responsável pela factory
  3. Gerenciar ciclo de vida de Entity Managers no escopo dos métodos que fazem acesso à banco de dados.
1. O Listener

public class PersistenceAppListener implements ServletContextListener {
  
     public void contextInitialized(ServletContextEvent evt) {
     }


     public void contextDestroyed(ServletContextEvent evt) {
          PersistenceManager.getInstance().closeEntityManagerFactory();
     }
}


precisamos adicioná-lo ao web.xml:

<description>ServletContextListener</description>
<listener>
     <listener-class>my.package.PersistenceAppListener</listener-class>
</listener>

A idéia do listener é observar se a aplicação foi finalizada, para liberar os recursos.


2. O Singleton

public class PersistenceManager {
 
 public static final boolean DEBUG = true;
   
   private static final PersistenceManager singleton = 
                                             new PersistenceManager();
   
   protected EntityManagerFactory emf;
   
   public static PersistenceManager getInstance() {
     
     return singleton;
   }
   
   private PersistenceManager() {
   }
  
   public EntityManagerFactory getEntityManagerFactory() {
     
     if (emf == null)
       createEntityManagerFactory();
     return emf;
   }
   
   public void closeEntityManagerFactory() {
     
     if (emf != null) {
       emf.close();
       emf = null;
       if (DEBUG)
         System.out.println("Persistence finished at " + 
                                       new java.util.Date());
     }
   }
   
   protected void createEntityManagerFactory() {
     
     this.emf = Persistence.createEntityManagerFactory("MyUnit");
     if (DEBUG)
       System.out.println("Persistence started at " + 
                                 new java.util.Date());
   }
}

O Singleton é responsável por fornecer a factory global através do método getEntityManagerFactory().

3. Utilizando Entity Managers

Segue o exemplo de código cliente, que utiliza a factory para obter um EntityManager

public class MyRepository  {
     
     // variável de instância 
     private EntityManagerFactory emf = PersistenceManager.getInstance().getEntityManagerFactory();
     
     // Insere objeto no banco
     public void add(myObject object) {
      
          EntityManager em = this.emf.createEntityManager();
          try{
               EntityTransaction et = em.getTransaction();
               try {
                    et.begin();
                    em.persist(object);
                    et.commit();
               } finally {
                    if (et.isActive()) et.rollback();
               }
          } finally {
               em.close();
          }  
     }
}

No geral, esta abordagem atendeu bem às minhas necessidades, mas eu enxerguei duas desvantagens neste modelo:
  1. A dificuldade de utilizar LAZY LOAD de objetos, pois o ciclo de vida dos Entity Managers têm escopo de método. Os relacionamentos @OneToMany e @ManyToMany, utilizam lazy load por default. Uma forma de resolver este problema é anotar forçando EAGER LOADING, ex.: @OneToMany(fetch=FetchType.EAGER)
  2. A quantidade de código extra (try's aninhados), necessário para manter a gerência de ciclo de vida dos Entity Managers, de forma eficiente.
Para saber mais: