Uma das mais promissoras linguagens de programação ganhou mais força de uso com a criação de frameworks RAD, como é o exemplo do GXT que foi desenvolvido pelo Sencha e do outro lado está o GWT (Google Web Toolkit). Essas duas ferramentas permitem o desenvolvimento de aplicações web muito ricas de interface gráfica e garantem um bom desempenho.

A Aplicação

Para facilitar o desenvolvimento, a Google desenvolveu um plugin para o IDE Eclipse.

Download plugin GWT para Eclipse
Figura 1. Download plugin GWT para Eclipse

Após efetuar o donwload do plugin é preciso criar uma aplicação Web: File > New > Web Application Project.

Criando o projeto
Figura 2. Criando o projeto

É preciso informar o nome do projeto e um pacote padrão, no exemplo deste artigo não será utilizado o Google App Engine, ao clicar em Finish será criado o projeto com a estrutura padrão do projeto.

Estrutura do projeto
Figura 3. Estrutura do projeto

O GWT trabalha de forma assíncrona e como toda aplicação web precisa ser mapeado em um arquivo XML (web.xml). A separação das camadas dá-se pelos pacotes br.com, br.com.client, br.com.server e br.com.shared, tudo o que for referente à camada de visão da aplicação deverá ser colocado dentro do pacote br.com.client. Por padrão são gerados um arquivo HTML e um CSS, os dois são necessários para carregar o layout da aplicação juntamente com o código escrito em Java, que depois de compilado é transformado para JavaScript, o que aumenta ainda mais o desempenho da aplicação.

Para trabalhar com GXT é preciso além do projeto web, criar um projeto Java comum que irá conter o core da aplicação, dessa forma podemos dividir os interesses de negócios e a visão, obedecendo o padrão MVC. Dentro do projeto web deverão ser criadas as classes contendo os mesmos atributos das classes feitas no core da aplicação, essas classes deverão herdar da classe BaseModel, pois é o tipo aceito pelo GXT, ou seja todo objeto “bean” dentro do projeto web deverá ser um BaseModel.

Criando o Código


package br.com.client.domain;
import java.io.Serializable;
import com.extjs.gxt.ui.client.data.BaseModel;

public class PersonG extends BaseModel implements Serializable {

	private static final long serialVersionUID = 1L;
	private Integer code;
	private String name;
	private String phone;
	private String cell;
	private String email;

	public Integer getCode() {
		return get("code");
	}

	public void setCode(Integer code) {
		set("code", code);
	}

	public String getName() {
		return get("name");
	}

	public void setName(String name) {
		set("name", name);
	}

	public String getPhone() {
		return get("phone");
	}

	public void setPhone(String phone) {
		set("phone", phone);
	}

	public String getCell() {
		return get("cell");
	}

	public void setCell(String cell) {
		set("cell", cell);
	}

	public String getEmail() {
		return get("email");
	}

	public void setEmail(String email) {
		set("email", email);
	}

}
Listagem 1. Classe estendendo BaseModel

Não é preciso criar os atributos de referência de tipo primitivo, nem atributos do tipo String, porém os de outros tipos deverão ser explicitados dentro da classe. Os métodos Getters() e Setters() são um pouco diferentes dos que normalmente são utilizados em uma aplicação Java, justamente pelo fato de estender a classe BaseModel.

ASYNC Callback e mapeamento de Web.xml

O GXT é assíncrono, dessa forma é preciso criar elementos que suportem trabalhar dessa forma, boas práticas de programação aconselham que o desenvolvimento da aplicação seja feita orientada a interfaces. Seguindo essa ideia serão criadas duas interfaces que possibilitarão trabalhar de forma assíncrona.


package br.com.client.application;

import br.com.client.domain.PersonG;

import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

@RemoteServiceRelativePath ("personController")
public interface PersonApplication extends RemoteService{

	public void save(PersonG person);
	public void update(PersonG person);
	public PersonG findById(int id);
	public void delete(PersonG person);
}
Listagem 2. Criando a inteface

package br.com.client.application;

import br.com.client.domain.PersonG;

import com.google.gwt.user.client.rpc.AsyncCallback;

public interface PersonApplicationAsync {

	void save(PersonG person, AsyncCallback<Void> callback);

	void update(PersonG person, AsyncCallback<Void> callback);

	void delete(PersonG person, AsyncCallback<Void> callback);

	void findById(int id, AsyncCallback<PersonG> callback);

}
Listagem 3. Criando a interface assíncrona

Por padrão o projeto web cria o arquivo web.xml que permite o mapeamento dos servlets da aplicação (padrão em toda aplicação web). No arquivo web.xml será adicionado o servlet mapeado pela anotação @RemoteServiceRelativePath ("personController").


<?xml version="1.0" encoding="UTF-8"?>
<web-app 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_2_5.xsd"
         version="2.5"
         xmlns="http://java.sun.com/xml/ns/javaee">

  <!-- Servlets -->
  <servlet>
    <servlet-name>personService</servlet-name>
    <servlet-class>br.com.server.PersonApplicationImpl</servlet-class>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>personService</servlet-name>
    <url-pattern>/gwtproj/personController</url-pattern>
  </servlet-mapping>
  
  <!-- Default page to serve -->
  <welcome-file-list>
    <welcome-file>GWTProj.html</welcome-file>
  </welcome-file-list>
</web-app>
Listagem 4. Arquivo web.xml

O arquivo web.xml deverá estar contido do diretório WEB-INF contida na pasta war dentro do projeto.

Localização do web.xml
Figura 4. Localização do web.xml

Para utilizar o GWT é preciso criar classes que servirão de conversor das classes do projeto Java para o projeto web. Esse conversor será utilizado tanto para as transações de persistência quanto para as transações de busca entre a aplicação web e a aplicação core.


package br.com.client.converter;
import br.com.client.domain.PersonG;
import br.com.domain.Person;

public class PersonConverter {

	private Person person;
	private PersonG personG;
	
	public Person personConverterG(PersonG personG){
		person = new Person();
		person.setCode(personG.getCode());
		person.setName(personG.getName());
		person.setEmail(personG.getEmail());
		person.setPhone(personG.getPhone());
		person.setCell(personG.getCell());
		return person;
	}
	
	public PersonG personConverter(Person person){
		personG = new PersonG();
		
		personG.setCode(person.getCode());
		personG.setName(person.getName());
		personG.setPhone(person.getPhone());
		personG.setCell(person.getCell());
		personG.setEmail(person.getEmail());
		return personG;
	}
}
Listagem 5. Criando o conversor

Para este artigo utilizamos o conversor construído de forma manual, porém já existem artefatos como por exemplo o Jazon utilizado para efetuar o papel de conversão dos objetos de forma automática.


package br.com.server;
import br.com.client.application.PersonApplication;
import br.com.client.converter.PersonConverter;
import br.com.client.domain.PersonG;
import br.com.database.impl.PersonRepositoryImpl;
import br.com.domain.Person;
import br.com.domain.PersonRepository;

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

public class PersonApplicationImpl extends RemoteServiceServlet 
implements PersonApplication{

	private static final long serialVersionUID = 1L;
	private PersonConverter pConverter;
	private PersonRepository pRep;
	private Person person;
	
	public PersonApplicationImpl() {
		pConverter = new PersonConverter();
		pRep = new PersonRepositoryImpl();
	}
	
	@Override
	public void save(PersonG personG) {
		person = pConverter.personConverterG(personG);
		pRep.save(person);
	}

	@Override
	public void update(PersonG person) {
		// TODO Auto-generated method stub
	}

	@Override
	public PersonG findById(int id) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void delete(PersonG person) {
		// TODO Auto-generated method stub
	}
}
Listagem 6. Implementando a interface

Para este artigo será mostrado somente como persistir um objeto em um banco de dados, porém a lógica para as demais funções podem ser implementadas seguindo a mesma estrutura lógica.

Domínio da Aplicação

A aplicação Java Project será utilizada para receber os objetos convertidos da aplicação web e efetuar a lógica de persistência no banco. Para o exemplo deste artigo foi utilizado o framework JPA.


package br.com.domain;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
@Entity (name="tb_person")
public class Person implements Serializable {

	private static final long serialVersionUID = 1L;

	@Id
	@SequenceGenerator (initialValue = 1, allocationSize=1, 
	sequenceName="person_seq", name="person_seq")
	@GeneratedValue (generator="person_seq", strategy=GenerationType.AUTO)
	@Column (nullable=false)
	private Integer code;
	private String name;
	private String email;
	private String phone;
	private String cell;

	public Integer getCode() {
		return code;
	}

	public void setCode(Integer code) {
		this.code = code;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getPhone() {
		return phone;
	}

	public void setPhone(String phone) {
		this.phone = phone;
	}

	public String getCell() {
		return cell;
	}

	public void setCell(String cell) {
		this.cell = cell;
	}
}
Listagem 7. Bean do domínio

Para efetuar as funcionalidades de transação com o banco de dados, será utilizada a interface PersonRepository contendo as assinaturas dos métodos de transação.


package br.com.domain;
public interface PersonRepository {
	public void save(Person person);
	public void update(Person person);
	public Person findById(int id);
	public void delete(Person person);
}
Listagem 8. Interface contendo assinaturas dos métodos de persistência

O JPA utilizado na aplicação contém o arquivo persistence.xml obrigatoriamente contido na pasta WEB-INF dentro de SRC do projeto.

Estrutura do domínio
Figura 5. Estrutura do domínio

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" 
xmlns="http://java.sun.com/xml/ns/persistence" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence 
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
  <persistence-unit name="JPAUnit">
      
    <class>br.com.domain.Person</class>
    
    <properties>
      <property name="hibernate.dialect" 
      value="org.hibernate.dialect.PostgreSQLDialect"/>
      <property name="hibernate.connection.url" 
      value="jdbc:postgresql://localhost:5432/persondb"/>
      <property name="hibernate.connection.driver_class" 
      value="org.postgresql.Driver"/>
      <property name="hibernate.connection.password" value="123456"/>
      <property name="hibernate.connection.username" value="postgres"/>
      <property name="hibernate.hbm2ddl.auto" value="update"/>
    </properties>
  </persistence-unit>
</persistence>
Listagem 9. Persistence.xml

Para efetuar a conexão com o banco de dados é preciso criar uma fábrica de conexões que inicia a transação com o banco.


package br.com.database;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import org.hibernate.Session;

public class ConnectionFactory {
	private static EntityManagerFactory emf = Persistence
			.createEntityManagerFactory("JPAUnit");
	private static EntityManager entityManager;

	public static EntityManager getEntityManager() {
		entityManager = emf.createEntityManager();
		return entityManager;
	}

	public static Session getSession() {
		return (Session) entityManager.getDelegate();
	}
}
Listagem 10. Classe de conexão com banco de dados

Para implementar a interface que contém as assinaturas dos métodos de persistência e utilizar a fábrica de transações, será preciso criar a classe que implementa a interface PersonRepository, que irá conter a lógica de persistência.


package br.com.database.impl;

import javax.persistence.EntityManager;
import br.com.database.ConnectionFactory;
import br.com.domain.Person;
import br.com.domain.PersonRepository;

public class PersonRepositoryImpl implements PersonRepository{

	private EntityManager em;
	
	public PersonRepositoryImpl() {
		em = ConnectionFactory.getEntityManager();
	}
	
	@Override
	public void save(Person person) {
		em.getTransaction().begin();
		em.persist(person);
		em.getTransaction().commit();
		em.close();
	}

	@Override
	public void update(Person person) {
		// TODO Auto-generated method stub
	}
	@Override
	public Person findById(int id) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void delete(Person person) {
		// TODO Auto-generated method stub
	}
}
Listagem 11. Classe para implementar PersonRepository

O métod save() recebe um objeto Person, inicia a transação com o banco de dados e persiste o objeto, logo após a transação com o banco é finalizada.

Criação de tela de cadastro com o GXT

Após toda a lógica de negócios estar criada, é preciso exportar a aplicação de domínio para dentro da pasta lib da aplicação web, a pasta lib pode ser encontrada dentro da pasta WEB-INF contida na pasta war do projeto.

Exportando o projeto
Figura 6. Exportando o projeto

A criação da interface gráfica no GXT pode ser feita utilizando códgio Java, para isso serão necessários a biblioteca do GXT, o “gxt-2.2.5-gwt22.jar” e o “gwt-servlet.jar”. Ao criar o projeto exemplo do GXT, a classe conhecida como EntryPoint é criada automaticamente, essa classe é utilizada para ser o ponto de partida da aplicação, é uma espécie de main da aplicação, essa classe implementa a interface EntryPoint, que contém o método onModuleLoad(), é necessário também criar pelo menos um módulo para que a aplicação seja executada com sucesso, o módulo é um arquivo xml criado no pacote principal do projeto, onde contém o link das bibliotecas que serão utilizadas na aplicação.


<?xml version="1.0" encoding="UTF-8"?>
<module rename-to='gwtproj'>
	<!-- Inherit the core Web Toolkit stuff.                        -->
	<inherits name='com.google.gwt.user.User'/>

	<!-- Inherit the default GWT style sheet.  You can change       -->
	<!-- the theme of your GWT application by uncommenting          -->
	<!-- any one of the following lines.                            -->
	<inherits name='com.google.gwt.user.theme.clean.Clean'/>
	<inherits name="com.google.gwt.resources.Resources" />
	<inherits name="com.google.gwt.http.HTTP" />
	<inherits name="com.extjs.gxt.ui.GXT" />
	<!-- <inherits name='com.google.gwt.user.theme.standard.Standard'/> -->
	<!-- <inherits name='com.google.gwt.user.theme.chrome.Chrome'/> -->
	<!-- <inherits name='com.google.gwt.user.theme.dark.Dark'/>     -->

	<!-- Other module inherits                                      -->

	<!-- Specify the app entry point class.                         -->
	<entry-point class='br.com.client.GWTProj'/>

	<!-- Specify the paths for translatable code                    -->
	<source path='client'/>
	<source path='shared'/>

</module>
Listagem 12. Criando o Módulo

package br.com.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.RootPanel;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class GWTProj implements EntryPoint {

	@Override
	public void onModuleLoad() {
		RootPanel.get().add(new PessoaView());
	}
}
Listagem 13. Classe EntryPoint

A classe RootPanel contém os métodos get() e add() estáticos e são utilizados para receber uma instância de qualquer objeto que estenda um UIObject, e assim poder renderizar a tela.


package br.com.client;

import br.com.client.application.PersonApplication;
import br.com.client.application.PersonApplicationAsync;
import br.com.client.domain.PersonG;
import com.extjs.gxt.ui.client.event.ButtonEvent;
import com.extjs.gxt.ui.client.event.Events;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.widget.Info;
import com.extjs.gxt.ui.client.widget.Window;
import com.extjs.gxt.ui.client.widget.button.Button;
import com.extjs.gxt.ui.client.widget.form.FormPanel;
import com.extjs.gxt.ui.client.widget.form.TextField;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;

public class PessoaView extends Window{

	private PersonApplicationAsync app = GWT.create(PersonApplication.class);
	
	private TextField<String> fieldName = new TextField<String>();
	private TextField<String> fieldEmail = new TextField<String>();
	private TextField<String> fieldPhone = new TextField<String>();
	private TextField<String> fieldCell = new TextField<String>();
	
	private PersonG person;
	
	private Button btSave = new Button("Salvar");
	
	private FormPanel fp = new FormPanel();
	public PessoaView() {
		setHeading("Incluir Pessoa");
		setMinimizable(false);
		setSize(350, 175);
		configure();
		configureBtSave();
	}
	
	private void configure(){
		fp.setHeaderVisible(false);
		fieldName.setFieldLabel("Nome");
		fp.add(fieldName);
		
		fieldEmail.setFieldLabel("Email");
		fp.add(fieldEmail);
		
		fieldPhone.setFieldLabel("Fixo");
		fp.add(fieldPhone);
		
		fieldCell.setFieldLabel("Celular");
		fp.add(fieldCell);
		add(fp);
		
		fp.add(btSave);
		add(fp);
		layout();
	}
	
	private PersonG getPerson(){
		person = new PersonG();
		
		person.setName(fieldName.getValue());
		person.setEmail(fieldEmail.getValue());
		person.setPhone(fieldPhone.getValue());
		person.setCell(fieldCell.getValue());
		return person;
	}
	
	private void configureBtSave(){
		btSave.addListener(Events.OnClick, new Listener<ButtonEvent>() {

			@Override
			public void handleEvent(ButtonEvent be) {
				app.save(getPerson(), new AsyncCallback<Void>() {
					
					@Override
					public void onSuccess(Void result) {
						Info.display("Sucesso","");
					}
					
					@Override
					public void onFailure(Throwable caught) {
						Info.display("Falha","");
					}
				});
			}
		});
	}
}
Listagem 14. Criando o layout

Por fim a interface gráfica será criada utilizando os componentes da biblioteca do GXT, componentes como TxtField, que cria uma caixa de texto contendo um Label e um Button, que cria um botão que receberá as ações da tela. O FormPanel é utilizado para alinhar os componentes dentro de um formulário, contendo um tamanho configurável em pixels e uma barra de títulos, por a classe estender de Window, o método setHeading() inclui um título na janela.

Após executar a aplicação a tela de cadastro será exibida e estará pronta para persistir um objeto do tipo Person. Após mandar executar a aplicação um link será gerado.

Executando a aplicação
Figura 7. Executando a aplicação
Aplicação em execução
Figura 8. Aplicação em execução

Conclusão

Desenvolver aplicações utilizando o framework GXT foi um salto e tanto para o desenvolvimento utilizando a linguagem Java, dessa forma podem ser utilizado vários outros frameworks para auxiliar na execução de uma aplicação robusta como por exemplo o Spring, dessa forma pode-se construir aplicações ricas para a web diminuindo o esforço e seguindo todos os padrões de engenharia de software.