Como Criar Tag Libs

Este artigo foi criado diante da necessidade de adaptar o Fusion Charts, o Google Maps, e também o YouTube ao JSF com RichFaces .

Durante as pesquisas e tutoria à equipe da Strata Engenharia, foram feitas diversas pequisas na internet e foi encontrado um post no forum em http://www.fusioncharts.com/forum/Topic8629-33-1.aspx, infelizmente ainda foi possivel identificar exatamente o nome do autor, mas seu apelido no forum é "srividya_sharma".

Este artigo auxíliou muito nesta tarefa, mas faltava algumas coisas, como por exemplo, o uso da tag com Facelets, mais integração com outros componentes, no caso GoogleMaps e YouTube . Nossa equipe na Strata Engenharia precisava de uma expansão do componente GoogleMap do RichFaces e outro componente para exibição de videos do YouTube com o SWFObject.

Além disto tudo, presisavamos de um aprofundamento nos detalhes de como criar componentes, o que não era objetivo do post do "srividya_sharma". Diante disto acreditamos ser válido por aqui toda a experiência adquirida nesta empreitada.

Os livros "JavaServer Faces - the Complete Referente" e "Core JavaServer Faces" - Second Edition, foram usados como referência para criação inicial das Tags Libs e o artigo http://www.jsfcentral.com/articles/facelets_2-2.html além de diversos outros sites foram usados para solucionar o acesso as TAGs através do Facelets.

Vamos então ao trabalho

Como Funciona o sistema de TAGs do JSF e Facelets

Na imagem abaixo é exibida a relação entre os itens utilizados para criar um componente no JSF incluindo o Facelets. Diagrama_de_relacao_dos_itens_relacionados_para_criar_um_componente.jpg

No arquivo TLD que pode ter qualquer nome, mas obrigatoriamente deve ter a extenção .tld é feito a referência à classe que mantem o estado do componente, esta classe armazena as propriedades quando usado em uma página JSP, esta classe deve extender a classe UIComponentTag ou UIComponentELTag , tal classe possui dois metodos cada um responsavel respectivamente por informar o identificador para se obter a classe que representa o componente para o JSF e o respectivo Rederizador.

Tanto a classe que representa o componente para o JSF, os rederizadores e também as classes que atuam como validadores podem ser referenciados no arquivo faces-config.xml.

Quando usando Facelets é necessário termos um arquivo extra, que pode ter qualquer nome, mas deve ter o sufixo .taglib.xml, que faz referência a detalhes dos componentes, este arquivo faz referência ao código que identifica o componente conforme definido no faces-config.xml. É interessante observar que estes arquivos tanto o "*.taglib.xml" como o arquivo "faces-config.xml" podem estar na pasta web-inf do projeto que a utiliza, ou em uma pasta META-INF de um arquivo jar que empacota o componente, este arquivo deverá ser encontrado pelo classpath do projeto.

Sugiro que crie o arquivo ".taglib.xml" e ".tld" com o mesmo prefixo, por exemplo:

  • fusioncharts.taglib.xml
  • fusioncharts.tld

Criando um projeto de componentes JSF no Eclipse

Usaremos o Eclipse para criar nosso componente, e iremos nos basear no post comentado acima. Portanto criaremos um componente para uso do framework FusionChart dentro do JSF. (Com o amadurecimento do artigo, não podemos esquecer que isto aqui é um wiki-wiki, iremos introduzindo outros tipos de componentes)

Vemos ao lado a janela de criação de um projeto Java no Eclipse. Criando_um_projeto_java_no_eclipse.jpg

Eu criei um projeto chamado FusionChartComponents -Tutorial como pode ser visto na imagem ao lado, o eclipse usado é o Ganymede. Caso ainda não saiba como criar um projeto java no Eclipse, sugiro que procure no google um tutorial de como criar projetos java com Eclipse.

Em seguida adicionei ao projeto as bibliotecas básicas para podemos trabalhar com servlets e a api do JSF, veja na imagem como ficou o meu projeto.Bibliotecas_participantes_no_projeto.jpg

