Caelum | Ensino e Inovação - Cursos de Java, Scrum, Ruby on Rails


Metaprogramação em Java? O papel do APT.

Por Paulo Silveira em 08/12/09

Em 2004, com o lançamento do Java 5, muitas novidades entraram pra linguagem. As anotações são um recurso hoje fundamental, que utilizamos como metadados. O Hibernate, junto com a JPA/EJB 3.0, popularizaram muito o uso das anotações para afetar o comportamento em tempo de execução do framework em relação às suas classes anotadas.

Em linguagens dinâmicas isso vai além. O Ruby utiliza muito metadata, até mesmo para gerar acessores e modificadores de atributos em tempo de execução:

class ContaPagar
  attr_accessor :descricao, :valor, :data
end

Como poderíamos fazer um exemplo simples desse em Java? Infelizmente não é algo trivial como o uso de anotações feito pelo Hibernate/JPA, pois, mesmo usando manipulação de bytecode, não adianta gerar os getters ou setters em tempo de execução, já que não conseguiremos utilizá-los pela característica de tipagem estática da linguagem. Precisaremos usar uma abordagem em tempo de compilação.

O JAX-WS utiliza bastante de anotações para a geração de código cliente dos webservices através de duas feramentas: o wsimport para trabalhar com WSDL e o Annotation Processor Tool (APT) para trabalhar com código fonte Java. Através do APT, incluso apenas como ferramenta no Java 5, você pode criar Processadores de anotações que são acionados pelo APT e podem gerar novos códigos Java.

O Java 6 leva o APT para um outro nível: além de fazer parte da API (não apenas uma ferramenta com.sun) agora é possível que um Processor sejá executado pelo próprio compilador, sem a necessidade de executá-lo a parte. Para isso, basta que exista um jar no seu classpath que contenha o arquivo javax.annotation.processing.Processor dentro de META-INF/services, fazendo com que os processadores lá citados sejam executados a cada compilação, possivelmente gerando novo código Java.

Onde isso é útil?

Um dos aguardados recursos da JPA 2.0 no Java EE 6 é o sistema de Criteria parecido com o do Hibernate, que pode ser agora utilizado de uma maneira typesafe sem o uso de Strings. Como iremos nos referenciar à um atributo de uma classe sem String? Para uma classe, sabemos que podemos escrever NomeDaClasse.class, e um Class<NomeDaClasse> carregado pelo mesmo classloader será retornado.

Apesar de ser um dos 25 recursos mais pedidos pela comunidade, o Java não possui suporte para literais de construtores, métodos e atributos. Isso é, se quero pegar uma referência para um Method que tenho certeza absoluta que existe, preciso de qualquer forma utilizar a API de reflection e lidar com as checked exceptions e diversos passos necessários. Uma forma de contornar isso e fazer a Criteria typesafe da JPA 2.0 ser viável foi o uso do StaticMetaModel.

Imagine que para a classe ContaPagar a seguir:

@Entity
public class ContaPagar {

  @Id
  @GeneratedValue
  private int id;

  private String descricao;

  private Double valor;

  @Temporal(TemporalType.DATE)
  private Calendar data;

  @ManyToOne
  private Fornecedor fornecedor;

  private boolean pago;

  // metodos
}

Precisaríamos criar uma classe paralela, a ContaPagar_, que contém atributos estáticos que de certa forma representam os atributos persistidos da nossa entidade JPA:

@StaticMetamodel(ContaPagar.class)
public abstract class ContaPagar_ {

  public static 
    volatile SingularAttribute<ContaPagar, Integer> id;
  public static 
    volatile SingularAttribute<ContaPagar, String> descricao;
  public static 
    volatile SingularAttribute<ContaPagar, Double> valor;
  public static 
    volatile SingularAttribute<ContaPagar, Calendar> data;
  public static 
    volatile SingularAttribute<ContaPagar, Fornecedor> fornecedor;
  public static 
    volatile SingularAttribute<ContaPagar, Boolean> pago;
}

