Firefox

Firefox

Paradoxo

if (qualidade < produtividade) {
   throw new CompetenciaError();
}

sábado, 21 de julho de 2007

Integração entre Struts 2 e JSF

O framewor Struts, muito famoso entre desenvolvedores java web, chegou em sua versar 2.* muito diferente4 doque se tinha antes, mais estruturado, menos acoplado e muito mais produtivo. Tenho trabalhado com a versão 2.6 a alguns meses e reconheço que ficou muito mais facil desenvolver um formulario ou uma pagina simples de apresentação de dados doque seria utilisando JSF, sem contar que o código fica indiscutivelmente mais limpo, sem ter que usar uma tag só para exibir um texto em um ponto qualquer da página como no JSF. sem contar que aquela tabela que é feita para organisar os label ao lado do campo text eh dispensavel, deixanda a cargo das tags do Struts. Usar ajax com o Struts é tão facil quanto fazer um formulário, sem exigir do programador qualquer conhecimento em javascript. Sem dúvida o tempo de desenvolvimento de páginas JSPs diminui muito com o Struts 2, e ainda tem os interceptors que acrescentam uma camada a mais localizada entre a página e a action possibilitando uma serie de controles adicionais às ações do usuário como impedir o duplo submit de um formulario (quando o usuário clica duas vezes no botao de submit de acaba salvando duas vezes a mesma informação).

Porém, com toda essa facilitação das coisas faceis, acaba ficando complicado com o Struts de fazer algumas páginas mais elaboradas, minoria em um sistema, mas que levam do desenvolvedor umas longas horas de sofrimento, como fazer uma lista de registros e possibilitar que na própria lista os registros possam ser alterados, além de fazer a paginação dos registros. Esse tipo de problema não é nenhum desafio para quem trabalha com JSF, mas em Struts isso é um nó na cuca. Embora possa parecer, isso não é uma falha do Strus 2, mas sim uma característica, se tem quem faça bem, por que reenventar a roda? A solução é usar o JSF nessas páginas mais complicadas e o Struts para fazer o trabalho mais trivial (chato e repetitivo) de uma maneira mais rápida (pra não ficar entediado).

A seguir eu demonstro como usar o plugin do Struts para JSF. Esse plugin certamente possibilita a utilização do JSF padão da sun, do MyFaces, Tomahawk e demais extensões da Apache, outras implementações eu não posso garantir, já que eu não consegui implementar com RichFaces.

As configurações do web.xml são as especificadas por cada framework. Como demonstrado a seguir com configurações para uso do MyFaces e do Tomahawk.

<? xml version ="1.0" encoding ="UTF-8" ?>
<
web-app >
     <display-name >STRUTS2 com JSF </display-name >

     <!-- Struts -->
    
<filter >
          <filter-name >struts2</ filter-name>
          <filter-class >
               org.apache.struts2.dispatcher.FilterDispatcher
         