Criando as Classes do Componente

Vamos começar pela criação das Classes do componente. em seguida criaremos os arquivos necessários para reconhecimento do novo componente pelo JSF e Facelets, e finalmente, falaremos rapidamente dos rederizadores extras para cada tipo de dispositivo/navegador.

Precisamos de duas classes para termos um componente básico funcional. A classe que representa a TAG no JSP processando seus atributos e a classe que rederiza o componente quando este for apresentado no dispositivo destino, armazena seu estado, e processa as entradas.

Caso deseje criar mais components é importante se aprofundar nas seguintes classes:

  • javax.faces.component.UIComponent
  • javax.faces.webapp.UIComponentELTag
  • javax.faces.context.FacesContext
  • javax.faces.application.Application
  • javax.faces.context.ResponseWriter

Veja o Tópico ClassesImportantesCriarComponentes para mais detalhes.

Criando a Classe que representa a TAG

A classe que representa a TAG deve extender a classe UIComponentTag ou UIComponentELTag caso vá trabalhar somente com Faces 1.2 e Containers compátiveis com JSP Version 2.1.

Iremos chama-la de ChartTag e a colocarei no pacote "br.com.strata.charts" já que estou criando esta tag com objetivo de atender os projetos da "Strata Engenharia". Observe que a classe tem como posfixo o termo Tag, assim fica fácil identifica-la.

Segue o código usado para criar a classe

package br.com.strata.charts;

import javax.faces.component.UIComponent;
import javax.faces.webapp.UIComponentELTag;
import javax.faces.webapp.UIComponentTag;
 
public class ChartTag extends UIComponentELTag {

   String chartId;
   String filename;
   String xml;
   String url;
   String width;
   String height;
   String debugMode;
   String registerWithJS;

   public String getChartId() {
      return chartId;
   }

   public void setChartId(String id) {
      this.chartId = id;
   }

   public String getFilename() {
      return filename;
   }

   public void setFilename(String filename) {
      this.filename = filename;
   }

   public String getXml() {
      return xml;
   }

   public void setXml(String xml) {
      this.xml = xml;
   }

   public String getUrl() {
      return url;
   }

   public void setUrl(String url) {
      this.url = url;
   }

   public String getWidth() {
      return width;
   }

   public void setWidth(String width) {
      this.width = width;
   }

   public String getHeight() {
      return height;
   }

   public void setHeight(String height) {
      this.height = height;
   }

   public String getDebugMode() {
      return debugMode;
   }

   public void setDebugMode(String debugMode) {
      this.debugMode = debugMode;
   }

   public String getRegisterWithJS() {
      return registerWithJS;
   }

   public void setRegisterWithJS(String registerWithJS) {
      this.registerWithJS = registerWithJS;
   }

   public void release() { // the super class method should be called
      super.release();
      chartId = null;
      filename = null;
      xml = null;
      url = null;
      width = null;
      height = null;
      debugMode = null;
      registerWithJS = null;
   }

   protected void setProperties(UIComponent component) {
      // the super class method should be called
      super.setProperties(component);

      if (chartId != null)
         component.getAttributes().put("id", chartId);
      if (filename != null)
         component.getAttributes().put("filename", filename);
      if (xml != null)
         component.getAttributes().put("xml", xml);
      if (url != null)
         component.getAttributes().put("url", url);
      if (width != null)
         component.getAttributes().put("width", width);
      if (height != null)
         component.getAttributes().put("height", height);
      if (debugMode != null)
         component.getAttributes().put("debugMode", debugMode);
      if (registerWithJS != null)
         component.getAttributes().put("registerWithJS", registerWithJS);
   }

   public String getComponentType() {
      return "br.com.strata.charts.jsf.FusionChartComponent";
   }

   public String getRendererType() {
      // null means the component renders itself
      return null;
   }

}

Iremos futuramente descrever em detalhes cada parte da classe, de imediato vamos comentar apenas o método

public String getComponentType()
pois este método está diretamente ligado comas configurações que iremos promover neste tutorial.

