Introdução ao framework web Apache Struts 2
Agenda Histórico Características Arquitetura Elementos Configurações Actions Resultados Exceções Internacionalização Interceptors Taglibs Validação
Histórico Projeto Apache Struts foi lançado em Maio de 2000 Criador: Craig R. McClanahan Versão 1.0 veio em Julho de 2001 Framework web Model 2 (baseado no Smalltalk MVC) Revolução no desenvolvimento Java para Web Controle de fluxo da aplicação [C] Separação entre o modelo e a apresentação [M e V] Configuração (XML), validação, conversão, i18n Problemas arquiteturais e desenho Componentes acoplados, dependentes de herança Muita configuração XML, flexibilidade limitada, etc
Histórico Web Work projeto paralelo ao Struts Ótima arquitetura e desenho, além de mais flexível Não tão utilizado pelo mercado quanto o Struts Struts 2 foi criado após junção das duas comunidades Força de mercado do Struts + qualidade do Web Work
Características Framework web baseado em Actions Actions são POJOs (desacoplamento) facilita testes Anotações ou configurações em XML Convenção (convention over configuration) Uso de interceptadores Diferentes tipos de Result (JSP, Velocity, etc) Tags baseadas em temas e tags com Ajax Trabalha com JSTL, Expression Language (OGNL) Integração natural com Spring, Tiles, SiteMesh Plugins para extender e modificar as funcionalidades
Arquitetura Principais componentes (core components) Actions Interceptors Value Stack / OGNL Result Types Results / View
Ciclo de vida de uma requisição no Struts 2: Filtros Action Mapper Action Proxy Configuration Manager Action Invocation Interceptor Action Result Template Arquitetura
Arquitetura Requisição chega ao Servlet Container, que executa a cadeia de filtros configurada Filter Dispatcher é chamado, que por sua vez consultará o Action Mapper para determinar se o pedido deve invocar uma ação configurada Se uma ação deve ser invocada, o Filter Dispatcher delega o controle ao Action Proxy Action Proxy consulta o Configuration Manager Action Proxy cria um Action Invocation, que é responsável pela execução do command pattern Action Proxy invoca Interceptors e a Action em si
Arquitetura Após retorno da Action, o Action Invocation analisa se há um Result mapeado associado ao retorno da Action O resultado é executado envolvendo um Template JSP, FreeMarker, etc, a ser renderizado (nem sempre, como é o caso de Action Chaining) Templates podem usar as Struts Tags Interceptors são executadas novamente (ordem inversa) A resposta retorna através dos filtros configurados (ordem inversa) Se o filtro ActionContextCleanUp está presente, o Filter Dispatcher não irá limpar o Action Context
ObjectFactory Arquitetura Todos os objetos desta arquitetura (Actions, Results, Interceptors, etc) são criados por um ObjectFactory ObjectFactory é plugável Outra implementação de ObjectFactory pode ser usada Spring Plugin tem implementação do ObjectFactory
Filter Dispatcher Elementos Filtro do Struts 2 que lida com todas as requisições feitas e é responsável por executar as Actions e o conteúdo estático (html, css, scripts, etc) Action É a unidade básica de trabalho, que pode ser associada a uma requisição HTTP. Em geral são simples POJOs, mas pode estender a classe ActionSupport para ter funcionalidades extras, por exemplo
Result Elementos É o resultado final do processamento de uma Action, ou seja, uma String que indica para onde o fluxo da aplicação deve seguir Result Type Tipo do resultado a ser gerado, que pode ser renderizar um JSP, um template Tiles, redirecionar (dispatch) para outra Action, devolver apenas o header HTTP, etc. Além de ser possível criar novos Result Types
Interceptors Elementos Interceptadores adicionam muitas funcionalidades extremamente úteis ao Struts 2. Eles são executados antes e depois da execução da Action e, similares ao filtros, podem ser configurados em determinada ordem Value Stack Uma pilha que serve como área de armazenagem de objetos da aplicação que são necessários durante o processamento da requisição. Os valores são lidos desta pilha quando o resultado é gerado (páginas)
Elementos OGNL (Object-Graph Navigation Language) É uma poderosa linguagem de expressão que é usada para referenciar e manipular valores da Value Stack
Struts 1 x Struts 2 Struts 1 Action ActionForm ActionForward struts-config.xml ActionServlet RequestProcessor validation.xml Struts 2 Action Action ou POJO Result struts.xml FilterDispatcher Interceptors Action-validation.xml
Configuração do Struts 2 na Aplicação Configurando o Struts 2 na aplicação web (web.xml): <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> Copiar os seguintes JARs para o WEB-INF/lib/ struts2-core-2.1.6.jar; xwork-2.1.2.jar; ognl-2.6.11.jar; commons-logging-1.0.4.jar; commons-fileupload-1.2.1.jar; freemarker-2.3.13.jar
Configuração do Struts 2 É possível mudar o comportamento padrão do Struts 2 configurando o ambiente de execução de três formas: web.xml - tag <init-param /> struts.properties - propriedades (par chave/valor) struts.xml - tag <constants /> Valores alterados substituem os valores-padrão configurados no arquivo struts-default.properties que fica dentro do struts2-core.jar Arquivos devem ficar no classpath (WEB-INF/classes/) Mais informações: http://struts.apache.org/2.x/docs/strutsproperties.html
Configuração do Struts 2 O arquivo struts.xml serve para configurar: Constantes do Struts 2 Interceptadores e pilhas de interceptadores Resultados globais (global results) Manipulação de exceções (exception handling) Pacotes (packages) e Actions <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="default" namespace="/" extends="struts-default"> </package> </struts>
Configuração do Struts 2 O arquivo struts.xml pode ser segmentado em outros arquivos de configuração (iguais ao struts.xml) e incluídos com a tag <include /> <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <include file="admin-config.xml" /> <package name="default" namespace="/" extends="struts-default"> </package> </struts> Dois arquivos são incluídos automaticamente: strutsdefault.xml e struts-plugin.xml (que já vem no Struts 2)
Struts 1 x Struts 2 A tag package serve para agrupar configurações comuns Actions, pilhas de interceptadores e namespace de URL Atributos da tag: Atribuição name extends namespace abstract Nome único do pacote. Descrição Nome do pacote o qual estende. Todas as configurações do pacote serão herdadas. Nome do mapeamento da URL para o pacote. Se true, serve apenas como configuração e suas Actions não podem ser acessadas.
Action Unidade básica de trabalho associada à requisição HTTP Classe comum com um método execute() Simples POJO Não precisa implementar interface ou estender classe Possui três funções: Executar a lógica de negócio Carregar os dados da requisão para a view Determinar qual resultado deve renderizar a view Pode estender classes e implementar interfaces especiais Adicionam funcionalidades do framework e plugins
Action Action simples: package company.hr.action; public class LoginAction { private String username; private String password; // métodos GET e SET } public String execute() { System.out.println("user: " + username); return "success"; } Configuração (struts.xml): <package name="default" namespace="/" extends="struts-default"> <action name="login" class="company.hr.action.loginaction"> <result>/login.jsp</result> </action> </package> URL: http://<host>[:porta]/<contexto>/login.action
Action Invocando um método diferente de execute(): package company.hr.action; public class LoginAction { //... código pré-existente aqui } public String logoff() { System.out.println("LOGOFF"); return "success"; } Configuração (struts.xml): <action name="logoff" class="company.hr.action.loginaction" method="logoff"> <result>/login.jsp</result> </action> URL: http://<host>[:porta]/<contexto>/logoff.action
Página de login (login.jsp): Action <%@ page contenttype="text/html; charset=iso-8859-1" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>company - HR</title> </head> <body> <h4>human Resources</h4> <s:form action="login"> <s:textfield name="username" label="username"/> <s:password name="password" label="password"/> <s:submit/> </s:form> </body> </html>
Action Action de redirecionamento (pass-through): <action name="auth"> <result>/login.jsp</result> </action> URL: http://<host>[:porta]/<contexto>/auth.action
Action Action com resultados múltiplos: package company.hr.action; public class LoginAction { private String username; private String password; } // métodos GET e SET public String execute() { if( "admin".equals(username) && "admin".equals(password) ) return "success"; else return "fail"; } Modificando a configuração (struts.xml): <action name="login" class="company.hr.action.loginaction"> <result>/home.jsp</result> <result name="fail">/login.jsp</result> </action> URL: http://<host>[:porta]/<contexto>/login.action
Action Struts 2 suporta mapeamento com coringas (wildcards) Útil quando existe um padrão de mapeamentos Ajuda a reduzir o número de configurações no struts.xml <action name="dept_*" class="company.hr.action.deptaction" method="{1}"> <result>/dept.jsp</result> <result name="{1}">/dept_{1}.jsp</result> </action> package company.hr.action; import java.util.*; import company.hr.entity.dept; public class DeptAction { private List<Dept> depts; // métodos GET e SET } public String list() { // busca a lista de Departamentos return "list"; } URL: http://<host>[:porta]/<contexto>/dept_list.action
Exibindo a lista no JSP: Action <%@ page contenttype="text/html; charset=iso-8859-1" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>company - HR</title> </head> <body> <h4>human Resources</h4> <s:iterator value="depts"> <s:property value="id" /> - <s:property value="name" /> <br /> </s:iterator> </body> </html>
Action Pattern Matching muito útil para CRUDs <action name="/*/*" class="company.hr.action.{1}action" method="{2}"> <result>/{1}.jsp</result> <result name="{1}">/{1}_{2}.jsp</result> </action> Habilitar a configuração do Struts 2 para usar barras: <constant name="struts.enable.slashesinactionnames" value="true" /> URL: http://<host>[:porta]/<contexto>/dept/list.action
Action Support ActionSupport é uma implementação da interface Action Oferece acesso a recursos do ambiente e framework Validação, localização (i18n) e mensagens package company.hr.action; import com.opensymphony.xwork2.actionsupport; public class LoginAction extends ActionSupport { public String execute() { if ( "admin".equals(username) && "admin".equals(password) ) { return "success"; } else { addactionmessage("login incorreto"); return "fail"; } } } // código pré-existente <s:actionmessage />
Interfaces Aware Actions podem ter acesso a recursos do ambiente web Request, Sessão, Contexto e os seus atributos Actions devem implementar interfaces correspondentes RequestAware - Atributos do request SessionAware - Atributos da sessão ApplicationAware - Atributos da aplicação ServletRequestAware - HttpServletRequest ServletResponseAware - HttpServletResponse ServletContextAware - ServletContext
Interfaces Aware package company.hr.action; import java.util.map; import javax.servlet.servletcontext; import javax.servlet.http.*; import org.apache.struts2.interceptor.* import org.apache.struts2.util.servletcontextaware; import com.opensymphony.xwork2.actionsupport; public class GenericAction extends ActionSupport implements SessionAware, RequestAware, ApplicationAware, ServletRequestAware, ServletResponseAware, ServletContextAware { private Map<String, Object> session; private Map<String, Object> request; private Map<String, Object> application; private HttpServletRequest servletrequest; private HttpServletResponse servletresponse; private ServletContext servletcontext; public Map<String, Object> getsession() { return this.session; } public Map<String, Object> setrequest() { return this.request; } public Map<String, Object> getapplication() { return this.application; } public HttpServletRequest getservletrequest() { return this.servletrequest; } //...
Interfaces Aware } public HttpServletResponse getservletresponse() { return servletresponse; } public ServletContext setservletcontext() { return this.servletcontext; } public void setsession(map<string, Object> session) { this.session = session; } public void setrequest(map<string, Object> request) { this.request = request; } public void setapplication(map<string, Object> application) { this.application = application; } public void setservletrequest(httpservletrequest servletrequest) { this.servletrequest = servletrequest; } public void setservletresponse(httpservletresponse servletresponse) { this.servletresponse = servletresponse; } public void setservletcontext(servletcontext servletcontext) { this.servletcontext = servletcontext; }
Result Types Struts 2 suporta diferentes tipos de resultados (JSP, etc) Definido na tag <result type= /> da Action Tipo padrão é dispatcher, que despacha para um JSP Um Result Type próprio pode ser criado e configurado Na tag <result-types /> dentro de <package /> O tipo padrão pode ser alterado (struts.xml) Na tag <result-type /> dentro de < result-types /> <result-types> <result-type name="dispatcher" class="org.apache.struts2.dispatcher.servletdispatcherresult" /> <result-type name="meuresultado" class="company.results.meuresultado" default="true"/> </result-types>
Result Types O framework oferece outros tipos de resultado: Chain - encadeamento de Action Freemarker - uso do framework Freemarker HTTP Header - retorno de cabeçalho HTTP Redirect - redirecionamento para um JSP Redirect Action - redirecionamento para um Action Velocity - uso do framework Velocity Definindo o tipo do resultado: <action name="login" class="company.hr.action.loginaction"> <result type="dispatcher">/home.jsp</result> <result name="fail" type="chain">auth</result> </action>
Global Result Resultados globais podem ser configurados no pacote São visíveis dentre todas as Actions do pacote <package > <global-results> <result name="logon">/logon.jsp</result> <result name="error">/error.jsp</result> <global-results> </package>
Exception Handling Struts 2 suporta manipulação declarativa de exceções Exceções que podem ser lançadas pelas Actions Declaração pode ser global ao pacote ou local à Action <global-exception-mappings> <exception-mapping result="sqlerror" exception="java.sql.sqlxception"/> <exception-mapping result="error" exception="java.lang.exception"/> </global-exception-mappings> <action name="my" class="example.myaction" > <result>view.jsp</result> <exception-mapping result="myerror" exception="java.lang.exception" /> </action>
Internacionalização Suporte a internacionalização via Resource Bundles Pode estar contida em um ou vários arquivos (resources) <constant name="struts.custom.i18n.resources" value="applicationresources" /> Os arquivos properties podem estar divididos: Por classe Action (NomeDaAction.properties) Por classe base na hierarquia da Action Por interface ou sub-interface Por classe de modelo (se Action for ModelDriven) Por pacote de classes (package.properties) Geral (configurado no struts.xml)
Internacionalização Meios de acessar as mensagens configuradas: Na Action (deve estender ActionSupport): String msg = gettext("chave.da.mensagem"); No JSP (via taglib): <s:text name="label.saudacao"/> <s:text name="label.saudacao.nome"> <s:param>ronaldo</s:param> </s:text> <s:property value="gettext( label.saudacao.nome )"/> <s:textfield label="%{gettext( label.saudacao )}"/> <s:textfield key="label.saudacao" name="textfieldname"/> Arquivo de properties: label.saudacao=olá, Mundo! label.saudacao.nome=olá, {0}!
Interceptors Interceptadores tem um papel vital no Struts 2 Proporcionam funcionalidades ao framework São responsáveis pela maior parte do processamento Promovem separação de responsabilidades (concerns) Fazem pré e pós-processamento das Actions São similares aos filtros da API de Servlets Struts 2 fornece vários interceptadores prontos Pilhas de interceptadores são agrupamentos ordenados Desenvolvedor pode criar novos interceptadores e novas pilhas de interceptadores Pacotes e Actions podem usar pilhas/interceptadores configuradas especificamente para cada um
Interceptors Alguns interceptadores do Struts 2: chain - repassa os atributos de Action pra Action execandwait - executa a Action em background exception - mapeia exceções para um resultado i18n - trata da localidade do usuário params - atribui os parâmetros do request na Action static-params - atribui parâmetros do struts.xml na Action validation - faz a validação definida no xml workflow - chama o validate() e direciona se há erro roles - verifica autorização (role) do usuário prepare - chama prepare() se Action é Preparable Mais informações: http://struts.apache.org/2.x/docs/interceptors.html
Interceptors Algumas pilhas de interceptadores do Struts 2: basicstack - pilha mais básica do Struts 2 chainstack - adiciona chaining à basicstack i18nstack - adiciona i18n à basicstack defaultstack - pilha padrão com quase todos interceptors completestack - pilha completa (compatível com WebWork) execandwaitstack - execução assíncrona à defaultstack paramsprepareparamsstack - pilha mais complexa aplica os parâmetros na Action, executa o método prepare() e depois reaplica os parâmetros na Action Pacotes que estendem struts-default usam pilha default
Interceptors São definidos na tag <interceptors /> da tag <package />: <package...> <interceptors> <interceptor name="meuinterceptor" class="exemplo.intercept.meuinterceptor" /> <interceptor name="outrointerc" class="exemplo.intercept.outrointerceptor" /> </interceptors> </package> Actions devem fazer referência para usá-los: <action...> <interceptor-ref name="meuinterceptor" /> <interceptor-ref name="outrointerc" /> <result>/pagina.jsp</result> </action> Pacote pode definir interceptador padrão para as Actions: <default-interceptor-ref name="meuinterceptor" />
Interceptors Em geral as Actions precisam de vários interceptadores Pilhas de interceptadores servem para agrupá-los Configuração é bem similar aos interceptadores <package...> <interceptors> <interceptor name="meuinterceptor" class="exemplo.intercept.meuinterceptor" /> <interceptor name="outrointerc" class="exemplo.intercept.outrointerceptor" /> <interceptor-stack name="minhastack"> <interceptor-ref name="meuinterceptor" /> <interceptor-ref name="outrointerc" /> </interceptor-stack> </interceptors> <default-interceptor-ref name="minhastack" /> <action...> <interceptor-ref name="minhastack" /> <result>/pagina.jsp</result> </action> </package>
Interceptors Criando seu próprio interceptador: package company.hr.intercept; import com.opensymphony.xwork2.actioninvocation;; public class SecurityInterceptor implements Interceptor { } public String intercept(actioninvocation inv) throws Exception { System.out.println("pré-processamento"); String result = inv.invoke(); System.out.println("pós-processamento"); return result; } public void init() {} public void destroy() {} ActionInvocation dá acesso a objetos do ciclo do request Action, ActionContext, ActionProxy, Result, ValueStack
Taglibs Struts 2 fornece tags JSP, Velocity e Freemarker Tipos de tags: Dados, Controle, Úteis, Visuais
Tags de Dados Acessam ou colocam valores contidos na Value Stack Property tag escreve uma propriedade no HTML <s:property value="user.username"/> Set tag atribui uma propriedade para outro nome <s:set name="username" value="user.username"/> Push tag cria referências a valores na Value Stack <s:push value="user"> Nome: <s:property value= username"/> </s:push> Bean tag Misto de Set e Push, mas criando o objeto <s:bean name="org.apache.struts2.util.counter" var="counter"> <s:param name="last" value="7"/> </s:bean> <s:iterator value="#counter"> <li><s:property/></li> </s:iterator> Action tag execute uma Action e grava o resultado <s:action name="targetaction" executeresult="true"/>
Tags de Controle Iterator tag itera sobre coleções de objetos <s:iterator value="users" status="st"> <s:property value="#st.count" />: <s:property value="portfolioname"/><br> </s:iterator> If e Else tags controle de IF e ELSE <s:if test="user.age < 18">Menor de idade.</s:if> <s:elseif test="user.age > 65">Terceira idade.</s:elseif> <s:else>idade adulta.</s:else>
Tags de Úteis Include tag include a saída de outro recurso web <s:include value="/algumaaction.action" /> URL tag trabalha com a criação de URLs <s:url action="algumaaction" var="myurl"> <s:param name="id" value="2"/> </s:url> <a href='<s:property value="#myurl" />'> Clique Aqui </a> i18n tag especifica um resource bundle a ser usado <s:i18n name="myresourcebundle_jp"> Text tag escreve um texto associado a i18n <s:text name="label.descricao"/> Param tag serve para passar valores de parâmetros a outras tags que a utilizam
Tags Visuais Head tag gera informação necessária no cabeçalho <head> <s:head /> </head> Form tag representa o form html <s:form action="myaction" method="post" >... </s:form> Textfield tag representa um campo de texto no form <s:textfield key="username" /> Password tag representa um campo de senha no form <s:password name="username" label="username" /> Textarea tag representa um textarea no form <s:textarea name="corpotexto" cols="30" rows="10" /> Checkbox tag representa um checkbox no form <s:checkbox name="ativado" label="ativado" fieldvalue="true" />
Tags Visuais Select tag represente um select box no form <s:select name="estado"> <option value="sp">sp</option> </s:select> <s:select name="pais" list="paises" listvalue="id" listkey="nome" /> Hidden tag representa um campo hidden no form <s:hidden name="codigo" /> Radio tag representa um campo radio no form <s:radio name="sexo" list="generos" value="nome" listkey="id" listvalue="nome" label="sexo" /> Label tag representa um label no form <s:label name="username" label="username" />
Temas Temas são usados como templates das tags visuais Struts 2 vem com alguns temas para utilizar simple - renderiza o html básico do componente xhtml - usa table para gerar o layout (padrão) css_xhtml - usa puro css para gerar o layout ajax - estende xhtml e provê ajax (beta) Para alterar a configuração default (struts.xml): <constant name="struts.ui.theme" value="simple" />
Validação Validação pode ser feita de algumas formas Básica No cliente (javascript) Via Ajax Via anotações Outras Validação programática - Action deve ter o método validate() e estender ActionSupport Validação via XML arquivo Action-validation.xml no pacote da Action e incluir interceptador de validação
Validação Arquivo de validação de exemplo: <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> <validators> <field name="nome"> <field-validator type="requiredstring"> <message>você deve colocar um nome</message> </field-validator> </field> <field name="age"> <field-validator type="int"> <param name="min">13</param> <param name="max">19</param> <message>formulário para pessoas entre 13 e 19 anos</message> </field-validator> </field> </validators>