Archive for the ‘Java’ category

Criando e compartilhando componentes Facelets

July 23rd, 2010

Atualmente estou trabalhando em alguns projetos que possuem diversas características em comum. Para facilitar o reaproveitamento de código, criamos um módulo à parte, uma espécie de framework, com todo o código comum aos projetos, e modularizamos os projetos usando Maven.

Quando chegamos na camada de apresentação, percebemos que não estávamos reaproveitando código como nas demais camadas. Pelo contrário, os XHTMLs de várias telas eram bastante parecidos, e estávamos basicamente copiando e colando quando surgia uma tela nova. Inclusive dentro de um mesmo XHTML, muita parte do código era copiada, pois vários elementos se repetiam (ex: elementos de formulário, com um label e um campo de texto ao lado). Decidimos então criar componentes Facelets.

A criação de componentes Facelets é muito simples, basta seguir os passos abaixo:

  1. Criar o componente. Como exemplo, criei um chamado itemFormulario, que exibe um label, um campo de texto e as mensagens de erro correspondentes:
  2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml"
        xmlns:ui="http://java.sun.com/jsf/facelets"
        xmlns:h="http://java.sun.com/jsf/html"
        xmlns:f="http://java.sun.com/jsf/core"
        xmlns:t="http://myfaces.apache.org/tomahawk"
        xmlns:c="http://java.sun.com/jstl/core"
        xmlns:fn="http://java.sun.com/jsp/jstl/functions">
    
    <ui:component>
        <h:outputLabel for="#{id}" value="#{label}" />
        <h:inputText id="#{id}" value="#{value}" />
        <h:message for="#{id}" />
    </ui:component>
    </html>
    

    Esse arquivo será salvo em /WEB-INF/facelets/ com o nome itemFormulario.xhtml. Neste exemplo, o componente utiliza os parâmetros id, label e value.

  3. Criar um arquivo de taglib para registrar os componentes criados. Este arquivo, que vou chamar de projeto.taglib.xml, será criado no diretório /WEB-INF/facelets do projeto, e será como no exemplo abaixo:
  4. <?xml version="1.0"?>
    <!DOCTYPE facelet-taglib PUBLIC "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN" "https://facelets.dev.java.net/source/browse/*checkout*/facelets/src/etc/facelet-taglib_1_0.dtd">
    <facelet-taglib>
        <namespace>http://exemplo.com.br/jsf</namespace>
        <tag>
            <tag-name>itemFormulario</tag-name>
            <source>itemFormulario.xhtml</source>
        </tag>
    </facelet-taglib>
    

    Neste exemplo, registrei o componente itemFormulario. Sempre que criar um novo componente, ele deverá ser registrado neste arquivo, através de uma nova tag <tag>.

  5. Registrar a biblioteca de taglib no projeto, adicionando o trecho abaixo ao arquivo web.xml:
  6. <context-param>
        <param-name>facelets.LIBRARIES</param-name>
        <param-value>/WEB-INF/facelets/projeto.taglib.xml</param-value>
    </context-param>
    

Desta forma, os componentes declarados no arquivo de taglib poderão ser usados no seu projeto, como neste exemplo:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
 xmlns:ui="http://java.sun.com/jsf/facelets"
 xmlns:h="http://java.sun.com/jsf/html"
 xmlns:f="http://java.sun.com/jsf/core"
 xmlns:t="http://myfaces.apache.org/tomahawk"
 xmlns:c="http://java.sun.com/jstl/core"
 xmlns:fn="http://java.sun.com/jsp/jstl/functions"
 xmlns:custom="http://exemplo.com.br/jsf">

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
    <title>
    </title>
</head>
<body>
    <custom:itemFormulario id="username" label="Digite seu login:" value="#{loginController.username}" />
</body>
</html>

A partir deste ponto, precisamos compartilhar estes componentes entre os diferentes projetos. Para isso, seguimos os passos abaixo:

  1. Mover o conteúdo do diretório /WEB-INF/facelets (arquivo de taglib e componentes criados) para o módulo compartilhado. Colocar estes arquivos na raiz do diretório /META-INF
  2. Atualizar no arquivo web.xml o trecho que registra a taglib:
  3. <context-param>
        <param-name>facelets.LIBRARIES</param-name>
        <param-value>/META-INF/projeto.taglib.xml</param-value>
    </context-param>
    

    Se for utilizar mais de uma taglib, declare-as separadas por “;” no trecho acima.

  4. Repetir o passo anterior para cada projeto que irá utilizar os componentes criados

