Skip to content

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:

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 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:
Constante
Valeur
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:

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

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:

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

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&param2=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
<s:property name= "message "/>

, é 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.