Dev (Back & Front)ARTIGO

Criar aplicativos da Web elaborados e centrados em dados usando JAX-RS, JPA e Dojo

Neste artigo, você desenvolverá um aplicativo da Web centrado em dados
usando algumas das tecnologias Java do lado do servidor
mais recente e o
Dojo Toolkit para criação de uma interface de usuário
elaborada.

Essas
tecnologias reduzem significativamente a quantidade de
código que se tem
de escrever, em ambos os lados – servidor e cliente.
Para tirar o máximo deste artigo, é recomendado ter
familiaridade com
Java e JavaScript. Será necessário ter um Java 1.6 JDK
para compilar e
executar o código.

Este artigo usou o JDK 1.6.0_20 e você
também precisará
de um contêiner de Web Java. Ainda foi utilizado o Apache
Tomcat 6.0.14.
Para a persistência de dados, pode ser usado qualquer
banco de dados com
um driver compatível com JDCB 2.0.

Para manter as coisas
simples, foi
usado um banco de dados incorporado, o Apache Derby
10.6.1. Este artigo
usa o Java API for RESTful Web Services (JAX-RS), com
Jersey 1.3 para a
implementação do JAX-RS.

Será usado também o Java
Persistence API (JPA) com Hibernate 3.5.3 para a implementação.
Finalmente, este artigo também
usou o Dojo Toolkit 1.4. Ao final do artigo, confira a seção Recursos para
obter os links para essas ferramentas.

Dados Durante a Execução com a Java Persistence API

Muitos aplicativos da Web são centrados em dados – eles
apresentam dados persistentes e permitem que o usuário crie ou atualize
seus dados. Parece bastante simples, mas mesmo quando se trata de algo
tão básico como ler e escrever dados de um banco de dados, as coisas
podem ficar feias.

No entanto, a Java Persistence API (JPA) reduz
grandemente a quantidade de código clichê, tedioso, que precisa ser
escrito. Vamos ver um exemplo simples de como usar a JPA.

Neste artigo, será desenvolvido um aplicativo simples para gerenciamento
de uma liga de futebol jovem. Você começará desenvolvendo um modelo de
dados simples para manter controle das equipes na liga e dos jogadores
nessas equipes. Usará a JPA para todo o acesso a esses dados e iniciará
com o primeiro dos dois modelos, uma Team. A Listagem 1 mostra essa classe.

Listagem 1. A classe de modelo de dados Team

@Entity
public class Team {
....
....@Id
....@GeneratedValue(strategy = GenerationType.IDENTITY)
....private long id;
....
....private String name;
....
....@OneToMany
....private Collection<Player> players;
....
// getters and setters........
}

Essa é uma classe anotada JPA típica. Você usa a anotação @Entity para declarar que essa classe será
mapeada a um banco de dados. É possível especificar o nome da tabela para
a classe ou implementar a convenção de onde usar o mesmo nome que a
classe.

Em seguida, você anota o campo id da
classe. Você deseja que esta seja a primeira chave da tabela, assim usa a
anotação @Id para declarar isso. O id não é importante da perspectiva da lógica de negócios. É necessário fazer isso apenas para o banco de dados. Como você
deseja que o banco de dados se encarregue do que acontecerá com seus
valores, use a anotação @GeneratedValue.

Na listagem 1, também é declarado outro campo, name. Esse será o nome da equipe. Observe que não há nenhuma anotação da JPA nesse campo. Por padrão, ele estará mapeado a uma coluna com o mesmo nome, e isso é o suficiente para os fins deste artigo.

Finalmente, cada equipe tem vários jogadores associados a ela. Você usa a anotação @OneToMany para permitir que o tempo de execução da JPA saiba que esta é uma relação gerenciada com uma equipe que tem muitos jogadores. Na sua classe Java, esse é apenas um objeto Player de java.util.Collection. A listagem 2 mostra a classe Player que está sendo referenciada.

Listagem 2. A classe de modelo de dados Player