Nas versões atuais do Facelets, a declaração da taglib no arquivo web.xml é desnecessária caso este arquivo esteja na raiz do diretório /META-INF. Os arquivos de componentes poderão ficar em outro diretório (ex: /META-INF/facelets), bastanto atualizar o arquivo de taglib de acordo com o diretório escolhido:

<?xml version="1.0"?>
<!DOCTYPE facelet-taglib PUBLIC "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN" "https://facelets.dev.java.net/source/browse/*checkout*/facelets/src/etc/facelet-taglib_1_0.dtd">
<facelet-taglib>
    <namespace>http://exemplo.com.br/jsf</namespace>
    <tag>
        <tag-name>itemFormulario</tag-name>
        <source>/META-INF/facelets/itemFormulario.xhtml</source>
    </tag>
</facelet-taglib>

Há ainda um passo opcional, que é a criação de um arquivo TLD (taglib descriptor) para que a IDE possa validar os componentes que você criou. Um arquivo TLD tem o seguinte formato:

<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">

<taglib xmlns="http://java.sun.com/JSP/TagLibraryDescriptor">
    <tlib-version>1.0</tlib-version>
    <jsp-version>2.0</jsp-version>
    <short-name>Componentes</short-name>
    <uri>http://exemplo.com.br/jsf</uri>
    <display-name>Minha biblioteca de componentes</display-name>
    <tag>
        <name>itemFormulario</name>
        <tag-class />
        <body-content>empty</body-content>
        <description>
            Cria um item de formulário com label, campo de texto e mensagens de erro.
        </description>
        <attribute>
            <name>id</name>
            <required>true</required>
            <rtexprvalue>false</rtexprvalue>
            <type>java.lang.String</type>
            <description>
                Identificação do componente
            </description>
        </attribute>
        <attribute>
            <name>label</name>
            <required>false</required>
            <rtexprvalue>false</rtexprvalue>
            <type>java.lang.String</type>
            <description>
                Texto exibido no label do componente
            </description>
        </attribute>
        <attribute>
            <name>value</name>
            <required>true</required>
            <rtexprvalue>false</rtexprvalue>
            <type>java.lang.String</type>
            <description>
                Valor associado ao inputText do componente
            </description>
        </attribute>
    </tag>
</taglib>

Salve esse arquivo com o nome projeto.taglib.tld, no mesmo diretório do arquivo projeto.taglib.xml. Agora, ao abrir um arquivo XHTML que esteja usando o componente itemFormulario, a IDE exibirá os erros de validação (ex: um atributo marcado como required não foi definido). Com este arquivo criado, ao abrir um XHTML no Eclipse usando o editor de JSP, você terá também o recurso de autocomplete (Control + espaço), que exibirá todos os atributos do componente, assim como a descrição de cada um.

Referências:

Renderização condicional com JSF

April 29th, 2010

Muitos componentes JSF, como os do Tomahawk, por exemplo, possuem o atributo rendered. Este atributo, como diz o nome, indica se o componente será renderizado ou não. No valor deste atributo, é possível utilizar expressões JSF EL; desta forma, a lógica que indicará se o elemento será exibido ou não pode (e deve) ser transferida para um Managed Bean. Isso permite que evitemos colocar muita lógica nos XHTMLs, deixando-os mais limpos e organizados.

Porém, em determinados momentos, precisamos fazer a renderização condicional de elementos que os componentes JSF não oferecem. Uma possível solução seria inserir este elemento num <t:div>, por exemplo, pois este componente do Tomahawk possui o atributo rendered. Porém, estaríamos criando um <div> em torno do elemento desejado sem necessidade. O melhor neste caso seria utilizar o componente <t:htmlTag>.

O <t:htmlTag> possui um atributo value que define a tag HTML que será renderizada em seu lugar, caso o atributo rendered seja igual a true. Se o value for vazio, ele não criará nenhuma tag adicional; assim, o <t:htmlTag> servirá somente para a renderização condicional de tudo o que estiver dentro deste componente.

Atenção: o atributo value é obrigatório; se você omití-lo, o componente não funcionará! Para que ele não gere nenhuma tag, é necessário definir value=”", como no exemplo abaixo:

<t:htmlTag rendered="#{tipo.exibeDescricao}" value="">
    <div id="divDescricao_#{tipo.id}">
        <h:outputText value=" #{messages.descricao}" />
        <h:inputTextarea styleClass="inputAreaTexto" id="descricao" value="#{descricaoTipo}" />
    </div>
