Introdução ao Google Web Toolkit


Por Stéphanie Antoine, Julien Dubois y Jean-Philippe Retaillé

Aprenda a usar o Google Web Toolkit para fazer de tudo, desde tarefas básicas até avançadas, como comunicação RPC, gerenciamento de históricos e empacotamento de um aplicativo pronto para produção.


Publicado em outubro de 2006

A Web 2.0 e seu parente técnico, o Ajax (Asynchronous JavaScript and XML), estão ganhando força graças a aplicativos como Gmail e Google Maps. Para aplicativos da Web, o maior benefício do Ajax é proporcionar uma experiência do usuário bastante aprimorada. Apesar de o JavaScript e DHTML – ou seja, as bases técnicas do Ajax – estarem à disposição há anos, a maioria dos programadores os ignorava porque era difícil aprender a usá-los. Hoje, as estruturas escritas em JavaScript, como o Dojo, podem ajudar a desenvolver aplicativos Ajax, mas ainda é necessário ter bons conhecimentos em JavaScript para isso. O Google oferece outra maneira de ajudar os desenvolvedores Java a criar aplicativos Ajax de forma mais produtiva. Essa nova estrutura, chamada Google Web Toolkit (GWT), pode ser usada de maneira eficiente com o Oracle JDeveloper. O GWT está disponível gratuitamente com o Apache License v. 2.0 em http://code.google.com/webtoolkit.

Principais recursos e restrições


Um dos grandes problemas com o desenvolvimento em Ajax é a necessidade de dominar inúmeras tecnologias heterogêneas. Dependendo da natureza do projeto (por exemplo, aplicativos de negócios), essa pode ser uma grande desvantagem.

Além disso, diferentes browsers da Web não suportam JavaScript e DHTML da mesma maneira. Por exemplo, o Microsoft Internet Explorer e o Mozilla Firefox são compatíveis com essas tecnologias, mas com comportamentos um pouco diferentes; você precisará lidar com isso se desejar que o aplicativo seja executado tranqüilamente nos PCs dos usuários.

É verdade que a maioria das estruturas Ajax disponível hoje simplifica o trabalho de desenvolvimento, mas ainda são necessários bons conhecimentos dos recursos tecnológicos. Assim, se você pretende usar o Ajax para melhorar somente a experiência do usuário com o aplicativo –se não o estiver usando também como vantagem estratégica para os negócios – talvez seja insensato investir muito tempo e dinheiro na tecnologia.

O GWT propõe uma abordagem diferente à criação de aplicativos Ajax. Ele usa o Java como uma única linguagem de programação tanto para o cliente quanto para o servidor. Então seria isso o retorno dos applets Java? De maneira alguma: o GWT fornece um compilador que traduz o código Java no cliente em JavaScript e DTHML. Essa solução simplifica em grande medida os recursos tecnológicos do ponto de vista do programador: basta dominar apenas o Java. A desvantagem é que há menor controle sobre o código do cliente do aplicativo, porque por fim ele é gerado pelo compilador do GWT.

O código Java do cliente do aplicativo está sujeito a restrições porque o JavaScript não implementa todos os conceitos orientados a objeto e APIs disponíveis em Java. É possível usar apenas um subconjunto de palavras-chave Java e APIs (java.lang e java.util):

  • Todos os tipos primitivos (como byte, char, short e int), além de suas classes correspondentes (como Byte e Char) são suportados diretamente – com exceção de long, que é traduzido para o equivalente double no JavaScript. Recomendamos usar int em vez de long.
  • Exceções definidas pelo usuário (verificadas ou não) são possíveis, mas o método Throwable.getStackTrace() não está disponível. Algumas exceções JVM também estão disponíveis (como IndexOutOfBoundException).
  • A palavra-chave synchronized não surte nenhum efeito porque o JavaScript trabalha com um único thread. A API multithread não está disponível.
  • Não há suporte para reflexão. Entretanto, você pode obter o nome da classe de um objeto usando o método GWT.getTypeName(Object).
  • Não há suporte para finalização.
  • Vários containers de objetos provenientes do java.util podem ser usados, como Stack, Vector e HashMap. A classe Date também está disponível.

Além disso, o GWT fornece APIs específicos para gerenciar a GUI, a internacionalização e a análise de XML. Também fornece uma biblioteca completa para gerenciar a comunicação entre o cliente e o servidor. Usa os famosos princípios de Remote Procedure Call (RPC) implementados por um servlet genérico (RemoteServiceServlet), que pode ser especializado para suas próprias necessidades. Também é possível usar o JavaScript Object Notation (JSON) como formato de intercâmbio de dados para suas mensagens HTTP enviadas com a classe GWT HTTPRequest.

O GWT também oferece uma interface, chamada JavaScript Native Interface (JSNI), que permite combinar seu próprio código JavaScript criado manualmente com o código gerado pelo compilador do GWT. O JSNI emprega a palavra-chave native usada pelo Java Native Interface (JNI) para definir suas próprias funções JavaScript. O corpo dessas funções é definido em comentários com formatação específica.

Por fim, você pode executar o teste de unidade do código no JDeveloper com o GWTTestCase, uma especialização da classe TestCase fornecida pelo JUnit.


Foco na programação de GUI com o GWT

O Ajax muda drasticamente a maneira de desenvolver aplicativos da Web. Na maior parte do tempo, um aplicativo Ajax só precisa de uma única página da Web. O conteúdo é modificado dinamicamente pelo JavaScript e DHTML para proporcionar uma experiência do usuário semelhante àquela oferecida por aplicativos nativos.

Portanto, o GWT oferece um modelo de programação cujos princípios serão familiares aos programadores de Swing ou AWT. A GUI não é mais especificada por tags HTML como nos aplicativos da Web tradicionais. Ela é programada diretamente com código Java de maneira semelhante ao AWT ou Swing. Os bem-conhecidos conceitos da programação de GUI estão disponíveis com o GWT:

  • Widgets, incluindo os itens de costume (como Button, TextBox e CheckBox) e itens mais avançados como Tree e Menu Bar
  • Painéis, que contêm widgets, com o próprio layout (os painéis e o layout não são separados como no Swing)
  • Eventos gerados por widgets. Os listeners precisam implementar interfaces específicas.

É fácil carregar a biblioteca de JavaScript do GWT e especificar o ponto de entrada do aplicativo: basta criar uma simples página HTML.

O GWT usa folhas de estilo em cascata (CSS). Cada widget tem o próprio estilo, que pode ser alterado para atender às suas necessidades. Você precisa criar o próprio CSS para substituir os padrões definidos pelo GWT.

Se os widgets padrão não atenderem às suas necessidades, você também poderá definir seu próprio widget. (Entretanto, esse assunto foge ao escopo deste artigo)


Estrutura do Projeto

Um projeto do GWT precisa estar em conformidade com uma estrutura predefinida para ser aceito pelo compilador. Assim, é obrigatório que você defina um pacote global para o aplicativo. A última parte do nome do pacote precisa ser o nome do aplicativo (como global.package.NomeDoSeuAplicativo). O arquivo XML que descreve o seu aplicativo precisa estar localizado na raiz desse pacote global. O nome desse arquivo precisa ser o nome do aplicativo seguido da extensão .gwt.xml (por exemplo, NomeDoSeuAplicativo.gwt.xml). Além disso, você precisa criar três subpacotes:

  • “client”, que contém o código Java do cliente (esse código precisa estar em conformidade com as restrições mencionadas anteriormente)
  • “server”, que contém o código Java do servidor (você pode usar a API J2SE/J2EE integral aqui)
  • “public”, que contém as páginas HTML, o CSS e as imagens do aplicativo

O projeto precisa declarar vários jars:

  • gwt-dev-windows.jar ou gwt-dev-linux.jar: ferramentas de programação, incluindo o compilador. O browser da Web incorporado fornecido pelo GWT depende da plataforma.
  • gwt-user.jar: o tempo de execução do GWT.
  • gwt-servlet.jar: o jar que deverá ser implementado no servidor de aplicação com o código gerado pelo compilador do GWT. Ele contém o RemoteServiceServlet.