Daria um certo trabalho manter a estranha classe ContaPagar_ atualizada de acordo com toda modificação na classe ContaPagar. A solução é utilizar um gerador de código, e esse gerador é um processador do APT, que será invocado toda vez que o javac rodar (no Eclipse é necessário ativar o APT). O Hibernate 3.5-beta2 já disponibiliza esse gerador (mas com limitado suporte ao Criteria do JPA2), assim como o EclipseLink. Dessa forma podemos selecionar o atributo valor de todas as ContaPagar, com garantia de tipos através de ContaPagar_.valor:

CriteriaBuilder cb = manager.getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery(Double.class);
Root cpagar = cq.from(ContaPagar.class);
CriteriaQuery select =
	cq.select(cpagar.get(ContaPagar_.valor));
return this.manager.createQuery(select).getResultList();

Se isso é uma vantagem ou não, é outra discussão: alguns dizem que esse ganho de tipagem forte na Criteria não compensa a quantidade de código extra, além de que seus testes unitários pegariam erros no caso de String erradas. Diferente do que parece a primeira vista, o código não é refatorável da maneira clássica: renomear o atributo valor para valorTotal vai gerar um novo atributo ContaPagar_.valorTotal, mas quem se referenciava ao atributo com nome antigo não será refatorado, pois refatoramos o nome do modelo, e não do metamodelo. As ferramentas devem evoluir para conseguir lidar com casos como esse.

Mas seria essa metaprogramação tão poderosa quanto a do Ruby, onde métodos são inclusos na classe de acordo com o uso de metadados (como o attr_acessor)?

O Sérgio Lopes e o Nico Steppat da Caelum me apresentaram ao lombok, um processador do APT que, através de recursos da API não pública da JVM da Sun, gera código na própria classe que está sendo compilada:

meta programação com Java e APT

À esquerda um código que não possui métodos, à direita uma surpresa: o outline do Eclipse acusando a exitência de métodos no bytecode dessa classe.



Com isso podemos gerar métodos em tempo de compilação que podem ser acessados pela sua IDE, fornecendo até mesmo code completion! Isso abre caminho para recursos que se assemelham aos mixins, e com tipagem estática, e ao mesmo tempo para prejuízos parecidos com os do monkey patching. A força de uma linguagem mais dinâmica permitiu a criação do grails com recursos similares aos de ActiveRecord do rails, onde – no caso do rails – atributos do banco podem ser acessados diretamente sem requerer a declaração no modelo.

O APT abre caminho para implementações similares na plataforma Java usando a própria linguagem, alguém poderá em um futuro próximo adicionar os getters e setters necessários para seu modelo baseado na engenharia reversa de tabelas, relacionamentos e configurações, como o Rails faz, mas com algumas limitações. Há também a questão de que se isso é útil para uma linguagem como Java.

  • Share/Bookmark

Escrevendo e migrando aplicações para o Google App Engine

Por Pedro Matiello em 17/11/09

Recentemente migramos nosso site para o Google App Engine (GAE), o serviço de cloud computing do Google, fazendo uso do suporte a aplicações Java. A ideia é que você desenvolva sua webapplication normalmente e faça o upload do war para os servidores do Google usando um SDK, que também possui um servidor local para desenvolvimento. Pronto! Sua aplicação web agora oferece alta escalabilidade e disponibilidade.

Algumas restrições existem, no entanto. Vamos passar por elas e comentar cada uma, que no início podem ser uma barreira para o desenvolvedor. Para começar, há uma whitelist especificando as classes do Java que são suportadas. As classes da biblioteca padrão não listadas são bloqueadas. Pode parecer inconveniente, mas estas limitações são necessárias para garantir a segurança e escalabilidade do serviço — basta lembrar que uma mesma máquina é dividida entre muitas aplicações, e que cada aplicação pode estar rodando em um grande número de servidores.