@Entity
public class Player {
....
....@Id
....@GeneratedValue(strategy = GenerationType.IDENTITY)
....private long id;
....
....private String firstName;
....
....private String lastName;
....
....private int age;
....
....@ManyToOne (cascade=CascadeType.ALL)
....private Team team;
....
// getters and setters
}

A classe Player mostrada na listagem 2 é similar à classe Team na listagem 1. Ela tem mais campos, mas, novamente, na maioria dos casos, você não terá de se preocupar em anotá-los. A JPA fará a coisa certa para você.

A única diferença entre as listagens 1 e 2 é o modo de especificação do relacionamento entre as classes Player e Team. Neste caso, você usa uma anotação @ManyToOne, porque há muitos Players em uma única Team. Observe que você especificou uma política de cascata.

Examine alguma documentação da JPA para selecionar a política de cascata certa para o seu aplicativo. Neste caso, com essa política, é possível criar uma nova Team e um Player, ao mesmo tempo, e a JPA salvará a ambos, o que é conveniente para o aplicativo.

Agora que você declarou essas duas classes, precisará informar ao tempo de execução da JPA como se conectar ao seu banco de dados. Isso é possível criando um arquivo persistence.xml. O tempo de execução da JPA precisa localizar esse arquivo para usar os metadados nele. O modo mais fácil de fazer isso é colocá-lo em um diretório /META-INF que seja subdiretório do seu código de origem (ele deve estar na raiz do diretório de saída das classes compiladas).

A listagem 3 mostra o arquivo persistence.xml file.

Listagem 3. O arquivo persistence.xml do aplicativo soccer

<persistence version="1.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_1_0.xsd">
....<persistence-unit name="soccer">
........<class>org.developerworks.soccer.model.Team</class>
........<class>org.developerworks.soccer.model.Player</class>
........<properties>
............<property name="hibernate.dialect"
value="org.hibernate.dialect.DerbyDialect" />
............<property name="hibernate.connection.driver_class"....
value="org.apache.derby.jdbc.EmbeddedDriver" />
............<property name="hibernate.connection.url"
value="jdbc:derby:soccerorgdb;create=true" />
............<property name="hibernate.hbm2ddl.auto" value="update" />
............<property name="hibernate.show_sql" value="true" />
............<property name="hibernate.connection.characterEncoding"
value="UTF-8" />
............<property name="hibernate.connection.useUnicode"
value="true" />
........</properties>
....</persistence-unit>
</persistence>

Olhando novamente as listagens 1 e 2, observe que todo o código é JPA genérico. Na verdade, tudo o que é usado são anotações da JPA e algumas de suas constantes. Não há nada que seja específico do seu banco de dados ou da implementação da JPA usada.

Como é possível ver na listagem 3, o arquivo persistence.xml é onde as coisas específicas se encontram. Várias implementações de JPA excelentes estão disponíveis, inclusive OpenJPA e TopLink.

Você usou o venerável Hibernate, assim tem várias propriedades específicas do Hibernate especificadas por você. Essas são as coisas mais simples como o driver JDBC e a URL, e algumas coisas úteis como informar ao Hibernate para registrar o SQL em que ele está executando (algo que você definitivamente não desejará fazer em uma situação de produção, mas que é muito útil quando realizado para fins de depuração durante o desenvolvimento).

Também é possível observar na listagem 3 que está sendo usado o banco de dados do Apache Derby. Na verdade, você está usando uma versão integrada do banco de dados. Assim, não é necessário inicializar o banco de dados separadamente nem se preocupar com a sua configuração.

Adicionalmente, foi especificado na URL da conexão que o banco de dados deve ser criado automaticamente, e se informou ao Hibernate para criar automaticamente o esquema (essa é a propriedade hibernate.hbm2ddl.auto). Assim, basta executar seu aplicativo, para que o banco de dados e as tabelas sejam criados.

Isso é grande para o desenvolvimento, mas é evidente que você desejará configurações diferentes em um sistema de produção. Agora que foi criado todo o código do modelo de dados e o acesso à JPA já foi ativado, vamos examinar a exposição desses dados, de modo que um aplicativo da Web possa tirar proveito disso.

Acesso do RESTful aos Dados com JAX-RS

Se estivesse criando este aplicativo cinco anos atrás, agora seria o momento de começar a criar algumas Java Server Pages (JSPs) ou Java Server Faces (JSFs), ou até mesmo alguma tecnologia de criação de modelos semelhante.

Em vez de criar a UI desse aplicativo no servidor, será usado o Dojo para criá-la no cliente. Tudo o que é necessário fazer é fornecer um modo para que o código do lado do cliente acesse esses dados usando o Ajax.

É possível ainda usar uma solução de modelos para fazer algo como isso, mas é muito mais simples usar a API for RESTful Web Services (JAX-RS). Vamos iniciar pela criação de uma classe para leitura de todas as Teams no banco de dados e para criação de novas Teams. A listagem 4 mostra essa classe.

Listagem 4. Classe de acesso a dados de Teams

@Path("/teams")
public class TeamDao {
....
....private EntityManager mgr =
DaoHelper.getInstance().getEntityManager();
....
....@GET
....@Produces("application/json")....
....public Collection<Team> getAll(){
........TypedQuery<Team> query =
mgr.createQuery("SELECT t FROM Team t", Team.class);
........return query.getResultList();
....}
....
....@POST
....@Consumes("application/x-www-form-urlencoded")
....@Produces("application/json")
....public Team createTeam(@FormParam("teamName") String teamName){
........Team team = new Team();
........team.setName(teamName);
........EntityTransaction txn = mgr.getTransaction();
........txn.begin();
........mgr.persist(team);
........txn.commit();
........return team;
....}
}

A listagem 4 mostra uma classe de objeto de acesso a dados de classe, daí o nome TeamDao. Nós vamos chegar às anotações nessa classe em breve, mas deixe-me primeiro explicar o acesso aos dados.

A classe tem uma referência à classe EntityManager da JPA. Essa é uma classe central na JPA e fornece acesso ao banco de dados subjacente. Para o primeiro método, que recupera todas as equipes na liga, use EntityManager para criar uma consulta.

A consulta usa a linguagem de consulta da JPA, que é muito similar à SQL. Essa consulta simplesmente obtém tudo das Teams. Para o segundo método, simplesmente crie uma nova Team usando o nome da equipe informado, crie uma transação, salve a nova equipe e confirme a transação usando a classe EntityManager. Todo esse código é código JPA baunilha, já que todas essas classes e interfaces são parte da API básica.

Agora que a parte da JPA na listagem 4 está entendida, vamos falar sobre os aspectos do JAX-RS. Primeiramente, é possível observar a anotação @Path que expõe isso para os clientes baseados em HTTP. A cadeia de caractere /teams especifica o caminho relativo para essa classe. O caminho completo da URL será <host>/SoccerOrg/resources/teams.

/SoccerOrg especificará o caminho para o aplicativo da Web (evidentemente, é possível configurá-lo para algo diferente ou removê-lo por completo). A parte /resources será usada para especificar um terminal do JAX-RS. /teams corresponde à anotação @Path e especifica quais classes do JAX-RS usar.

Em seguida, o primeiro método, getAll, tem uma anotação @GET nele. Isso especifica que esse método deverá ser chamado se a solicitação GET do HTTP for recebida.

Em seguida, o método tem uma anotação @Produces. Essa declara o tipo MIME da resposta. Neste caso, você deseja produzir JSON, visto ser a coisa mais fácil de usar com o cliente baseado em JavaScript.

Isso é tudo o que é necessário fazer para usar o JAX-RS para expor essa classe aos clientes da Web. No entanto, talvez esteja se perguntando: se esse método retornar uma java.util.Collection de objetos Team, como isso será enviado aos clientes da Web?

A anotação @Produces declara que você deseja enviar como JSON, mas como o JAX-RS serializará isso no JSON? Tudo o que é necessário, é incluir mais uma anotação na classe Team mostrada na listagem 5.

Listagem 5. Classe Team modificada

@XmlRootElement
@Entity
public class Team {
....
// unchanged from Listing 1
........
}

Incluindo a anotação @XmlRootElement, o JAX-RS pode agora transformar essa classe em um objeto JSON. É possível reconhecer essa anotação. Ela não pertence ao JAX-RS. É parte da API do Java Architecture for XML Binding (JAXB) que compõe a plataforma Java 1.6 principal.

Essa anotação parece indicar que ela é para XML, mas ela pode ser, na verdade, usada para diversas saídas do JAXB, inclusive o JSON. Há muitas outras anotações do JAXB, mas essa é a única que você precisa usar neste caso. Simplesmente serão usadas convenções para serializar todos os campos da classe Team para JSON.

Agora, voltemos à listagem 4 e examinemos o segundo método da classe, o método createTeam. Esse método usa a anotação @POST para especificar que ele deve ser chamado quando uma solicitação POST do HTTP é recebida.

Em seguida, ele usa a anotação @Consumes para declarar que tipo de solicitação POST ele pode consumir. O valor especificado aqui corresponde ao cabeçalho de tipo de conteúdo da solicitação HTTP. Nesse caso, ele é especificado como x-www-form-urlencoded. Esse é o tipo que você receberá quando um formulário HTML for submetido. Assim, esse método será chamado quando um formulário HTML for submetido com o terminal /SoccerOrg/resources/teams.

Finalmente, observe que o método apresenta um único parâmetro de entrada, uma cadeia de caractere denominada teamName. Note que esse parâmetro está decorado com a anotação @FormParam. Essa informa ao tempo de execução do JAX-RS para procurar um parâmetro de formulário no corpo da solicitação de nome teamName (o valor da anotação) e ligar o parâmetro a uma variável informada na chamada do método.

Desse modo, é possível facilmente manusear um envio de formulário simples e ligá-lo ao código. Isso pode ficar confuso se você tiver um monte de dados sendo enviados. Nesse caso, talvez seja apropriado usar uma abordagem mais estruturada. A listagem 6 mostra um exemplo de como criar um objeto Player.

Listagem 6. Manipulação de dados POST estruturados usando o JAX-RS

@Path("/players")
public class PlayerDao {
....private EntityManager mgr =
DaoHelper.getInstance().getEntityManager();
....
....@POST
....@Consumes("application/json")
....@Produces("application/json")
....public Player addPlayer(JAXBElement<Player> player){
........Player p = player.getValue();
........EntityTransaction txn = mgr.getTransaction();
........txn.begin();
........Team t = p.getTeam();
........Team mt = mgr.merge(t);
........p.setTeam(mt);
........mgr.persist(p);
........txn.commit();
........return p;
....}
....
....@GET
....@Produces("application/json")
....public List<Player> getAllPlayers(){
........TypedQuery<Player> query =
............mgr.createQuery("SELECT p FROM Player p", Player.class);
........return query.getResultList();
....}
}

A classe PlayerDao na listagem 6 é muito similar à classe TeamDao da listagem 5. A principal diferença que você deseja examinar é seu método addPlayer. Esse manipula as solicitações POST do HTTP, similar ao método createTeam no TeamDao. No entanto, ele consome application/json – ou seja, está esperando dados JSON.

Isso implica em duas coisas. Primeiro, a solicitação precisa especificar um tipo de conteúdo application/json de modo que o método seja chamado. Em seguida, o corpo da postagem deve ser dados JSON.

Observe que o parâmetro de entrada desse método é do tipo JAXBElement<Player>, ou seja, é um wrapper do JAXB ao redor de um objeto Player. Isso informa ao JAX-RS para analisar automaticamente os dados postados em um wrapper JAXBElement, assim não é necessário ter o incômodo de escrever código de análise com esse objetivo.

Observe que no corpo do método, leva apenas uma linha de código para obter um objeto Player completo que pode ser usado para salvar o novo Player no banco de dados usando a JPA.

A última coisa a ser feita para concluir a história do JAX-RS é mostrar a configuração necessária para ligar tudo isso. Para tal, é necessário apenas modificar o web.xml do seu aplicativo. A Listagem 7 mostra o web.xml do aplicativo.

Listagem 7. web.xml do aplicativo

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
....xmlns="http://java.sun.com/xml/ns/javaee"
....xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
....xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
....id="Soccer_Org" version="2.5">
<display-name>SoccerOrg</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>JAXRS-Servlet</servlet-name>

<servlet-class>com.sun.jersey.spi.container.servlet.
ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>

<param-value>org.developerworks.soccer.model;org.developerworks.
soccer.web</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>JAXRS-Servlet</servlet-name>
<url-pattern>/resources/*</url-pattern>
</servlet-mapping>
</web-app>

Como é possível ver na listagem 7, o seu aplicativo tem um único servlet declarado. Esse é o servlet fornecido pelo Jersey, a implementação do JAX-RS que está sendo usada. Você passa um parâmetro de inicialização ao servlet – os pacotes contendo as classes sobre as quais o JAX-RS deve saber.

Neste caso, há um pacote onde são mantidos os modelos de dados e outro que mantém os objetos de acessos a dados. Você precisa que os modelos sejam descobertos de modo que o JAX-RS possa convertê-los em JSON.

Evidentemente, é necessário que os DAOs sejam descobertos de modo que o JAX-RS possa encaminhar solicitações para eles. Finalmente, observe o mapeamento do servlet. É aí que a parte /resources dos caminhos URL é especificada. Agora você está pronto para usar todo esse código de backend no cliente para criar uma UI usando o Dojo.

Usando o REST no Cliente com Dojo

O Dojo Toolkit fornece praticamente qualquer tipo de biblioteca ou utilitário que pode ser necessário para desenvolver o lado do cliente do seu aplicativo da Web. Você verá como ele é útil quando trabalhar com Ajax, formulários, JSON e criar as widgets da UI. No entanto, ele pode fazer muito mais do que isso.

Acontece que isso é tudo o que este exemplo simples necessita. Em um sistema grande, talvez você queira fazer download do kit de ferramentas completo e um desenvolvimento customizado para obter exatamente o que o seu aplicativo precisa.

Neste aplicativo de exemplo, será preciso usar as Google Ajax APIs para obter acesso às diversas partes do conjunto de ferramentas necessário. Além de isso ser conveniente, há algumas vantagens interessantes de desempenho visto que as cópias do Dojo do Google são fornecidas pela content delivery network (CDN) altamente eficiente do Google.

O seu aplicativo é centrado a dados, assim para iniciar é necessário incluir algum dado nele. Usaremos o Dojo para criar uma UI de inclusão de Teams. A listagem 8 mostra todo o código necessário para isso.

Listagem 8. Incluindo Teams usando o Dojo

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test Harness</title>
<link rel="stylesheet" type="text/css"

....href="http://ajax.googleapis.com/ajax/libs/dojo/1.4/dijit
/themes/soria/soria.css"/>
<script type="text/javascript"
src="http://ajax.googleapis.com/ajax/libs/dojo/1.4/dojo/dojo.xd.js"
djConfig="parseOnLoad: true"></script>
<script type="text/javascript">
....function init(){
.... var btn = dijit.byId("addTeamBtn");
.... dojo.connect(btn, "onClick", function(event){
.... ....event.preventDefault();
event.stopPropagation();
dojo.xhrPost({
form : dojo.byId("addTeamForm"),
....handleAs: "json",
....load : function(data){
........addTeam(data);
........alert("Team added");
....},
....error : function(error){
.... alert("Error adding team: " + error);
....}
});
.... });
....}
</script>
</head>
<body class="soria">
....Add a Team<br/>
....<form method="POST" action="/SoccerOrg/resources/teams" id="addTeamForm">
........<label for="teamName">Team Name:</label>
........<input name="teamName" type="text" id="teamName"
dojoType="dijit.form.TextBox"/>
........<button type="submit" id="addTeamBtn" dojoType=
"dijit.form.Button">Add Team</button>
....</form>
....<script type="text/javascript">
........dojo.require("dijit.form.Button");
.... dojo.require("dijit.form.TextBox");
.... dojo.addOnLoad(init);
....</script>
</body>
</html>

Observe na listagem 8 que você faz referência à biblioteca base do Dojo a partir da CDN do Google. Uma vez feito isso, é possível solicitar cada parte adicional desejada do Dojo usando a função dojo.require (consulte o bloco de scripts na parte inferior da listagem 8).

Observe que você acabou de criar um formulário HTML normal, mas são usados alguns atributos extras específicos do Dojo. Isso informa ao Dojo para incluir algum estilo adicional nos elementos visuais e adicionar alguns recursos extras aos elementos DOM correspondentes.

Você informa ao Dojo uma vez para executar a função init e tudo o mais é carregado (todos os componentes do Dojo). Nessa função, é usada a função dijit.byId para obter uma manipulação no botão do formulário.

Dijit é a biblioteca de widgets do Dojo. É possível usar dojo.byId para fazer referência a qualquer elemento do DOM por seu ID, mas o dijit.byId similar fornece um widget com recursos extras (se o elemento estiver marcado como widget, o que é o caso do botão na listagem 8).

O Dojo é usado para associar um manipulador de eventos para quando o botão é clicado. O manipulador para o envio do formulário e, em vez disso, usa o Ajax por meio da função dojo.xhrPost. Essa função facilita a postagem (POST) de formulários HTML. Ela descobre o terminal do Ajax inspecionando o atributo action do formulário HTML.

Ela também lê todos os elementos do formulário e os passa para o POST do Ajax. Quando obtém uma resposta do servidor, ela chama a função load que é transmitida para xhrPost. Observe que você declarou que JSON será retornado pelo servidor configurando a propriedade handleAs passada para a função xhrPost.

Você verá a função addTeam em breve, mas pode transmitir o objeto de dados diretamente porque o Dojo já analisou com segurança os dados JSON em um objeto JavaScript utilizável. Esta função addTeam é usada em conjunto com outro formulário, para a inclusão de Players. A Listagem 9 mostra o HTML desse formulário.

Listagem 9. Inclusão do formulário Player

Add a Player<br/>
<form id="addPlayerForm" action="/SoccerOrg/resources/players">
....<label for="firstName">First Name:</label>
....<input name="firstName" id="firstName" type="text"
dojoType="dijit.form.TextBox"/>
....<label for="lastName">Last Name:</label>
....<input type="text" name="lastName" id="lastName"
dojoType="dijit.form.TextBox"/><br/>
....<label for="age">Age:</label>
....<input type="text" name="age" id="age"
dojoType="dijit.form.TextBox"/><br/>
....<label for="team">Team:</label>
....<select id="team" name="team" dojoType="dijit.form.
Select"></select>
....<button type="submit" id="addPlayerBtn" dojoType=
"dijit.form.Button">Add Player</button>
</form>
<script type="text/javascript">
dojo.require("dijit.form.Select");
dojo.addOnLoad(loadTeams);
</script>

Esse formulário, a exemplo do presente na listagem 8, é um formulário HTML válido. No entanto, ele também tem os atributos específicos do Dojo incluídos nos seus elementos.

Observe que ele tem um elemento SELECT que servirá como lista suspensa das equipes (Teams), de modo que o usuário pode selecionar qual Team incluir para o novo Player. Esses são dados dinâmicos que precisam ser carregados do servidor.

Observe que você incluiu outra função para ser chamada na inicialização – a função loadTeams. E essa função que carrega as equipes do servidor. A listagem 10 mostra essa função, além da função addTeam que você viu referenciada na listagem 9.

Listagem 10. As funções loadTeams e addTeam

var teams = {};
function loadTeams(){
....var select = dijit.byId("team");
....dojo.xhrGet({
........url: "/SoccerOrg/resources/teams",
........handleAs:"json",
........load : function(data){
............var i = 0;
............for (i in data.team){
................addTeam(data.team[i]);
............}
........},
........error : function(error){
............alert("Error loading team data: " + error);
........}
....});
}
function addTeam(team){
....teams[team.id] = team;
....var select = dijit.byId("team");
....var opt = {"label":team.name, "value":team.id};
....select.addOption(opt);
}

Aqui, mais uma vez, os utilitários Ajax do Dojo são usados para acessar os dados fornecidos pelo terminal do JAX-RS criado anteriormente. Desta vez, é usado o dojo.xhrGet, que faz uma solicitação GET do HTTP para um terminal do Ajax.

Neste caso, é necessário especificar a URL, mas por outro lado, ele seria muito similar ao xhrPost visto na listagem 9. Finalmente, é possível ver o método addTeam. Esse, mais uma vez, usa os recursos extras de widget do Dojo para incluir facilmente novas opções na lista suspensa que mostra as equipes.

Agora que você viu como o formulário do jogador é criado, examine o código que manipula os envios. Veja a listagem 11.

Listagem 11. Incluindo um novo Player

var button = dijit.byId("addPlayerBtn");
dojo.connect(button, "onClick", function(event){
.... event.preventDefault();
event.stopPropagation();
var data = dojo.formToObject("addPlayerForm");
var team = teams[data.team];
data.team = team;
data = dojo.toJson(data);
var xhrArgs = {
postData: data,
handleAs: "json",
load: function(data) {
alert("Player added: " + data);
dojo.byId("gridContainer").innerHTML = "";
loadPlayers();
},
error: function(error) {
alert("Error! " + error);
},
url: "/SoccerOrg/resources/players",
headers: { "Content-Type": "application/json"}
};
var deferred = dojo.xhrPost(xhrArgs);
});

Este código enviará dados para o método PlayerDao.addPlayer já mencionado na listagem 6. Esse código espera que o objeto Player seja serializado em uma estrutura de dados do JSON.

Primeiro, o Dojo é usado mais uma vez para ligar um manipulador de eventos a um clique de botão no formulário. Em seguida, você usa a função de conveniência do Dojo, dojo.formToObject, para transformar todos os dados do formulário em um objeto JavaScript.

Então, esse objeto JavaScript é modificado um pouco para corresponder com a estrutura esperada no servidor. Em seguida, a função dojo.toJson do Dojo é usada para transformar o objeto em uma cadeia de caractere do JSON.

Em seguida, a sequência é passada para o dojo.xhrPost, do mesmo modo como o formulário addTeam foi enviado. Observe que é incluído um cabeçalho do HTTP, Content-Type, para assegurar que ela será encaminhada para o método PlayerDao.addPlayer.

O xhrPost mais uma vez tem uma função load que será chamada assim que a solicitação do Ajax retornar com uma resposta bem-sucedida do servidor. Neste caso, ela é claramente um elemento na página denominado gridContainer e chamando uma função denominada loadPlayers. Esse é outro widget do Dojo usado para mostrar todos os jogadores. A listagem 12 mostra o HTML e o JavaScript usados para tal.

Listagem 12. HTML e JavaScript da grade de jogadores

<style type="text/css">
@import
"http://ajax.googleapis.com/ajax/libs/dojo/1.4/dojox/grid/resources/Grid.css";
@import
"http://ajax.googleapis.com/ajax/libs/dojo/1.4/dojox/grid/resources/soriaGrid.css";
.dojoxGrid table { margin: 0; }
html, body { width: 100%; height: 100%; margin: 0; }
</style>
<script type="text/javascript">
function loadPlayers(){
....var pStore = new dojox.data.JsonRestStore({
........target: "/SoccerOrg/resources/players"
....});
....pStore._processResults = function(data, deferred){
........return {totalCount:deferred.fullLength || data.player.length,
items: data.player};
....};
var pLayout = [{
field: "firstName",
name: "First Name",
width: "200px"
},
{
field: "lastName",
name: "Last Name",
width: "200px"
},
{
field: "age",
name: "Age",
width: "100px"
},
{
field : "teamName",
name : "Team",
width: "200px"
}];

var grid = new dojox.grid.DataGrid({
store: pStore,
clientSort: true,
rowSelector: "20px",
structure: pLayout
}, document.createElement("div"));
dojo.byId("gridContainer").appendChild(grid.domNode);
grid.startup();
}
</script>
<div id="gridContainer" style="width: 100%; height: 100%;"></div>
<script type="text/javascript">
dojo.require("dojox.grid.DataGrid");
dojo.require("dojox.data.JsonRestStore");
dojo.addOnLoad(loadPlayers);
</script>

A Listagem 12 mostra o widget DataGrid do Dojo. Esse é um dos widgets mais complexos no Dojo e, portanto, requer algum CSS extra também. Para criar uma grade, é necessário fazer duas coisas. Primeiro, é necessário criar um armazenamento de dados para ela. Neste caso, esses serão os dados do JSON provenientes do seu servidor, assim crie um novo objeto JsonRestStore e aponte-o para a URL no seu servidor que produzirá esses dados.

Em seguida, substitua seu _processResults. Você só tem que fazer isso porque é esperado um array de dados JSON e seu terminal JAX-RS produzirá um objeto um pouco mais complicado (ele terá uma propriedade denominada player cujo valor estará no array JSON esperado pelo JsonRestStore).

A próxima coisa que a grade precisa são os metadados do layout que informam o que as colunas mostram e qual será a propriedade correspondente no objeto JavaScript. Em seguida, é possível criar a grade e soltá-la na árvore do DOM.

Agora você concluiu o aplicativo de futebol de amostra e tem um modo muito elaborado de mostrar os jogadores de futebol na liga. É possível expandir facilmente este exemplo simples adicionando a edição de jogadores, a classificação da grade ou incluindo mais dados como jogos e resultados.

Conclusão

Este artigo mostrou um modo rápido para criar um aplicativo da Web
elaborado, centrado em dados. Você usou várias
tecnologias-chave para
remover código de clichê, tedioso, do lado do servidor e
do lado do
cliente: JPA, JAX-RS e Dojo.

Em muitos casos, as
convenções padrão foram
usadas para reduzir adicionalmente a quantidade de
código necessária para
criar o aplicativo da Web. O resultado é um aplicativo
da Web muito
moderno, criado com o mínimo de código.

Todas as
tecnologias usadas são
extensíveis e têm qualidade de produção, assim é
possível expandir
confiantemente o aplicativo de amostra (ou seu próprio
aplicativo) para
casos de uso mais robustos de uma maneira simples.

E o
melhor é que não há nenhum impedimento. Foram usados padrões abertos no
lado do servidor.
É possível trocar facilmente as tecnologias de banco de
dados, por
exemplo. Foram usados REST e JSON no front-end, o que
significa que é
possível usar um kit de UIs diferente ou conectar
facilmente um cliente
móvel.

Download

Descrição Nome Tamanho Método de download
Article source code SoccerOrg.zip 14KB HTTP

Recursos

Aprender

Obter produtos e tecnologias

  • Faça download do Dojo
    Toolkit
    .
  • Obtenha o Java SDK.
    Este artigo usou o JDK 1.6.0_17.
  • Obtenha Apache Tomcat.
    Este artigo usou o Apache Tomcat 6.0.14.
  • Obtenha Apache
    Derby 10.6.1.0
    .
  • Jersey
    é a implementação de referência do JAX-RS: é software livre e tem
    qualidade de produção.
  • Hibernate é uma
    implementação da Java Persistence API (JPA). Este artigo usou a Versão
    3.5.3.

Discutir

***

artigo publicado originalmente no developerWorks Brasil, por Michael Galpin

Michael Galpin vem desenvolvendo software Java profissionalmente desde
1998. Atualmente, ele trabalha no Ludi Labs, um negócio novo em Mountain
View, Calif. Ele possui formação superior em matemática do California
Institute of Technology.

é o portal de tecnologia da IBM para profissionais de TI de todo o mundo. Colabore, aprenda a criar aplicativos inovadores e compreenda tecnologias avançadas. Acesse mais artigos e tutoriais em www.ibm.com/developerworks/br

Ver perfil