2. Um primeiro exemplo
A maioria dos nossos exemplos limitar-se-á à camada web implementada com o Struts 2:
![]() |
Depois de abordarmos os conceitos básicos, analisaremos um exemplo mais complexo com uma arquitetura multicamadas.
2.1. Gerar o exemplo
Estamos a construir a nossa primeira aplicação.
![]() |
- Em [1], criamos um novo projeto
- Em [2], selecione o tipo Java Web / Aplicação Web
- Em [3], nomeie o projeto
- Em [4], especifique a localização do projeto.
- Em [5], definimos o novo projeto como projeto principal.
![]() |
- Em [6], selecione o servidor Tomcat. Quando o NetBeans 7.01 foi instalado, foram instalados dois servidores: o Apache Tomcat e o GlassFish 3.1.
- Em [7], especifique que irá trabalhar com a estrutura Struts 2. Esta opção está disponível porque o plugin Struts 2 foi instalado. Sem o plugin, a estrutura Struts 2 não é oferecida.
- Em [8], solicitamos a criação do projeto de exemplo que iremos estudar.
- Em [9], pode verificar quais as bibliotecas Struts 2 que serão utilizadas.
![]() |
- Em [10], o projeto gerado. Voltaremos a este ponto mais tarde.
- Em [11], as bibliotecas do projeto. Estas foram integradas pelo plugin Struts 2. Se não tiver o plugin, pode encontrar estas bibliotecas na pasta [lib] da distribuição do Struts 2 que descarregou. Deve então seguir os passos 12 e 13.
2.2. O projeto gerado no sistema de ficheiros
![]() |
- Em [1], o separador [Projects] apresenta uma vista «de programador» do projeto
- Em [2], o separador [Ficheiros] apresenta a pasta do projeto no sistema de ficheiros
- Em [2A], o ramo [Páginas Web] é representado em [2] pela pasta [web] [2B]
- Em [3A], o ramo [Pacotes de código-fonte] é representado em [2] pela pasta [java] [3B]
2.3. O ficheiro de configuração [META-INF/context.xml]
![]() |
Este ficheiro tem o seguinte conteúdo:
<?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="/exemple-01"/>
A linha 2 indica que o contexto da aplicação web é /example-01. Todos os URLs do tipo [http://machine:port/exemple-01/...] serão tratados por esta aplicação. Este contexto pode ser encontrado nas propriedades do projeto [2]: clique com o botão direito do rato no projeto / Propriedades / Executar.
2.4. O ficheiro de configuração [WEB-INF/web.xml]
![]() |
Todas as aplicações web são configuradas pelo ficheiro [web.xml] na pasta [WEB-INF] da aplicação. O ficheiro gerado é o seguinte:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 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 http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<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>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>example/HelloWorld.jsp</welcome-file>
</welcome-file-list>
</web-app>
- As linhas 3–6 definem um filtro implementado pela classe Struts 2 [org.apache.struts2.dispatcher.FilterDispatcher]. Esta classe atua como o Controlador (C) no modelo MVC.
- Linhas 7–10: definem um mapeamento entre um padrão de URL e o filtro que deve tratar as URLs que correspondam a esse padrão. Aqui, especifica-se que qualquer URL (padrão /*) deve ser tratada pelo filtro denominado struts2. Este é o filtro definido nas linhas 3–6. Por conseguinte, todas as URLs passarão pelo controlador Struts 2.
- Linhas 11–15: definem a duração de uma sessão de utilizador, aqui definida para 30 minutos. Durante várias solicitações, um utilizador é rastreado por um token de sessão que lhe é atribuído na sua primeira solicitação, o qual ele envia sistematicamente com cada nova solicitação. Isto permite que o servidor web reconheça o utilizador e gerencie uma «memória» para ele, conhecida como sessão. Se mais de 30 minutos se passarem entre duas solicitações, um novo token de sessão é gerado para o utilizador, que assim perde a sua «memória» e inicia uma nova.
- Linhas 16–18: definem o ficheiro a apresentar quando o utilizador acede à aplicação web sem solicitar um documento. Assim, quando o URL solicitado for [http://machine:port/exemple-01], o URL servido será [http://machine:port/exemple-01/example/HelloWord.jsp].
2.5. O ficheiro de configuração [struts.xml]
![]() |
O ficheiro [struts.xml] é o ficheiro de configuração do Struts 2. Pode estar localizado em qualquer local do ClassPath do projeto. No projeto NetBeans acima, o ClassPath do projeto consiste em duas ramificações:
- Pacotes de código-fonte
- Bibliotecas
Qualquer pasta ou biblioteca localizada nestes dois ramos faz, portanto, parte do ClassPath do projeto. É prática comum colocar o [struts.xml] no <pacote padrão>. Aqui, o seu conteúdo é o seguinte:
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<include file="example.xml"/>
<!-- Configuration for the default package. -->
<package name="default" extends="struts-default">
</package>
</struts>
- Linhas 5 e 10: A tag raiz do documento é a tag <struts>
- linha 6: o ficheiro [example.xml] é inserido aqui. Por isso, traz a sua própria configuração. Voltaremos a este assunto mais tarde.
- Linhas 8–9: definem aqui um pacote chamado «default». Um pacote permite-lhe configurar um grupo de ações Struts 2 que partilham o mesmo URL. Por exemplo, [/path/Action1] e [/path/Action2]. Pode então definir um pacote para estas ações:
<package name="employes" namespace="/employes" extends="struts-default">
... configuration
</package>
O pacote acima chama-se «employees» e configura ações com a URL /employees/Action. Um pacote pode herdar de outro pacote utilizando a palavra-chave «extends». No exemplo acima, o pacote «employees» herda do pacote «struts-default». Este pacote encontra-se no ficheiro [struts-default.xml] dentro da biblioteca struts2-core.jar:
![]() |
O pacote "struts-default" definido no ficheiro [struts-default.xml] configura vários elementos, incluindo uma lista de interceptores executados quando uma ação é chamada. Voltemos à estrutura MVC de uma aplicação Struts 2:
![]() |
Para processar uma URL do tipo [http://machine:port/.../Action], o controlador [FilterDispatcher] instanciará a classe que implementa a ação solicitada e executará um dos seus métodos — por predefinição, um método chamado execute. A chamada a este método execute passará por uma série de interceptores:
![]() |
Os interceptores e a ação processam todos a mesma solicitação. A lista de interceptores definida no pacote struts-default é suficiente na maioria das vezes. Portanto, os nossos pacotes Struts irão sempre estender o pacote struts-default. Para saber o que os vários interceptores fazem, consulte o Capítulo 4 de [ref2].
Voltemos ao ficheiro de configuração struts.xml:
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<include file="example.xml"/>
<!-- Configuration for the default package. -->
<package name="default" extends="struts-default">
</package>
</struts>
- Linha 8: O pacote denominado «default» tem uma função especial. Ele lida com ações que não foram configuradas nos outros pacotes. Aqui, nas linhas 8–9, não é especificada nenhuma configuração para o pacote «default». Poderíamos, portanto, remover a definição deste pacote.
Agora, vamos analisar o ficheiro [example.xml] incluído na linha 6 do ficheiro [struts.xml]:
<?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="example" namespace="/example" extends="struts-default">
<action name="HelloWorld" class="example.HelloWorld">
<result>/example/HelloWorld.jsp</result>
</action>
</package>
</struts>
- Linha 8: define um pacote chamado example que estende o pacote struts-default. Este pacote trata de URLs do tipo /example/Action (namespace /example).
- linhas 9–11: definem uma ação chamada HelloWorld (atributo name) que corresponde à URL /example/HelloWorld. Esta ação é tratada por uma instância da classe example.HelloWorld (atributo class).
- O controlador [FilterDispatcher] executará o método execute desta classe.
- Este método irá devolver uma string denominada chave de navegação.
- As várias chaves de navegação devem ser definidas utilizando as tags <result name="key"/> (linha 10). Se o atributo name for omitido, a chave success é utilizada por predefinição. É o que acontece no caso acima. Por conseguinte, o método execute da classe example.HelloWorld deve devolver a chave success ao controlador [FilterDispatcher].
- O controlador [FilterDispatcher] exibe então a página [/example/HelloWorld.jsp] (linha 10).
Se fundirmos os dois ficheiros [struts.xml] e [example.xml] e removemos o pacote padrão, que parece desnecessário, é como se tivéssemos reduzido o ficheiro [struts.xml] apenas ao ficheiro [example.xml].
2.6. A ação HelloWorld
![]() |
De acordo com o ficheiro [struts.xml] que analisámos, a ação HelloWorld é acionada quando o URL solicitado pelo cliente é /example/HelloWorld. O seu método execute é então executado. Deve devolver uma chave de navegação. Vimos que havia apenas uma: success, e que a página /example/HelloWorld.jsp foi enviada em resposta ao utilizador.
O código para a ação HelloWorld é o seguinte:
package example;
import com.opensymphony.xwork2.ActionSupport;
public class HelloWorld extends ActionSupport {
public String execute() throws Exception {
setMessage(getText(MESSAGE));
return SUCCESS;
}
public static final String MESSAGE = "HelloWorld.message";
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
- Linha 5: A classe HelloWorld estende a classe ActionSupport do Struts 2. Isto acontece quase sempre. Isto permite-nos utilizar determinados métodos, como o método getText na linha 8.
- Linhas 7–10: O método `execute`, que é executado quando o controlador Struts é invocado. Sabemos que ele deve retornar uma string, daí a sua assinatura na linha 7. Aqui, ele retorna a constante `SUCCESS`, que também está definida em `ActionSupport`. Outras constantes são definidas da mesma forma para o resultado do método `execute`:
SUCCESS | "sucesso" |
ERRO | "erro" |
ENTRADA | "entrada" |
LOGIN | "login" |
Assim, aqui, o método execute devolve a cadeia de caracteres "success". De acordo com o ficheiro [struts.xml], isto significa que a página /example/HelloWorld.jsp será apresentada ao utilizador.
- Linha 8: O método `execute` inicializa o campo `message` na linha 14 com o valor de `getText("HelloWorld.message")`. O método `getText` pertence à classe pai `ActionSupport`. Este método recupera texto de um ficheiro com base no idioma utilizado. Por predefinição, será utilizado aqui o ficheiro `package.properties` localizado no mesmo pacote que a ação. Este ficheiro existe em duas versões:
O ficheiro [package.properties] é o seguinte:
Consiste numa série de linhas de texto no formato chave=valor. Se este ficheiro for utilizado, getText("HelloWorld.message") irá devolver o valor Struts está em funcionamento ...
O ficheiro [package_es.properties] é o seguinte:
Se o idioma utilizado pelo navegador do cliente for o espanhol (o atributo «es» em package_es.properties), getText(«HelloWorld.message») irá devolver o valor ¡Struts está bien! ... Em todos os outros casos, será utilizado o ficheiro [package.properties].
2.7. A vista HelloWorld.jsp
![]() |
Esta é a peça final do quebra-cabeças do Struts. É a vista que é exibida quando a URL /example/HelloWorld é solicitada. Vimos o caminho que a solicitação inicial percorreu para finalmente exibir esta resposta. O código da página é o seguinte:
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title><s:text name="HelloWorld.message"/></title>
</head>
<body>
<h2><s:property value="message"/></h2>
<h3>Languages</h3>
<ul>
<li>
<s:url id="url" action="HelloWorld">
<s:param name="request_locale">en</s:param>
</s:url>
<s:a href="%{url}">English</s:a>
</li>
<li>
<s:url id="url" action="HelloWorld">
<s:param name="request_locale">es</s:param>
</s:url>
<s:a href="%{url}">Espanol</s:a>
</li>
</ul>
</body>
</html>
- A página utiliza tags HTML (linhas 5, 6, ...) e tags de uma biblioteca definida na linha 3. Todas as tags <s:xx> pertencem a esta biblioteca.
- Linha 7: A tag <s:text> permite exibir texto diferente dependendo do idioma do navegador do cliente. O atributo name especifica a chave a procurar nos ficheiros de mensagens. Aqui, também, serão utilizados os ficheiros package_xx.properties. Recorde-se que estes contêm apenas uma única mensagem com a chave HelloWorld.message.
- Linha 11: A tag <s:property name="property"> é utilizada para escrever o valor de uma propriedade de um objeto chamado ActionContext. Este objeto contém:
- as propriedades da ação que foi executada. name="message" exibirá o valor do campo message da ação atual. Os métodos get e set associados ao campo são utilizados para recuperar o seu valor ou inicializá-lo. Estes métodos devem, portanto, existir.
- os atributos do pedido atual, indicados como <s:property name="#request['key']">
- os atributos da sessão do utilizador, indicados como <s:property name="#session['key']">
- atributos da própria aplicação, indicados como <s:property name="#application['key']">
- parâmetros enviados pelo navegador do cliente, indicados como <s:property name="#parameters['key']">
- A notação <s:property name="#attr['key']"> exibe o valor de um objeto procurado na página, na solicitação, na sessão e na aplicação, nessa ordem.
- Linhas 16–18: A tag <s:url> é utilizada para definir um URL. O atributo id atribui um nome ao URL que será criado. Este nome é então utilizado na linha 19. O atributo action especifica para que ação o URL deve apontar.
- Linha 17: A tag <s:param ..> permite adicionar parâmetros à URL no formato ?param1=valor1¶m2=valor2&... Aqui, o parâmetro adicionado será ?request_locale=es.
Por fim, a URL gerada será a seguinte:
Para compreender esta URL, lembre-se de que a página [HelloWorld.jsp] é apresentada em dois casos:
- mediante pedido direto da URL [/example-01/example/HelloWorld.jsp]
- quando a ação [/example-01/example/HelloWorld.action] é solicitada
Em ambos os casos, o caminho da URL é /example-01/example. A tag <s:url action= "... "> acrescenta a ação definida pelo atributo action a este caminho. Isto resulta em /example-01/example/HelloWorld. Em seguida, acrescenta o sufixo .action à URL anterior, juntamente com quaisquer parâmetros de URL, se presentes. A URL resultante /example-01/example/HelloWorld.action?request_locale=en chamará a ação HelloWorld definida no ficheiro [struts.xml], passando o parâmetro request_locale=en. Este parâmetro não será processado pela ação HelloWorld, mas por um dos interceptores do Struts, especificamente aquele que lida com a internacionalização da página. O parâmetro request_locale será reconhecido e processado. O idioma da página mudará para inglês (en).
- Linha 19: define um link HTML. O atributo href da tag <a> espera uma string. Aqui, queremos usar o valor da URL definida na linha 16 com o id "url". Para isso, escrevemos href="%{url}". A variável url é avaliada e o seu valor é atribuído ao atributo href. Na maioria dos casos, a avaliação de variáveis é implícita. Por exemplo, quando escrevemos
<s:property name="message"/>
o valor da propriedade message é exibido, e não a string "message". Mas, noutros casos, é necessário forçar a avaliação de variáveis ou propriedades. Se tivéssemos escrito href="url", a string "url" teria sido atribuída ao atributo href.
- Linhas 23–27: criam um link HTML para alterar o idioma da página para espanhol.
2.8. Executar a aplicação
Iniciamos o projeto:
![]() |
- Em [1], executamos o projeto [example-01]. O servidor web Tomcat é então iniciado automaticamente, caso ainda não o estivesse. A URL [/example-01] é solicitada [3]. O ficheiro [web.xml] é então utilizado:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 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 http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<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>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>example/HelloWorld.jsp</welcome-file>
</welcome-file-list>
</web-app>
- Como o URL solicitado [/example-01] não especifica uma página, o Tomcat utilizará a tag <welcome_file-list> nas linhas 16 e 18. Por conseguinte, o URL /example-01/example/HelloWorld.jsp será servido.
- Como o Struts 2 processa todas as URLs (linhas 8 e 9), esta URL será filtrada pelo Struts. Uma vez que não corresponde a uma ação, mas a uma página JSP, esta última será apresentada.
- O que vemos em [2] é, portanto, a página HelloWorld.jsp que estudámos.
- Em [4], vemos que a tag <title><s:text name="HelloWorld.message"/></title> não surtiu efeito, nem a tag <h2><s:property value="message"/></h2>. A razão é que nenhuma ação foi chamada. A lista de interceptores que são executados antes da ação não foi, portanto, executada, nomeadamente aquele que lida com a internacionalização. A tag de internacionalização <s:text ...> não pôde ser processada corretamente. Além disso, a propriedade message, que faz referência ao campo message da classe Action1, não existe. Daí a ausência de exibição.
Agora, vamos seguir o link [Inglês]. Obtemos a seguinte página:
![]() |
- em [1], o URL solicitado. Já explicámos como este URL é formado. Desta vez, é solicitada uma ação Struts: a ação HelloWorld definida em [example.xml].
<struts>
<package name="example" namespace="/example" extends="struts-default">
<action name="HelloWorld" class="example.HelloWorld">
<result>/example/HelloWorld.jsp</result>
</action>
</package>
</struts>
- O método execute desta ação foi executado. Vimos que ele devolveu a chave «success». A partir da linha 4 acima, podemos deduzir que a página /example/HelloWorld.jsp é devolvida ao cliente. É isto que vemos em [3].
- Em [1], vemos que o URL solicitado é definido pelo parâmetro `request_locale=en`. O idioma das páginas será agora o inglês. Na verdade, esta escolha de idioma é armazenada na sessão do utilizador, que manterá esta escolha até que o utilizador a altere.
- Em [2] e [3], vemos a internacionalização em ação. As tags <title><s:text name="HelloWorld.message"/></title> e <h2><s:property value="message"/></h2> entraram em vigor desta vez.
Se selecionarmos agora o link [Espanol], obtemos a página em espanhol:
![]() |
2.9. Conclusão
Analisámos um exemplo gerado automaticamente pelo plugin Struts 2 para o NetBeans. Constatámos que era bastante difícil acompanhar o processamento de um pedido. Estão envolvidos os seguintes elementos:
- a configuração [web.xml], [struts.xml]
- a ação executada: interceptores e o método execute.
À primeira vista, o funcionamento do Struts 2 pode parecer complexo. Leva algum tempo a habituar-se a ele. Apresentaremos agora uma série de exemplos, cada um ilustrando um aspeto específico do Struts.