O Google App Engine também não permite que novos arquivos sejam criados pela aplicação no servidor, já que isso poderia implicar em problemas de sincronização entre os sistemas de arquivos de suas muitas instâncias. Também não é oferecido um banco de dados relacional, mas sim uma datastore sobre o BigTable, que pode ser acessada pelas APIs do JDO e da JPA. Relacionamentos many-to-many, consultas com join, agregações (sum, avg, max, etc) e polimórficas não são suportadas, mas a maior parte das demais operações funciona como de costume sem a necessidade de maiores mudanças. Trata-se de uma característica comum dos bancos de dados não-relacionais, que abrem mão de algumas funcionalidades para obter maior desempenho, escalabilidade e disponibilidade. Pode ser trabalhoso migrar de um banco de dados relacional para o BigTable, além de ser uma tecnologia proprietária do Google.

Outra limitação importante é a impossibilidade de criar sockets e Threads. Todavia, o App Engine possui mecanismos próprios que permitem a realização de requisições HTTP e o envio de emails (com restrições na especificação do remetente). Em breve uma API de agendamento de tarefas será disponibilizada para o Google App Engine Java.

Ainda, o suporte a Expression Language vem desativado por padrão. Para ativá-lo, é necessário adicionar a seguinte linha em todos os arquivos JSP que fazem uso de EL:
<%@ page isELIgnored="false" %>

No caso de arquivos de tags, deve-se adicionar:
<%@ tag isELIgnored="false" %>

A tag <include-prelude> no web.xml também é ignorada, mas é possível contornar o problema adicionando algo como a linha abaixo no começo dos arquivos JSP:
<%@ include file="../prelude.jspf" %>

Um problema de outra é ordem é a inicialização e a manutenção das instâncias, conhecido como cold start. Quando sua aplicação é chamada pela primeira vez, várias instâncias são produzidas de forma distribuída pelos servidores da nuvem. Este processo é bastante lento, podendo consumir vários segundos, e depende também da velocidade do contexto da sua aplicação web. Os acessos seguintes são respondidos com baixa latência, mas, após um período de inatividade, as instâncias são destruídas, exigindo que todo o lento processo de inicialização seja refeito pelo serviço em um próximo acesso. Para evitar isso, a prática mais comum tem sido usar o agendador de tarefas do próprio App Engine para visitar alguma página do site a cada poucos minutos — e esta é uma solução que também possui seus problemas.

Por causa da grande quantidade de componentes restritos, muitos frameworks, como Spring, implementações de JSF, mapeadores XML, exigem ajustes para rodar no GAE. O mesmo se aplica ao VRaptor 3 e, para facilitar o trabalho dos usuários, muitos deles oferecem uma versão voltada para o uso no Google App Engine, incluindo todas as alterações necessárias. No caso do VRaptor 3, você pode obter um blank-project pro GAE na página de downloads e usa-lo como esqueleto de um novo projeto, que pode ser facilmente importado e modificado no Eclipse (existe um plugin do Google bastante prático, mas um build.xml adequado ao GAE é uma alternativa bastante razoável).

Em post anterior, o Paulo Silveira já havia falado, de forma geral, sobre as vantagens de manter sua aplicação no cloud, mesmo quando o volume de requisições não é grande o bastante para se representar um gargalo, como é o nosso caso e pode ser visto pelos paineis de controle do GAE:

Gráfico de requisições por segundo no www.caelum.com.br

Requisições por segundo no www.caelum.com.br

Sobre o Google App Engine, em particular, vale ainda acrescentar que a administração é bastante simples, com relatórios e estatísticas apresentados de forma coerente, e também que é possível executar múltiplas versões da sua aplicação facilmente. O serviço ainda é beta e tem seus problemas ocasionais, mas, até o momento, tem se comportado de forma bastante satisfatória. Considere o uso do cloud para sua próxima aplicação e livre-se de boa parte da preocupação com hardware, grids, clusters e infraestrutura.

  • Share/Bookmark

A java.net.SocketException Broken Pipe

Por Paulo Silveira em 19/10/09

Quando começamos a programar com banco de dados, rapidamente aprendemos que devemos sempre usar um pool de conexões para acessa-lo, caso contrário podemos facilmente atrapalhar o bom funcionamento do mesmo, devido o excesso de conexões.

Passamos então a usar um pool de conexões, e ao colocar o sistema em produção, nos deparamos com outro problema: o broken pipe:

java.net.SocketException: Broken pipe
at java.net.SocketOutputStream.socketWrite0(Native Method)
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:92)
at java.net.SocketOutputStream.write(SocketOutputStream.java:136)
at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:65)
at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:123)
at com.mysql.jdbc.MysqlIO.send(MysqlIO.java:2690)
...

No GUJ, são mais de 100 mensagens a respeito de broken pipes! Recentemente Tomaz Lavieri abriu um detalhado tópico sobre esse mesmo assunto, que me incentivou a escrever esse post, dada sua relevância.

Por que essa exception acontece? São dois motivos principais:

O primeiro é que muitos bancos de dados possuem um timeout para conexões inativas (o padrão do MySQL é de 8 horas, mas em alguns hosts isso pode estar configurado em segundos!). Depois de determinado tempo, o banco de dados mata essa conexão ociosa numa tentativa de economizar recursos, pois conclui que alguém simplesmente a esqueceu aberta. Quando, no lado do cliente, seu pool decide usá-la, a socket rapidamente percebe que a conexão foi fechada do outro lado, foi quebrada (daí o nome broken pipe). Muito comum ao começar a usar um pool!

O segundo motivo é que você pode estar tratando suas transações sem o devido cuidado: esquecendo de fazer o commit ou o rollback em alguns casos. O banco de dados então pode matar essa conexão depois de algum timeout de transação, porém o seu pool não sabe disso, e quando for utilizar essa conexão, ela está quebrada!

Solução rápida? Configurar o seu pool para testar se as conexões continuam válidas. No C3P0, pool de conexões que recomendamos fortemente, quando usado com o Hibernate, basta fazer no seu hibernate.cfg.xml:

<property name="hibernate.connection.provider_class">
  org.hibernate.connection.C3P0ConnectionProvider
</property>
<property name="hibernate.c3p0.min_size">1</property>
<property name="hibernate.c3p0.max_size">20</property>
<property name="hibernate.c3p0.timeout">30</property>
<property name="hibernate.c3p0.idle_test_period">100</property>

É a configuração hibernate.c3p0.idle_test_period que resolve o broken pipe. Nesse caso o C3P0 fara essa verificação de maneira assíncrona: ele cria threads (3 por padrão) que checam de tanto em tanto tempo (100 segundos nesse caso) se alguma das conexões do pool está inválida (broken pipe é um dos casos). Na existência de uma conexão assim, essa será eliminada do seu pool! Você ainda pode ter um azar muito grande, pois uma conexão pode ter algum problema logo depois que a thread a verificou! Se você quer ter uma confiabilidade de 100% em relação a suas conexões, você pode configurar a variável testConnectionOnCheckout no arquivo c3p0.properties que deve ser colocado no seu classpath. Isso não é muito recomendado, pois toda vez que uma conexão é pega do pool, alguma forma de ping será feito no banco de dados para saber se ela é válida, perdendo um pouco de performance.

Vale lembrar de que isso não é motivo para você se descuidar no tratamento de transações, centralizando isso dentro de um interceptador/filtro que faça o uso correto do try, catch e finally, precavendo-se de qualquer vazamento. Transações, assim como qualquer outro recurso caro (arquivos, conexões, sockets, threads, etc…), deve ter seu ciclo de vida tratado com atenção, de preferência de maneira isolada.

Caso você não use Hibernate, Jerônimo Mozer mostra como usar o C3P0 programaticamente.

Mais detalhes podem ser vistos nas configurações de teste de conexões do C3P0, detalhes do seu funcionamento com o Hibernate e a página do próprio Hibernate sobre esse pool, mas que se encontra um pouco defasada. Também vemos muitos detalhes como esses no capítulo de dia a dia com Hibernate do nosso curso FJ-26.

  • Share/Bookmark

Enfrentando a LazyInitializationException no Hibernate