</ filter-class>
     </ filter>
     < filter-mapping>
          < filter-name>struts2 </filter-name >
          < url-pattern>/* </url-pattern >
     </filter-mapping >

     <!-- Extensão do MyFaces -->
     <!— declaração do filtro para extensões do MyFaces,
         como manda a documentação -->

     < filter>
          < filter-name>MyFacesExtensionsFilter </filter-name >
          < filter-class>
               org.apache.myfaces.webapp.filter.ExtensionsFilter
         
</filter-class >
     </filter >
 
     < filter-mapping>
          < filter-name>MyFacesExtensionsFilter </filter-name >
          < servlet-name>Faces Servlet</ servlet-name>
     </ filter-mapping>
          < filter-mapping>
          < filter-name>MyFacesExtensionsFilter </filter-name >
          < url-pattern>/faces/myFacesExtensionResource/* </url-pattern >
     </ filter-mapping>

     <!— aqui normalmente seria "*.jsf", mas como estamos fazendo um
         projeto em Struts 2 o sufixo é "*.action".
         Eu acredito que aqui o ideal seria mapear apenas as action
         de uma pasta onde estariam todas as paginas que usam o
         MyFaces, e não ficar filtrando toda a aplicação, afinal esse
         Recurso, geralmente, será utilizado em apenas algumas páginas,
         mas eu não sei como que faz isso, se alguém souber diga como
         é num comentário.
     -->

     < filter-mapping>
          < filter-name>MyFacesExtensionsFilter </filter-name >
          < url-pattern>*.action </url-pattern >
     </ filter-mapping>

     <listener >
          <listener-class >
               org.apache.myfaces.webapp.StartupServletContextListener
         
</ listener-class>
     </ listener>

     <!—JSF -->

      < servlet>
          < servlet-name>Faces Servlet</ servlet-name>
          < servlet-class>
               javax.faces.webapp.FacesServlet
         
</ servlet-class>
          < load-on-startup> 1</ load-on-startup>
     </ servlet>

     <!— o mesmo caso do mapeamento da extensão, normalmente seria
         "*.jsf" ou "*.faces" -->

     <servlet-mapping >
          < servlet-name>Faces Servlet </servlet-name >
          < url-pattern>*.action </url-pattern >
     </ servlet-mapping>

      < welcome-file-list>
          < welcome-file>/pages/default.jsp </welcome-file >
     </ welcome-file-list>
</
web-app >

Deve-se prestar atencão no mapeamento das actions: o path das actions deve condizer com o caminho real das JSPs, se uma página está na pasta "/pages/" deve ser criado um package de mapeamento com o namespace "/pages". Além disso o nome da action deve ser o mesmo da JSP a que se refere e é obrigatório implementar um result success nulo para cada mapeamento ( < result name ="success" type ="jsf" />). Segue um exemplo de um arquivo de mapeamento com configurações para JSF.

<! DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd" >
<
struts >

     <!-- esse é o package com os interceptors do plugin JSF -->
  <package name ="jsf" extends ="jsf-default" >

    
     < result-types>
    
          < result-type name = "jsf" class ="org.apache.struts2.jsf.FacesResult " />
    
     </ result-types>

       < interceptors >
               < interceptor-stack name ="jsfFullStack" >
               < interceptor-ref name ="params" />
               < interceptor-ref name ="basicStack" />
               < interceptor-ref name ="jsfStack" />
               </ interceptor-stack>
          </ interceptors>

          < default-interceptor-ref name ="jsfFullStack" />

     </ package>

     < package name ="acesso" extends ="jsf" namespace ="/pages" >
    
          < action name ="editar" class ="agenda.AgendaAction ">
               < result name ="success" type ="jsf" />
               < result name ="sucesso" type ="jsf" > /pages/sucesso.jsp </ result>
               < result name ="error" type ="jsf" > /pages/erro.jsp </ result>
          </ action>

        < action name ="buscar" class ="agenda.AgendaAction ">
               < result name ="success" type ="jsf" />
               < result name ="sucesso" type ="jsf" > /pages/resultado_busca.jsp</ result >
               < result name ="error" type ="jsf" > /pages/erro.jsp </result >
          </ action>

          <!-- action para redirecionamentos, usada quando a funcao é
                      apenas um link e nao deve executar nenhuma acao -->
          < action name ="go_*" class ="agenda.AgendaAction ">
               < result name ="success" type ="jsf" > /pages/{1}.jsp</ result >
          </ action>

          < action name ="listar_contatos*" class ="agenda.AgendaAction " method ="{1}" >
               < result name ="success" type ="jsf" />
               < result name ="sucesso" type ="jsf" > /pages/sucesso.jsp </ result >
               < result name ="editar" type ="jsf" > /pages/editar.jsp </result >
               < result name ="error" type ="jsf" > /pages/erro.jsp</ result >
          </ action>

     </ package>

</struts >


Agora só faltam o JSF, a sintax das páginas sofre apenas uma alteração: os atributos do bean (no nosso caso falamos em classes Actions) devem ser representados com o prefixo "action.", como no exemplo a seguir.

<%@ page contentType ="text/html;charset=UTF-8" %>
<%@
taglib uri =" http://myfaces.apache.org/tomahawk" prefix ="t" %>
<%@
taglib uri =" http://java.sun.com/jsf/core" prefix ="f" %>
<%@
taglib uri =" http://java.sun.com/jsf/html" prefix ="h" %>
<%@
taglib uri =" http://java.sun.com/jsp/jstl/core" prefix ="c" %>

< c:url var ="link_css" value ="/css/default.css" />
<
f:view>
     < html >
          < head>
               <Link href =" ${link_css} " rel ="stylesheet" type ="text/css" />
               < title> Inserir </title >
          </head >
          < body>
                <center >
                    <h:form >
                        <h:inputHidden value ="#{ action.contato.id}" />
                         <table >
                              <tr >
                                   < td colspan ="2" >
                                        < center >
                                             < h2 class ="header" >Inserção de Contato</ h2>
                                        </center >
                                        < br>
                                   </td >
                              </tr >
                              <tr >
                                   <td colspan ="2" ><h3 >Entre com os dados abaixo</ h3></ td>
                             </ tr>
                              <tr >
                                   <td >Nome e Sobrenome:</ td>
                                   < td>
                                        <h:inputText id ="nome" value = "#{action.contato.nome}" required = "true"/>
                                        <h:message for ="nome" />
                                   </ td>
                              </ tr>
                              < tr>
                                   <td >Endereço: </ td>
                                   <td >
                                        < h:inputText id ="endereco" value ="#{action.contato.endereco}" />
                                   </ td>
                              </ tr>
                              < tr>
                                   < td >Cidade: </td >
                                   < td >
                                        <h:inputText id ="cidade" value= "#{action.contato.cidade}"/>
                                   </ td>
                              </ tr>
                              < tr>
                                   <td >Telefone: </ td>
                                   <td >
                                        < h:inputText id ="telefone" value ="#{action.contato.telefone}" required ="true" maxlength ="10" >
                                             <t:valid ateRegExpr pattern ="(\d+)-(\d+)" message= "Preencha: [número]-[número]."/>
                                        </h:inputText >
                                        < h:message for ="telefone" />
                                   </ td>
                              </ tr>
                              < tr>
                                   <td align ="right" colspan ="2" >
                                        <h:commandButton value ="salvar" action ="#{action.salvar}" styleClass ="botao" />
                                   </ td>
                              </ tr>
                              < tr>
                                   <td colspan ="2" >
                                        <br >
                                        < h:outputLink value ="javascript: history.back ()">
                                             <f:verbatim >voltar </f:verbatim >
                                        </ h:outputLink>
                                   </td >
                              </tr >
                         </ table>
                    </h:form >
               </center >
          </ body>
     </html >
</
f:view>


Nas  classes  Action, a única diferença que surge é que por algum motivo o plugin não instância o objeto usado na página (aqui seria o objeto "contato" com os atributos "id", "nome", "endereco", "cidade" e "telefone") portando uma instância desse objeto deve ser criada no método contrutor da Action. O contrutor da Action do exemplo ficou assim:

public
AgendaAction()
{
    
contato = new Contato();
}


Espero que com esse artigo seja possível a qualquer um com conhecimentos básicos em Struts 2 usar em conjunto com este o Java ServerFaces.

Você pode baixar o exemplo completo aqui.

O arquivo war deve ser importado para o IDE (eu usei o eclipse) e acrecentado ao path, além da biblioteca do servidor, as seguintes biliotecas:

*Do Struts 2.0.6 ( http://struts.apache.org/downloads.html );

* Do MyFaces 1.1.5 e do Tomahawk 1.1.5 – é importante que ambos estejam de acordo com a tabela de compatibilidade entre suas versões (tabela e  links para download: http://wiki.apache.org/myfaces/CompatibilityMatrix ).
* Do FileUpload, usado pelo Tomahawk ( http://jakarta.apache.org/site/downloads/downloads_commons-fileupload.cgi ).



Arquivo do blog