O método

getComponentType()
retorna o identificador do Component que está registrado no arquivo faces-config.xml, iremos ver mais detalhes de como intervir no arquivo faces-config.xml logo abaixo.

Criando a Classe que rederiza o componente

Neste Tutorial não iremos em primeira instancia entra em detalhes sobre rederizadores especializados. Esta classe é o caração do componente pois ela não so pode cuidar da rederização do componente quando não existem Rederizadores especializados, como também cuida de outros detalhes das funcionalidades do componente.

Abaixo segue o código da classe:

package br.com.strata.charts;

import java.io.IOException;
import javax.faces.component.UIOutput;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;

public class UIChart extends UIOutput {

   public void encodeBegin(FacesContext context) throws IOException {
      ResponseWriter writer = context.getResponseWriter();
      
      String chartId = getClientId(context);
      writer.startElement("div", this);
      writer.writeAttribute("id", chartId + "_Div", null);
      writer.writeAttribute("align", "center", null);
      writer.writeText("Chart.", null);
      writer.endElement("div");
      
      String chartSWF = (String) getAttributes().get("filename");
      String url = (String) getAttributes().get("url");
      String xml = (String) getAttributes().get("xml");
      String chartWidth = (String) getAttributes().get("width");
      String chartHeight = (String) getAttributes().get("height");
      String debugMode = (String) getAttributes().get("debugMode");
      int debugModeInt = boolToNum(new Boolean(debugMode));
      String registerWithJS = (String) getAttributes().get("registerWithJS");
      int regWithJSInt = boolToNum(new Boolean(registerWithJS));
      
      writer.startElement("script", this);
      writer.writeAttribute("type", "text/javascript", null);
      writer.writeText("var chart_" + chartId + " = new FusionCharts('"
            + chartSWF + "', '" + chartId + "', '" + chartWidth + "', '"
            + chartHeight + "', '" + debugModeInt + "', '" + regWithJSInt
            + "');", null);
      if (xml == null || xml.equals(""))
         writer.writeText("chart_" + chartId + ".setDataURL(\"" + url
               + "\");", null);
      else
         writer.writeText("chart_" + chartId + ".setDataXML(\"" + xml
               + "\");", null);
      
      // Finally render the chart
      writer.writeText(
            "chart_" + chartId + ".render('" + chartId + "_Div');", null);
   }

   public void encodeEnd(FacesContext context) throws IOException {
      ResponseWriter writer = context.getResponseWriter();
      writer.endElement("script");
   }

   /**
    * Converts a Boolean value to int value<br>
    * 
    * @param bool
    *            Boolean value which needs to be converted to int value
    * @return int value correspoding to the boolean : 1 for true and 0 for
    *         false
    */
   private int boolToNum(Boolean bool) {
      int num = 0;
      if (bool.booleanValue()) {
         num = 1;
      }
      return num;
   }
   @Override
   public String getFamily() {
      return "br.com.strata.charts.jsf.family.FusionChartComponents";
   }
}

Observe que esta classe possui o prefixo UI, é indicado usa-lo em todas as classes que representam o core do componente.

Esta classe possui mais métodos interessantes para nos analisarmos agora do que a Classe ChartTag , portanto iremos dar uma maior atenção a estes metodos e iremos começar pelos mais simples.

O método getFamily()

O método encodeBegin(FacesContext context)

Este método é responsável pelo inicio da rederização do componente permitindo a criação das tags que compoem o inicio do componente.

Neste método existem 3 partes muito interessantes e fundamentais para nosso trabalho que serão usadas para nos ajudar a montar o codigo que irá apresentar a página no dispositivo:

obtendo o objeto para escrever a resposta
Para efetuar a construção da resposta que este componente irá fornecer em sua posição na página é necessário através do objeto FacesContext fornecido como parametro do método, obter o objeto ResponseWriter que permite construir a resposta adequada ao tipo de dispositivo esperado.

 
ResponseWriter writer = context.getResponseWriter();
.
.
.
writer.startElement("div", this);
writer.writeAttribute("id", divId, null);
writer.writeAttribute("align", "center", null);
String msg = (String) getAttributes().get("msg");
.
.
.
writer.writeText("Fusion Chart - Strata JSF Component.", null);

