Skip to content

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`:
Constante
Valor
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:

HelloWorld.message= Struts is up and running ...

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:

HelloWorld.message= ¡Struts está bien! ...

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&param2=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.