O resultado da compilação é armazenado em um único diretório cujo nome é aquele do pacote global do aplicativo. Esse diretório contém todos os elementos (como páginas HTML, CSS e arquivos JavaScript) que compreendem o lado do cliente do aplicativo. Esses elementos precisam ser implantados no aplicativo da Web, como de costume.

 


Modos hospedado e Web

Há dois modos de execução possíveis com o GWT. O modo hospedado executa o código do aplicativo em um servidor incorporado e um browser da Web, por isso não é preciso implantar o código em um servidor de aplicação. Ele é útil durante o teste de aplicativos porque simplifica a depuração.

O modo Web é a implantação do aplicativo da Web em Ajax em um autêntico servidor de aplicação como o OC4J. Geralmente ele é usado quando o aplicativo é executado em produção.


Criando seu primeiro aplicativo da Web em GWT com o Oracle JDeveloper

Até agora, você aprendeu como funciona o GWT; agora, vamos codificar uma amostra de aplicativo da Web ( download).

A amostra de aplicativo é um gerenciador de listas de tarefas. Seus recursos são bem simples: criar, editar, excluir e priorizar listas de tarefas. Escolhemos esse exemplo porque ele é fácil de entender, embora sua implementação abranja inúmeros recursos do GWT.

A seguir está uma captura de tela do aplicativo final:

Figura 1

 


Etapa 1: instale o GWT

Baixe o GWT no site do Google em http://code.google.com/webtoolkit/. Na época em que este artigo foi escrito, o GWT era oferecido nas versões Windows e Linux. O GWT é específico para cada plataforma porque seu modo hospedado funciona com uma versão modificada do Firefox, que por si só é dependente da plataforma. (Conseguimos usar a versão Linux do GWT em um computador Apple, mas o modo hospedado não funcionou.)

O download do GWT é feito na forma de arquivo, que precisa ser descompactado com o comando tar -xvf no Linux ou um utilitário de descompactação no Windows. Isso é tudo o que você precisa fazer para instalar o kit de ferramentas.


Etapa 2: execute o script applicationCreator

Abra uma linha de comando e vá até o diretório de instalação do GWT. Esse diretório contém o script applicationCreator, que usaremos para iniciar nosso aplicativo. Como queremos que o aplicativo seja armazenado no diretório Oracle Technology Network, acrescentamos “-out otn” como parâmetro do script. No Linux, digite:

./applicationCreator -out otn otn.todo.client.TodoApp


Figura 2

No Windows, use:

applicationCreator -out otn otn.todo.client.TodoApp


Esse script gera a estrutura básica do projeto, uma amostra de código “Hello world” na classe de aplicativo solicitada, além de dois scripts: TodoApp-shell, que é usado para executar o aplicativo em modo hospedado; e TodoApp-compile, que é usado para empacotar o aplicativo para ser usado em modo Web.


Etapa 3: abra o projeto no JDeveloper

Inicie o JDeveloper e crie um novo projeto da Web:

Figura 3

Clique no botão Next. O JDeveloper perguntará o local do novo projeto. Use o nome do aplicativo como Nome do projeto e escolha o diretório raiz do aplicativo, conforme definido na Etapa 2, como o Nome do diretório:

Figura 4

Clique no botão Next e valide se o aplicativo é J2EE 1.4:

Figura 5

Clique no botão Next e escolha as propriedades da Web do projeto: o Document Root é o diretório www do projeto atual, e J2EE Web Application Name e J2EE Context Root são o nome do projeto:

Figura 6

Isso criará o projeto do JDeveloper, mas ocorrerão alguns erros de compilação porque a biblioteca do GWT não está incluída no caminho de classe do projeto. Nas propriedades do projeto, selecione o nó Libraries na árvore lateral à esquerda e adicione a biblioteca gwt-user.jar:

Figura 7

Agora o projeto deve ser compilado e ficar assim:

Figura 8


Escrevendo o código do cliente

O script applicationCreator anterior criou um aplicativo básico “Hello world”, que está disponível no pacote otn.todo.client. Aqui está seu método principal:


public void onModuleLoad() {
    final Button button = new Button("Click me");
    final Label label = new Label();

    button.addClickListener(new ClickListener() {
      public void onClick(Widget sender) {
        if (label.getText().equals(""))
          label.setText("Hello World!");
        else
          label.setText("");
      }
    });

    RootPanel.get("slot1").add(button);
    RootPanel.get("slot2").add(label);
  }
}

Esse método cria um botão que informa “Click Me”. Ao clicar no botão, é exibido o texto “Hello World”.
Esse método é dividido em três partes:

  1. A criação dos widgets Button e Label
  2. A criação de um objeto ClickListener. Esse código é bem próximo ao que você teria escrito em Swing; será mais fácil entendê-lo se houver um plano de fundo Java na área de trabalho.
  3. A exibição dos widgets na página HTML: slot1 e slot2 são elementos HTML na página


A página HTML usada como esqueleto está localizada no diretório src/otn/todo/public. Ela define os dois elementos HTML, slot1 e slot2, como células de tabela.

Executando e depurando em modo hospedado

Agora que você criou o aplicativo e viu o resultado gerado, vamos executá-lo.

Você pode executar facilmente o projeto usando o script TodoApp-shell com a linha de comando. Esse é um método correto de iniciar o aplicativo, mas talvez você prefira iniciá-lo diretamente do JDeveloper. Para fazer isso, clique no menu Run e selecione Choose Active Run Configuration > Manage Run Configurations. Edite a configuração de execução padrão e use os comandos a seguir:

  • Para Default Run Target: Use com.google.gwt.dev.GWTShell que está no jar do GWT específico da plataforma. No Linux, ficará assim:
  • 
    path.to.your.gwt.installation.directory/gwt-devlinux.jar!/com/google/
    gwt/dev/GWTShell.class
                             
    

    No Windows, ficará assim:

    path.to.your.gwt.installation.directory/gwt-dev-windows.jar!/com/google/
    gwt/dev/GWTShell.class
    
    
Para Program Arguments, use:

-out path.to.your.gwt.installation.directory/otn/www otn.todo.TodoApp/TodoApp.html

Para Run Directory, use:

path.to.your.gwt.installation.directory/otn


O resultado final deverá ficar assim:

Figura 9

Para executar o aplicativo, você precisa adicionar duas outras bibliotecas ao caminho de classe: o jar específico da plataforma do GWT e o diretório src do aplicativo:

Figura 10

Agora você deverá conseguir executar o aplicativo do JDeveloper.

Essa foi uma configuração um tanto complicada, mas felizmente é possível reutilizá-la para depurar o aplicativo: basta usar o botão Debug, em vez do botão Run. Você pode usar o depurador como de costume – defina pontos de interrupção, execute o código passo a passo e assim por diante:

Figura 11


O que é particularmente surpreendente nesse recurso é que você pode depurar o código do cliente escrito em Java com o depurador do JDeveloper padrão.

Estendendo o aplicativo da Web do GWT


Agora que você criou um aplicativo da Web simples do GWT, vamos estendê-lo usando dois dos recursos mais usados do GWT: o mecanismo RPC, que permite que o aplicativo chame o código do servidor, e o objeto History, que permite ao usuário o manuseio preciso do botão Voltar do browser.


Intercâmbio de dados entre cliente e servidor usando RPC

Até agora, você criou somente o código do cliente do nosso aplicativo: usando o compilador do GWT, você gerou vários arquivos HTML e JavaScript que serão executados no browser do usuário final. Entretanto, esse aplicativo não será de muita utilidade se não conseguir se comunicar com o servidor.

Com o GWT, a comunicação cliente/servidor é uma questão de codificar um servlet e fazê-lo se comunicar com o aplicativo. Veja o que você tem de fazer.

Crie uma interface que defina o seu serviço. Essa interface precisa estender a interface com.google.gwt.user.client.rpc.RemoteService do Google e estar localizada no pacote do cliente (otn.todo.client, em nosso exemplo).

Depois, codifique uma interface que permita ler e escrever uma lista de tarefas no servidor:

package otn.todo.client;
import java.util.List;
import com.google.gwt.user.client.rpc.RemoteService;
public interface TodoListBackupService extends RemoteService {
/**
* Save the to-do list on the server.
*/
void saveTodoList(List todoList);
/**
* Get the to-do list on the server.
*/
List getTodoList();
}
                      

Codifique o servlet. No servidor, você precisa codificar uma classe que:

  1. Estenda a classe com.google.gwt.user.server.rpc.RemoteServiceServlet do Google (que por sua vez estende o javax.servlet.http.HttpServlet do Java, tornando-o um servlet)
  2. Implemente a interface escrita na Etapa 1
  3. Esteja localizada no pacote do servidor (otn.todo.server, em nosso exemplo)
  4.  
package otn.todo.server;

import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import otn.todo.client.Todo;
import otn.todo.client.TodoListBackupService;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;

public class TodoListBackupServiceImpl extends RemoteServiceServlet implements
		TodoListBackupService {

	private static final String TODOLIST_KEY = "TODOLIST_KEY";
	
	public void saveTodoList(List todoList) {
		HttpServletRequest request = this.getThreadLocalRequest();
		HttpSession session = request.getSession();
		session.setAttribute(TODOLIST_KEY, todoList);
	}

	public List getTodoList() {
		HttpServletRequest request = this.getThreadLocalRequest();
		HttpSession session = request.getSession();
		if (session.getAttribute(TODOLIST_KEY) == null) {
			List todoList = new ArrayList();
			Todo todo = new Todo("Hello from the server");
			todoList.add(todo);
			return todoList;			
		} else {
			return (List) session.getAttribute(TODOLIST_KEY);
		}
	}
}
                      

Esse servlet armazena somente a lista de tarefas no HttpSession do usuário; essa é, evidentemente, uma maneira básica de salvar dados. Em um aplicativo normal, você poderia usar JNDI para acessar EJBs, ou qualquer um dos padrões tradicionais usados para acessar um serviço de negócios a partir de um servlet.

Por fim, é necessário configurar esse servlet no container do servlet. Se estiver usando o shell do GWT, você poderá configurá-lo no arquivo de configuração *.gwt.xml, que é TodoApp.gwt.xml em nosso exemplo:

<module>
	<!-- Inherit the core Web Toolkit stuff. -->
	<inherits name='com.google.gwt.user.User'/>
	<!-- Specify the app entry point class. -->
	<entry-point class='otn.todo.client.TodoApp'/>
	<servlet path="/todoListBackupService" 
         class="otn.todo.server.TodoListBackupServiceImpl"/>
</module>
                      

Se quiser configurá-lo em outro servidor de aplicação, como o OC4J, bastará adicionar a configuração XML normal ao arquivo WEB-INF/web.xml:

<servlet>
	<servlet-name>TodoListBackupService</servlet-name>
 <servlet-class>otn.todo.server.TodoListBackupServiceImpl</servlet-class>
</servlet>
	
<servlet-mapping>
	<servlet-name>TodoListBackupService</servlet-name>
	<url-pattern>/todoListBackupService</url-pattern>
</servlet-mapping>
                      

Adicione um componente de ligação. O componente de ligação necessário é a classe Async, que precisa seguir estas regras:

  • Estar localizado no pacote do cliente (otn.todo.client).
  • Ter o mesmo nome da interface descrita na Etapa 1, com o acréscimo de Async no final.
  • Ter os mesmos métodos da interface descrita na Etapa 1, mas todos eles recebem um parâmetro adicional, o callback com.google.gwt.user.client.rpc.AsyncCallback

package otn.todo.client;

import java.util.List;
    
    
    import com.google.gwt.user.client.rpc.AsyncCallback;
    
    public interface TodoListBackupServiceAsync {
    
     /**
     * Save the to-do list on the server.
     */
     void saveTodoList(List todoList, AsyncCallback callback);
    
     /**
     * Get the to-do list on the server.
     */
     void getTodoList(AsyncCallback callback);
    }


Use-o no aplicativo. Para acessar o código do servidor a partir do aplicativo cliente, use a classe com.google.gwt.core.client.GWT, que pode criar um objeto muito especial:

TodoListBackupServiceAsync todoListBackupService = (TodoListBackupServiceAsync)
 GWT.create(TodoListBackupService.class);


Isso cria em tempo de execução uma classe que implementa duas interfaces:

  • A interface Async que acabamos de codificar na Etapa 3
  • A interface com.google.gwt.user.client.rpc.ServiceDefTarget do Google

A segunda interface é usada para configurar a classe para que possa apontar para o servlet definido na Etapa 2:

ServiceDefTarget endpoint = (ServiceDefTarget) todoListBackupService; 
endpoint.setServiceEntryPoint("/todoListBackupService");


Agora que você configurou esse objeto para acessar o serviço do servidor, vamos acessar o serviço. Conforme visto na Etapa 3, a interface Async permite acessar todos os métodos definidos no serviço, com o acréscimo do parâmetro de callback AsyncCallback. Esse parâmetro é usado para definir o comportamento do aplicativo, dependendo do sucesso ou falha da chamada do servidor:

AsyncCallback callback = new AsyncCallback() {
         public void onSuccess(Object result) {
             printTodoList();
         }

         public void onFailure(Throwable caught) {
           Window.alert("Warning :the to-do list could not be saved on the server.
          Maybe the server is down.");
         }
     };
                       

Vamos reunir tudo. A seguir está o código completo dos dois métodos do cliente que acessam o serviço de negócios TodoListBackupService: um para salvar a lista de tarefas no servidor, e outro para lê-la:

 /**
     * Update the to-do list with data from the server.
     */
    private void updateTodoListFromServer() {
        TodoListBackupServiceAsync todoListBackupService =
            (TodoListBackupServiceAsync)GWT.create(TodoListBackupService.class);

        ServiceDefTarget endpoint = (ServiceDefTarget)todoListBackupService;
        endpoint.setServiceEntryPoint("/todoListBackupService");

        AsyncCallback callback = new AsyncCallback() {
                public void onSuccess(Object result) {
                    todoList = (List)result;
                    saveTodoListInHistory();
                }

                public void onFailure(Throwable caught) {
                    Todo todo =
                        new Todo("ERROR!! Server could not be reached.");
                    todoList.add(todo);
                    saveTodoListInHistory();
                }
            };

        todoListBackupService.getTodoList(callback);
    }

    /**
     * Save the to-do list on the server.
     */
    private void saveTodoListOnServer() {
        saveTodoListInHistory();
        
        TodoListBackupServiceAsync todoListBackupService =
            (TodoListBackupServiceAsync)GWT.create(TodoListBackupService.class);

        ServiceDefTarget endpoint = (ServiceDefTarget)todoListBackupService;
        endpoint.setServiceEntryPoint("/todoListBackupService");

        AsyncCallback callback = new AsyncCallback() {
                public void onSuccess(Object result) {
                    printTodoList();
                }

                public void onFailure(Throwable caught) { 
                 Window.alert("Warning : the to-do list could not be saved on the server.
		Maybe the server is down.");
                }
            };

        todoListBackupService.saveTodoList(todoList, callback);
    }
             

O aplicativo de exemplo faz uma chamada do servidor na inicialização. Essa chamada retorna a lista de tarefas mais recente salva no HttpSession do usuário ou uma nova lista de tarefas que contém a tarefa “Hello from the server”:

Figura 12


Gerenciando o botão Voltar


Em aplicativos da Web sofisticados, o botão Voltar do browser muitas vezes fica desativado. Aplicativos Ajax tradicionais não suportam o comportamento da Web padrão de retornar para a página anterior.

O GWT, em contrapartida, permite a manipulação programática do botão Voltar. Esse é um recurso poderoso, porém caprichoso, que exploraremos com o nosso exemplo de aplicativo. A idéia é usar o botão Voltar como um botão Desfazer: se você clicar nele, será exibida a lista de tarefas conforme ela estava antes do evento mais recente. Da mesma maneira, o botão Avançar funcionará como um botão Refazer.

Implemente a interface HistoryListener. Para gerenciar o botão Voltar via programação, o aplicativo do GWT precisa implementar a interface com.google.gwt.user.client.HistoryListener. Isso obriga a escrever o método onHistoryChanged(String _historyToken):

public class TodoApp implements EntryPoint, HistoryListener {

    /**
     * This method is called whenever the application's history changes.
     */
    public void onHistoryChanged(String _historyToken) {
        if (Integer.parseInt(_historyToken) + 1 != historyToken) {
            if (historyMap.get(_historyToken) != null) {
                historyToken = Integer.parseInt(_historyToken);
                todoList = (List) historyMap.get(_historyToken);
            }
        }
        printTodoList();
    }
                      

Esse método é destinado a receber eventos quando o histórico do browser é alterado. É necessário adicioná-lo como listener ao objeto History do GWT. Normalmente isso é feito no método onModuleLoad(), por isso o objeto History é executado corretamente na inicialização:

/**
  * This is the entry point method.
  */
    public void onModuleLoad() {

        History.addHistoryListener(this);
        
    }
                       

Agora, o método onHistoryChanged(String _historyToken) é chamado sempre que o histórico do browser é alterado.

Esse método é capaz de recriar o estado do aplicativo de acordo com um token transmitido como parâmetro. Em nosso exemplo, você usará esse token como uma chave para localizar uma lista de tarefas armazenada em um mapa de histórico.

Adicione itens ao histórico. Para que o método onHistoryChanged(String _historyToken) funcione, você precisa ter os itens armazenados no histórico com antecedência.

É fácil fazer isso com o objeto History, usando o método newItem(String historyToken) estático:

private void saveTodoListInHistory() {
        List todoListClone = new ArrayList();
        Iterator it = todoList.iterator();
        while (it.hasNext()) {
            Todo todo = (Todo) it.next();
            todoListClone.add(todo.clone());
        }
        historyMap.put(String.valueOf(historyToken), todoListClone);
        History.newItem(String.valueOf(historyToken));
        historyToken++;
    }
                      

Em nosso exemplo, você armazenou o estado do aplicativo em um mapa para que ele possa ser localizado usando o token de histórico. Observe que você usou um número como token de histórico, mas que qualquer seqüência de caracteres pode ser usada no lugar.


Implantando o aplicativo da Web

Para implantar um aplicativo da Web criado com o GWT, compile o código do cliente, empacote o resultado interno do arquivo .war do aplicativo da Web e implante o arquivo .war no seu servidor de aplicação preferido, o OC4J.


Compilando o código do cliente

Há várias maneiras de compilar seu código do cliente. Quando você usa o script applicationCreator, o GWT cria um script de shell chamado TodoApp-compile. Você pode iniciá-lo com a linha de comando. Como o TodoApp-shell, é uma ótima maneira de compilar o aplicativo; entretanto, talvez você queira iniciá-lo diretamente do JDeveloper.

Outra maneira de compilar o código é executar o aplicativo em modo hospedado para que possa ser executado diretamente do JDeveloper. A barra de ferramentas da janela do aplicativo contém um botão de compilação/procura, como este:

Figura 13

Ao final do processo de compilação, o browser da Web padrão será aberto para você poder testar o resultado. A janela do shell de desenvolvimento do GWT será exibida se a compilação for bem-sucedida:

Figura 14

Independentemente de como o código é compilado, você encontrará os arquivos gerados no www/otn.todo.TodoApp de seu projeto.

A última maneira de compilar o código é usar Ant. O GWT não oferece uma tarefa Ant específica, mas é possível iniciar qualquer classe Java (como GWTCompiler) com a tarefa Ant Java padrão. Primeiro, defina o caminho para incluir os jars do GWT:

<path id="project.class.path">
 <pathelement path="${java.class.path}/"/>
 <pathelement location="src"/>
 <pathelement path="/your/path/to/gwt-user.jar"/>
 <pathelement path="/your/path/to/gwt-dev-linux.jar"/>
 <!-- ... -->
</path>


Agora, defina uma tarefa dedicada à compilação do seu código do cliente:

<target name="GWTcompile">
<java classpathref="project.class.class.path"
classname="com.google.gwt.dev.GWTCompiler"
fork="true">
<arg value="-out"/>
<arg value="${gwt.output.dir}"/>
<arg value="${entry.point.class}"/>
</java>
</target>

                      

Defina as variáveis gwt.output.dir e entry.point.class em um arquivo de propriedades, como este:

gwt.output.dir=www
entry.point.class=otn.todo.TodoApp
                      

Por fim, declare o arquivo de propriedades (neste caso, build.properties) no script Ant, assim:

<property file="build.properties"/>


Você pode iniciar diretamente esse novo destino selecionando Run Target GWTCompile no menu Context da tarefa:

Figura 15


A janela de Apache Ant Log mostrará o resultado a seguir:

GWTcompile:
 [java] Output will be written into www\otn.todo.TodoApp
 [java] Compilation succeeded

BUILD SUCCESSFUL                      

 


Implantação no OC4J

Após compilar o aplicativo, implantá-lo em OC4J é apenas uma questão de criar um perfil de implantação correto. Se você seguiu as etapas descritas anteriormente, você já deve ter um perfil de implantação padrão. Caso contrário, basta selecionar File > New... > Deployment Profiles > WAR File e criar um novo perfil.

Usando sua configuração, tudo deve funcionar imediatamente. Entretanto, se encontrar algum problema, confira os seguintes erros comuns:

  • Em Project Properties, em Project Content > Web Application, o HTML Root Directory deve ser o diretório www de seu aplicativo (onde GWT compilará o aplicativo para ser executado em modo hospedado).
  • No perfil de implantação, em File Groups > WEB-INF/lib > Contributors, o gwt-user.jar deve estar adicionado. Esse arquivo jar inclui o pacote javax.servlet da especificação J2EE. Isso não causa nenhum problema em nosso exemplo; entretanto, você normalmente não deve implantar essas classes em um aplicativo da Web, porque elas podem causar algum problema. Se isso acontecer, o GWT também oferecerá um arquivo gwt-servlet.jar, que é o gwt-user.jar sem o pacote javax.servlet.
  • A raiz de contexto do aplicativo da Web influencia o mecanismo RPC do GWT. Se o aplicativo cliente do GWT estiver conversando com o servidor, conforme descrito em “Data Exchange between client and server using RPC”, ele deverá ser capaz de localizar o servidor. Essa é a finalidade do método endpoint.setServiceEntryPoint("") que abordamos. Em nosso exemplo, implantamos o aplicativo na raiz do servidor, que é como o shell do GWT funciona como padrão. Mas se você implantar o aplicativo no contexto do TodoApp Web (nas propriedades gerais do perfil de implantação), defina o endpoint como /TodoApp/todoListBackupService, e não como /todoListBackupService. Não se esqueça de que esse URL também deve ser mapeado corretamente no arquivo web.xml do aplicativo, conforme descrito anteriormente.
  • Pressupondo que o OC4J esteja instalado corretamente no seu sistema, a implantação do aplicativo no servidor é simples: basta clicar com o botão direito do mouse no perfil de implantação e selecionar Deploy to OC4J.

O aplicativo implantado tem duas partes:

  • O aplicativo do cliente é um conjunto de arquivos HTML e JavaScript que foram compilados anteriormente. (Consulte a seção “Compilando o código do cliente”.) O OC4J atua como servidor Web tradicional, que fornece esses arquivos ao usuário.
  • O aplicativo do cliente é basicamente um servlet que manipula a comunicação RPC. Após a implantação no OC4J, esse servlet pode acessar recursos empresariais como os provedores de EJBs ou JMS.

Em termos de performance, servir recursos estáticos é muito eficiente; o principal gargalo de performance do nosso aplicativo deve vir da comunicação cliente/servidor. Mas graças ao OC4J, temos acesso a alguns gráficos interessantes de performance do servidor:

Figura 16

Conforme mostra esse gráfico, em carga normal (algumas solicitações por segundo) a parte do servidor do aplicativo responde em menos de 4 ms em média – um resultado excelente.



Stéphanie Antoine
é desenvolvedora de J2EE sênior e trabalha em uma grande empresa francesa de consultoria em software. Julien Dubois e Jean-Philippe Retaillé são especialistas em J2EE e autores de vários livros; seu livro mais recente, Spring par la Pratique (Eyrolles, 2006) é o primeiro em francês sobre a estrutura do Spring J2EE.