Por Paulo Silveira em 13/10/09

Sem dúvida o primeiro balde de água fria que levamos ao começar a trabalhar com o Hibernate é a LazyInitializationException. Afinal, quando e por que ela acontece?

Para chegar lá precisamos de um exemplo de relacionamento: uma nota fiscal tem vários itens de compra, um produto tem uma categoria:

@Entity
class NotaFiscal {
  
  @OneToMany
  List<Item> items;
}

Depois de mapeadas nossas entidades, podemos facilmente percorrer esse relacionamento através do acesso a um getter:

NotaFiscal nf = (NotaFiscalsession.load(NotaFiscal.class, 42);
List<Item> items = nf.getItems();

Nesse caso o Hibernate fará dois selects: um para pegar os dados da NotaFiscal com id 42, e outro procurando todos os items where nota_fiscal_id=42. Há também a possibilidade de indicar para o Hibernate de que tudo deva ser feito em um único join, para isso basta configurar que esse relacionamento deve ser pego (fetched) de maneira ansiosa, prontamente (eager):

class NotaFiscal {
  ...
  @OneToMany(fetch=FetchType.EAGER)
  List<Item> items;
}

Vale relembrar os defaults do Hibernate: todos os relacionamentos *ToOne são EAGER, e os *ToMany são LAZY, isso porque relações *toMany são provavelmente mais custosas, trazendo mais objetos para a memória.

É muito interessante ter relacionamentos LAZY: os dados são puxados apenas quando realmente necessários. Ao mesmo tempo deve-se tomar cuidado: pode gerar o problema das n+1 queries, e muitas vezes sabemos que tal relacionamento será tão utilizado, que deve ser feito de maneira EAGER, evitando mais uma query ser disparada ao banco de dados.

Como exatamente funciona o relacionamento lazy? O Hibernate tira proveito de proxies dinâmicas: ele te devolve objetos que fingem ser listas nesse caso, e quando você invoca algum método deles, o Hibernate então faz a respectiva query para carregar o relacionamento. E há um momento em que isso falha: quando a sessão que carregou o objeto já estiver fechada, como no seguinte código caso o relacionamento seja LAZY:

Session session = sessionFactory.openSession();
NotaFiscal nf = (NotaFiscalsession.load(NotaFiscal.class, 42);
session.close();

List<Item> items = nf.getItems();
System.out.println("numero de pedidos dessa nota:" + items.size());

Esse código tem o seguinte resultado:

org.hibernate.LazyInitializationException: failed to lazily initialize a collection - no session or session was closed.

Ao invocar o método size() a proxy dinâmica devolvida pelo getItems() tenta se conectar ao banco para puxar todos os itens dessa nota, porém a sessão já foi fechada! Por que o Hibernate não reabre a sessão? Como eles mesmos afirmam, não faria mais sentido ter demarcação de transação e nem de abertura e fechamento de sessões! E essa responsabilidade seria demais para um framework ORM, pois não é tão óbvio decidir sobre o ciclo de vida de objetos caros, como a Session e a Connection, que poderiam acabar sendo abertas inúmeras vezes para a renderização de uma única página! Outros frameworks de mapeamento objeto relacional usam essa outra abordagem e reconectam a sessão e conexão caso necessário, que eu também considero uma má escolha.

Para resolver este caso acima parece simples: sempre fechar a sessão ao término do trabalho. Mas e quando estamos trabalhando na Web? Após pegarmos os dados necessários no nosso controlador, fechamos a sessão e passamos os objetos ao JSP através de atributos. O JSP, ao acessar um getter do seu objeto para fazer um loop, como ${notaFiscal.items}, recebe LazyInitializationException da mesma forma. Em um primeiro momento o desenvolvedor muda o relacionamento para EAGER, mas isso gera uma enorme sobrecarga, pois provavelmente em muitos lugares não era necessário carregar todos os itens da compra sempre que uma determinada nota fiscal é requisitada.

Como então evitar a LazyInitializationException sem modificar o relacionamento para EAGER?

Open Session In View

A solução é manter a session aberta durante a renderização da camada de visualização, ou como o Hibernate chama esse pequeno padrão: open session in view. A idéia é bastante simples: a sessão deve ser mantida aberta até o fim da renderização do JSP (ou de qualquer outra camada de apresentação). Isso pode ser obtido através da implementação de um Servlet Filter, algum tipo de interceptador do seu framework preferido ou até mesmo aspectos.

O Spring foi sem dúvida um dos primeiros frameworks a já trazer classes para isso embutidas (assim como também foi o primeiro a fazer o wrap da HibernateException dentro de uma exceção unchecked, pois até o Hibernate 2.x essa exceção era checked). Há as classes OpenSessionInViewInterceptor e sua análoga OpenEntityManagerInViewInterceptor. No VRaptor, além de você pode usar os componentes embutidos do Spring, já existem componentes que fazem o mesmo trabalho (ver componentes embutidos).

Aproveite para ler aqui no blog da Caelum a respeito de outras exceptions frequentes no Hibernate, sobre os estados de uma entidade na JPA, hábitos importantes para todo desenvolvedor Hibernate e mapeamento de herança, além de muitos outros artigos relacionados ao framework. O nosso curso FJ-26 trata bastante de Hibernate e JSF com detalhes importantes do dia a dia como esses.

  • Share/Bookmark

VRaptor 3.0 final lançado!

Por Lucas Cavalcanti em 05/10/09

vraptor3 icon

Depois de 8 meses de intenso desenvolvimento, e quase 2 meses depois do primeiro beta público, o framework web MVC VRaptor 3 final está disponível para donwload. O site oficial foi inteiramente reformulado, com uma nova versão da palestra de apresentação do framework e uma extensa documentação.

O princípio básico do VRaptor é que você pode expor os métodos do seu controlador de maneira RESTFul, através de simples anotações. No exemplo a seguir, acessando a URI /usuarios/adiciona por POST, teremos esse método insere invocado e um objeto Usuario populado através dos parâmetros usuario.nome, usuario.endereco, assim por diante:


@Post
@Path("usuarios/adiciona")
void insere(Usuario usuario) {
...
}

Se seu retorno não fosse void, o que é retornado é exposto ao seu view, através de um atributo de request. A partir desse simples modelo temos acesso aos mais variados recursos: a injeção de dependências é feita pelo construtor, e há total integração com Spring, permitindo a criação fácil de testes unitários. E há suporte fácil a Hibernate e JPA, através de ComponentFactories já embutidas no framework, basta você registrá-los e receber Session/EntityManager no construtor. Validação, conversores, redirecionamentos, URIs parametrizadas e todo mais prossegue da mesma maneira elegante.

Diversas empresas já estão usando o VRaptor 3 desde suas versões beta: Wine.com.br, a maior empresa online de vinhos do Brasil, através da Giran.com.br, a Locaweb, a AgenciaClick, a Defferrari, entre outros. Há desenvolvedores rodando o VRaptor 3 em um cluster com 32 máquinas e outras no cloud do Google App Engine!

Como começar já?

Faça o download do projeto vazio (blank project) já preparado para o Eclipse, e siga as instruções do guia de 1 minuto! Você está pronto para tirar suas dúvidas no fórum de discussão do GUJ para frameworks brasileiros!

Agradecemos a todos os desenvolvedores do projeto, e em especial aos usuários, que contribuiram no fórum de maneira surpreendente: são mais de 1000 mensagens sobre o novo VRaptor desde sua versão beta 1!

  • Share/Bookmark



Caelum | Ensino e Inovação
São Paulo: Rua Vergueiro, 3185, cj. 87, próximo ao Metrô Vila Mariana   |   Tel. (11) 5571-2751
Rio de Janeiro: Rua Senador Dantas, 80, cj. 307/308 - Centro   |   Tel. (21) 2220-4156 ou 2297-0033
Brasília: SCS Qd. 8 Bl. B-50, Sala 521 - Ed. Venâncio 2000   |   Tel. (61) 3039-4222