writer.endElement("div");

No fragmento de código acima pode ser visto alguns métodos do objeto ResponseWriter sendo usados para construir a página conforme os parametros informados tais parametros podem ser obtidos conforme descrito logo abaixo, agora vamos ver rapidamente os metodos do Objeto.

writer.startElement("div", this); Este método inicia a tag div na resposta a requisição a página, construindo assim uma tag div (

String chartId = getClientId(context); String chartSWF = (String) getAttributes().get("filename");

Criando um Arquivo TLD

Bem agora vamos criar nosso arquivo TLD, se vc tiver o plugin "JBoss Tools" instalado em seu Eclipse esta tarefa é bem mais fácil, caso contrario basta copiar o codigo abaixo em um arquivo com o nome FusionCharts .tld. Você pode escolher qualquer nome que desejar somente o posfixo (extenção) deverá ser obrigatoriamente ".tld" em minusculo.

O Arquivo TLD é importante para defininir o componente para que seja usado com JSP

O codigo abaixo não está completo ele apenas descreve a biblioteca de TAGs que usaremos. <taglib version="2.1" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee web-jsptaglibrary_2_1.xsd"> <display-name>Fusion Charts</display-name> <tlib-version>1.0</tlib-version> <short-name>FC</short-name> http://www.full.srv.br/taglibs/fusioncharts

No codigo acima temos as seguintes TAGs (caso não conheça de XML, sugiro que pesquise no google um tutorial sobre o tema).

  • display-name -- Identifica o nome que será exibido como sendo um nome amigavel na IDE
  • tlib-version -- Versão da Taglib adotada
  • short-name -- Nome curto da taglib sugerido
  • uri -- Name Space adotado para identificar esta tag lib

Log abaixo da TAG "uri" iremos adiconar o seguinte codigo:

fusionchart <tag-class>br.srv.full.taglibs.FusionChart</tag-class> <body-content>JSP</body-content> <display-name>Fusion Chart</display-name> Permite a adoção do framework FusionChart com o JSF.

Este bloco de codigo pode ser repetido para definir cada TAG que iremos criar, como so estamos criando uma tag de nome fusionchart, teremos apenas um bloco.

Neste bloco temos as seguintes informações definidas:

  • name -- nome da TAG que será usado no arquivo jsp ou xhtml
  • tag-class -- nome da classe java que representa o estado da TAG, no nosso caso será a tag "fusionchart"
  • body-content -- tipo de conteúdo da TAG
  • display-name -- nome amigável a ser utilizado pela IDE
  • description -- Descrição a ser apresentada pela IDE

Informando ao JSF da existência do novo Componente

Agora informando ao Faceletes sobre o novo Componente

Outros Rederizadores

Links Úteis


Agradecimentos a ajuda do Juliano desenvolvedor da Chart Informática.

-- CarlosDelfino - 09 Oct 2008

Topic attachments
I Attachment Action Size Date Who Comment
jpgjpg Bibliotecas_participantes_no_projeto.jpg manage 16.1 K 2008-10-10 - 16:51 CarlosDelfino Bibliotecas participantes no projeto
jpgjpg Criando_um_projeto_java_no_eclipse.jpg manage 53.6 K 2008-10-12 - 13:34 CarlosDelfino  
jpgjpg Diagrama_de_relacao_dos_itens_relacionados_para_criar_um_componente.jpg manage 27.8 K 2008-10-09 - 23:26 CarlosDelfino Diagrama de relação dos itens relacionados para criar um componente
Edit | WYSIWYG | Attach | Printable | Raw View | Backlinks: Web, All Webs | History: r10 < r9 < r8 < r7 < r6 | More topic actions
Topic revision: r10 - 2009-02-01 - 21:35:19 - CarlosDelfino
 
This site is powered by the TWiki collaboration platformCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback