Desenvolvendo aplicações Java com os frameworks GWT e GXT

Veja neste artigo como desenvolver aplicações na linguagem Java, utilizando os frameworks GXT da Sencha e GWT (Google Web Toolkit), persistindo os dados em um banco PostgreSQL.

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.

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.

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.

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.

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.

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.

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.

Figura 7. Executando a aplicaçã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.

Ebook exclusivo
Dê um upgrade no início da sua jornada. Crie sua conta grátis e baixe o e-book

Artigos relacionados