</t:htmlTag>

Problema com múltiplos joins em Criteria

November 12th, 2009

O Criteria é uma API do Hibernate que facilita muito quando precisamos montar uma query complexa com filtros opcionais. Adicionar restrições ou criar joins com esta API é muito mais simples de gerenciar do que concatenando Strings, como faríamos ao trabalhar com SQL puro.

Apesar das vantagens, o Criteria também tem alguns problemas. O último que encontrei foi ao tentar fazer 2 joins entre as mesmas 2 tabelas. No meu caso, eu tinha no banco as tabelas projeto e historico. A segunda tabela é populada através de uma trigger no banco: sempre que o status do projeto muda, a tabela historico registra o status anterior do projeto com data/hora da mudança. Eu precisava fazer uma query que buscasse um projeto com status “iniciado” num determinado período de datas e com status “finalizado” em outro período. Inicialmente, pensei simplesmente em criar 2 joins entre as tabelas, cada um com um alias diferente e filtrando pelas datas específicas:

Criteria criteria = getSession().createCriteria(Projeto.class);

// Primeiro join
criteria.createCriteria("historicoList", "historicoIniciado", Criteria.LEFT_JOIN)
        .add(Restrictions.eq("historicoIniciado.status", Status.INICIADO.value()))
        .add(Restrictions.ge("historicoIniciado.data", dataIniciadoDe))
        .add(Restrictions.le("historicoIniciado.data", dataIniciadoAte));

// Segundo join
criteria.createCriteria("historicoList", "historicoFinalizado", Criteria.LEFT_JOIN)
        .add(Restrictions.eq("historicoFinalizado.status", Status.FINALIZADO.value()))
        .add(Restrictions.ge("historicoFinalizado.data", dataFinalizadoDe))
        .add(Restrictions.le("historicoFinalizado.data", dataFinalizadoAte));

O código acima, apesar de semelhante ao que eu já havia criado para adicionar outros filtros à query de projetos, fazendo joins com outras tabelas, não funcionava. Tentei retirar um dos joins com a tabela historico e funcionou. Ou seja, o problema estava na criação do segundo join com as mesmas tabelas, mesmo utilizando aliases diferentes. Ao pesquisar este problema, descobri que não é um bug. Na verdade, o Criteria não suporta múltiplos joins para a mesma associação.

Sendo assim, a solução que encontrei para este problema foi criar uma subquery para a tabela historico, utilizando um DetachedCriteria:

DetachedCriteria historicoCriteria = DetachedCriteria.forClass(Historico.class, "historicoIniciado")
        .setProjection(Projections.distinct(Projections.property("projeto")))
        .add(Restrictions.eq("historicoIniciado.status", Status.INICIADO.value()));
        .add(Restrictions.ge("historicoIniciado.data", dataIniciadoDe));
        .add(Restrictions.le("historicoIniciado.data", dataIniciadoAte));
criteria.add(Subqueries.propertyIn("id", historicoCriteria));

Desta forma, apenas um dos joins precisa ser substituído por uma subquery. O outro join pode ser mantido sem problemas.

Livros técnicos traduzidos nunca mais

July 16th, 2009

Há alguns meses atrás, eu estava interessado no livro Effective Java, segunda edição. A princípio, eu pretendia comprar na Amazon, mas decidi pesquisar antes. Pela internet, não achei o livro mais barato em nenhum lugar, mas acabei encontrando em uma livraria perto do trabalho por um precinho camarada. Só tinha um problema: era a edição traduzida em português.

Inicialmente nem pensei em comprar a edição traduzida, pois acho que livros técnicos devem ser mantidos na língua original. Algumas pessoas que discordam disto argumentam que nem todos dominam o inglês, mas, sinceramente, se você tem dificuldades com inglês e quer estudar informática, procure um curso de inglês urgente! Por mais que você procure ler somente livros em português, em algum momento vai precisar consultar alguma documentação, referência ou fórum de discussão em inglês. Além disso, acho que nas traduções, por melhores que sejam, acaba-se perdendo um pouco do original (como num filme dublado, em que algumas piadas em inglês perdem completamente o sentido quando traduzidas), aumentando a possibilidade de erros e a dificuldade de compreensão do assunto. Num livro de programação então nem se fala, pois é muito comum utilizarmos muitos termos em inglês que não têm tradução para português – ou até tem, mas o original se popularizou tanto que é usado como se fosse uma palavra do nosso vocabulário.

