Este documento resume os principais conceitos e funcionalidades do framework GWT para desenvolvimento de aplicações web: 1) GWT compila código Java em JavaScript para executar aplicações ricas na web; 2) Características como processamento no cliente, codificação em Java e compilador JavaScript; 3) Exemplos básicos de "Hello World", uiBinder e arquitetura MVP.
3. Introdução
O que é o GWT?
– Framework para desenvolvimento de
aplicações ricas para internet (RIA) que
traduz código java em código javascript.
A pronúncia é gwit
3
7. Introdução
GWT
Execução de
processamento
de apresentação
apenas no
navegador
Clássico
Html
7
8. Características
Processamento de apresentação é todo realizado no
cliente
– menor uso de recursos do servidor
• Escalabilidade
– menor tráfego de rede
• após o carregamento inicial apenas dados trafegam, pois a
lógica de exibição e as telas já estão todas no cliente.
8
9. Características
Codificação em Java
– Orientação a objetos
– Padrões de desenho
– Anotações
– IDE Java
• Refatoração
• Auto completar
• Templates de código
• Debug
– Testes de unidade executam em Java
9
11. Características
E as desvantagens?
– Tamanho do javascript inicial pode ser grande
– Classes utilizadas no lado cliente devem ser ser
traduzíveis para javascript
• Bibliotecas de terceiros
• Entidades de persistência
– DTOs: lógica duplicada
– Indexação
• Aplicação é criada dinamicamente em javascript, portanto
indexadores não a visualizam diretamente. Um trabalho
extra deve ser realizado com intuito de deixá-la indexável.
https://developers.google.com/webmasters/ajax-
crawling/docs/html-snapshot
– Código JSNI não é fácil de manter
11
12. Definições
Módulo
– Unidade do gwt
– Definido em xml
– Um módulo pode importar outros módulos
– O módulo determina quais são as pastas que serão compiladas
pelo compilador GWT
EntryPoint
– Classes que tem o método onModuleLoad executado quando
um módulo é carregado
– Um módulo pode possuir vários EntryPoints
– Todos EntryPoints de cada módulo importado são executados
Página HTML hospedeira
– Página que possui a importação do javascript de um módulo
12
13. Definições
Organização de pacotes
– O GWT normalmente tem uma organização
padronizada de pacotes, possuindo 3 sub-pacotes
dentro do pacote do módulo.
• client: classes que são utilizadas apenas no cliente, todas
são transformadas em javascript e o código do servidor não
deve normalmente utilizá-las.
• server: classes exclusivas para utilização pelo servidor, o
compilador GWT nem as compila para javascript, portanto
nunca devem ser utilizadas no cliente.
• shared: classes que podem ser utilizadas tanto no servidor
quanto no cliente, normalmente são enums e classes
utilitárias utilizadas em ambos os ambientes.
13
14. Hello World
Módulo (HelloWorld.gwt.xml)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit
2.4.0//EN" "http://google-web-
toolkit.googlecode.com/svn/tags/2.4.0/distro-source/core/src/gwt-
module.dtd">
<module>
<inherits name="com.google.gwt.user.User" />
<source path="client" />
<source path=“shared" />
<entry-point class="com.massahud.helloworld.client.HelloWorld" />
</module>
14
15. Hello World
EntryPoint
(com.massahud.helloworld.client.HelloWorld)
package com.massahud.helloworld.client;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
public class HelloWorld implements EntryPoint {
@Override
public void onModuleLoad() {
Label labelHello = new Label("Hello world");
RootPanel.get().add(labelHello);
}
}
15
21. uiBinder
O que é?
– Framework que permite trabalhar com HTML
e CSS para definir a estrutura das telas.
Componentes
– HTML com a estrutura das telas, permite
tanto tags HTML quanto tags de
componentes GWT, inclusive componentes
customizados.
– Classe java de mesmo nome, com vinculação
automática de campos e eventos através de
anotações
21
22. Vantagens
– Visualizar a estrutura da página em HTML é
mais fácil do que lendo um código que a cria.
– Para modificar a aparência da tela é
necessário apenas modificar o HTML, a
lógica continua intacta na classe java
– Reuso: é fácil criar uma tela e inseri-la dentro
de outra
22
26. Model-View-Presenter
Padrão de arquitetura de UI
Criado para aumentar a testabilidade
– No padrão MVC a View trabalha diretamente
com o Modelo, podendo possuir lógica de
negócio ou guardar o estado atual da
execução. Isso torna difícil o teste de
unidade, pois normalmente as telas só
conseguem ser testadas com emulação de
execução em testes de integração.
26
27. Model-View-Presenter
Separa a lógica e a manutenção de estado da View
– View apenas repassa para o controle, que nesse
caso é chamado de Presenter, os eventos gerados
pelo usuário,
– Presenter possui toda a lógica e o estado da
apresentação, sendo ele o responsável, inclusive, por
atualizar os dados dos widgets da View.
• Mesmo podendo existir lógica de exibição na View, os testes
de unidade podem ser realizados apenas no Presenter, pois
o risco de problemas de negócio na View é baixo.
27
28. Model-View-Presenter
Foi subdividido em 2011 por Martin Fowler
– Passive-View
• View completamente passiva, não conhece as
classes do modelo, se limita a repassar as
modificações do usuário ao presenter e esperar
que ele diga o que ela deve exibir.
– Supervising Controller
• View pode fazer data binding simples com o
modelo, de forma a facilitar a implementação.
Apesar dela poder editar o modelo, a edição é tão
simples que o risco de não testá-la com testes de
unidade continua mínimo
28
29. Model-View-Presenter
Passive View Supervising Controller
View: View:
@UiHandler("buttonOk") @UiHandler("buttonOk")
public void onButtonOkClick(ClickEvent event) { public void onButtonOkClick(ClickEvent event) {
presenter.onEntradaModificada hello.setQuem(txtBoxEntrada.getText());
(txtBoxEntrada.getText()); presenter.onHelloEditado(hello);
} }
public void setEntrada(String texto) { public void setEntrada(String texto) {
txtBoxEntrada.setText(texto); txtBoxEntrada.setText(texto);
} }
public void setSaida(String texto) { public void setHello(Hello hello) {
labelSaida.setText(texto); this.hello = hello;
} labelSaida.setText(hello.getValor());
}
Presenter:
@Override Presenter:
public void onHelloEditado(String texto) { @Override
Hello hello = new Hello(texto); public void onHelloEditado(Hello hello) {
if (isValido(hello)) { if (isValido(hello)) {
this.hello = hello; this.hello = hello;
view.setSaida(hello.getValor()); view.setHello(new Hello(hello));
} }
view.setEntrada(""); view.setEntrada("");
} }
29
30. Model-View-Presenter
Qual utilizar?
– Depende
• Se os dados exibidos da View e repassados para
o presenter pelo método de evento forem poucos,
usar Passive View garante que os testes de
unidade tenham cobertura maior de lógica.
• Se a entidade possuir muitos campos, o método
de evento do Passive View pode ficar com uma
assinatura muito grande, gerando a necessidade
de algum TO. Portanto pode valer a pena utilizar a
própria entidade, o que nos leva ao Supervising
Controller
30
31. Model-View-Presenter
Maneiras de implementar
– View sem gets
• Todas os valores são passados ao presenter como
parâmetros do método do evento acionado.
• Pode gerar mais código repetitivo
• Garante que o desenvolvedor que fez o presenter não irá
usar a view para guardar estado.
– View com gets dos valores
• Método do evento acionado não precisa de parâmetros.
• O desenvolvedor do presenter pode utilizar a view
inequivocadamente para guardar o estado atual, o que
impossibilita o teste de unidade do presenter.
– O que importa é que siga a ideia de separar a lógica
e a manutenção de estado da view para os testes.
31
33. Model-View-Presenter
public void setPresenter(Presenter presenter) {
this.presenter = presenter;
}
@UiHandler("buttonOk")
public void onButtonOkClick(ClickEvent event) {
fireEntradaEditada();
}
@UiHandler("txtBoxEntrada")
public void onEnterPressionado(KeyDownEvent event) {
if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
event.preventDefault();
fireEntradaEditada();
}
}
private void fireEntradaEditada() {
presenter.onEntradaEditada(txtBoxEntrada.getText());
}
public void setSaida(String texto) {
labelHello.setText(texto);
}
public void setEntrada(String texto) {
txtBoxEntrada.setText(texto);
}
} 33
34. Model-View-Presenter
public class HelloWorldPresenter implements HelloWorldView.Presenter {
private HelloWorldView view;
public HelloWorldPresenter(HelloWorldView view) {
this.view = view;
view.setPresenter(this);
view.setEntrada("");
view.setSaida("Hello World");
}
@Override
public void onEntradaEditada(String entrada) {
view.setEntrada("");
if (!entrada.isEmpty()) {
view.setSaida("Hello " + entrada);
}
}
}
34
35. Model-View-Presenter
public class HelloWorld implements EntryPoint {
/**
* Método executado ao carregar o módulo.
*/
@Override
public void onModuleLoad() {
HelloWorldView view = new HelloWorldView();
HelloWorldPresenter presenter = new HelloWorldPresenter(view);
RootPanel.get().add(view);
}
}
35
36. Comunicação com servidor
Assíncrona
– Envia requisição ao servidor e espera
resposta, mantendo interface respondendo a
entradas do usuário, podendo inclusive
enviar/enfileirar outras requisições.
– Não garante que a ordem de chegada das
respostas das requisições seja a mesma
ordem das requisições.
36
37. Comunicação com servidor
Funcionamento geral para chamadas assíncronas
– Prepara-se o método que será chamado, passando
os parâmetros que serão utilizados.
– Um objeto callback é passado para receber a
resposta da requisição quando ela é disparada. Esta
resposta é recebida a partir de métodos do callback,
que são:
• onSuccess: nenhum erro ocorreu na requisição e ela
retornou, tem como parâmetro o tipo de retorno da chamada
executada.
• onFailure: quando alguma falha ocorre na requisição. Tem
como parâmetro um objeto que especifica o erro.
37
39. Comunicação com servidor
Um ponto importante
– Toda a lógica e estrutura inicial das telas já estão no
lado cliente ao carregar o módulo, porém sem
nenhum dado.
– Se uma tela que fica visível depende de dados do
servidor para sua inicialização ela deveria ser exibida
desabilitada até que os dados necessários cheguem.
39
42. Comunicação com servidor
Possibilidades
– Remote Procedure Call (RPC)
• É a mais simples e que será mostrada nesta apresentação.
Um único método é chamado por vez.
– RequestFactory
• Uma chamada de procedimento remoto mais robusta, que
dá um tratamento mais completo a entidades, produz DTOs
automaticamente, permite que várias chamadas a
procedimentos sejam unidos em uma única requisição.
– JsonP
• Chamada que obteém resposta Json with Padding. Por ser
uma resposta javascript normalmente utiliza JSNI.
• É a única forma de fazer requisições para outro domínio.
42
43. Comunicação com o servidor
Criando um serviço de Remote Procedure Call
– O RPC executa procedimentos definidos em classes
chamadas de services.
• No cliente deve-se definir a interface do service, que terá os
métodos poderão ser chamados via RPC, esta interface
deve estender a interface RemoteService.
• No servidor é feita a implementação da interface definida no
cliente, essa implementação deve estender a classe abstrata
RemoteServiceServlet. Esta classe é também um servlet
java.
• Na aplicação web deve-se declarar a classe de
implementação do service como um servlet no web.xml.
43
44. Comunicação com o servidor
HelloService.java
package com.massahud.helloworld.client.rpc;
@RemoteServiceRelativePath("hello")
public interface HelloService extends RemoteService{
public String hello(String quem);
}
HelloServiceImpl.java
package com.massahud.helloworld.server.rpc;
public class HelloServiceImpl extends RemoteServiceServlet implements HelloService {
@Override
public String dizHello(String quem) {
return "Hello " + quem;
}
}
web.xml
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.massahud.helloworld.server.rpc.HelloServiceImpl</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/HelloWorld/hello</url-pattern>
</servlet-mapping>
44
45. Comunicação com o servidor
Chamando o serviço do RPC
– O cliente precisa do callback como parâmetro dos
métodos para chamá-los de forma assíncrona.
• Uma nova interface deve ser criada no cliente, com o
mesmo nome da interface do service mas adicionada do
sufixo Async.
• Cada método da interface do service existente deve ser
duplicado na interface Async, porém com o parâmetro
AsyncCallback<TipoDeRetornoDoMetodo> adicionado ao
fim da lista de parâmetros.
• Crie uma instância da interface através do GWT.Create()
• Chame o método RPC passando como último parâmetro o
callback assíncrono que será chamado no retorno.
45
46. Comunicação com o servidor
HelloServiceAsync.java
package com.massahud.helloworld.client.rpc;
public interface HelloServiceAsync {
void dizHello(String quem, AsyncCallback<String> callback);
}
46
47. Comunicação com o servidor
HelloWorldPresenter.java
public class HelloWorldPresenter implements HelloWorldView.Presenter {
private HelloServiceAsync service;
private HelloWorldView view;
public HelloWorldPresenter(HelloWorldView view) {
this.view = view;
view.setPresenter(this);
view.setEntrada("");
view.setSaida("Hello World");
service = GWT.create(HelloService.class);
}
@Override
public void onEntradaEditada(String entrada) {
view.setEntrada("");
if (!entrada.isEmpty()) {
service.dizHello(entrada, new AsyncCallback<String>() {
@Override
public void onSuccess(String result) {
onHelloDito(result);
}
@Override
public void onFailure(Throwable caught) {
onFalhaAoDizerHello(caught);
}
});
}
}
}
47
48. Comunicação com o servidor
Pontos a considerar
– Tentar limitar o corpo dos callbacks a uma única
chamada de evento do seu presenter
• Facilita o teste do comportamento ao receber o retorno do
servidor
• Provê reuso, com a funcionalidade podendo ser chamado
diretamente, não precisando ser executado um RPC.
– Apesar do exemplo estar criando uma classe
anônima interna, é interessante criar uma classe não
anônima para o callback
• Provê reuso
• Permite testes do callback
48
49. Conclusão
Maior escalabilidade:
– Executa lógica no cliente.
– Após carregar o módulo troca apenas dados com o
servidor.
– Não mantém no servidor o estado da apresentação.
– Servidor passa a ser um provedor de serviços.
Maior usabilidade
– Interface responde ao usuário mesmo durante
execução de ações no servidor.
– Troca mais rápida de telas, pois elas já estão pré-
carregadas
49
50. Conclusão
Maiores facilidades (para RIA)
– Manter código java é normalmente mais fácil que
código javascript.
– Permite testes de unidade do cliente em java, que
executam muito mais rápido que em javascript.
– Acesso a toda a gama de funcionalidades que um
IDE java provê.
Outro paradigma de comunicação
– Pode ser uma barreira para quem está acostumado
com comunicação síncrona.
50
51. Conclusão
Funcionalidades importantes ou interessantes não vistas
– Controle de histórico e fluxo de execução
• Place
• Activity
– Eventos
– RequestFactory
– JsonP
– Cell widgets
– Editors
– Bean Validation (JSR303)
– JSNI
– GWTTestCase
– Layout Panels
– Suporte HTML5
51