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 >
<! 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 >
<%@ 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 ).