2. Um primeiro exemplo
A maioria dos nossos exemplos limitar-se-á apenas à camada web implementada com o Struts 2:
![]() |
Quando tivermos adquirido os conceitos básicos, iremos estudar um exemplo mais complexo com uma arquitetura multicamadas.
2.1. Geração do exemplo
Vamos construir a nossa primeira aplicação.
![]() |
- em [1], criamos um novo projeto
- em [2], escolhe-se o tipo Java Web / Aplicação Web
- em [3], atribuímos um nome ao projeto
- em [4], indicamos a localização do projeto.
- em [5], define-se o novo projeto como projeto principal.
![]() |
- em [6], escolhe-se o servidor Tomcat. Na instalação do NetBeans 7.01, foram instalados dois servidores: o Apache Tomcat e o GlassFish 3.1.
- em [7], indicamos que vamos trabalhar com o framework Struts 2. É a instalação do plugin para o Struts 2 que nos dá essa possibilidade. Sem o plugin, o framework Struts 2 não nos é proposto.
- No [8], solicita-se a criação do projeto de exemplo que iremos estudar.
- Em [9], é possível verificar quais as bibliotecas do Struts 2 que serão utilizadas.
![]() |
- em [10], o projeto gerado. Voltaremos a este ponto.
- em [11], as bibliotecas do projeto. Foram integradas pelo plugin Struts 2. Se não dispusermos do plugin, poderemos encontrar estas bibliotecas na pasta [lib] da distribuição Struts 2 descarregada. Seguiremos então 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 [Files] apresenta a pasta do projeto no sistema de ficheiros
- em [2A], o ramo [Web Pages] é representado em [2] pela pasta [web] [2B]
- em [3A], o ramo [Source Packages] é representado em [2] pela pasta [java] [3B]
2.3. O ficheiro de configuração [META-INF/context.xml]
![]() |
Este ficheiro é o seguinte:
<?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="/exemple-01"/>
A linha 2 indica que o contexto da aplicação web é /exemple-01. Todos os URL do tipo [http://machine:port/exemple-01/...] serão processados por esta aplicação. É possível encontrar este contexto 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], localizado 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 [org.apache.struts2.dispatcher.FilterDispatcher] do Struts 2. É esta classe que desempenhará o papel do controlador C do modelo MVC.
- Linhas 7-10: definem uma ligação entre um modelo URL e o filtro que deve processar os URL que seguem este modelo. Aqui, indica-se que qualquer URL (modelo /*) deve ser processado pelo filtro denominado struts2. Trata-se do filtro definido nas linhas 3-6. Assim, neste caso, todos os URL passarão pelo controlador Struts 2.
- linhas 11-15: definem a duração de uma sessão do utilizador, neste caso 30 minutos. Ao longo das diferentes solicitações, um utilizador é acompanhado por um token de sessão que lhe foi atribuído na sua primeira solicitação e que, posteriormente, envia sistematicamente em cada nova solicitação. Isto permite que o servidor web o reconheça e gere uma «memória» para o utilizador, a que se chama sessão. Se, entre duas solicitações, decorrerem mais de 30 minutos, é gerado um novo token de sessão para o utilizador, que perde assim a sua «memória» e recomeça 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 fornecido 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 em qualquer local do ClassPath do projeto. No projeto NetBeans acima, o ClassPath do projeto é composto por duas ramificações:
- Pacotes de código-fonte
- Bibliotecas
Qualquer pasta ou biblioteca localizada nestes dois ramos faz, portanto, parte do ClassPath do projeto. É habitual 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"/>
<!-- Configuração para o pacote predefinido. -->
<package name="default" extends="struts-default">
</package>
</struts>
- linhas 5 e 10: a baliza raiz do documento é a baliza <struts>
- linha 6: o ficheiro [example.xml] é inserido aqui. Por conseguinte, traz a sua própria configuração. Voltaremos a este ponto mais tarde.
- linhas 8-9: definem um pacote denominado aqui «default». Um pacote permite configurar um grupo de ações Struts 2 com o mesmo URL. Por exemplo, [/chemin/Action1] e [/chemin/Action2]. É então possível definir um pacote para estas ações:
<package name="employes" namespace="/employes" extends="struts-default">
... configuration
</package>
O pacote acima chama-se «empregados» e configura as ações de URL e /employes/Action. O pacote pode herdar de outro pacote com a palavra-chave «extends». No exemplo acima, o pacote employes herda do pacote struts-default. Este pacote encontra-se no ficheiro [struts-default.xml] da biblioteca struts2-core.jar:
![]() |
O pacote «struts-default», definido no ficheiro [struts-default.xml], configura vários aspetos, incluindo uma lista de interceptores executados aquando da chamada a uma ação. Voltemos à estrutura MVC de uma aplicação Struts 2:
![]() |
Para processar uma URL do tipo [http://machine:port/.../Action], o controlador [FilterDispatcher] irá instanciar a classe que implementa a ação solicitada e executará um dos seus métodos, por predefinição um método denominado execute. A chamada a este método execute irá 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. Por isso, os nossos pacotes Struts irão sempre estender o pacote struts-default. Para saber o que fazem os diferentes interceptores, consulte o capítulo 4 do [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"/>
<!-- Configuração para o pacote predefinido. -->
<package name="default" extends="struts-default">
</package>
</struts>
- linha 8: o pacote denominado default tem uma função específica. Trata das ações que não foram configuradas nos outros pacotes. Aqui, nas linhas 8-9, não há qualquer configuração para o pacote default. Por isso, poderíamos eliminar a definição deste pacote.
Vejamos agora 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 denominado example que estende o pacote struts-default. Este pacote gere os URL do tipo /example/Action (namespace /example).
- linhas 9-11: definem uma ação denominada HelloWorld (atributo name), que corresponde, portanto, a URL /example/Helloworld. Esta ação é processada por uma instância da classe example.HelloWorld (atributo class).
- O controlador [FilterDispatcher] irá executar o método execute desta classe.
- Este método irá devolver uma cadeia de caracteres denominada «chave de navegação».
- As diferentes chaves de navegação devem ser definidas por balizas <result name="chave"/> (linha 10). Na ausência do atributo name, é utilizada por predefinição a chave success. É o que acontece no exemplo acima. Assim, o método execute da classe example.HelloWorld deve devolver ao controlador [FilterDispatcher] a chave success.
- O controlador [FilterDispatcher] faz então com que a página [/example/HelloWorld.JSP] seja apresentada (linha 10).
Se fundirmos os dois ficheiros [struts.xml] e [example.xml] e eliminarmos o pacote default, que parece desnecessário, tudo acontece como se tivéssemos o ficheiro [struts.xml] reduzido apenas ao ficheiro [example.xml].
2.6. A ação HelloWorld
![]() |
De acordo com o ficheiro [struts.xml] analisado, a ação HelloWorld é acionada quando o URL solicitado pelo cliente é /example/HelloWorld. O seu método execute é então executado. Este deve devolver uma chave de navegação. Vimos que só existia uma: success e que, nesse caso, era a página /example/HelloWorld.JSP que era enviada como resposta ao utilizador.
O código da ação HelloWorld é o seguinte:
- linha 5: a classe HelloWorld deriva da classe ActionSupport do Struts 2. É praticamente sempre assim. Isto permite beneficiar de certos métodos, como o método getText da linha 8.
- linhas 7-10: o método execute, que é executado quando o controlador Struts é invocado. Sabemos que este método deve devolver uma cadeia de caracteres, daí a sua assinatura na linha 7. Aqui, irá devolver a constante SUCCESS, também definida em ActionSupport. Outras constantes são assim definidas para o resultado do método execute:
SUCCESS | «success» |
ERROR | "error" |
INPUT | "input" |
LOGIN | "login" |
Assim, neste caso, o método execute gera a cadeia success. Se nos referirmos ao ficheiro [struts.xml], será então a página /example/HelloWorld.JSP que será devolvida como resposta ao utilizador.
- linha 8: o método execute inicializa o campo message da linha 14 com o valor de getText(" HelloWorld.message "). O método getText pertence à classe pai ActionSupport. Permite recuperar um texto de um ficheiro de acordo com o idioma utilizado. Por predefinição, será aqui utilizado 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:
Trata-se de uma sequência de linhas de texto com o formato clé=valeur. Se este ficheiro for utilizado, getText("HelloWorld.message") terá como valor «Struts is up and running ...»
O ficheiro [package_es.properties] é o seguinte:
Se o idioma utilizado pelo navegador do cliente for o espanhol (atributo «es», em package_es.properties), getText("HelloWorld.message") terá como valor «¡Struts está bem!». ... Em todos os outros casos, será utilizado o ficheiro [package.properties].
2.7. A vista HelloWorld.JSP
![]() |
Este é o último elemento do quebra-cabeças do Struts. É a vista que é apresentada quando o URL /example/HelloWorld é solicitado. Vimos por que labirinto a solicitação inicial passou para, finalmente, apresentar 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 baliza <s:text> permite apresentar um texto diferente consoante o idioma do navegador do cliente. O atributo name indica a chave a procurar nos ficheiros de mensagens. Também aqui 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 baliza <s:property name="propriedade"> permite escrever o valor de uma propriedade de um objeto denominado ActionContext. Neste objeto encontram-se:
- as propriedades da ação que foi executada. name="message" irá apresentar o valor do campo «message» da ação atual. São os métodos get e set associados ao campo que são utilizados para obter o seu valor ou para o inicializar. Estes métodos têm, portanto, de existir.
- os atributos do pedido atual indicados como <s:property name="#request['clé']">
- os atributos da sessão do utilizador indicados como <s:property name="#session['clé']">
- os atributos da própria aplicação, indicados como <s:property name="#application['clé']">
- os parâmetros enviados pelo navegador do cliente, indicados por <s:property name="#parameters['clé']">
- a notação <s:property name="#attr['clé']"> apresenta o valor de um objeto procurado na página, na consulta, na sessão e na aplicação, por esta ordem.
- linhas 16-18: a baliza <s:url > serve para definir uma URL. O atributo id atribui um nome à URL que vai ser criada. Este nome é depois utilizado na linha 19. O atributo action indica para que ação a URL deve apontar.
- linha 17: a baliza <s:param ..> permite adicionar parâmetros à URL na forma ?param1=valeur1¶m2=valeur2&.... Aqui, o parâmetro adicionado será ?request_locale=es.
No final, o URL gerado será o seguinte:
Para compreender este URL, é necessário ter em conta que a página [HelloWorld.JSP] é apresentada em dois casos:
- (continuação)
- mediante pedido direto do URL [/exemple-01/example/HelloWorld.JSP]
- mediante solicitação da ação [/exemple-01/example/HelloWorld.action]
Em ambos os casos, o caminho do URL é /exemple-01/example.. A baliza <s:url action= "... "> adiciona a ação definida pelo atributo action a este caminho. Assim, obtém-se /exemplo-01/exemplo/HelloWorld. Em seguida, adiciona o sufixo .action ao URL anterior, bem como os parâmetros do URL, caso existam. O URL obtido /exemplo-01/exemplo/HelloWorld.action?request_locale=en irá chamar a ação HelloWorld definida no ficheiro [struts.xml], passando o parâmetro request_locale=en. Este último não será processado pela ação HelloWorld, mas sim por um dos interceptores do Struts, o que gere a internacionalização das páginas. O parâmetro request_locale será reconhecido e processado. O idioma das páginas passará a ser o inglês (en).
- linha 19: define uma ligação HTML. O atributo href da baliza <a> espera uma cadeia de caracteres. Aqui, pretendemos utilizar o valor de URL definido na linha 16 e com o id URL. Para tal, escreve-se href="%{URL}". A variável URL é avaliada e o seu valor é atribuído ao atributo href. Na maioria dos casos, a avaliação das variáveis é implícita. Por exemplo, quando se escreve
, é o valor da propriedade message que é apresentado e não a cadeia "message". Mas, noutros casos, é necessário forçar a avaliação das variáveis ou propriedades. Se tivéssemos escrito href= "URL", teria sido a cadeia URL a ser atribuída ao atributo href.
- linhas 23-27: criam um link HTML para alterar o idioma das páginas para espanhol.
2.8. Execução da aplicação
Iniciamos a execução do projeto:
![]() |
- em [1], iniciamos a execução do projeto [exemple-01]. O servidor web Tomcat é então iniciado automaticamente, caso ainda não o estivesse. O URL [/exemple-01] é solicitado [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>
- porque, no URL solicitado ([/exemple-01]), não é especificada nenhuma página, pelo que o Tomcat irá utilizar a baliza <welcome_file-list> das linhas 16 e 18. Portanto, será servida a página URL /exemplo-01/exemplo/HelloWorld.JSP.
- Como o Struts 2 processa todas as URL (linhas 8 e 9), esta URL será filtrada pelo Struts. Como não corresponde a uma ação, mas sim a uma página JSP, esta última será apresentada.
- O que vemos em [2] é, portanto, a página HelloWorld.JSP que analisámos.
- Em [4], vemos que a baliza <title><s:text name="HelloWorld.message"/></title> não produziu o efeito pretendido, tal como a baliza <h2><s:property value="message"/></h2>. A razão para tal é 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 gere a internacionalização. A baliza 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 visualização.
Agora, sigamos a ligação [English]. Obtemos a seguinte página:
![]() |
- em [1], o URL solicitado. Já explicámos a formação deste URL. 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 esta devolvia a chave success. A partir da linha 4 acima, deduz-se que a página /example/HelloWorld.JSP é devolvida como resposta ao cliente. É isso que vemos em [3].
- Em [1], vemos que a página URL solicitada é configurada pelo parâmetro request_locale=en. A língua das páginas será, a partir de agora, o inglês. Com efeito, esta escolha de língua é guardada na sessão do utilizador, que manterá esta escolha até que a altere.
- Nos códigos [2] e [3], é possível observar a internacionalização em ação. As balizas <title><s:text name="HelloWorld.message"/></title> e <h2><s:property value="message"/></h2> produziram, desta vez, o efeito pretendido.
Se selecionarmos agora o link [Espanol], obtemos a página em espanhol:
![]() |
2.9. Conclusion
Analisámos um exemplo gerado automaticamente pelo plugin Struts 2 para o NetBeans. Verificá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 método execute.
No início, o funcionamento do Struts 2 pode parecer complexo. É preciso algum tempo para se habituar. Vamos agora apresentar uma série de exemplos que esclarecem, cada um, um aspeto específico do Struts.