Na época em que fiz essa pesquisa, o livro estava mais caro na Amazon do que hoje, e ainda tinha que incluir o frete. Aquela edição traduzida sairia por menos da metade. Além disso, eu não teria que esperar a entrega, era só dar um pulo na livraria e comprar. Decidi dar uma folheada no livro, só pra ver se tinha algum erro gritante de tradução, como trechos de código traduzidos – sim, eu já vi livros com aberrações como “calculaMédia”, com acento mesmo. Como não encontrei nada de mais, acabei me deixando levar pela tentação de economizar uns trocados e comprando o livro traduzido.

Quando comecei a ler, achei a tradução muito boa. Nos primeiros capítulos, o único termo que machucou os ouvidos foi “encaixotamento automático” em vez de autoboxing – por mais que esteja correto, esse é um daqueles termos que não se costuma traduzir. Além disso, os trechos de código foram mantidos originais, e abaixo de cada um há a tradução de cada linha de comentário. Achei um pouco desnecessário, porém interessante.

Ao chegar no capítulo 4, estranhamente os comentários nos códigos passaram a ser substituídos pelos traduzidos. Isso continua até o final do livro. Aparentemente a tradução foi feita por pessoas diferentes, e faltou uma revisão mais rigorosa no final. Porém, não é nada que atrapalhe. Mais adiante, vi uma citação a uma referência bibliográfica e resolvi verificar qual era o nome do livro referenciado. Descobri que o livro não tinha bibliografia! Obviamente isso é uma exclusividade da edição em português. Mais uma falha de revisão, e esta é mais grave.

Quando comecei a ler o capítulo 5, que fala sobre generics, achei o trecho abaixo bem estranho:

Antes da versão 1.5, essa teria sido uma declaração de coleção exemplar:

/**
* Minha coleção de selos. Contém apenas instâncias de Stamp.
*/
private final stamps = …;

Não está faltando definir o tipo de stamps? Tudo bem, eu não devo ter entendido direito. Sigo a leitura e, um pouco mais à frente aparece o seguinte:

Com os genéricos, você substituiria o comentário por uma declaração de tipo aperfeiçoada para a coleção que passaria ao compilador as informações que anteriormente ficariam ocultas no comentário:

private final stamps = …;

Ué, mudou alguma coisa? Não é exatamente igual ao código anterior? Resolvi pegar o livro original em inglês emprestado com um amigo, e vejo que nele aparecem os seguintes trechos:

Before release 1.5, this would have been an exemplary collection declaration:

// Now a raw collection type – don´t do this!

/**
* My stamp collection. Contains only Stamp instances.
*/
private final Collection stamps = … ;

e

With generics, you replace the comment with an improved type declaration for the collection that tells the compiler the information that was previously hidden in the comment:

// Parameterized collection type – typesafe
private final Collection <Stamp> stamps = … ;

Os destaques em negrito são do livro original. Aparentemente, tudo o que estava em negrito foi omitido na tradução! E, se estava em negrito, é porque é exatamente o trecho mais importante do código! Posteriormente, descobri que isso acontece em todo o capítulo 5. Do 6 em diante os códigos voltam ao normal e não encontrei mais nenhuma bizarrice, exceto a tradução de “thread” para “segmento”.

Outro erro que se repetiu bastante no livro são as referências a números de páginas (ex: “veja a página 147″). Em todas as situações em que isso ocorre, o número da página que é referenciado é o do livro original, tornando a referência inútil na edição traduzida.

A conclusão que tirei dessa situação: livro técnico traduzido nunca mais! Os trocados que resolvi economizar com o livro acabaram saindo muito caros. Vale muito mais a pena investir um pouco mais no livro original – e num curso de inglês, caso você precise – para não ter dor de cabeça com livros mal traduzidos.

Tutoriais de JSF

May 25th, 2009

Uma das maiores dificuldades para se aprender JSF é seu aparentemente complexo ciclo de vida, composto por 6 fases. Ao debugar uma aplicação JSF, percebe-se que alguns métodos dos Managed Beans são executados várias vezes, uma em cada fase, o que não é muito intuitivo, principalmente quando se está acostumado com outros frameworks web que utilizam abordagens bem diferentes.

Para facilitar a compreensão do ciclo de vida do JSF, recomendo o tutorial The JSF application lifecycle, da IBM. Ele explica cada fase, e apresenta um exemplo bem simples de uma aplicação usando JSF. Este tutorial é a segunda parte da série “JSF for nonbelievers”. As demais partes também são bem interessantes: