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:

     <!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.

  2. 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:

     <?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>.

  3. Registrar a biblioteca de taglib no projeto, adicionando o trecho abaixo ao arquivo web.xml:

     <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:

     <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.

  3. 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:


Outro dia, quando fui me logar no Gmail, digitei rapidamente meu login, apertei tab, digitei a senha e apertei enter. Na pressa, não percebi que, em vez de tab, eu tinha apertado shift. Ou seja, o campo login ficou preenchido como “loginsenha”, e a senha ficou em branco. Até aí tudo bem, o Gmail simplesmente me deu um aviso de login ou senha inválidos. Porém, esse “loginsenha” ficou no histórico de preenchimento de campos, o que é bem incômodo, já que contém minha senha. Eu queria excluir este item do histórico, mas sem perder todo o histórico do browser.

O Firefox possui uma opção “Clear Recent History”, no menu “Tools”, que permite excluir o histórico somente para um período de tempo (última hora, últimas 2 ou 4 horas, último dia ou tudo), e permite selecionar também o que se deseja excluir (URLs acessadas, downloads, formulários preenchidos, cookies, cache, logins ativos e preferências de sites). Apesar de esta opção ser bastante completa, eu queria algo mais específico: excluir somente aquele item.

Após uma rápida pesquisa no Google, encontrei algumas referências interessantes, e descobri que a solução era extremamente simples. Para excluir um item do histórico de preenchimento de campos de formulários, basta clicar no campo e, ao aparecer a lista de valores armazenados no histórico, selecione o item que você quer excluir usando a seta para baixo e pressione Del. É só isso!

Também é possível excluir itens individuais do histórico de URLs acessadas. Para isso, repita o procedimento acima, trocando apenas o Del por Shift+Del.

Referências:


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 [](http://myfaces.apache.org/tomahawk-project/tomahawk/tagdoc/t_htmlTag.html).

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>

Muitos usuários de Linux têm dual boot com uma instalação de Windows, geralmente para executar alguma aplicação específica, como jogos mais recentes ou o iTunes (apesar de que o Ubuntu 10.04 deve suportar iPod e iPhone).

Quem utiliza esta configuração e usa Firefox em ambos os sistemas operacionais acaba tendo dois perfis independentes no browser, e não consegue acessar bookmarks, histórico e extensões instaladas no outro sistema. Uma solução para este problema é compartilhar os perfis do Firefox entre os sistemas operacionais.

O Firefox armazena um diretório com configurações pessoais do usuário. No Linux, esse diretório fica tipicamente em /home/usuario/.mozilla/Firefox/, e no Windows, em C:\Documents and Settings\usuario\Dados de aplicativos\Mozilla\Firefox\. Este diretório contém, entre outras coisas, um subdiretório Profiles com todos os perfis criados (mais detalhes sobre isso no post sobre gerenciamento de perfis no Firefox) e um arquivo profiles.ini. Este arquivo é carregado na inicialização do browser, e faz referência aos perfis utilizados. O formato do arquivo é como mostra o exemplo abaixo:

[General]
StartWithLastProfile=1

[Profile0]
Name=default
IsRelative=1
Path=Profiles/dwi06ij0.default

[Profile1]
Name=teste
IsRelative=1
Path=Profiles/nkc5ofgt.default
Default=1

O arquivo é dividido em seções. A primeira, General, tem somente o parâmetro StartWithLastProfile: se for 0, será exibida a tela de seleção de perfis; se for 1, o último perfil que foi usado será escolhido automaticamente.

As seções seguintes definem todos os perfis criados. Cada perfil tem um nome e um path - que será relativo ao diretório de configurações do Firefox se IsRelative for 1, e absoluto em caso contrário. O parâmetro Default indica se este é o perfil padrão.

Para que os dois sistemas operacionais compartilhem um perfil, primeiramente é necessário montar a partição do usuário utilizada pelo outro sistema operacional. Isso pode ser feito de duas maneiras:

Mapeando uma partição do Windows no Linux

Para montar uma partição do Windows, basta utilizar o comando mount, como no exemplo abaixo:

sudo mount /dev/sda1 /media/windows

Neste exemplo, a partição do Windows é /dev/sda1, e o diretório destino (que deve ser criado antes) é /media/windows. A partição pode ser FAT32 ou NTFS - neste caso, é necessário instalar o ntfs-3g, que já vem por padrão na maioria das distribuições atuais. Para que a partição seja montada automaticamente na inicialização do sistema, inclua a linha abaixo no arquivo /etc/fstab:

/dev/sda1 /media/windows ntfs defaults 0 0

Caso a partição seja FAT32, substitua ntfs por vfat na linha acima.

A configuração acima também pode ser feita utilizando uma ferramenta gráfica como o ntfs-config.

Mapeando uma partição do Linux no Windows

Para isso, é necessário instalar uma ferramenta como o Ext2 IFS - que, apesar do nome, também suporta ext3. Ao instalar esta ferramenta, será possível mapear qualquer partição ext2 ou ext3 como um drive comum. A figura abaixo mostra uma tela da ferramenta, onde aparecem todas as partições dos discos locais. Em cada partição do Linux, há uma combo box que permite selecionar a letra em que a partição será mapeada - selecionando none o mapeamento será desfeito.

Ext2 IFS

Entre as duas opções, pessoalmente, prefiro a segunda, pois não gosto da ideia do Windows acessando minhas partições do Linux - só mapeio uma partição com o Ext2 IFS quando preciso copiar algum arquivo, depois desfaço o mapeamento.

Após realizar o mapeamento da partição, de uma das duas formas acima, basta configurar o arquivo profiles.ini do Firefox. Para isso, é possível configurar o path completo para o perfil desejado e definir o parâmetro IsRelative=0, ou fazer um link no diretório onde o Firefox armazena os perfis e configurar o path relativo, com o parâmetro IsRelative=1. A primeira configuração ficaria como no exemplo abaixo:

[Profile0]
Name=default
IsRelative=0
Path=/media/windows/Documents and Settings/guilherme/Dados de aplicativos/Mozilla/Firefox/Profiles/sa8ww6mz.default

Para a segunda configuração, é necessário criar um link com o comando ln:

ln -s /media/windows/Documents\ and\ Settings/guilherme/Dados\ de\ aplicativos/Mozilla/Firefox/Profiles/sa8ww6mz.default /home/guilherme/.mozilla/Firefox/Profiles/

Neste caso, a configuração do arquivo ficaria assim:

[Profile0]
Name=default
IsRelative=1
Path=Profiles/sa8ww6mz.default

Possíveis problemas

Apesar de funcionar muito bem, o compartilhamento de perfis pode trazer alguns problemas:

  • Se as versões do Firefox nos dois sistemas forem diferentes (o que é muito comum, pois o repositório do Ubuntu costuma demorar para atualizar as versões), cada vez que você abrir o browser num sistema após tê-lo acessado no outro será como se você tivesse atualizado a versão do Firefox, ou seja, a inicialização será mais lenta, pois ele verificará a compatibilidade de cada extensão instalada com a versão atual;
  • Se você tentar abrir o Firefox num sistema operacional após ele ter travado no outro sistema, ele exibirá uma mensagem informando que já existe uma sessão aberta. Isso acontece por que, quando o Firefox é iniciado, ele cria um arquivo vazio chamado parent.lock no diretório do perfil que você estiver usando. Este arquivo é excluído quando o browser é fechado. Se isso ocorrer de maneira incomum (ex: travamento do browser ou do sistema operacional), este arquivo impedirá a abertura de outra sessão. Para resolver o problema, exclua esse arquivo;
  • Algumas extensões são incompativeis com determinados sistemas operacionais e versões de browser. Se for o caso, estas extensões serão desativadas pelo browser.

No final do século 20, a disputa entre os dois browsers mais populares da época, Netscape e Internet Explorer, ficou conhecida como guerra dos browsers. Nos últimos anos, após um período negro de domínio do IE, outros browsers começaram a crescer e recuperar o espaço que era ocupado pelo falecido Netscape.

Mais recentemente, desde o ano passado, os browsers mais modernos começaram a dar suporte ao HTML 5 - o Firefox, por exemplo, suporta desde a versão 3.5. Entre diversas novidades, o recurso mais popular do HTML 5 é a tag <video>, que permite a execução de vídeos sem o uso de plugins. A imensa maioria dos sites de vídeo usam Flash, que é uma tecnologia proprietária, e não é suportada em plataformas como o iPhone/iPod Touch. Porém, juntamente com esta nova tag surgiu uma polêmica.

No mês passado, o Youtube - seguido posteriormente por outros sites de vídeos - anunciou que começaria a oferecer vídeos em HTML 5, porém usando o codec H.264. Apesar de suportar HTML 5, o Firefox não tem suporte a este codec, pelo fato de ele ser proprietário, e exigir o pagamento de licenças caríssimas. O Firefox suporta somente o codec OGG Theora, que é open source. Esta atitude do Youtube criou uma grande polêmica: por que eles deixariam de usar uma tecnologia proprietária (Flash) para adotar outra igualmente proprietária (H.264)? O uso de HTML 5 deixa a impressão de que o Youtube partiu para tecnologias abertas, mas isto não ocorreu, devido ao codec escolhido.

Enquanto isso, o Safari (incluindo aí o iPhone) suporta o codec H.264, mas não suporta Theora; o Google Chrome suporta ambos os formatos; o Opera começará a suportar vídeos com codec Theora a partir da versão 10.50; e o IE 8 nem suporta HTML 5, criando um cenário totalmente heterogêneo. Um dos problemas desta divisão é que começamos a nos distanciar da padronização - que é o objetivo principal do W3C - e criar um ambiente onde cada browser suporta diferentes formatos de vídeo, retornando à época da guerra dos browsers, onde dificilmente uma página era igualmente visualizada no IE e no Netscape. Outro problema é que, como o Firefox não consegue exibir os vídeos em HTML 5 do Youtube, muita gente diz erradamente que o Firefox não suporta HTML 5, quando na verdade é um problema de codec.

A solução para os desenvolvedores é oferecer seus vídeos em mais de um formato, multiplicando, desta forma, o espaço necessário e o trabalho de codificação dos vídeos. O Video For Everybody ajuda a resolver este problema, oferecendo um trecho de código HTML que deixa a cargo do browser a escolha da opção mais adequada - se o browser suportar HTML 5 e Theora, este vídeo será exibido; se suportar HTML 5 e H.264, usará este formato; caso contrário, usará o plugin de Flash ou QuickTime, caso estejam instalados. Em último caso, exibe uma imagem do vídeo com um link para download. Esta solução exige o armazenamento do vídeo em dois formatos, mas parece uma boa solução diante de tantas variantes.

Referências: