Sei sulla pagina 1di 76

java127.

indb 1 15/04/2014 11:15:17


java127.indb 2 15/04/2014 11:15:19
Assine agora e tenha acesso a
todo o contedo da DevMedia:
www.devmedia.com.br/mvp

Edio 127 2014 ISSN 1676-8361

Atendimento ao leitor Fale com o Editor!


EXPEDIENTE A DevMedia possui uma Central de Atendimento on-line, onde voc
pode tirar suas dvidas sobre servios, enviar crticas e sugestes e muito importante para a equipe saber o que voc est achando da revista:
falar com um de nossos atendentes. Atravs da nossa central tambm que tipo de artigo voc gostaria de ler, que artigo voc mais gostou e qual
artigo voc menos gostou. Fique a vontade para entrar em contato com os
Editor possvel alterar dados cadastrais, consultar o status de assinaturas
editores e dar a sua sugesto!
e conferir a data de envio de suas revistas. Acesse www.devmedia. Se voc estiver interessado em publicar um artigo na revista ou no site Java
Eduardo Spnola (eduspinola@gmail.com)
com.br/central, ou se preferir entre em contato conosco atravs do Magazine, entre em contato com o editor, informando o ttulo e mini-resumo
telefone 21 3382-5038. do tema que voc gostaria de publicar:
Consultor Tcnico Davi Costa (davigc_08@hotmail.com)
Publicidade
publicidade@devmedia.com.br 21 3382-5038
Produo
Jornalista Responsvel Kaline Dolabella - JP24185 Anncios Anunciando nas publicaes e nos sites do Grupo DevMedia, Eduardo Oliveira Spnola
voc divulga sua marca ou produto para mais de 100 mil desenvolvedores eduspinola.wordpress.com
Capa e Diagramao Romulo Araujo
de todo o Brasil, em mais de 200 cidades. Solicite nossos Media Kits, com
@eduspinola / @Java_Magazine
Distribuio detalhes sobre preos e formatos de anncios.
FC Comercial e Distribuidora S.A
Rua Teodoro da Silva, 907, Graja - RJ Java, o logotipo da xcara de caf Java e todas as marcas e logotipos
CEP 20563-900, (21) 3879-7766 - (21) 2577-6362 baseados em/ ou referentes a Java so marcas comerciais ou
marcas registradas da Sun Microsystems, Inc. nos Estados Unidos e
em outros pases.

java127.indb 3 15/04/2014 11:15:24


Sumrio
Contedo sobre Boas Prticas

06 Orientao a Objetos: princpios de OO para arquiteturas robustas


[ Peter P. Lupo e Cristina T. Cerdeiral ]

Artigo no estilo Soluo Completa

12 Apache Hive: simplificando solues BigData


[ Brunno F. M. Attorre ]

Contedo sobre Novidades

20 Spring Framework: as novidades do Spring 4


[ Luciano Davoglio Molinari ]

Feedback
Artigo no estilo Soluo Completa eu
s
D

30 Desenvolvendo aplicaes com Java EE 7

sobre e
[ Alexandre Arcanjo de Queiroz e Felipe Pierin ]
s
ta
edio
Artigo no estilo Soluo Completa, Artigo no estilo Curso

48 Java EE 7: desenvolvendo aplicaes batch Parte 2 D seu feedback sobre esta edio!
[ Jefferson S. de Araujo ]
A Java Magazine tem que ser feita ao seu gosto.
Para isso, precisamos saber o que voc, leitor, acha
da revista!
Contedo sobre Boas Prticas D seu voto sobre esta edio, artigo por artigo, atra-
vs do link:
64 Introduo a linguagem Clojure
www.devmedia.com.br/javamagazine/feedback
[ Plnio Balduino ]

java127.indb 4 15/04/2014 11:15:27


java127.indb 5 15/04/2014 11:15:31
Orientao a Objetos:
princpios de OO para
arquiteturas robustas
O que analisar no design do seu software

N Fique por dentro


o nosso cotidiano, usamos generalizaes todo
o tempo. A generalizao um mecanismo
extremamente til que nos permite fazer refe- Este artigo aborda conceitos de orientao a objetos que permitiro
rncia a um grupo de indivduos de uma determinada ao leitor desenvolver cdigo com maior qualidade visando maior
populao atravs de suas caractersticas comuns. Por reutilizao, menos defeitos, design mais simples e melhor separao
exemplo, ao referenciarmos os alunos de uma turma de responsabilidades. Para isto, so apresentadas interpretaes dos
atravs do nome da turma, estamos utilizando a carac- conceitos fundamentais da orientao a objetos que serviro como
terstica daquelas pessoas serem alunos e a participao alicerce para o aprofundamento nos conceitos de design que nortearo
em um determinado grupo como caractersticas comuns. o desenvolvimento de aplicaes mais robustas.
muito mais simples do que enumerar todos, um a um,
pelo nome.
Assim, recorremos a duas generalizaes (alunos e a do mundo real e de instncia os indivduos que desempenham os
participao em uma turma especfica) para delimitar comportamentos descritos nestas classes. Por sua vez, chamamos
o universo de indivduos a que estamos nos referindo. de atributos as caractersticas de um indivduo (como pertencer a
Caso tivssemos apenas dito a turma, vrios outros uma determinada turma) e de mtodos, a representao de seus
conceitos poderiam estar includos, como o professor, comportamentos.
por exemplo. E caso tivssemos dito apenas alunos, Esta capacidade trouxe uma nova forma de entender e codificar
estaramos nos referindo a alunos de diversas turmas. software, permitindo que este possa, de forma modularizada,
Desta forma, vemos que a generalizao de uma carac- evoluir a um nvel de complexidade muito maior que na progra-
terstica comum a todos aqueles indivduos fez surgir mao estruturada.
um conceito abstrato, o conceito de aluno. Quando softwares se tornam grandes e complexos, ficam mais
Aluno um conceito abstrato, criado pelo nosso difceis de serem mantidos; porm, quando so bem modulariza-
poder de abstrao. O que podemos dizer sobre um dos com responsabilidades isoladas em componentes especficos,
aluno de maneira genrica que existe um indivduo a manutenibilidade favorecida, agilizando o desenvolvimento
que desempenha um comportamento e a conveno atravs de reuso.
de que este indivduo quando desempenha este com- De tal modo, uma srie de cuidados so necessrios ao se projetar
portamento tem o nome aluno. Em uma sala de aula bem as classes e os componentes a fim de se construir softwares
possvel apontar para um aluno (um indivduo), mas grandes e complexos tendo um baixo ndice de retrabalho, man-
no possvel apontar para aluno (conceito). tendo razoveis os nveis de produtividade dos desenvolvedores
A capacidade de codificar em um software os conceitos e a confiabilidade do software (importante para a confiana do
abstratos do mundo real (o conceito aluno, por exemplo) cliente na qualidade do servio prestado).
e instanciar estes conceitos o fundamento da orienta- Ainda, conforme uma organizao usa um software, suas ne-
o a objetos. Deste modo, em um software orientado a cessidades mudam e os requisitos para que este software mante-
objetos, chamamos de classe a descrio dos conceitos nha sua utilidade mudam junto. Isto (alm da correo de erros

6 Java Magazine Edio 127

java127.indb 6 15/04/2014 11:15:31


provenientes do processo de construo do software) acarreta, seus lados, atribuir os valores 13, 1 e 2008. Seja qual for a unidade
muitas vezes, em pedidos de mudanas ou melhorias, fazendo com de medida (supondo a mesma unidade em todos os atributos), uma
que a qualidade do software seja prejudicada. Este processo natu- instncia desta classe no representa um tringulo.
ral de degenerao do software torna necessrios cuidados extras Uma forma de garantir que o estado de um objeto respeite o
para manter sua qualidade, garantindo que possa ser facilmente espao-estado de sua classe validar as mudanas atravs do
entendido por quem for implementar novas funcionalidades ou encapsulamento de seus atributos. Com o encapsulamento, os
consert-lo, reduzindo o impacto da mudana a poucas classes mtodos da classe iro definir o comportamento das instncias da
ou pequenos mdulos bem delimitados. classe. Com estes dois ltimos exemplos fica claro que o compor-
tamento de uma instncia de Data diferente do comportamento
Definindo uma classe de uma instncia de Triangulo. Os mtodos de Data permitiriam
De acordo com a orientao a objetos, classes so conceitos que seus trs atributos inteiros assumissem o valor 13, 1 e 2008.
definidos atravs de atributos e mtodos que representam a Da mesma forma, 2009, 2000 e 2008 seria um tringulo vlido e
descrio de objetos. uma data invlida. Sem o encapsulamento e a correta definio
Este processo ento exige uma traduo de conceitos para do comportamento das instncias da classe poderiam ser criadas
cdigo. Assim como uma mensagem pode ser escrita em diferen- inconsistncias. Portanto, uma das primeiras coisas a ser obser-
tes frases ou textos, um conceito pode ser codificado de diversas vada ao definir uma classe a definio de seu comportamento
maneiras e no h regras para esta traduo. e a verificao do mesmo para que no viole seu espao-estado.
Geralmente, quando se fala de orientao a objetos, trs conceitos Desta forma, o encapsulamento define a interface da classe e
so citados: consequentemente seu comportamento.
Herana: A habilidade de uma classe adquirir a estrutura e o O prximo conceito, intimamente ligado herana, o princ-
comportamento de outra. Quando uma herana implementada, pio da conformidade de tipo. Antes de introduzir este conceito
diz-se que uma subclasse herda de uma superclasse. Atravs importante notar a diferena entre classe e tipo. Pode-se definir
deste conceito, uma subclasse pode estender o comportamento uma classe Lista implementada como uma lista encadeada ou uma
da superclasse; classe Lista implementada como um vetor. Em outras palavras, um
Encapsulamento: A habilidade de separar a interface da classe tipo pode ser implementado por diferentes classes. J uma classe
de sua estrutura interna; define um tipo, pois descreve um conceito com comportamento
Polimorfismo: Permite que diferentes objetos respondam determinado. Deste modo, por conformidade de tipo, entende-se
mesma mensagem de formas diferentes. que se A subclasse de B, toda instncia de B pode ser substituda
por uma instncia de A, pois A se comporta como B. Esta , na
As definies destes conceitos no ressaltam consequncias mais realidade, a verdadeira interpretao de uma herana na orien-
profundas deste processo de traduo. Assim, importante obser- tao a objetos. Se A herda de B, A se comporta como B. Isso no
var tambm certos aspectos inerentes a este processo de forma a significa dizer que A no pode ter outros comportamentos, mas
definir classes mais fiis aos conceitos mapeados, levando inva- que A se comporta, pelo menos, como B (e o espao-estado de A
riavelmente a cdigos de melhor qualidade e menos erros. Alguns deve conter todo o espao-estado de B). Este conceito traz algumas
destes aspectos so abordados por conceitos vistos a seguir. implicaes. Para explic-las, necessrio introduzir os conceitos
O primeiro conceito a ser explorado, intimamente ligado com o de covarincia e contravarincia, ligados ao polimorfismo.
conceito de encapsulamento, o espao-estado de uma classe. Para a Em uma herana, uma subclasse pode redefinir um mtodo
explicao, consideremos uma classe Data contendo trs nmeros da superclasse. Neste processo, a assinatura do mtodo deve ser
inteiros (dia, ms e ano) como atributos. mantida para que seja considerado o mesmo mtodo e, portanto
O espao-estado desta classe o conjunto de todas as combi- uma redefinio. Em Java, a assinatura do mtodo composta
naes de valores que os trs atributos podem assumir simul- por seu nome e sua lista de parmetros. Como o valor de retorno
taneamente. Por exemplo, dia pode variar de 1 a 31 e ms de 1 a est fora da assinatura, este poderia ser facilmente modificado.
12, porm, para os meses 4, 6, 9 e 11, o dia s pode variar de 1 a No entanto, o princpio da covarincia diz que o valor de retorno
30. Estas combinaes de valores vlidos determinam o espao- na subclasse deve ser do mesmo tipo (ou um subtipo) do valor de
estado de uma classe. Falando de um objeto desta classe, uma retorno na superclasse. Mais ainda, a faixa de valores possveis
combinao especfica de valores (dia 25, ms 2, ano 2014, por deve ser igual ou menor. No exemplo, supondo em B um mtodo
exemplo) estar relacionada a estes atributos em um determinado m1() cujos valores de retorno sejam inteiros entre 0 e 10, em A
momento. Esta combinao o estado do objeto. Assim, para que o o mtodo m1() s pode retornar quaisquer inteiros entre 0 e 10,
sistema permanea consistente, importante que todos os objetos mas nunca 11. Caso uma instncia de A venha a substituir uma
estejam em estados contidos no seu espao-estado, ou seja, todos instncia de B e o valor retornado seja 11, um erro aconteceria em
os estados sejam vlidos. um sistema preparado para trabalhar com inteiros de 0 a 10.
Um exemplo de como quebrar um espao-estado seria em uma Note que da mesma forma o tipo retornado em A deve ser um sub-
classe Triangulo com trs atributos representando os valores de tipo do tipo retornado em B (novamente a conformidade de tipo).

Edio 127 Java Magazine 7

java127.indb 7 15/04/2014 11:15:31


Orientao a Objetos: princpios de OO para arquiteturas robustas

Em Java, esta caracterstica somente est presente a partir da instncias diferentes e at certo ponto com verses diferentes do
verso 5.0. mesmo SGBD. O mesmo tambm acontece com classes de interao
Analogamente, o princpio da contravarincia diz que os par- com o usurio. Uma classe de Janela no Windows diferente de
metros dos mtodos em A devem ter limites iguais ou maiores uma classe de Janela no Linux.
que os de B, ou seja, supondo que o mtodo m1() em B possua Uma das grandes vantagens da linguagem Java nos distanciar
um parmetro com valores variando entre 0 e 10, o mtodo m1() destas peculiaridades, dando um grau de reutilizao para estas
em A pode funcionar recebendo valores entre -5 e 15 (intervalo classes prximo das classes do domnio de base.
de m1() em A contm o intervalo de m1() em B), mas no pode- O domnio de negcio compreende as classes de um negcio
ria deixar de funcionar para valores dentro do intervalo de 0 a especificamente. So classes do contexto do software sendo desen-
10 (se o intervalo de m1() em A fosse entre 5 e 9 por exemplo, ao volvido. Por exemplo, em um sistema hospitalar, temos as classes
substituir uma instncia de B, uma instncia de A falharia ao Medico e Paciente. Em um sistema de trfego areo, Aeronave
receber o valor 3). e Pista. Estas classes tm bastante utilidade em aplicaes dife-
Tendo os conceitos bsicos mais aprofundados, possvel de- rentes, contanto que no mesmo domnio. Podem ser de atributo,
senvolver classes de forma mais confivel, melhorando significa- quando existem para representar um dado, como Matrcula, que
tivamente a estabilidade do sistema como um todo. representaria o nmero de matrcula do Aluno, de papel, como
Entretanto, para que o design geral do sistema conserve caracte- Aluno e Professor, ou de relacionamento, como Inscricao, que
rsticas prprias para facilitar a manutenibilidade e o reuso, uma define o relacionamento de um Aluno inscrito em uma Turma.
anlise mais detalhada sobre domnios de classes deve ser levada As classes compreendidas no domnio de aplicao so es-
em considerao, o que feito no prximo tpico. pecficas para cada aplicao. Sua reutilizao baixa, ficando
restrita a aplicao sendo construda. So classes reconhecedoras
Domnios de classes de objetos de eventos, como para monitorar colises entre aeronaves, e ge-
Agora que foram vistos os aspectos que permeiam a definio renciadoras de eventos, que tomam aes como modificar a rota
de uma classe, importante examin-las do ponto de vista de sua de uma aeronave no caso de rotas que levem a uma coliso. Em
finalidade, ou seja, qual o papel que elas desempenharo no sis- um software de monitorao de espao areo, Aeronave poderia
tema. Sem o correto entendimento do papel a ser desempenhado, ser uma classe do domnio de negcio e poderia ser reutilizada
poderemos incorrer em erros comuns, que podem prejudicar o em um software de manuteno de aeronaves, mas neste caso as
entendimento, a manutenibilidade e principalmente comprometer classes do domnio de aplicao no fariam sentido.
o reuso. Alguns erros comuns sero apresentados mais adiante.
As classes, de acordo com sua finalidade, podem ser categori- Coeso
zadas em domnios diferentes. Os chamados domnios de classes Muitas vezes ouve-se dizer que as classes devem ter alta coeso
so: base, arquitetura, negcio e aplicao, e sero apresentados e baixo acoplamento (assunto explorado mais adiante). Uma classe
a seguir. com baixa coeso apresenta mtodos e atributos dispersos, que
As classes no domnio de base so aquelas que, em geral, encon- no so caractersticos especificamente da abstrao sendo defi-
tramos na linguagem de programao. So classes fundamentais, nida. Por exemplo, no faz sentido colocar na classe Matricula o
como Boolean e Integer, classes estruturais como ArrayList e TreeMap mtodo criarTurma().
ou classes mais ricas semanticamente, como Calendar e Locale, Neste tpico so abordados problemas decorrentes de um design
que apesar disso so altamente reutilizveis e independentes com baixa coeso, gerando, muitas vezes, um alto acoplamento.
da arquitetura, negcio ou aplicao. Podem estar presentes na Geralmente estes problemas so de fcil percepo, no entanto
linguagem de programao, ser incorporadas atravs de biblio- h casos sutis que merecem ateno para ilustrar como a busca
tecas externas, como a Commons Collections, ou produzidas por uma alta coeso pode ser traioeira se no for prestada a
pelos prprios desenvolvedores de software, para auxili-los no devida ateno. Um exemplo o caso de um desenvolvedor que
desenvolvimento. recebe por hora trabalhada e um desenvolvedor que recebe um
As classes no domnio de arquitetura tm tambm alta reu- salrio fixo. Se for colocada na classe Desenvolvedor o mtodo
tilizao, mas esto restritas para uma arquitetura especfica. getSalario(double horas), o desenvolvedor com salrio fixo ter
Por exemplo, as classes de comunicao entre mquinas podem um mtodo que no lhe serve. Para este, um mtodo mais cab-
operar sobre caractersticas fsicas das mquinas para as quais vel seria getSalario(), que por sua vez no teria utilidade para o
foram destinadas. Dentro de uma mesma srie de mquinas, estas desenvolvedor com salrio varivel.
classes so reutilizveis, mas podem ser incompatveis com outra Esta coeso chamada de coeso de instncia mista, onde a
srie de mquinas. classe tem algumas caractersticas que so indefinidas para alguns
Assim tambm pode acontecer com as classes de manipulao objetos. Neste caso, geralmente a soluo refinar melhor a hierar-
de banco de dados, visto que as classes destinadas para trabalhar quia, retirando os provveis ifs na classe afetada. Criar subclas-
diretamente com Oracle so diferentes das classes destinadas a ses (DesenvolvedorSalarioFixo e DesenvolvedorSalarioVariavel,
trabalhar com MySQL, mas ambas podem ser reutilizadas com neste caso) pode solucionar o problema. Uma soluo mais flexvel

8 Java Magazine Edio 127

java127.indb 8 15/04/2014 11:15:32


pode envolver a criao das classes DesenvolvedorSalarioFixo princpio pode parecer simples, no fim das contas, pode levar a
e DesenvolvedorSalarioVariavel com os mtodos diferentes, alteraes em um grande nmero de classes, pois a modificao
ambos replicando os mtodos da classe Desenvolvedor, mas em em uma leva, pelo acoplamento, a uma modificao em outra.
vez de redefini-los, apenas delegar suas execues a uma instncia Com isso, manter o sistema torna-se complicado, caro e trabalho-
de Desenvolvedor. Esta soluo permite que o comportamento so. Alm disso, uma mudana que deveria ser isolada se difunde
sobre esta caracterstica possa ser trocado no futuro. pelo sistema, o que pode torn-lo instvel. O objetivo a ser buscado
Outra forma de coeso mal definida quando h uma coeso deve ser sempre o de produzir software flexvel, com interfaces
de domnio misto. Esta ocorre quando uma classe tem uma bem definidas e mdulos desacoplados.
dependncia com outras classes de domnios diferentes do Para concluir este artigo, sero abordados alguns importantes
seu. Dificilmente conseguiramos definir Pessoa sem utilizar o princpios da orientao a objetos que podem ajudar na solu-
domnio de base diretamente, mas podemos (e devemos) evitar o de problemas no projeto das classes e na identificao de
o acoplamento com classes de domnios que possuam menor melhorias.
capacidade de reutilizao, semanticamente mais ricos ou mais
especficos da aplicao quando em comparao com o domnio Princpios de Design Orientado a Objetos
da classe em questo. Ao longo deste artigo, as caractersticas do design orientado a
Exemplificando, no faz muito sentido colocar o mtodo obter- objetos foram aprofundadas e alguns princpios deste paradigma
ListagemPeasSubstituidas() em Aeronave em um software de foram citados. Nesta ltima parte, mais alguns princpios de de-
trfego areo. O mesmo no pode ser dito de uma classe Aeronave sign orientado a objetos sero explorados devido a sua importncia
em um software de controle de manuteno. Porm, no segundo durante o projeto de classes seguindo este paradigma.
caso, o mtodo tem utilidade. A razo para no ter este mtodo em O princpio da alta coeso e baixo acoplamento praticamente um
Aeronave que esta classe do domnio de negcio e obterLis- mantra na orientao a objetos. Em sua simplicidade, diz que deve
tagemPeasSubstitudas(), um mtodo do domnio de aplicao. ser buscada uma alta coeso dentro de um mdulo (seja este uma
Note que a classe Aeronave, com este mtodo, teria pouco sentido classe ou um componente) e um baixo acoplamento entre mdulos.
no software de trfego areo. Imaginando, por exemplo, que as
necessidades do software de manuteno cresam, eventualmente
a classe Aeronave ganharia os mtodos substituirPea(Pea),
getAnoFabricao(), etc.
Este problema bem parecido com a coeso de papel misto,
onde uma classe fica fortemente dependente de outras do mesmo
domnio. Um exemplo seria uma classe de comunicao com um
mtodo escrever(Arquivo, SaidaRede), onde Arquivo uma
entrada (uma fonte de dados qualquer) e SaidaRede um destino
na rede. Quando a classe utilizada, so passadas referncias
para a entrada e para a sada. Posteriormente, nota-se tambm a
necessidade de obter o dado da rede, e tambm de escrev-lo em
arquivo. A cada mtodo acrescentado na nossa classe de comu-
nicao, mais complexidade adicionada. E tambm possvel
que cada funcionalidade destas exija mais de um mtodo, fazendo
com que tenhamos conjuntos (disjuntos ou no) de mtodos na
mesma classe.
Apesar do problema a ser tratado estar no mesmo domnio, esta
classe ter pouca reutilizao em outras aplicaes sem que seja tra-
zida com vrias outras classes de suporte a suas funcionalidades.
A soluo neste caso poderia ser criar uma interface comum para
entradas e outra para sadas de dados ou o emprego do padro
de projeto Decorator (veja a seo Links), como feito em java.io,
onde um stream especificamente para ler dados de um arquivo
pode ser decorado com a capacidade de bufferizao atravs de
uma classe tambm especfica para esta finalidade, eliminando a
coeso por haver diferentes papis em uma mesma classe.
Estes problemas normalmente levam a acoplamentos desne-
cessrios. O problema de se ter alto acoplamento no sistema o
impacto das modificaes. Deste modo, uma modificao que a

Edio 127 Java Magazine 9

java127.indb 9 15/04/2014 11:15:34


Orientao a Objetos: princpios de OO para arquiteturas robustas

A alta coeso leva a interfaces bem definidas. Um exemplo comum o nome diz, utilizada quando a pessoa que deseja contrair um
de uma baixa coeso a famosa classe Util (ou Utilitario ou Utils) emprstimo uma pessoa fsica. A regra do banco nesta situao
presente em vrios sistemas. Estas classes normalmente agrupam diz que o emprstimo aceito se o rendimento em conta for maior
mtodos que no tm a ver um com o outro e esto juntos ape- que 10.000 Reais. Para AvaliacaoEmprestimoPessoaJuridica, a
nas porque no se sabia onde deveriam estar. Como j foi dito, regra do banco diz que necessrio ter um rendimento de, no
a interface de uma classe deve definir um comportamento e, no mnimo, 50.000 Reais. Esta herana soluciona o problema at o
caso destas classes, no se tem mtodos compatveis em termos momento em que a estratgia do banco determine que pessoas
de funcionalidade e finalidade. jurdicas com menos de dois anos devem passar pela mesma
Ao criar esta classe, a princpio inofensiva, o desenhista acopla avaliao que uma pessoa fsica. Neste ponto, h duas solues
diversas partes do sistema. Assim, uma mudana nesta classe atravs de herana:
pode acabar levando inconsistncias a outros mdulos. Conse- 1- Criar duas subclasses de AvaliacaoEmprestimoPessoaJuridica,
quentemente, em pouco tempo no se sabe mais se um mtodo uma para pessoas jurdicas com menos de dois anos e outra para
pode ser modificado, pois no se sabe mais em que partes do pessoas jurdicas com mais de dois anos. O problema desta soluo
cdigo ele est sendo utilizado. a duplicao de cdigo entre AvaliacaoEmprestimoPessoaFisica
O alto acoplamento tambm pode ser entendido atravs deste e a subclasse para pessoas jurdicas com menos de dois anos;
exemplo: O mdulo X modificado, levando a uma modificao 2- Criar uma subclasse de AvaliacaoEmprestimoPessoaFisica
na classe Util, que leva a uma modificao no mdulo Y ou causa para empresas iniciantes, com menos de dois anos. O problema
um erro no mesmo. Alm deste tipo de problema, classes altamente aqui que empresas iniciantes no se comportam como pessoas
acopladas tendem a alterar o estado de partes diferentes do sistema, fsicas. Alm disso, espera-se que uma modificao em Avalia-
podendo causar modificaes imprevisveis primeira vista. So caoEmprestimoPessoaJuridica impacte em todas as avaliaes
aqueles famosos efeitos colaterais que aparecem em uma parte do de pessoas jurdicas.
sistema que a princpio no tem relao com o que foi alterado.
Quando isto acontece, os mdulos X e Y esto acoplados. Pelo princpio do reuso por composio, a soluo simples e
O acoplamento facilmente identificvel quando h um atributo bastante flexvel. Criar uma interface Avaliacao com o mtodo
de uma classe do mdulo Y em X ou vice-versa. Porm, um objeto avaliar(). Classes que implementam esta interface so respons-
tambm pode estar acoplado por necessitar de dados ou alterar veis por uma avaliao, independentemente da pessoa avaliada
dados em outros. Este ltimo caso costuma ser mais prejudicial, ser fsica ou jurdica. Seguindo o princpio do reuso por compo-
pois a execuo de um mtodo no modifica unicamente aquela sio, uma classe AvaliacaoBaixoRendimento implementaria a
instncia, mas altera outras partes do sistema, podendo gerar os concesso do emprstimo com rendimento acima de 10.000 Reais
indesejados efeitos colaterais. Um princpio que trata especifica- e a classe AvaliacaoAltoRendimento implementaria a concesso
mente deste problema a Lei de Demeter (veja a seo Links). com rendimento acima de 50.000 Reais.
Segundo este princpio, um mtodo m() de um objeto O deve Assim, todas as subclasses continuam herdando de Avaliacao-
modificar primariamente: Emprestimo e cada subclasse referencia a implementao da
1. Atributos de O; interface Avaliacao que faz a avaliao correta no seu contexto.
2. Parmetros de m(); Com isso, pouco impacto haveria no caso de novas frmulas de
3. Objetos criados em m(); avaliao e estratgias de fornecimento de emprstimos serem
4. Componentes diretos de O. definidas pelo banco, alm da vantagem de permitir a troca da
frmula de avaliao em tempo de execuo.
Desta forma, o impacto da chamada de um mtodo fica restrito A questo da conformidade de tipo sempre uma das mais
a um escopo esperado. importantes a ser observada. Definir novas classes significa, alm
O princpio do reuso por composio (veja a seo Links) de abstraes e comportamento, a definio de novos tipos no
provavelmente um dos mais debatidos dentre os princpios de sistema. Observar esta questo torna os sistemas mais robustos e
design mais importantes. Heranas no sistema surgem das mais confiveis e produz classes com maior capacidade de reuso, que
variadas formas. O principal problema da herana a falta de uma das grandes vantagens da orientao a objetos.
flexibilidade (vide discusso sobre conformidade de tipo). Uma Um princpio muito importante nesta questo o princpio da
herana entre duas classes implica em dizer que uma se comporta substituibilidade, tambm conhecido como princpio da substi-
como outra. Portanto, criar subclasses com o intuito de reutilizar tuio de Liskov (veja a seo Links). O enunciado deste princpio
cdigo muitas vezes no o aconselhvel. Para ilustrar o pro- simples. Se para cada instncia o1 do tipo S existe uma instncia
blema, suponha que em um sistema bancrio haja uma classe o2 do tipo T tal que para todos os sistemas P definidos em termos
AvaliacaoEmprestimo responsvel por avaliar se uma pessoa de T o comportamento de P se mantm inalterado quando o1
pode contrair um emprstimo. Esta classe tem duas subclasses: trocado por o2, ento S subtipo de T.
AvaliacaoEmprestimoPessoaFisica e AvaliacaoEmprestimoPes- As implicaes deste princpio so diversas. A mais simples
soaJuridica. A classe AvaliacaoEmprestimoPessoaFisica, como de ser detectada se imaginarmos o que acontece quando este

10 Java Magazine Edio 127

java127.indb 10 15/04/2014 11:15:34


princpio quebrado. Imaginemos uma classe S com subclasses. A inteno neste artigo foi mostrar alguns dos conceitos de
Ento criamos uma nova subclasse T, e a partir de um determi- OO menos citados, explorando o papel destes como ferramentas
nado momento, um mtodo definido em S chamado de uma importantes para se obter o mximo de benefcios da orientao
instncia de T e o sistema falha. a objetos, meta que deve ser buscada no somente, mas principal-
Isto acontece porque a conformidade de tipo no foi observada mente em grandes sistemas, onde a aplicao deste conhecimento
ou porque o mtodo assumia caractersticas especficas para de- trar impacto positivo em custos, prazos e na confiabilidade geral
terminadas subclasses. Em Java, isto pode ser feito com typecasts da aplicao sendo desenvolvida.
e instanceof e por isto recomenda-se evitar estas operaes. Nesta
situao, o acrscimo de uma subclasse envolve modificar os
possveis mtodos que utilizam as superclasses para que possam Autora
se adaptar a subclasse, uma situao geralmente indesejada, que
Cristina T. Cerdeiral
prejudica a manuteno do sistema.
cerdeiral@cos.ufrj.br
Outra caracterstica mais sutil a que define as caractersticas bacharel em Cincia da Computao pela Universidade Federal
da classe no s atravs da sua interface, mas tambm atravs do Rio de Janeiro (2006), mestre (2008) e doutoranda em Enge-
do seu comportamento. Em outras palavras, uma classe (ou um nharia de Software na rea de processos e qualidade de software na COPPE/
conjunto de classes) no pode ser validada sendo analisada fora Universidade Federal do Rio de Janeiro com ps-graduao em inovao
do contexto para o qual foi desenhada. Ao fazer isto, ignora-se o na UFRJ e membro da instituio implementadora de melhoria de processos de software
fato de que os clientes daquela classe esperam que determinadas Implementum. Possui experincia em processos de engenharia de software, qualidade
aes sejam executadas. Um desenvolvedor que utiliza objetos de software, gerncia de projetos, gerncia de conhecimento, inovao em processos de
atravs da superclasse S assume que certas caractersticas com- software, metodologias de desenvolvimento de software, mtodos geis e tecnologias Java.
implementadora de processos de software segundo os modelos de referncia MR - MPS
portamentais de S sero mantidas por suas subclasses.
e CMMI credenciada com 7 anos de experincia e avaliadora com mais de 30 avaliaes
Desta forma, o comportamento deve ser observado na confor-
entre MPS.BR e conjuntas com CMMI. Oracle Certified Associate Java SE 5/SE 6, Certified
midade de tipo. Isso significa, acima de tudo, que a metfora ScrumMaster e fornece consultoria e treinamento em melhoria de processos, metodologias
da herana como sendo um relacionamento do tipo um geis e desenvolvimento em Java desde 2004.
(se A herda de B, um objeto do tipo A um objeto do tipo B)
melhor especificada como se comporta como, como j foi dito
anteriormente.
Links e Referncias:
H outros princpios ainda interessantes no design de classes
como, por exemplo, segregao de interfaces, aberto-fechado,
inverso de dependncia e responsabilidade nica, que ficaro Lei de Demeter no site original, a Northeastern University.
para pesquisa de cada leitor ou para uma prxima edio. http://www.ccs.neu.edu/home/lieber/LoD.html
Reuso por composio no exemplo mais clssico e claro do problema sendo
tratado.
Autor http://www.cs.sjsu.edu/faculty/pearce/cs251b/principles/crp.htm

Peter P. Lupo Artigo original no qual Barbara Liskov apresentou o princpio que herdou seu
peter@pplupo.com http://www.pplupo.com nome.
analista de sistemas (nfase em Engenharia de Software) na http://www.cs.iastate.edu/~hridesh/teaching/362/07/01/papers/p50-liskov.pdf
Petrobras, bacharel em Cincia da Computao pela UFRJ, mes-
trando em Engenharia de Software na rea de processos e qualidade de Livros
software na COPPE/UFRJ, Oracle Certified Associate Java SE 5/SE 6, Certified Fundamentals of Object-Oriented Design in UML, Meilir Page-Jones, Addison-
ScrumMaster, implementador credenciado de processos de engenharia de software segundo Wesley / Prentice Hall, 1999
o modelo de referncia MPS.BR, membro da instituio implementadora de melhoria de Livro que introduz UML e apresenta, em seguida, conceitos fundamentais de design orientado
processos de software Implementum. Possui experincia com desenvolvimento, anlise, a objetos de forma simples e direta.
projeto orientado a objetos utilizando Java e UML entre outras tecnologias, gerncia de
projetos, processos e metodologias de desenvolvimento de software, mtodos geis
e tecnologias Java. Fornece consultoria e treinamento em desenvolvimento utilizando Voc gostou deste artigo?
tecnologias Java e frameworks relacionados, UML, engenharia de requisitos e outras reas
da engenharia de software j tendo lecionado para vrias turmas incluindo alunos de gra-
duao, mestrado, doutorado e profissionais do mercado desde 2004 alm de consultoria D seu voto em www.devmedia.com.br/javamagazine/feedback
para melhoria de processos de software. Mantm um blog de Engenharia de Software em Ajude-nos a manter a qualidade da revista!
http://craftnicely.pplupo.com.

Edio 127 Java Magazine 11

java127.indb 11 15/04/2014 11:15:37


Apache Hive:
simplificando solues
BigData
A ferramenta ideal para simplificar sua soluo de
Big Data

O
Apache Hadoop vem sendo, nos ltimos anos, Fique por dentro
o grande nome na computao moderna. Dis-
ponibilizando solues para tratar dados que Este artigo ir abordar a ferramenta Apache Hive, um Data Warehouse
antes, devido sua complexidade e escala, eram sim- criado com base no Apache Hadoop, demonstrando exemplos de seu
plesmente descartados, essa ferramenta da Apache j uso para manipular dados atravs da linguagem HiveQL e, tambm, da
foi adotada por diversos gigantes da informtica, como sua utilizao dentro de uma aplicao Java.
Yahoo, eBay e Facebook. Esse tema til para desenvolvedores que tenham interesse em ferra-
Com o avano dos requisitos e o amadurecimento das mentas para manipulao e tratamento de informaes em grande escala,
implementaes do Hadoop nessas empresas, surgiu, de visando uma melhor performance e facilidade no manuseio de dados.
dentro de uma delas, a primeira verso do Apache Hive. Alm disso, programadores que tenham interesse em conhecer solues
Introduzido pelo Facebook em 2009, o Apache Hive foi para diminuir a complexidade das tarefas dentro do Apache Hadoop,
concebido com a ideia de construir uma aplicao de sem perder as vantagens da utilizao do Map/Reduce, encontraro uma
Data Warehouse open source, que utilizasse conceitos alternativa fcil e eficiente na ferramenta Apache Hive.
do Hadoop, como Map/Reduce e HDFS, para manipular
e armazenar dados.
Explicando um pouco o conceito dessa categoria de Esses pontos, por sua vez, foram as principais preocupaes
aplicaes, softwares de Data Warehouse so respon- na implementao do Hive que, alm disso, buscou diminuir a
sveis por armazenar dados de diversos sistemas em complexidade e a curva de aprendizado da utilizao das funcio-
um repositrio nico onde, atravs de transformaes nalidades do Hadoop atravs da linguagem HiveQL, permitindo
das informaes que so enviadas por suas interfaces seu uso por desenvolvedores que no possuem conhecimento
de acesso, esse contedo formatado de acordo com extenso da plataforma de Map/Reduce, com um cdigo intuitivo
um padro especfico de armazenagem definido para e mais prximo do SQL.
utilizao no sistema em questo. O objetivo de nosso artigo ser, portanto, demonstrar as prin-
Como exemplo dessas aplicaes, temos os bancos de cipais caractersticas dessa linguagem e ferramenta, sua relao
dados relacionais, como o MySQL, sistemas de indexa- com o Apache Hadoop e criar um exemplo de aplicao em Java
o, como o Apache Solr, bancos de dados no relacio- capaz de se comunicar com a base de dados do Hive e manipular
nais, como o MongoDB, entre outros. Cada um desses seu contedo.
sistemas apresenta solues mais customizadas para
determinadas situaes, porm tentam sempre manter Principais conceitos do Hadoop
o foco em alguns pontos principais como escalabilidade, Antes de iniciarmos nossa discusso sobre o Apache Hive, ne-
performance, usabilidade e confiabilidade. cessrio entendermos alguns conceitos bsicos do Apache Hadoop.

12 Java Magazine Edio 127

java127.indb 12 15/04/2014 11:15:37


Esse framework, criado em 2005, trouxe diversas novidades rea Listagem 1. Comparativo HiveQL x Hadoop Map/Reduce para fazer uma query
simples.
da informtica ao apresentar solues inovadoras para o tratamento
e armazenamento de grandes quantidades de dados. #HiveQL - 1 comando
Essas solues se baseiam, em sua grande maioria, no conceito de
select key, count(1) from kv1 where key>100 group by key;
Map/Reduce. Esse modelo de programao, implementado dentro
do Apache Hadoop, permite que os dados sejam manipulados por #Hadoop - 4 comandos
diversas tarefas independentes em paralelo, garantindo eficincia
$ cat > /tmp/reducer.sh
e um processamento das informaes de forma distribuda. uniq -c | awk {print $2\t$1}
As trs principais fases desse modelo so denominadas: Map, $ cat > /tmp/map.sh
onde o arquivo a ser processado dividido em pares de chave e awk -F \001 {if($1 > 100) print $1}
$ bin/hadoop jar contrib/hadoop-0.19.2-dev-streaming.jar
valor de acordo com o seu contedo; Group, onde os conjuntos -input /user/hive/warehouse/kv1 -mapper map.sh
de tuplas (pares de dados) so agrupados de acordo com o valor -file /tmp/reducer.sh -file /tmp/map.sh
de suas chaves; e a fase final Reduce, que ir executar uma ta- -reducer reducer.sh -output /tmp/largekey
-numReduceTasks 1
refa de reduo para cada agrupamento gerado na fase anterior, $ bin/hadoop dfs cat /tmp/largekey/part*
transformando-os em um nico resultado final.
Aliando uma implementao robusta do Map/Reduce e o con-
ceito de HDFS, sistema de arquivos que permite uma distribuio Como pode ser observado no comparativo da Listagem 1, en-
de dados entre diversas mquinas, o Hadoop se mostra extre- quanto a query no Hive roda com apenas um comando muito
mamente eficaz e seguro para a construo de clusters e parte semelhante ao SQL, a query pura no Hadoop exige a criao de
fundamental da maioria das aplicaes modernas de anlise e um arquivo para descrever o processo de Reduce, outro para des-
armazenamento de informaes em grande escala. crever o processo de Map, a execuo da tarefa utilizando ambos
os arquivos e, ao final, a traduo do resultado, ou seja, existem
Simplificando o Map/Reduce com o Apache Hive quatro vezes mais comandos envolvidos dentro do Hadoop do
Porm, juntamente com todos esses benefcios e inovaes que se comparado ao nico comando necessrio para rodar a
introduzidos pelo Apache Hadoop, comearam a surgir alguns consulta dentro da interface do Apache Hive.
questionamentos e dificuldades em relao utilizao desse
framework. A primeira dessas dificuldades foi o fato da grande Funcionamento do Apache Hive
maioria dos desenvolvedores no ter familiaridade com a sintaxe e Essa simplicidade em relao ao Hadoop se deve ao funciona-
uso da ferramenta, tornando a curva de aprendizado muito grande mento interno do tradutor de queries em HiveQL, presente
e a dificuldade de achar profissionais no mercado considervel. na implementao do Apache Hive. Esse processa os comandos
O segundo contratempo foi o da complexidade de comandos atravs de um sistema de armazenamento de Metadados, capaz de
para executar consultas nos arquivos armazenados dentro de guardar informaes pertinentes aos campos e tabelas definidas
um HDFS. A ao de ler e filtrar os dados contidos nos diretrios no Hive e fazer a relao dessas informaes com os arquivos
distribudos do Hadoop (HDFS) envolvem diversos comandos armazenados no HDFS, possibilitando, ao executar uma consulta
juntamente com a execuo de processos de Map/Reduce para, ou comando em HiveQL, a traduo para tarefas de Map/Reduce.
ao final, nos trazer os resultados adequados. Isso, apesar de no Na Figura 1 apresentado um diagrama que representa o fluxo de
ser muito extenso quando falamos de consultas simples, pode se traduo dentro dos principais componentes do Apache Hive.
tornar extremamente complexo ao manipularmos um nmero Como podemos observar, o Apache Hive possui trs pontos
extenso de informaes e arquivos. de entrada principais para executar suas queries. Iniciando pelo
Visando solucionar essas dificuldades, o Facebook lanou em Thrift Server, tambm conhecido por Hive Server, esse componente
2009 a primeira verso do Hive. Sua ideia era a de criar uma apli- disponibiliza pontos de conexo, com tecnologias como ODBC e
cao que serviria como um Data Warehouse para os arquivos JDBC, para possibilitar que aplicaes clientes possam se conec-
armazenados dentro do HDFS e de disponibilizar uma linguagem tar a instncia do Hive. O segundo componente, denominado
para manipular essa informao que fosse de fcil entendimento Command Line Interface, o que nos permite executar as nossas
e utilizao para os desenvolvedores, a denominada HiveQL. consultas atravs da linha de comando. Por ltimo, o mdulo de
Essa linguagem muito similar ao SQL, utilizada pelos bancos entrada que fornece acesso ao Apache Hive via browser, cujo nome
de dados relacionais, e trouxe a familiaridade necessria para em nosso diagrama Web Interface, uma interface web que nos
popularizar o Hive entre os desenvolvedores, permitindo que as disponibiliza um console para administrar as configuraes do
tarefas de Map Reduce fossem traduzidas para simples queries Hive, bem como fazer consultas nos dados armazenados.
em HiveQL. Na Listagem 1 demonstrado um comparativo entre Sendo esses trs componentes nossas portas de entrada, uma vez
uma query simples utilizando HiveQL e a mesma consulta sendo que um comando chega ao Hive, ele enviado ao componente Dri-
feita utilizando Hadoop puro, mostrando a diferena de comple- ver, denotado em azul na figura, que ser responsvel por fazer a
xidade entre as duas consultas. transformao da query em tarefas que o Hadoop possa entender.

Edio 127 Java Magazine 13

java127.indb 13 15/04/2014 11:15:38


Apache Hive: simplificando solues BigData

Instalao e configuraes iniciais


HiveQLQuery
Para iniciarmos nossa aplicao de exemplo, primeiramente
teremos que instalar o Hive Server em nossa mquina. Para tanto,
precisamos possuir o Hadoop e uma verso do JDK instalada em
nosso ambiente local.
Comeando pela instalao do Hadoop, para sua utilizao den-
JDBC/ODBC

tro do Apache Hive, no precisamos fazer muitas configuraes


Thrift Server/Hive Server Command Line Interface Web Interface
extras, sendo necessrio somente baixar o arquivo hadoop-2.2.0.tar
.gz (veja o endereo na seo Links) e descompactar o mesmo em
algum diretrio local. Uma vez descompactado, importante
configurar nossa varivel de ambiente HADOOP_HOME para
apontar para a pasta do Hadoop. Isto pode ser feito executando
HIVE o comando: export HADOOP_HOME= <diretrio do Hadoop>.
Terminado esses passos, teremos nosso Hadoop instalado na
Metadatastore verso default, ou seja, na verso de um nico n (local), ideal
para realizarmos nosso primeiro exemplo.
DRIVER (Compilador, Otimizador e executor)
Em relao ao JDK, caso ainda no possua uma verso instalada,
preciso instalar a 1.6 em sua mquina. O download da mesma
pode ser feito pelo prprio site da Oracle, onde tambm podem
ser encontradas instrues para sua instalao.
Com ambos os pr-requisitos completos, vamos iniciar a instalao
do Apache Hive. Para isso, deve-se realizar o download do arquivo
Job Tracker
hive-0.12.0.tar.gz pelo site da Apache (veja o endereo indicado na
seo Links) e descompact-lo em um diretrio local. Isso pode ser
Map Tasks
HADOOP Reduce Tasks feito executando o comando: tar -xzvf hive-0.12.0.tar.gz .
Com o arquivo descompactado, teremos que apontar nossa va-
Nodes
rivel de ambiente HIVE_HOME para o diretrio em que o Hive
foi instalado. O comando para realizar essa configurao : export
HIVE_HOME=<diretrio em que o Apache Hive foi descompactado>.
Figura 1. Fluxo de uma query dentro da arquitetura do Apache Hive
Assim, com as variveis de ambientes configuradas, precisamos
criar e dar as devidas permisses para as pastas em que o Hive
Primeiramente, com base nos arquivos de metadados, que pos- ir armazenar seus dados. Isso feito com a ajuda do Hadoop e
suem informaes como o nome das tabelas, campos e outros, o permitir que os diretrios que configuraremos sejam usados pelo
Hive executa a anlise e traduo da HiveQL enviada, verificando HDFS. Na Listagem 2 demonstramos quais so os comandos para
erros de sintaxe e validando se todos os elementos so vlidos. realizar essa configurao.
Quando esse processo termina, feito, logo em seguida, a compi-
lao das tarefas de Map/Reduce equivalentes HiveQL enviada,
Listagem 2. Criao e configurao do permissionamento para as pastas a serem
ou seja, feita a traduo desses comandos. utilizadas pelo Apache Hive.
Por fim, com as tarefas traduzidas, o Driver se comunica com o
#Tenha certeza que o diretrio /user/hive esteja criado
Hadoop e faz a execuo desses comandos, obtendo por final o #e as permisses estejam de acordo. #Caso na execuo do comando
resultado da HiveQL. #mkdir acontea uma exceo do diretrio j existir
Esse fluxo de execuo, juntamente com uma explicao dos #(em algumas distribuies do Linux a pasta /tmp j existe por default)
#o passo de criao pode ser ignorado, sendo somente necessrio
principais comandos da HiveQL, sero praticados no nosso #executar os comandos de permissionamento.
exemplo, que construiremos nas prximas pginas, atravs de um $HADOOP_HOME/bin/hadoop fs -mkdir /tmp
$HADOOP_HOME/bin/hadoop fs -mkdir /user/hive/warehouse
cliente Java que utilizar JDBC para se comunicar com o Hive e
$HADOOP_HOME/bin/hadoop fs -chmod g+w /tmp
manipular os dados l armazenados. $HADOOP_HOME/bin/hadoop fs -chmod g+w /user/hive/warehouse

Nota

Tanto o Apache Hive como o Apache Hadoop, que utilizaremos em nosso exemplo, no possuem Com essas modificaes feitas, podemos iniciar o Apache
compatibilidade com a plataforma Windows. Isto se deve, principalmente, ao fato de tanto os Hive. Para garantir que a instalao ocorreu com sucesso, va-
processos de Map/Reduce como o prprio princpio do HDFS se basearem na plataforma UNIX e mos, em primeiro lugar, acessar o mesmo pela linha de coman-
no terem sido desenvolvidos para outros ambientes. Devido a isso, todos os comandos e exemplos do e verificar se o Hive Console est funcionando corretamente
demonstrados a seguir foram baseados em um ambiente Linux. e se nenhum erro de configurao aconteceu.

14 Java Magazine Edio 127

java127.indb 14 15/04/2014 11:15:38


Para acessarmos o Hive console, digite o seguinte comando funcionalidades do Apache Hive. Como sugesto, indicamos que
no terminal: $HIVE_HOME/bin/hive. seja feito o download do arquivo ml-1m.zip do site de datasets
Caso o console seja executado com sucesso, podemos sair do do movielens (veja a seo Links) e que seja descompactado o
mesmo digitando o comando quit e iniciar o Hive Server. Para documento ratings.dat.
realizar essa inicializao, execute a seguinte linha no terminal: Esse arquivo possui como contedo um milho de pontuaes
$HIVE_HOME/bin/hive --service hiveserver. Com nosso Hive Server atribudas pelos usurios do site Movielens.com aos mais diversos
no ar, podemos dar incio criao da nossa aplicao Java, que filmes, sendo a sintaxe de seu texto a sequncia dos campos id
ir se conectar e manipular os dados dentro do Hive. de usurio, id do filme, pontuao e timestamp, separados pelos
caracteres ::.
Nota Uma vez a descompactao esteja concluda, precisamos executar
uma substituio dos divisores :: por :, podendo o mesmo ser
importante observar que as instalaes apresentadas nesse artigo, tanto do Hive como do
feito por qualquer editor de texto que possua a funcionalidade
Hadoop, so as instalaes default, ou seja, no modo local. Para realizar as instalaes do Hive e
de search/replace. Realizado essa substituio, nosso arquivo de
do Hadoop nos modos pseudo-distributed ou fully-distributed, ideais para simular e manipular
entrada para o Hive est pronto e podemos iniciar nosso exemplo
ambientes distribudos, possvel encontrar as instrues especficas no site oficial da Apache
em Java.
(veja a seo Links).

Conectando-se ao Apache Hive


Criando nosso exemplo em Java O primeiro passo para comearmos nosso desenvolvimento
Com a instalao do Hive completa, podemos dar incio cons- dentro do Java ser o de criar um conector a nosso Hive Server,
truo de nossa aplicao exemplo. O objetivo desse desenvolvi- que inicializamos aps a instalao do Apache Hive. Para fazer
mento ser mostrar os principais aspectos do Hive e as principais isso, utilizaremos o driver JDBC nativo do Hive que fornece, de
sintaxes do HiveQL colocadas em prtica, alm de demonstrar forma transparente, uma conexo ao Hive Server como se esse
como manipular essa ferramenta atravs de uma aplicao Java. fosse um banco de dados.
Ao final, seremos capazes de executar consultas, inserir dados e Na Listagem 4 criamos nossa classe HiveConnector, responsvel
manipular informaes dentro do Apache Hive atravs das tarefas por fornecer uma conexo vlida para a execuo de queries.
de Map/Reduce do Hadoop.
Listagem 4. Cdigo da classe HiveConnector.
Construindo nosso projeto
Para iniciarmos nosso exerccio, podemos criar um projeto public class HiveConnector {
private static String driverName = org.apache.hadoop.hive.jdbc.HiveDriver;
simples na IDE de sua escolha e, uma vez criado, adicionar os private static String defaulURL=jdbc:hive://localhost:10000/default;
JARs hive-jdbc-0.12.0.jar e hadoop-common-2.2.0.jar no classpath de
public static Connection getHiveConnection(String url) throws SQLException{
sua aplicao.
try {
As configuraes dessas bibliotecas tambm podem ser feitas Class.forName(driverName);
pelo Apache Maven, um gerenciador de dependncias autom- if(url==null || url.isEmpty()){
url=defaulURL;
tico, atravs da configurao mostrada na Listagem 3, dentro do
}
arquivo pom.xml. } catch (ClassNotFoundException e) {
e.printStackTrace();
}
Listagem 3. Dependncias do Hive e Hadoop. Connection con = DriverManager.getConnection(url, , );
return con;
<dependencies>
<dependency> }
<groupId>org.apache.hive</groupId> }
<artifactId>hive-jdbc</artifactId>
<version>0.12.0</version>
</dependency>
<dependency> A construo dessa classe bem simples e sua implementao se
<groupId>org.apache.hadoop</groupId> assemelha bastante implementao de uma conexo com qual-
<artifactId>hadoop-common</artifactId>
<version>2.2.0</version> quer banco de dados. Definimos duas variveis indicando nosso
</dependency> driver JDBC e nossa URL de conexo default ao Hive e utilizamos
</dependencies>
o DriverManager para estabelecer essa conexo, retornando a
mesma e terminando o mtodo getHiveConnection().
Alm da configurao de nossas dependncias, iremos ne-
cessitar, na execuo de nosso exemplo, de um arquivo para Criando e populando tabelas
utilizarmos como massa de dados inicial, armazenando seus Utilizando nosso conector recm-criado, agora teremos que
dados no HDFS e usando seu contedo para demonstrar algumas popular o Hive com um arquivo de dados iniciais.

Edio 127 Java Magazine 15

java127.indb 15 15/04/2014 11:15:38


Apache Hive: simplificando solues BigData

Esse processo composto pela criao das tabelas dentro do de clusters computacionais, e ORC, tipo de arquivo otimizado
Apache Hive, metadados que relacionam os arquivos armazena- para armazenamento de dados dentro do Hive. Cada um desses
dos no Hadoop com uma organizao semelhante de uma tabela tipos apresenta vantagens e desvantagens na sua utilizao, sendo
relacional, e a leitura das informaes, que sero armazenadas cada um mais adequado para situaes especficas. Fica a critrio
dentro dos diretrios do Hive num formato que definiremos do leitor experimentar esses diferentes tipos de armazenamento
adiante. de dados na tabela, sendo somente necessrio modificar o tre-
A Listagem 5 apresenta a classe HiveCreate, responsvel por cho STORED AS TEXTFILE para o tipo de dados que deseja
criar e armazenar os dados conforme descrevemos anterior- utilizar.
mente e por apresentar algumas variaes das funcionalidades Uma vez criada nossa tabela ratings, temos que carregar os
disponibilizadas pela HiveQL para a criao das tabelas dentro dados do arquivo ratings.dat para o Hive. Isto feito no segundo
de nosso Hive. comando executado, LOAD DATA LOCAL INPATH, onde
Vamos agora entender os mtodos descritos na classe Hive- passamos o caminho local de nosso arquivo para que ele seja
Create. O primeiro deles, denominado createRatingsTable(), carregado e escrito, ou sobrescrito, caso j existam dados, em
ser responsvel por, a partir de nosso arquivo ratings.dat que nossa tabela. necessrio, antes da execuo do mtodo, que
baixamos anteriormente, criar a tabela ratings, com os campos esse local de armazenamento do arquivo seja alterado de acordo
userid, itemid, rating e timestamp. A sintaxe de criao dessa tabela com o caminho em que o arquivo ratings.dat foi descompactado
bastante similar sintaxe de criao de uma tabela relacional em sua mquina.
utilizando a linguagem SQL tradicional, com a diferena que, em Nosso segundo mtodo, createRatingsBucketed(), ter a funo
nossa tabela do Hive, definimos que nosso delimitador de campos de criar uma segunda tabela, denominada ratings_bucketed.
o smbolo : e, tambm, especificamos o modo que os dados A definio de seus campos bastante semelhante da tabela
de nossas tabelas sero armazenados no HDFS. No nosso caso, ratings, porm utilizamos duas novas funcionalidades do Hive
definimos o armazenamento em formato de TEXTFILE, ou seja, em sua criao: PARTITION e BUCKETING.
um arquivo texto padro. O comando PARTITIONED BY (ds STRING), utilizado em
O Apache Hive tambm disponibiliza, para o armazenamento nossa HiveQL, tem a funo de criar um campo String que
de suas informaes, outros formatos, como o SEQUENCEFILE, servir para indicar os nomes (ou valores) das parties de
arquivo especfico do Hadoop em forma de chaves e valores, o nossa tabela e, consequentemente, dividir os dados inseridos
RCFILE, formato de arquivo que define tabelas relacionais dentro nessas parties.

Listagem 5. Cdigo da classe HiveCreate.

public class HiveCreate { Connection conn = HiveConnector.getHiveConnection(null);


Statement stmt = conn.createStatement();
//Mtodo responsvel por criar a nossa tabela de exemplo, //HiveQL utilizando os conceitos de particionamento e
//denominada ratings, na qual iremos executar nosso primeiro exemplo //clustering para criao da tabela ratings_bucketed
String query=CREATE TABLE ratings_bucketed (userid INT,
public void createRatingsTable() throws SQLException{ itemid INT, rating INT,timestamp STRING)
PARTITIONED BY (ds STRING) CLUSTERED BY
//Utilizamos a classe HiveConnector que criamos anteriormente (userid) INTO 5 BUCKETS;
//para criar uma conexo stmt.execute(query);
Connection conn = HiveConnector.getHiveConnection(null); query= set hive.enforce.bucketing = true;
Statement stmt = conn.createStatement(); System.out.println(Running: + query);
//Script em HiveQL para criao de nossa tabela ratings stmt.execute(query);
//a partir do arquivo ratings.dat //Insero dos dados a partir da nossa tabela ratings na
String query=CREATE TABLE ratings (userid INT, itemid INT, //partio 1 de nossa tabela ratings_bucketed
rating INT, timestamp STRING) ROW FORMAT DELIMITED FIELDS query= FROM ratings INSERT OVERWRITE TABLE ratings_bucketed
TERMINATED BY : STORED AS TEXTFILE; PARTITION (ds = 1) SELECT userid, itemid, rating,
stmt.execute(query); Timestamp WHERE rating >4;
//Insero dos dados a partir de nosso arquivo stmt.execute(query);
query= LOAD DATA LOCAL INPATH /home/brunno/ratings.dat
OVERWRITE INTO TABLE ratings; }
System.out.println(Running: + query);
stmt.execute(query); //Mtodo main() para criar ambas as tabelas
} public static void main(String[] args) throws Exception{
HiveCreate hiveCreate = new HiveCreate();
//Mtodo responsvel por criar a tabela ratings_bucketed, hiveCreate.createRatingsTable();
//que usaremos para demonstrar como criar uma tabela particionada hiveCreate.createRatingsBucketed();
//e com informaes em clusters }

public void createRatingsBucketed() throws SQLException{ }

16 Java Magazine Edio 127

java127.indb 16 15/04/2014 11:15:40


Para contextualizar um pouco mais esse conceito, podemos dar anteriormente. Nesse passo de popular a tabela, podemos obser-
um exemplo de uma tabela particionada em dois, uma partio var dois comandos importantes. O primeiro o set hive.enforce
com o campo ds = 1 e outra com o campo ds = 2. Ao definirmos .bucketing = true, que faz com que o Hive faa o agrupamento
uma partio, qualquer consulta subsequente na tabela ser dire- dos dados que iremos inserir. O segundo onde definimos o nome
cionada para a partio correta e, consequentemente, a quantidade de nossa partio de insero para 1, fazendo com que o Hive
de dados e o tempo de busca sero bastante menores, aumentando crie essa partio caso ela no exista, no comando PARTITION
assim a performance no geral. (ds = 1).
Continuando o desenvolvimento de nosso mtodo, no comando Com a construo dos mtodos createRatingsTable() e createRa-
de construo de nossa segunda tabela, implementamos o concei- tingsBucketed() finalizada, podemos rodar nosso mtodo main(),
to de BUCKETING, talvez um dos mais importantes dentro do que ir criar e popular nossas tabelas ratings e ratings_bucketed
Hive. Ao definirmos em nossa tabela o comando CLUSTERED atravs dos mtodos desenvolvidos anteriormente.
BY (userid) INTO 5 BUCKETS, estamos dizendo ao Apache Hive
para, a partir de todos os dados de entrada, agrupar as entradas Criando consultas no Hive
com o campo userid semelhantes e armazenar esses agrupamen- Com nossa base de dados j alimentada aps a execuo do
tos em cinco clusters (buckets) diferentes em seu HDFS. Portanto, mtodo main(), podemos iniciar a construo de queries para
podemos imaginar que, se nosso userid variar de 1 a 5, teremos manipular essas informaes. A consulta no Hive, assim como a
todos os dados com userid=1 no cluster 1, todos com o userid=2 criao das tabelas, realizada atravs da linguagem HiveQL uti-
no cluster 2 e assim por diante. Entenderemos mais tarde, quan- lizando instrues bastante similares a um SQL tradicional. Uma
do criarmos nossas consultas dentro do Hive, como a criao de vez que esses comandos esto prontos para serem processados
buckets pode auxiliar na distribuio das tarefas de Map/Reduce em nosso Hive Server, eles so traduzidos pelo Hive, com base
dentro do Hadoop. nos metadados construdos na definio das tabelas.
Terminamos nosso mtodo populando nossa tabela ratings_bu- Na Listagem 6 exposto o cdigo da classe HiveSelect, na qual
cketed a partir de uma consulta na tabela ratings que criamos criamos quatro consultas com o objetivo de filtrar nosso contedo

Edio 127 Java Magazine 17

java127.indb 17 15/04/2014 11:15:43


Apache Hive: simplificando solues BigData

armazenado no HDFS e administrado pelo Hive, mostrando Iniciamos esse select definindo que nossa partio ter o valor
algumas funcionalidades teis que o Hive nos proporciona e do campo ds = 1, ou seja, a partio que especificamos na cria-
apresentando a sintaxe necessria para poder filtrar e visualizar o da tabela, e continuamos com a consulta normalmente. Uma
as informaes do HDFS. consulta em uma tabela clusterizada apresenta vrias vantagens
Iniciamos nossa classe HiveSelect declarando um mtodo sim- em relao a uma tabela normal, principalmente quando partimos
ples, que ir criar uma consulta para verificar a quantidade de de uma configurao do Hive de forma distribuda. Isto se deve
dados dentro de nossa tabela ratings. A sintaxe nessa primeira porque, em um ambiente distribudo, o Apache Hive distribui suas
query idntica de um SQL tradicional utilizado em bancos tarefas de Map/Reduce pelos buckets configurados, ficando mais
de dados relacionais, fazendo um SELECT com a contagem de eficiente a consulta paralelizada. Outro ponto que tambm vale
dados de retorno. Como resultado de nossa consulta, o mtodo destacar que, em consultas onde o campo de filtro o mesmo da
retorna um ResultSet igual ao retornado por qualquer consulta clusterizao da tabela, no nosso caso o campo userid, o tempo
JDBC, podendo esse ser manipulado da mesma maneira que um de processamento bem menor, uma vez que o Hive j sabe em
ResultSet de um banco de dados tradicional. qual cluster deve procurar.
O nosso segundo mtodo, simpleExplainSelect(), j utiliza a Por ltimo, utilizamos o mtodo selectWithJoin() para demons-
funcionalidade do Hive denominada explain. Sua funo de, trar como aplicar uma operao de join em duas tabelas do Hive.
a partir de uma query em HiveQL, mostrar quais as aes que o Neste caso, utilizamos a tabela ratings e ratings_bucketed, fina-
Hive executou dentro do Hadoop para fazer a consulta, retornando lizando assim a nossa aplicao de exemplo. Para executarmos as
em seu ResultSet uma explicao de todas as tarefas de Map/ consultas criadas na classe HiveSelect podemos rodar o mtodo
Reduce executadas sobre nossos arquivos. Em nosso exemplo, main() implementado no final da Listagem 6.
usamos a query do primeiro mtodo, simpleSelect(), e colocamos
o comando explain no incio da mesma, indicando que esperamos Limitaes e consideraes importantes
a explicao dos comandos executados. Uma vez apresentadas as principais funcionalidades do Apache
No prximo mtodo, selectWithBucketedTable(), criamos um Hive e seu funcionamento em geral, precisamos ressaltar alguns
select na tabela particionada e clusterizada ratings_bucketed. pontos para que os desenvolvedores e leitores possam melhor

Listagem 6. Cdigo da classe HiveSelect.

public class HiveSelect { System.out.println(Query Bucketed!);


while(executeQuery.next()){
//Query simples em nossa tabela ratings que traz a contagem System.out.println(###############Registro#################);
//de informao (linhas) armazenada no HDFS. for(int i = 1; i<5; i++){
public void simpleSelect() throws SQLException{ System.out.println(executeQuery.getObject(i));
Connection conn= HiveConnector.getHiveConnection(null); }
Statement statement = conn.createStatement(); }
String query = select count(1) from ratings; }
ResultSet executeQuery = statement.executeQuery(query);
while(executeQuery.next()){ //Demonstrao de um select com join
System.out.println(O Hive tem +executeQuery.getInt(1)+ public void selectWithJoin() throws SQLException{
registros!); Connection conn= HiveConnector.getHiveConnection(null);
} Statement statement = conn.createStatement();
} String query = SELECT r.* FROM ratings r JOIN ratings_bucketed b ON
(r.userid = b.userid);
//Mtodo responsvel por demonstrar a funcionalidade explain do Hive ResultSet executeQuery = statement.executeQuery(query);
public void simpleExplainSelect() throws SQLException{ System.out.println(Join query!);
Connection conn= HiveConnector.getHiveConnection(null); while(executeQuery.next()){
Statement statement = conn.createStatement(); System.out.println(###############Registro#################);
String query = explain select count(1) from ratings; for(int i = 1; i<5; i++){
ResultSet executeQuery = statement.executeQuery(query); System.out.println(executeQuery.getObject(i));
System.out.println(Explicando a query!); }
while(executeQuery.next()){ }
System.out.println(executeQuery.getObject(1).toString()); }
}
} public static void main(String[] args) throws Exception(){
HiveSelect hiveSelect = new HiveSelect();
//Query em nossa tabela particionada e clusterizada hiveSelect.simpleSelect();
public void selectWithBucketedTable() throws SQLException{ hiveSelect.simpleExplainSelect();
Connection conn= HiveConnector.getHiveConnection(null); hiveSelect.selectWithBucketedTable();
Statement statement = conn.createStatement(); hiveSelect.selectWithJoin();
String query = select * from ratings_bucketed where ds = \1\ }
and userid=2;
ResultSet executeQuery = statement.executeQuery(query); }

18 Java Magazine Edio 127

java127.indb 18 15/04/2014 11:15:43


escolher se a implementao do Apache Hive realmente a opo ambientes, deixando a cargo do Hive e do Hadoop a distribuio
adequada para seus projetos. e construo das tarefas necessrias para a execuo.
O primeiro ponto que o Apache Hive, apesar de utilizar uma Essa facilidade, por sua vez, fez com que o Apache Hive fosse
linguagem muito semelhante ao SQL, no um banco de dados. escolhido e utilizado por diversas empresas de tecnologia, como
natural o leitor pensar que est manipulando tabelas relacionais o Facebook, Grooveshark e Netflix, que viram nessa ferramenta
pela familiaridade que o JDBC traz em sua API, mas na realidade uma possibilidade de trazer a familiaridade do SQL ao mundo
o Hive est executando tarefas de Map/Reduce em arquivos fsicos do Big Data.
no HDFS do Hadoop por trs de cada comando executado. Vale O Apache Hive , portanto, a escolha certa em projetos nos
lembrar que a estrutura de tabelas no existe fisicamente e, sim, quais a necessidade do processamento de grandes quantidades
somente nos arquivos de metadados. de informao seja crucial, atravs de um ambiente clusterizado,
Outro ponto no qual o Hive se diferencia de um banco de dados e onde no queremos perder a familiaridade de uma linguagem
est no fato dele no suportar nativamente operaes de update e j conhecida pelos profissionais do mercado.
delete, funcionando somente em processos batch, da mesma forma Por esses e outros motivos, o Hive se mostra uma ferramenta
que o Hadoop. Para realizar tais operaes, existem ferramentas open source de alto potencial nos prximos anos e de importncia
e outras solues complementares que, juntamente com o Hive, fundamental no campo de Big Data, sendo um dos nomes mais
possibilitam uma alterao dos dados em tempo real no HDFS. comentados entre as aplicaes modernas, tanto em ambientes
Por fim, talvez a considerao mais importante, que o principal corporativos como dentro das grandes universidades.
benefcio do Apache Hive sua alta potencialidade de escalabili-
dade, trazida atravs do Hadoop, e a facilidade de implementao Links:
em ambientes distribudos, sendo extremamente eficaz no trata-
mento de informaes em grandes clusters. Em contrapartida, em Link contendo as distribuies para download do Apache Hive.
ambientes com poucas ou apenas uma mquina, os processos de http://www.apache.org/dyn/closer.cgi/hive/
Map/Reduce no so nada eficazes e acabam por deixar a desejar
Link contendo as distribuies para download do Apache Hadoop.
no quesito de performance. http://www.apache.org/dyn/closer.cgi/hadoop/common/
Com base no contedo terico e prtico apresentado, fcil
observar a familiaridade que o Apache Hive trouxe em seus Apache Hive Installation Guide.
comandos, simplificando a manipulao das funcionalidades http://hive.apache.org/#Getting+Started
do Hadoop. Atravs de sua sintaxe semelhante ao SQL, pode- Apache Hadoop Installation Guide (Cluster).
mos fazer consultas em nossos dados distribudos em diversos http://hadoop.apache.org/docs/r1.2.1/cluster_setup.html

Dataset de dados do site grouplens, utilizado como base de dados inicial.


http://grouplens.org/datasets/movielens/
Autor
Brunno F. M. Attorre
Voc gostou deste artigo?
brattorre@gmail.com
Trabalha com Java h trs anos. Apaixonado por temas como
Inteligncia Artificial, ferramentas open source, BI, Data Analysis D seu voto em www.devmedia.com.br/javamagazine/feedback
e Big Data, est sempre procura de novidades tecnolgicas na rea. Possui Ajude-nos a manter a qualidade da revista!
as certificaes OCJP, OCWCD e OCEEJBD.

Edio 127 Java Magazine 19

java127.indb 19 15/04/2014 11:15:44


Spring Framework:
as novidades do Spring 4
Conhea a nova verso de um dos frameworks
Java mais populares

C Fique por dentro


om 10 anos de existncia, o Spring ainda um
dos mais importantes e mais usados frameworks
do ecossistema Java. Criado por Rod Johnson, e Este artigo visa apresentar a nova verso do framework Spring, um dos
amplamente divulgado atravs dos livros de sua autoria, o mais populares e completos presentes no ecossistema Java. Durante o
Spring veio com o objetivo de facilitar o desenvolvimento artigo ser abordada tanto uma viso geral de seus novos recursos quanto
e deployment de aplicaes Java enterprise e rapidamente uma viso mais detalhada de alguns deles.
ganhou popularidade entre a comunidade e as empresas. Este estudo ajudar tanto os leitores que j esto familiarizados com
A introduo de conceitos e facilidades inexistentes at o framework Spring e querem saber o que essa verso est trazendo de
ento na plataforma Java EE o transformou na soluo novo quanto os que ainda no o conhecem na prtica e querem ter um
preferida a ser adotada em novos projetos. primeiro contato.
Com o passar do tempo, muitas das suas funcionali-
dades passaram a fazer parte da especificao Java EE,
como injeo de dependncia e inverso de controle. spring-data, spring-mvc, spring-social, entre outros. O projeto
Ao mesmo tempo, novos recursos do Java tambm Spring cresceu tanto com o passar do tempo que atualmente
ajudaram em sua evoluo, como por exemplo, o uso ele possui algumas dezenas de subprojetos, o que torna o um
de Annotations ao invs dos seus longos arquivos XML verdadeiro canivete suo.
de configurao. Desde a criao do Spring, sempre
existiu muita discusso (algumas at mais acaloradas) Spring Framework 4
comparando-o com a Java EE e qual papel cada um A verso 4 foi lanada recentemente, mais precisamente em de-
desses elementos desempenharia no desenvolvimento zembro do ano passado, e trouxe consigo um conjunto de novas
de aplicaes Java enterprise. funcionalidades e de melhorias visando suportar o Java 8 e a Java
O que parece ser mais razovel que de incio o Spring EE 7. Durante esse artigo vamos dar um panorama geral sobre esses
realmente atuava como um substituto s tecnologias novos recursos e mostrar na prtica como utilizar alguns deles.
padres da Java EE, como entity beans e session beans. Dentre algumas das novas funcionalidades liberadas nessa
verdade, porm, que muito de seu sucesso relaciona- verso, podemos citar:
do com o surgimento do Hibernate, que atuou e ainda Suporte completo ao Java 8: Disponibiliza recursos como
atua como seu grande parceiro em muitos projetos. expresses lambda e Date and Time API. Vale ressaltar que a
No entanto, com o passar do tempo e com a evoluo verso mnima do Java suportada pelo Spring 4 passa a ser JDK
da especificao Java EE, esses frameworks tambm 6u10, porm recomendado o uso da verso 7 ou 8;
passaram a ser vistos como ferramentas que podem ser Java EE 7: Suporta novas especificaes introduzidas na Java
usadas em conjunto com a especificao, e no somente EE 7, tais como JMS 2.0, JTA 1.2, JPA 2.1, Bean Validation 1.1 e
como substitutos. Afinal de contas, muitas das funciona- Concurrency Utilities;
lidades do Spring so baseadas em tecnologias padres, Groovy: possvel configurar o Spring e seus beans usando
como JNDI, JMS e JTA. O prprio Hibernate passou a ser Groovy atravs de arquivos externos. Parecido com a ideia inicial
uma implementao da especificao do JPA. de configurao via XML, mas usando uma linguagem mais bem
Mas o Spring no parou por a. Suas verses sempre definida para esse propsito;
trouxeram novos projetos visando novas funcio- Core: Melhorias gerais, tais como adio de novas anotaes
nalidades e facilidades, como o spring-integration, como, por exemplo, @Ordered, @Description e @Conditional;

20 Java Magazine Edio 127

java127.indb 20 15/04/2014 11:15:44


Web: Algumas novas funcionalidades so a adio da classe cdigo, o arquivo pom.xml deve ser alterado para que fique como
AsyncRestTemplate (que permite a criao de clientes REST no- na Listagem 1. Este arquivo define o Spring 4 como dependncia
bloqueantes), compatibilidade com a API de WebSocket e adio do projeto e configura o Maven para utilizar o Java 8 para compil-
da anotao @RestController; lo. Estamos usando o Java 8 porque uma das funcionalidades a
Documentao: Criao de um novo website com a disponibi- serem apresentadas nesse artigo justamente a integrao do
lizao de vrios guias que ensinam a trabalhar com o Spring e Spring com essa nova verso do Java.
seus mdulos.
Listagem 1. Contedo do arquivo pom.xml.
Aps essa anlise superficial de algumas das novas funcionali-
<project xmlns=http://maven.apache.org/POM/4.0.0
dades disponibilizadas no Spring 4, vamos agora tratar algumas xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
delas com mais profundidade atravs de exemplos. Como alguns xsi:schemaLocation=http://maven.apache.org/POM/4.0.0
dos exemplos requerem um servidor de aplicaes compatvel com http://maven.apache.org/xsd/maven-4.0.0.xsd>
<modelVersion>4.0.0</modelVersion>
a Java EE 7, iremos usar o WildFly para esse propsito. <groupId>br.com.javamagazine</groupId>
<artifactId>spring4-demo</artifactId>
O servidor WildFly <version>1.0.0</version>
<packaging>war</packaging>
O WildFly nada mais que a continuao do servidor de aplicaes
JBoss, porm com outro nome. Essa mudana ocorreu principalmente <properties>
para evitar uma confuso de nomes, j que a Red Hat tambm tem <spring.version>4.0.1.RELEASE</spring.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
uma soluo chamada JBoss Enterprise Application Platform (JBoss </properties>
EAP), que composta pelo prprio servidor de aplicaes e vrios
<build>
outros produtos, como Infinispan, Red Hat Linux, entre outros.
<finalName>${project.artifactId}</finalName>
A verso do WildFly usada nesse artigo a 8.0.0.Final, que foi lanada <plugins>
em meados de fevereiro e j certificada Java EE 7. <plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
Instalao <source>1.8</source>
O WildFly 8 possui a mesma estrutura do JBoss AS 7, portanto <target>1.8</target>
</configuration>
tanto a instalao quanto a operao so bem similares. Aps </plugin>
fazer o download do servidor, basta descompact-lo e definir a </plugins>
varivel de ambiente JBOSS_HOME, que deve apontar para o </build>

diretrio no qual o servidor foi descompactado. Apesar do nome <dependencies>


do servidor ter mudado, a varivel de ambiente foi mantida com o <dependency>
nome JBOSS_HOME, pelo menos por enquanto. Para inicializar <groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
o servidor, basta executar o script JBOSS_HOME/bin/standalone <version>${spring.version}</version>
.sh (ou .bat caso esteja no Windows). </dependency>
<dependency>
<groupId>org.springframework</groupId>
Explorando o Spring 4 com exemplos prticos <artifactId>spring-web</artifactId>
A demonstrao de alguns dos recursos do Spring 4 ser feita <version>${spring.version}</version>
</dependency>
atravs de um projeto que ser baseado no Maven e no Eclipse.
<dependency>
O objetivo desse projeto no criar uma aplicao completa, e <groupId>org.springframework</groupId>
sim permitir que o leitor veja algumas das novas funcionalida- <artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
des do Spring na prtica. Para a criao do projeto no Eclipse, v </dependency>
at o menu File | New > Other, digite Maven no campo Wizard, <dependency>
selecione a opo Maven Project e clique em Next. Na prxima <groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
tela, marque a opo Create a simple project (skip archetype location) <version>${spring.version}</version>
e clique em Next. Nessa tela, os campos devem ser preenchidos </dependency>
com as seguintes informaes: </dependencies>
</project>
Group Id: br.com.javamagazine;
Artifact Id: spring4-demo;
Version: 1.0.0; Configurao inicial do Spring
Packaging: war. A configurao do Spring no projeto ser feita de maneira evo-
lutiva, medida que os exemplos forem sendo explorados. Porm,
Para finalizar a criao, clique em Finish. Para que o projeto tenha j iremos abordar a configurao bsica do framework. Para
acesso s bibliotecas necessrias para compilao e execuo do isso, o primeiro passo configurar o arquivo web.xml dentro de

Edio 127 Java Magazine 21

java127.indb 21 15/04/2014 11:15:44


Spring Framework: as novidades do Spring 4

src/main/webapp/WEB-INF, conforme apresentado na Listagem 2. aguardar alguns milissegundos e logar outra mensagem. O cdigo
Essa configurao responsvel por fazer a inicializao do dessa classe pode ser visto na Listagem 4.
servlet do Spring. Alm disso, vamos criar uma classe anotada com @Controller
Esse arquivo de configurao define um servlet com o nome no Spring MVC, para que possamos acess-la via uma URL.
mvc-dispatcher, cuja classe a DispatcherServlet, e que deve ser O cdigo dessa classe mostrado na Listagem 5.
inicializado juntamente com a aplicao. O spring-mvc segue um
padro de nomenclatura em que seu arquivo de configurao Listagem 4. Cdigo da classe SimpleTask.
deve se chamar <servletname>-servlet.xml, o que no nosso caso
package br.com.javamagazine.spring4demo.concurrency;
mvc-dispatcher-servlet.xml. Sendo assim, crie um arquivo com esse
nome na mesma pasta WEB-INF e com o contedo apresentado import java.util.logging.Logger;
na Listagem 3.
public class SimpleTask implements Runnable {

Listagem 2. Configurao inicial do arquivo web.xml. private final int index;

<web-app xmlns=http://xmlns.jcp.org/xml/ns/javaee
private Logger logger = Logger.getLogger(SimpleTask.class.getName());
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xsi:schemaLocation=http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd public SimpleTask(int index) {
version=3.1> this.index = index;
}
<servlet>
<servlet-name>mvc-dispatcher</servlet-name> @Override
<servlet-class>org.springframework.web.servlet.DispatcherServlet public void run() {
</servlet-class> logger.info(Starting task + index);
<load-on-startup>1</load-on-startup> try {
</servlet>
Thread.sleep(50);
} catch (InterruptedException e) {
<servlet-mapping>
e.printStackTrace();
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/*</url-pattern> }
</servlet-mapping> logger.info(Finalizing task + index);
}
</web-app>
}
Listagem 3. Configurao do arquivo mvc-dispatcher-servlet.xml.
Listagem 5. Cdigo da classe TaskStarter.
<?xml version=1.0 encoding=UTF-8?>
<beans //schemas omitidos> package br.com.javamagazine.spring4demo.concurrency;

<context:component-scan base-package=br.com.javamagazine.spring4demo /> import java.util.logging.Logger;


import org.springframework.beans.factory.annotation.Autowired;
<mvc:annotation-driven />
import org.springframework.scheduling.concurrent.DefaultManagedTaskExecu-

</beans> tor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

O que foi definido nesse arquivo que iremos trabalhar com @Controller
anotaes e que o pacote base no qual o Spring deve procurar por public class TaskStarter {
seus beans o br.com.javamagazine.spring4demo.
@Autowired
private DefaultManagedTaskExecutor taskExecutor;
Concurrency Utilities
O primeiro recurso do Spring 4 que vamos analisar a sua private Logger logger = Logger.getLogger(TaskStarter.class.getName());
integrao com a JSR-236 (Concurrency Utilities). O Spring j
@RequestMapping(value = /startTask, method = RequestMethod.GET)
oferecia recursos de agendamento e execuo de tarefas assn-
public @ResponseBody void start() {
cronas atravs da integrao com o framework Quartz e com as logger.info(Starting tasks);
classes do pacote java.util.concurrent, tais como ExecutorService for(int i = 1; i <= 5; i++) {
e ScheduledExecutorService. Uma vez que as classes da especi- taskExecutor.execute(new SimpleTask(i));
ficao da Java EE estendem essas do Java SE, a estrutura entre }
}
elas bem parecida.

Para exemplificar essa funcionalidade, vamos fazer um demo }
que tem uma tarefa cujo nico papel logar uma mensagem,

22 Java Magazine Edio 127

java127.indb 22 15/04/2014 11:15:45


A anotao @Controller faz com que o Spring descubra automa- Com isso, todas as partes envolvidas no exemplo esto confi-
ticamente que essa classe um bean gerenciado e que ela pode guradas e o mesmo est completo. Para coloc-lo para funcionar,
ser acessada via HTTP. A varivel taskExecutor injetada auto- execute os seguintes passos:
maticamente pelo Spring, sendo que essa a classe responsvel 1. Compile o projeto atravs do comando mvn clean install, ou pelo
pela execuo de tarefas assncronas. Na sequncia veremos de plugin do Eclipse, se preferir;
que lugar o Spring est buscando essa instncia. 2. Reinicie o servidor de aplicao para que as mudanas que
O mtodo start() responsvel por tratar uma requisio HTTP fizemos no arquivo standalone.xml sejam aplicadas;
GET na URL /startTask. A anotao @ResponseBody indica que 3. Copie o arquivo spring4-demo.war gerado no passo 1 para
esse mtodo vai devolver seu contedo diretamente na resposta JBOSS_HOME/standalone/deployments.
do browser, ao invs de direcionar para alguma pgina. Como
no nosso caso apenas queremos iniciar o processo e no estamos Depois de realizar o deploy, basta acessar a URL http://
interessados em nenhum tipo de retorno, o mtodo est marcado localhost:8080/spring4-demo/startTask para iniciar a execuo das
para devolver void. O contedo do mtodo simplesmente cria cin- tarefas. Na sada do log, possvel ver as mensagens sobre a exe-
co instncias da tarefa SimpleTask e solicita que o taskExecutor cuo das tasks, conforme apresentado na Listagem 8.
as execute de maneira assncrona, o que significa que cada uma
dessas tarefas ser executada por uma das Threads do pool ou Listagem 7. Contedo a ser adicionado em mvc-dispatcher-servlet.xml.
ficar aguardando em uma fila caso no haja nenhuma disponvel,
<bean id=taskExecutor
no travando a execuo do loop.
class=org.springframework.scheduling.concurrent.DefaultManagedTaskExecutor>
Antes de configurarmos o Spring para prover esse pool de Thre- <property name=jndiName
ads, preciso definir esse pool no WildFly, uma vez que de l value=java:jboss/ee/concurrency/executor/jmManagedExecutorService />
</bean>
que o Spring ir obter as configuraes, tais como a quantidade
de Threads e o tamanho da fila de tarefas aguardando por uma Listagem 8. Log do WildFly com mensagens de execuo das tasks.
Thread livre no pool. Para tal, abra o arquivo JBOSS_HOME/
18:30:42,360 INFO (EE-ManagedExecutorService-jm-Thread-1) Starting task 1
standalone/configuration/standalone.xml, v at a seo <managed- 18:30:42,360 INFO (EE-ManagedExecutorService-jm-Thread-3) Starting task 3
executor-services> e crie um novo managed-executor-service cha- 18:30:42,360 INFO (EE-ManagedExecutorService-jm-Thread-2) Starting task 2
mado jm, conforme a Listagem 6. 18:30:42,361 INFO (EE-ManagedExecutorService-jm-Thread-4) Starting task 4
18:30:42,378 INFO (EE-ManagedExecutorService-jm-Thread-5) Starting task 5
18:30:42,425 INFO (EE-ManagedExecutorService-jm-Thread-1) Finalizing task 1
18:30:42,425 INFO (EE-ManagedExecutorService-jm-Thread-3) Finalizing task 3
Listagem 6. Configurao do managed-executor-service no standalone.xml.
18:30:42,425 INFO (EE-ManagedExecutorService-jm-Thread-2) Finalizing task 2
18:30:42,426 INFO (EE-ManagedExecutorService-jm-Thread-4) Finalizing task 4
..
18:30:42,431 INFO (EE-ManagedExecutorService-jm-Thread-5) Finalizing task 5
<managed-executor-services>
<managed-executor-service name=default
jndi-name=java:jboss/ee/concurrency/executor/default
context-service=default hung-task-threshold=60000 Pelas mensagens possvel notar que todas as tarefas foram
core-threads=5 max-threads=25 keepalive-time=5000/>
<managed-executor-service name=jm
executadas de forma paralela. Para verificar que o pool de Threads
jndi-name=java:jboss/ee/concurrency/executor/jmManagedExecutorService definido no WildFly est realmente sendo usado, altere a proprie-
context-service=default hung-task-threshold=60000 core-threads=5 dade core-threads que configuramos no arquivo standalone.xml
keepalive-time=5000 queue-length=10/>
</managed-executor-services>
para o valor 3. Em seguida, reinicie o servidor e acesse novamente
... a URL http://localhost:8080/spring4-demo/startTask. A sada do log
exibida na Listagem 9 mostra que somente trs tarefas foram
inicializadas, enquanto que as outras duas esperaram na fila at
As principais propriedades definidas so a core-threads, que que alguma Thread do pool fosse liberada.
define a quantidade de threads do pool, e queue-length, que
define o tamanho da fila de tarefas aguardando uma Thread para Listagem 9. Log do WildFly com mensagens de execuo das tasks.
execut-las. Alm disso, a propriedade jndi-name ser usada pelo
Spring para localizar esse recurso. 18:34:12,577 INFO (EE-ManagedExecutorService-jm-Thread-1) Starting task 1
18:34:12,577 INFO (EE-ManagedExecutorService-jm-Thread-2) Starting task 2
Vale ressaltar que algumas das configuraes que o WildFly
18:34:12,588 INFO (EE-ManagedExecutorService-jm-Thread-3) Starting task 3
prov por meio desse arquivo so lidas em tempo de execuo, 18:34:12,635 INFO (EE-ManagedExecutorService-jm-Thread-1) Finalizing task 1
como por exemplo, a configurao de logs, enquanto que outras 18:34:12,636 INFO (EE-ManagedExecutorService-jm-Thread-1) Starting task 4
exigem que o servidor seja reinicializado, como o caso da seo 18:34:12,636 INFO (EE-ManagedExecutorService-jm-Thread-2) Finalizing task 2
18:34:12,637 INFO (EE-ManagedExecutorService-jm-Thread-2) Starting task 5
que acabamos de alterar.
18:34:12,639 INFO (EE-ManagedExecutorService-jm-Thread-3) Finalizing task 3
Agora, para configurar o Spring, basta adicionar o seguinte 18:34:12,686 INFO (EE-ManagedExecutorService-jm-Thread-1) Finalizing task 4
contedo no arquivo mvc-dispacther-servlet.xml, como demonstra 18:34:12,687 INFO (EE-ManagedExecutorService-jm-Thread-2) Finalizing task 5
a Listagem 7.

Edio 127 Java Magazine 23

java127.indb 23 15/04/2014 11:15:45


Spring Framework: as novidades do Spring 4

Isso demonstra que o Spring realmente est usando o pool cria- Agora que temos um dataSource e um script usado para po-
do pelo WildFly e a especificao de Concurrency Utilities. Essa pular a tabela customer, podemos criar uma classe DAO para
abordagem traz duas grandes vantagens em relao forma como prover servios de acesso a esses dados, conforme apresentado
o pool de threads era configurado anteriormente no Spring: na Listagem 12.
1. A configurao agora fica no servidor de aplicao, facilitando
o dimensionamento do pool em tempo de deployment. Para efeito Listagem 10. Contedo a ser adicionado no arquivo mvc-dispatcher-servlet.xml.
comparativo, at ento essa configurao era feita diretamente no
<bean id=dataSource class=org.springframework.jndi.JndiObjectFactoryBean>
arquivo de configurao do Spring, conforme apresenta o cdigo: <property name=jndiName value=java:jboss/datasources/ExampleDS />
</bean>
<bean id=taskExecutor class=org.springframework.scheduling.concurrent.
<jdbc:initialize-database data-source=dataSource>
Thread-PoolTaskExecutor>
<jdbc:script location=classpath:/initDB.sql/>
<property name=corePoolSize value=5 /> </jdbc:initialize-database>
<property name=queueCapacity value=10 />
Listagem 11. Cdigo do script initDB.sql.
</bean>
DROP TABLE customer IF EXISTS;
2. A criao e gerenciamento das Threads passa a ser de respon-
CREATE TABLE customer (
sabilidade do container, o que o grande propsito da especifi- id INT PRIMARY KEY,
cao. name VARCHAR(60)
);
Apesar dessas vantagens, vale ressaltar que o Spring ainda
INSERT INTO customer VALUES(1, John);
pode ser configurado da maneira antiga, o que til em ambien- INSERT INTO customer VALUES(2, Mary);
tes em que um servidor compatvel com a Java EE 7 no esteja INSERT INTO customer VALUES(3, Peter);
disponvel.
Listagem 12. Cdigo da classe CustomerDAO.

RestController package br.com.javamagazine.spring4demo.rest;


O Spring sempre deu bastante ateno a funcionalidades de
//imports omitidos
exposio de web services, sejam eles SOAP, ou, principalmente,
REST. Seguindo essa linha de raciocnio, a verso 4 trouxe mais @Repository
public class CustomerDAO {
uma melhoria com o objetivo de facilitar a criao de web services
REST, a anotao @RestController. private JdbcTemplate jdbcTemplate;
Antes do Spring 4, para expor servios REST, era necessrio
@Autowired
anotar a classe com @Controller e a prpria classe ou os mtodos public void setDataSource(DataSource dataSource) {
com @ResponseBody. A anotao @Controller necessria para this.jdbcTemplate = new JdbcTemplate(dataSource);
que o Spring reconhea essa classe como uma classe que est rece- }

bendo requisies HTTP e @ResponseBody indica que o retorno public List<Customer> findAll() {
do mtodo deve ser usado diretamente no response do HTTP. Essa return jdbcTemplate.query(Select id, name from customer,
ltima anotao requerida porque por padro os mtodos de um new CustomerMapper());
}
Controller retornam o nome da pgina que ser exibida.
Antes de prosseguirmos com a criao dos servios REST, vamos public Customer findById(int id) {
try {
criar uma tabela no banco de dados e usar o Spring para acess-la,
return jdbcTemplate.queryForObject(Select id, name from
atravs de uma simples classe DAO. Esta classe, alm de ser usada customer where id = ?, new CustomerMapper(), id);
pelos servios REST para que estes possam acessar o banco de da- } catch (EmptyResultDataAccessException e) {
return null;
dos, tambm servir de base para o exemplo de suporte do Spring 4 }
ao Java 8. Dito isso, o primeiro passo adicionar o contedo da }
Listagem 10 no arquivo mvc-dispatcher-servlet.xml.
private static final class CustomerMapper implements RowMapper<Customer> {
Ao invs de criarmos um DataSource, no exemplo utilizamos
um que provido por padro pelo WildFly. Ele usa um banco @Override
de dados embarcado, que apesar de no ser recomendado para public Customer mapRow(ResultSet rs, int pos) throws SQLException {
return new Customer(rs.getInt(id), rs.getString(name));
ambientes de produo, atende perfeitamente o nosso cenrio }
de teste. Continuando com a configurao do Spring, a tag
jdbc:initialize-database executa o script initDB.sql (Listagem 11), }

criado dentro de src/main/resources, para carregar alguns registros }


no banco de dados.

24 Java Magazine Edio 127

java127.indb 24 15/04/2014 11:15:45


Essa classe bem simples e fornece dois mtodos de consulta, Listagem 14. Cdigo da classe CustomerWS para a verso Spring 3.
sendo um para acessar todos os registros da tabela e outro para
package br.com.javamagazine.spring4demo.rest;
fazer a busca baseada no id do cliente. A interface JDBCTempla-
te traz uma API bastante simplificada tanto para a execuo de import java.util.List;
consultas quanto de comandos de modificao (Insert, Update e
import org.springframework.beans.factory.annotation.Autowired;
Delete). No caso da execuo de consultas, podemos criar uma import org.springframework.stereotype.Controller;
implementao da interface RowMapper que responsvel por import org.springframework.web.bind.annotation.*;
fazer o mapeamento do ResultSet retornado para o objeto de
@Controller
domnio desejado, exatamente como foi feito com a classe interna @RequestMapping(/rest)
CustomerMapper, que converte o ResultSet para um Customer @ResponseBody
(Listagem 13). Vale ressaltar que os recursos usados por essa public class CustomerWS {

classe j fazem parte do Spring 3.x, no sendo, portanto, uma @Autowired


novidade do Spring 4. private CustomerDAO customerDAO;

@RequestMapping(/customer)
Listagem 13. Cdigo da classe Customer. public List<Customer> findAll() {
return customerDAO.findAll();
package br.com.javamagazine.spring4demo.rest; }

public class Customer { @RequestMapping(/customer/{customerId})


private Integer id; public Customer findById(@PathVariable Integer customerId) {
private String name; return customerDAO.findById(customerId);
}
public Customer() {
} }

public Customer(Integer id, String name) { Listagem 15. Cdigo da anotao @RestController.
this.id = id;
this.name = name; @Target(ElementType.TYPE)
} @Retention(RetentionPolicy.RUNTIME)
@Documented
//getters e setters omitidos @Controller
@ResponseBody
@Override public @interface RestController {
public String toString() {
return Customer [id= + id + , name= + name + ]; }
}
} Listagem 16. Novo cdigo de CustomerWS para a verso Spring 4.

package br.com.javamagazine.spring4demo.rest;
Com nossa camada de acesso a dados pronta, podemos prosse-
import java.util.List;
guir com a criao dos web services. Considerando a verso 3.x
do Spring, o cdigo apresentado na Listagem 14 necessrio para import org.springframework.beans.factory.annotation.Autowired;
a criao de um web service REST. import org.springframework.web.bind.annotation.*;
Repare que fizemos uso das anotaes @Controller e @Respon-
@RestController
seBody. Porm, ao invs usarmos elas, com o Spring 4 podemos
@RequestMapping(/rest)
aplicar a @RestController, que nada mais que uma anotao public class CustomerWS {
de convenincia, conforme podemos verificar no cdigo exposto
na Listagem 15. @Autowired
private CustomerDAO customerDAO;
Ela anotada com ambas as anotaes que citamos anteriormen-
te, @Controller e @ResponseBody, o que significa que ao anotar @RequestMapping(/customer)
uma classe com ela, no necessrio informar nenhuma dessas public List<Customer> findAll() {
outras duas anotaes. Com isso, podemos refatorar o web service return customerDAO.findAll();
criado na Listagem 14 para usar a anotao @RestController. }

O cdigo modificado pode ser visto na Listagem 16.


@RequestMapping(/customer/{customerId})
Apesar de simples, essa melhoria facilita e agiliza a criao de public Customer findById(@PathVariable Integer customerId) {
novos web services. Vale informar que a abordagem antiga tam- return customerDAO.findById(customerId);
bm funciona na verso 4 do Spring. Para testar os servios, basta }
acessar as URLs http://localhost:8080/spring4-demo/rest/customer e
}
http://localhost:8080/spring4-demo/rest/customer/2.

Edio 127 Java Magazine 25

java127.indb 25 15/04/2014 11:15:46


Spring Framework: as novidades do Spring 4

Acessando web services REST Veremos a seguir exemplos abordando os dois cenrios, comean-
As melhorias introduzidas no Spring 4, no que diz respeito a web do pelo uso da classe Future, conforme apresenta a Listagem 19.
services REST, no so somente para criao dos mesmos, mas A assinatura do mtodo getForEntity() a mesma do mtodo
tambm para acess-los. Mesmo nas verses anteriores, o Spring j getForObject() do exemplo da Listagem 17, recebendo a URL
fornecia uma classe de convenincia bastante til, chamada Rest- do servio, o objeto para o qual a resposta deve ser convertida e
Template, que abstrai toda a parte de comunicao e converso da os parmetros da invocao do web service. Quando o mtodo
resposta para o objeto desejado. Podemos ver um exemplo dessa get() invocado no objeto futureCostumer, a Thread corrente
classe sendo utilizada no cdigo exibido na Listagem 17. fica aguardando at que a resposta esteja pronta, j que ela est
sendo processada assincronamente. O objeto retornado pelo
mtodo get() do tipo ResponseEntity<Customer>, que possui
Listagem 17. CustomerWSClientSync Exemplo de uso da classe RestTemplate.
um conjunto de mtodos para obter informaes da resposta
package br.com.javamagazine.spring4demo.rest; da invocao do web service, tais como getStatusCode(), que
devolve o cdigo HTTP associado, e getBody(), que devolve a
import java.util.logging.Logger;
resposta propriamente dita j convertida para o objeto desejado.
import org.springframework.beans.factory.annotation.Autowired; Para que esse cdigo funcione, precisamos declarar um bean do
import org.springframework.web.bind.annotation.*; tipo AsyncRestTemplate no arquivo mvc-dispatcher-servlet.xml,
import org.springframework.web.client.RestTemplate;
como demonstra a Listagem 20.
@RestController
public class CustomerWSClientSync { Listagem 19. CustomerWSClientAsyncFuture Exemplo de uso da classe Async-
RestTemplate com Future.
@Autowired
private RestTemplate restTemplate; package br.com.javamagazine.spring4demo.rest;

private Logger logger = import java.util.concurrent.*;


Logger.getLogger(CustomerWSClientSync.class.getName()); import java.util.logging.*;

@RequestMapping(/restClientSync) import org.springframework.beans.factory.annotation.Autowired;


public void invoke() { import org.springframework.http.ResponseEntity;
Customer customer = restTemplate.getForObject( import org.springframework.web.bind.annotation.*;
http://localhost:8080/spring4-demo/rest/customer/{id}, import org.springframework.web.bind.annotation.RestController;
Customer.class, 2); import org.springframework.web.client.AsyncRestTemplate;
logger.info(Customer + customer);
} @RestController
public class CustomerWSClientAsyncFuture {
} @Autowired
private AsyncRestTemplate restTemplate;

private Logger logger = Logger.getLogger(


O mtodo getForObject() usado recebe como parmetros a URL CustomerWSClientAsyncFuture.class.getName());

do servio, o objeto para o qual a resposta deve ser convertida e @RequestMapping(/restClientAsyncFuture)


os parmetros da invocao do web service, o que nesse caso o public void invoke() {
id do cliente. Para que esse cdigo funcione, precisamos declarar Future<ResponseEntity<Customer>> futureCustomer = restTemplate.
getForEntity(http://localhost:8080/spring4-demo/rest/customer/{id},
um bean do tipo RestTemplate no arquivo mvc-dispatcher-servlet Customer.class, 2);
.xml, conforme demonstra a Listagem 18. try {
logger.info(Customer + futureCustomer.get().getBody());
} catch (InterruptedException | ExecutionException e) {
Listagem 18. Contedo a ser adicionado no arquivo mvc-dispatcher-servlet.xml. logger.log(Level.SEVERE, Error while retrieving customer, e);
}
<bean id=restTemplate class=org.springframework.web.client.RestTemplate /> }

}
O exemplo mostrado na Listagem 17 no nenhuma novidade,
Listagem 20. Contedo a ser adicionado no arquivo mvc-dispatcher-servlet.xml.
j que a forma sncrona e tradicional que o Spring oferece para
acessar web services REST. A novidade fica por conta da adio <bean id=asyncRestTemplate
da classe AsyncRestTemplate, que fornece o recurso de acessar class=org.springframework.web.client.AsyncRestTemplate />

servios REST de forma assncrona, o que pode trazer ganhos


de performance dependendo do cenrio. Essa classe prov duas Como informado anteriormente, a segunda maneira de acessar
maneiras de acesso assncronas, sendo uma por meio de objetos servios REST de maneira assncrona atravs do mecanismo de
do tipo Future, e outra por meio de callback, atravs da classe callback, usando-se a classe ListenableFutureCallback. Veja um
ListenableFutureCallback. exemplo na Listagem 21.

26 Java Magazine Edio 127

java127.indb 26 15/04/2014 11:15:46


Listagem 21. CustomerWSClientAsyncCallback Exemplo de uso da classe Async- Porm, com uma pequena modificao na forma como o bean
RestTemplate com callback asyncRestTemplate criado, podemos integr-lo diretamente ao
package br.com.javamagazine.spring4demo.rest;
bean taskExecutor, que foi criado mais cedo, na Listagem 7. Para
isso, basta modificar o contedo exibido na Listagem 20 para que
import java.util.logging.Logger; fique como o da Listagem 22.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity; Listagem 22. Contedo modificado no arquivo mvc-dispatcher-servlet.xml.
import org.springframework.util.concurrent.*;
import org.springframework.web.bind.annotation.*; <bean id=asyncRestTemplate
import org.springframework.web.client.AsyncRestTemplate; class=org.springframework.web.client.AsyncRestTemplate>
<constructor-arg ref=taskExecutor />
@RestController </bean>
public class CustomerWSClientAsyncCallback {

@Autowired Dessa forma, o Spring delega a criao de Threads para o bean


private AsyncRestTemplate restTemplate;
taskExecutor, que por sua vez usa um pool de threads gerenciado
private Logger logger = Logger.getLogger( pelo prprio container, deixando a soluo bem mais interessante
CustomerWSClientAsyncCallback.class.getName()); e integrada.
@RequestMapping(/restClientAsyncCallback)
public void invoke() { Java 8
ListenableFuture<ResponseEntity<Customer>> future = restTemplate. A release final do Java 8 est agendada para maro de 2014 e essa
getForEntity(http://localhost:8080/spring4-demo/rest/customer/{id},
Customer.class, 2); verso traz vrias melhorias, sendo algumas muito aguardadas,
como a Date & Time API e, principalmente, expresses Lambda.
future.addCallback(new ListenableFutureCallback<ResponseEntity Apesar do Java 8 ainda nem ter sido lanado oficialmente, o Spring 4
<Customer>>() {
j vem com suporte total a ele. Nesta seo, vamos ver brevemente
@Override como podemos reescrever a classe CustomerDAO usando expres-
public void onSuccess(ResponseEntity<Customer> result) {
ses Lambda, deixando o cdigo mais claro e enxuto.
logger.info(Rest call executed successfully.
Customer returned: + result.getBody()); As melhorias a serem implementadas nesse cenrio so todas
} relacionadas ao cdigo que faz o mapeamento do ResultSet para
o objeto desejado, que no nosso caso o Customer. A interface
@Override
public void onFailure(Throwable t) { JDBCTemplate prov duas maneiras de se fazer esse mapea-
logger.info(Error while accessing Rest WS); mento: criando uma classe annima que implemente a interface
t.printStackTrace();
}
RowMapper e passando-a para o parmetro rowMapper dos
}); mtodos query() ou queryForObject(); e criando uma classe que
implemente RowMapper que tambm deve ser passada para o
// aqui pode ser adicionado cdigo para ser executado concorrentemente
}
parmetro rowMapper nos mtodos citados anteriormente.
Tomando como exemplo a primeira opo e considerando o Java 7,
} o mtodo findAll() da classe CustomerDAO seria implementado,
conforme apresentado na Listagem 23.
A Listagem 24 exibe o cdigo acima refatorado usando Java 8.
A classe ListenableFuture nada mais que uma extenso da
classe Future com o mecanismo de callback, que provido atravs Listagem 23. Mtodo findAll() com Java 7.
do mtodo addCallback(). Este mtodo recebe um objeto do tipo
public List<Customer> findAll() {
ListenableFutureCallback, que tem um mtodo onSuccess() return jdbcTemplate.query(Select id, name from customer,
que invocado em caso de sucesso, recebendo como parmetro o new RowMapper<Customer>() {
ResponseEntity contendo os detalhes da resposta, e o onFailure(), @Override
public Customer mapRow(ResultSet rs, int pos) throws SQLException {
chamado em caso de erro, recebendo o Throwable associado. return new Customer(rs.getInt(id), rs.getString(name));
O leitor pode estar pensando que, se a execuo assncrona, }
o Spring de alguma forma precisa criar Threads para executar });
}
essas tarefas. Sendo assim, como ele est criando essas Threads?
Da forma manual e no recomendada pela especificao da Java Listagem 24. Mtodo findAll() com Java 8.
EE? A resposta sim. Por padro, a classe AsyncRestTemplate
public List<Customer> findAll() {
usa um SimpleAsyncTaskExecutor para criar as Threads, ou seja, return jdbcTemplate.query(Select id, name from customer, (rs, rowNum) ->
para cada chamada ao mtodo getForEntity() uma Thread criada new Customer(rs.getInt(id), rs.getString(name)));
}
manualmente pelo SimpleAsyncTaskExecutor.

Edio 127 Java Magazine 27

java127.indb 27 15/04/2014 11:15:46


Spring Framework: as novidades do Spring 4

inegvel que o cdigo fica bem mais enxuto, tornando desne- como parmetros o ResultSet e o rowNumber. Com isso, basta
cessria toda a verbosidade de declarao da classe annima e passarmos this::mapCustomer nos parmetros que necessitam
de seus parmetros. Porm, essa abordagem de criao de classe de uma implementao de RowMapper e o Method References
annima nesse cenrio traz a desvantagem de no podermos cuida do resto.
reutiliz-la em outros mtodos. Por isso, no cdigo original da O Java 8 tem essas e vrias outras novidades que visam tornar a
CustomerDAO definimos a classe interna CustomerMapper e a linguagem mais poderosa e com sintaxe mais enxuta. O leitor que
utilizamos nos mtodos findAll() e finById(). tiver interesse em conhecer as novidades do Java 8 pode buscar
No entanto, mesmo nesse tipo de abordagem o Java 8 oferece mais detalhes na documentao e em outros materiais disponveis
benefcios, atravs do recurso Method References. Um detalhamento na internet e na prpria Java Magazine.
mais profundo desse recurso foge do escopo desse artigo, mas inegvel que o Spring foi e continua sendo um dos frameworks
basicamente ele permite passar um mtodo por parmetro para mais populares da histria do Java. Toda essa popularidade
outro mtodo, no sendo necessrio criar uma classe somente para resultado de um trabalho muito bem feito sempre visando facilitar
esse propsito. O cdigo final da classe CustomerDAO pode ser a vida dos desenvolvedores, atravs de recursos funcionais
visto na Listagem 25. e prticos e da integrao com o restante do ecossistema,
Repare que agora, ao invs de termos a classe interna Customer- como outros frameworks, servidores de aplicao e a prpria
Mapper, temos apenas um mtodo mapCustomer(), que recebe especificao Java EE.
Essa verso 4 mais um importante passo na histria do Spring,
trazendo novas funcionalidades e mantendo o cenrio de se
Listagem 25. Cdigo final da classe CustomerDAO.
integrar facilmente plataforma Java, como por exemplo, ao Java 8
package br.com.javamagazine.spring4demo.rest; e Java EE 7.

import java.sql.ResultSet;
import java.sql.SQLException; Autor
import java.util.List;
Luciano Davoglio Molinari
import javax.sql.DataSource; lucmolinari@gmail.com | @LucMolinari
formado em Processamento de Dados pela Fatec Taquaritinga
import org.springframework.beans.factory.annotation.Autowired; e Ps-Graduado em Engenharia de Software com SOA pelo
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.*;
IBTA/Campinas. Trabalha com Java h sete anos e atualmente atua como
import org.springframework.stereotype.Repository; arquiteto de sistemas no CPqD. Possui as certificaes SCJP, SCWCD, SCBCD
e SCEA e mantm o blog http://lucianomolinari.wordpress.com.
@Repository
public class CustomerDAO {
Links:
private JdbcTemplate jdbcTemplate;

@Autowired Servidor de aplicaes WildFly.


public void setDataSource(DataSource dataSource) { http://www.wildfly.org/
this.jdbcTemplate = new JdbcTemplate(dataSource);
} Documentao do Spring 4.
http://docs.spring.io/spring/docs/4.0.0.RELEASE/spring-framework-reference/htmlsingle
public List<Customer> findAll() {
return jdbcTemplate.query(Select id, name from customer, Site do Spring.
this::mapCustomer);
}
http://www.spring.io

public Customer findById(int id) {


Documentao do Java 8.
try { http://download.java.net/jdk8/docs/api/
return jdbcTemplate.queryForObject(Select id, name from
customer where id = ?, this::mapCustomer, id); Documentao da API de concorrncia da Java EE 7.
} catch (EmptyResultDataAccessException e) { http://concurrency-ee-spec.java.net/javadoc/javax/enterprise/concurrent/package-
return null; summary.html
}
}

private Customer mapCustomer(ResultSet rs, int rowNum) throws SQLException
Voc gostou deste artigo?
{
return new Customer(rs.getInt(id), rs.getString(name));
}
D seu voto em www.devmedia.com.br/javamagazine/feedback
Ajude-nos a manter a qualidade da revista!
}

28 Java Magazine Edio 127

java127.indb 28 15/04/2014 11:15:50


Edio 127 Java Magazine 29

java127.indb 29 15/04/2014 11:15:54


Desenvolvendo
aplicaes com Java EE 7
Desenvolva solues web com GlassFish 4, EJB 3.2,
JPA 2.1 e WebSockets

N Fique por dentro


os dias de hoje, solues em tempo real so
uma necessidade global que tem se tornado
cada vez mais comum. Trata-se de um requisito Este artigo demonstra a construo de uma aplicao web utilizando
presente em uma ampla variedade de sistemas, tais como a Java API for WebSocket 1.0, uma nova especificao da Java EE 7.
jogos on-line, clientes de mensagens instantneas como No decorrer do artigo abordaremos o uso de EJBs para desenvolver a
o Hangouts do Google, o bate-papo do Facebook e em camada de negcio de nossa aplicao, JPA para a camada de persistncia
dashboards on-line, como o Trello ou o Target Process. em banco de dados relacional, os conceitos de Domain-Driven Design
Por diferentes motivos, esse tipo de experincia para o e cdigo limpo, e o desenvolvimento de cdigo JavaScript utilizando as
usurio imprescindvel em sites de compras coletivas, bibliotecas jQuery e Underscore.js na camada web. Sendo assim, este
aplicaes financeiras ou colaborativas. Alm disso, tema til a desenvolvedores interessados em se aprofundar mais em
considerado por muitos como uma tendncia de mercado aplicaes Java EE e em estudar alternativas ao Ajax reverso que permitam
e est sendo adotado no s em aplicaes web, mas a construo de aplicaes web em tempo real, uma caracterstica com
tambm em aplicativos para dispositivos mveis. crescente adoo na rea de desenvolvimento de software.
O crescimento expressivo de sistemas que demandam
a comunicao em tempo real tem impulsionado o mer-
cado e, portanto, a necessidade de criao de bibliotecas tempo real quando novos pedidos forem realizados e quando uma
que tornem a construo desse nicho de aplicaes mais pastelaria iniciar um atendimento.
eficiente. Essa a razo pela qual APIs como o Pusher ou
o PubNub, que facilitam a construo de funcionalidades Escolhendo nossas tecnologias
escalveis e realtime para celulares ou navegadores, tem Neste estudo, teremos como premissa apresentar um pouco
ganhado notoriedade no ambiente corporativo. da especificao Java EE 7, principalmente no que diz respeito
Nesse artigo vamos explorar esse tema utilizando a especificao WebSockets. Em virtude disso, dependeremos do
especificao Java API for WebSocket da Java EE 7, que auxlio de um servidor de aplicao capaz de suportar esse pr-
possibilita a comunicao bidirecional entre servidor e requisito. Contudo, no momento em que este artigo foi escrito, o
cliente em aplicaes web. Para isso, construiremos um sis- GlassFish 4 era o nico Application Server que implementava a
tema de atendimento de compras de pastis online (nada especificao Java EE 7 Web Profile e, juntamente com o TmaxSoft
melhor em uma feira-livre!). O objetivo deste ser permitir JEUS 8, so os nicos que implementam o Full Profile da Java EE 7.
ao cliente efetuar e acompanhar os prprios pedidos, e Dessa maneira, optamos pelo GlassFish 4 devido maturidade
de conceder s pastelarias cadastradas a capacidade de deste servidor de aplicao e por ser a implementao de refe-
escolher quais dos pedidos cada uma ir atender. Como rncia da especificao Java EE 7.
diferencial, faremos com que os clientes e, principalmente, Durante o desenvolvimento do projeto precisaremos tambm
as pastelarias, disponham de informaes atualizadas so- de um sistema gerenciador de banco de dados para armazenar
bre todos os pedidos realizados para evitar, por exemplo, os dados dos pedidos realizados. Optamos pelo MySQL 5.5
que ocorra a situao de uma pastelaria fritar um pastel por ser uma soluo robusta, bastante acessvel e bem aceita no
j entregue por uma concorrente. Com isso, esse sistema desenvolvimento web, mas o leitor est liberado para escolher o
poder ser utilizado por uma rede de pastelarias, possi- banco de dados que se sentir mais confortvel. Outras solues
bilitando que todas as conveniadas sejam notificadas em de software livre incluem o MariaDB, o fork do MySQL, e o

30 Java Magazine Edio 127

java127.indb 30 15/04/2014 11:15:55


PostgreSQL. Para a camada web, que est relacionada forma nativo verso 7 do Java e os plugins pr-instalados para trabalhar
pela qual o usurio ir interagir com o core da aplicao, opta- com o Maven que so oferecidos por essa IDE. No entanto, o leitor
mos por utilizar uma soluo leve que envolve a especificao est livre para utilizar outros ambientes de desenvolvimento,
Servlet 3.1, o JSP 2.3 e bibliotecas JavaScript como o jQuery e o tais como o IntelliJ IDEA ou o NetBeans. Consideramos ainda
Underscore.js. J na camada de negcio, optamos por utilizar o que o leitor possui conhecimento bsico da estrutura de projetos
EJB 3.2, por estar presente na stack Java EE e pela sua integrao e do gerenciamento de dependncias do Maven, visto que esse
com outros recursos da especificao, como os Servlets e Server assunto j foi amplamente abordado em edies anteriores da
Endpoints WebSocket na camada web, mas poderamos utilizar revista. A partir dessas informaes, passaremos agora criao
o Spring Framework no projeto sem muitos problemas. do nosso projeto.
O primeiro passo a criao do projeto web dentro do Eclipse
Preparando o GlassFish 4 a partir de um arqutipo do Maven, o maven-archetype-webapp.
Um pr-requisito indispensvel para o desenvolvimento do Para criar um novo projeto Maven, clique em File > New > Other
nosso projeto a instalao e configurao do GlassFish 4. Sendo e escolha Maven > Maven Project, conforme mostra a Figura 1.
assim, explicaremos brevemente o processo de configurao do Escolhida a opo, clique no boto Next e selecione o local onde
servidor de aplicao em ambiente local. o projeto ser criado.
Instalar o GlassFish um processo bastante trivial e depen-
de basicamente do download e descompactao do arquivo
glassfish-4.0.zip, que pode ser baixado diretamente no site do
projeto (veja a seo Links). Desse modo, realize o download
do zip e o descompacte em uma localizao de sua preferncia.
Identificaremos o diretrio de instalao do GlassFish atravs da
varivel de ambiente $GLASSFISH_HOME. Instalado o servidor
de aplicao, precisamos configurar a senha do administrador
que usaremos mais adiante.
A escolha da senha do usurio administrador no GlassFish um
processo simples que pode ser realizado por meio do utilitrio
asadmin, presente no diretrio $GLASSFISH_HOME/bin. Assim,
pela linha de comando, execute o asadmin e ento inicie o servi-
dor com o comando start-domain. Feito isso, utilize o comando
change-admin-password para alterar a senha de administrador do
GlassFish, conforme apresenta a Listagem 1.
Uma maneira alternativa e possivelmente mais rpida para reali-
zar a mesma operao pode ser realizada atravs da passagem de
argumentos para o comando asadmin, conforme ilustra a Listagem 2. Figura 1. Criao de um novo projeto Maven
De qualquer modo, ambos os procedimentos atendero de forma
satisfatria a alterao da senha do usurio administrador. Na tela de seleo de archetypes, selecione All Catalogs e digite
no campo Filter: maven-archetype-webapp. Isso reduzir a
Listagem 1. Alterao da senha padro do usurio admin do GlassFish. quantidade de artefatos exibidos e tornar mais fcil a seleo
do tipo correto. Logo aps, encontre, selecione o artifactId
cd $GLASSFISH_HOME/bin
maven-archetype-webapp e clique em Next, conforme ilustrado
./asadmin
asadmin> start-domain na Figura 2.
asadmin> change-admin-password A prxima tela diz respeito especificao dos parmetros do
exit archetype escolhido. Em outras palavras, nessa tela efetuaremos
Listagem 2. Comando alternativo para alterao da senha de super-usurio no
algumas configuraes do projeto a ser criado. Deste modo,
GlassFish. escreva no campo Group Id, que geralmente representa o nome
de domnio reverso da empresa responsvel pela aplicao,
cd $GLASSFISH_HOME/bin
br.com.devmedia. No campo Artifact ID, que representa o nome
./asadmin start-domain
./asadmin change-admin-password da aplicao, escreva pastelaria-online. Manteremos a verso
como 0.0.1-SNAPSHOT por enquanto, afinal, uma verso SNAP-
SHOT uma verso ainda no liberada para uso. Por fim, uma
A criao do projeto Maven boa prtica nomear o pacote principal do projeto com a sintaxe
Neste artigo adotamos a IDE Eclipse Kepler para o desenvolvi- <nome de domnio reverso> + <nome do artefato>, porque
mento do nosso projeto. Essa deciso visa aproveitar o suporte em um ambiente onde so implantadas inmeras aplicaes no

Edio 127 Java Magazine 31

java127.indb 31 15/04/2014 11:15:55


Desenvolvendo aplicaes com Java EE 7

mesmo servidor de aplicao, fica mais rpido identificar qual nosso cdigo. Em seguida adicione o Log4j, que usaremos para
projeto gerou uma exceo ou um erro sistmico atravs do log. imprimir as mensagens de log do sistema, e o Slf4j (Simple Logging
Por isso, o nome do pacote ser br.com.devmedia.pastelaria_on- Facade for Java), que uma fachada para importantes bibliotecas de
line, conforme demonstra a Figura 3. Em seguida clique em Finish Logging, como o java.util.Logging e o Logback (que considera-
para terminar a criao do projeto. do por muitos como uma ferramenta superior ao famoso Log4j).
Assim, ao trmino desses passos, teremos as linhas ilustradas na
Listagem 3 adicionadas no arquivo de configurao do projeto.

Listagem 3. Configurao de dependncias de log e testes unitrios no pom.xml.

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.4</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.4</version>
Figura 2. Escolhendo o archetype Maven
</dependency>

Seguindo com a configurao do projeto, adicionaremos agora


as referncias s bibliotecas Google Gson e Google Guava, da
mesma forma que as dependncias anteriores foram adicionadas
no pom.xml. Essas bibliotecas sero importantes no decorrer do
projeto, ficando a cargo do Google Gson a converso de JSON
em classes Java e vice-versa, e para o Google Guava, a adio de
classes utilitrias que nos auxiliam na escrita de cdigos mais
limpos. Como exemplos de classes do Guava que nos auxiliam
a melhorar a legibilidade do cdigo, temos a Precondition, para
tratar de validao e o lanamento de excees, e novas Collec-
tions, como a BiMap, uma implementao de mapa bidirecional,
e Multimap, um mapa que agrupa mltiplos valores para uma
mesma chave. Observe a configurao das dependncias dessas
bibliotecas na Listagem 4.
Nesse momento, importante ressaltar que, muito embora a
especificao Java EE 7 possua uma API prpria para a manipu-
lao de JSON, definida pela JSR-353 (Java API for Json Processing),
Figura 3. Configurando os parmetros do archetype Maven adotaremos nesse projeto o Google Gson, dada a sua eficincia na
converso de tipos. No entanto, possvel utilizar outras bibliote-
Configurando as dependncias do projeto cas de serializao, como o Jackson Data Mapper, que possui uma
O prximo passo a configurao das dependncias do projeto boa integrao com o Spring Framework e o XStream/Jettison,
Maven. Sendo assim, no projeto criado na seo anterior, vamos utilizado pelo VRaptor.
adicionar as dependncias que sero utilizadas para o desen- Finalizando a configurao das dependncias, adicionaremos
volvimento de nossa aplicao. Primeiramente, abra o pom.xml, agora a biblioteca JSTL (JavaServer Pages Standard Tag Library) e a
localizado na raiz do projeto, e adicione a dependncia do JUnit, API da Java EE 7. Para isso, inclua os artefatos jstl e javaee-web-api,
verso 4.8.2, para permitir a construo de testes unitrios para o que tornar as APIs do JSTL e da Java EE 7 Web Profile acessveis

32 Java Magazine Edio 127

java127.indb 32 15/04/2014 11:15:56


no projeto (utilize javaee-api se desejar acesso ao profile completo), Listagem 6. Configurando plugins auxiliares do Maven para compilao e deploy
conforme mostra a Listagem 5. Observe que o escopo da biblioteca de aplicaes.

javaee-web-api est definido como provided, j que essa biblioteca <properties>


no ser empacotada junto com nosso projeto, mas sim provida <glassfish.user>admin</glassfish.user>
pelo Application Server. <glassfish.password>admin</glassfish.password>
<!-- altere ${seu.diretorio} para o diretrio no qual est instalado o glassfish -->
<glassfish.directory>${seu.diretorio}/glassfish/</glassfish.directory>
Listagem 4. Configurao dos pacotes Google Guava e Google Gson. <glassfish.port>8080</glassfish.port>
</properties>
<dependency>
<groupId>com.google.guava</groupId> <build>
<artifactId>guava</artifactId> <finalName>pastelaria-online</finalName>
<version>15.0</version> <plugins>
</dependency> <plugin>
<dependency> <groupId>org.apache.maven.plugins</groupId>
<groupId>com.google.code.gson</groupId> <artifactId>maven-compiler-plugin</artifactId>
<artifactId>gson</artifactId> <version>2.4</version>
<version>2.2.4</version> <configuration>
</dependency> <compilerVersion>1.7</compilerVersion>
<source>1.7</source>
Listagem 5. Incluindo o web profile e o JSTL. <target>1.7</target>
</configuration>
<dependency> </plugin>
<groupId>jstl</groupId> <plugin>
<groupId>org.glassfish.maven.plugin</groupId>
<artifactId>jstl</artifactId>
<artifactId>maven-glassfish-plugin</artifactId>
<version>1.2</version>
<version>2.1</version>
</dependency> <configuration>
<user>${glassfish.user}</user>
<dependency> <adminPassword>${glassfish.password}</adminPassword>
<groupId>javax</groupId> <glassfishDirectory>${glassfish.directory}</glassfishDirectory>
<artifactId>javaee-web-api</artifactId> <echo>true</echo>
<version>7.0</version> <components>
<scope>provided</scope> <component>
<name>${project.artifactId}</name>
</dependency>
<artifact>${project.build.directory}/${project.build.finalName}.war</artifact>
</component>
</components>
Configurando os plugins do projeto <domain>
<name>${project.artifactId}</name>
Ao passo que a configurao das dependncias foi finalizada, <adminPort>4848</adminPort>
resta agora escolher os plugins que sero utilizados para o build de <httpPort>${glassfish.port}</httpPort>
nossa aplicao. Deste modo, vamos configurar o maven-compiler- <httpsPort>8443</httpsPort>
</domain>
plugin para compilar nosso projeto usando o Java 7 e utilizaremos </configuration>
o maven-glassfish-plugin para implantar o projeto no GlassFish 4. </plugin>
A Listagem 6 apresenta trechos do pom.xml com as configuraes </plugins>
</build>
dos plugins do projeto.
Repare que durante a configurao do maven-glassfish-plugin
definimos uma senha no nula para o usurio do Glassfish. Essa Configurando a API de persistncia
senha justamente aquela que configuramos anteriormente, Neste artigo adotaremos o provedor de persistncia padro
durante a seo de preparao do servidor de aplicao, e isso do GlassFish 4, o EclipseLink. Logo, no haver necessidade de
decorre porque na instalao padro do servidor o usurio adicionar dependncias especficas da implementao do JPA 2.1,
admin tem como padro uma senha vazia, mas o plugin do j que tudo ser provido pelo prprio servidor de aplicao. Por
Maven para integrao precisa de uma senha no nula. Sabendo outro lado, ainda precisamos configurar o JPA no projeto. Portan-
disso, altere a propriedade glassfish.password no pom.xml to, crie o arquivo persistence.xml em src/main/resources/META-INF
do seu projeto e inclua a mesma senha que voc configurou e adicione o seguinte trecho da Listagem 7.
nos passos anteriores. Aqui vale a pena ressaltar que esse plu- Ao adicionar essa configurao no persistence.xml estamos
gin realmente til durante o processo de desenvolvimento criando uma unidade de persistncia chamada default. Ela
e deploy local; porm, em outros ambientes, tais como os de utilizar o provedor de persistncia padro do GlassFish 4, o
homologao e produo, desejvel utilizar um servidor de EclipseLink, atravs do provider org.eclipse.persistence.jpa
integrao contnua como o Jenkins ou o TeamCity com todas .PersistenceProvider, que far o gerenciamento de transaes
as restries de acesso e rastreabilidade configuradas para a partir da API JTA 1.2 (Java Transaction API) e se conectar com
atingir o mesmo objetivo. nosso banco de dados atravs de um Data Source JTA de nome

Edio 127 Java Magazine 33

java127.indb 33 15/04/2014 11:15:56


Desenvolvendo aplicaes com Java EE 7

Listagem 7.Definio do persistence.xml JNDI jdbc/MySQL. Alm disso, inclumos propriedades referen-
tes ao gerenciamento do nvel de log do EclipseLink e a gerao
<?xml version=1.0 encoding=UTF-8?>
<persistence version=2.1 xmlns=http://xmlns.jcp.org/xml/ns/persistence
e/ou atualizao automtica de tabelas durante a inicializao
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance da unidade de persistncia (embora no seja uma boa prtica em
xsi:schemaLocation=http://xmlns.jcp.org/xml/ns/persistence ambientes de homologao/produo).
http://www.oracle.com/webfolder/technetwork/jsc/xml/ns/persistence/
persistence_2_1.xsd>
A criao do datasource no GlassFish outro processo que no
<persistence-unit name=default transaction-type=JTA> complicado, de modo que possvel realiz-lo diretamente no
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> console de administrao oferecido pelo prprio servidor de
<jta-data-source>jdbc/MySQL</jta-data-source>
<properties> aplicao. Assim, para criar o Connection Pool e um JDBC Resource
<property name=eclipselink.logging.level value=FINE/> basta acessar o endereo localhost:4848 a partir do seu navegador
<property name=eclipselink.logging.logger value=org.eclipse.persistence. e realizar a autenticao com o usurio admin e a senha definida
logging.DefaultSessionLog/>
<property name=eclipselink.ddl-generation value=create-or-extend-tables /> nos passos anteriores. Apesar dessa facilidade, imprescindvel
<property name=eclipselink.deploy-on-startup value=true/> que o driver do MySQL tenha sido copiado para o diretrio lib do
</properties>
GlassFish, uma vez que o pacote inicial de instalao do Applica-
</persistence-unit>
</persistence> tion Server no inclui o driver JDBC desse SGBD. Assim, preciso
copiar o JAR do driver do MySQL no diretrio $GLASSFISH_
HOME/glassfish/domains/<seu domain>/lib e
reiniciar o GlassFish, para ento o driver ser
reconhecido pelo console de administrao.
Como estamos utilizando JTA para o ge-
renciamento de transaes do JPA, precisa-
remos de um tipo especfico de DataSource
no GlassFish capaz de lidar com transaes
distribudas, o XA Data Source. Para us-lo,
no entanto, devemos primeiramente criar
um JDBC Connection Pool. Deste modo,
clique em JDBC > JDBC Connection Pools e,
em seguida, no boto New..., como mostra
a Figura 4.
Como voc pode observar na Figura 5, de-
fina o nome do Connection Pool no campo
Pool Name como MySQLConnection Pool,
selecione javax.sql.XADataSource em Re-
source Type, MySql no campo Database Driver
Figura 4. Criando um JDBC Connection Pool Vendor e clique em Next. Optamos por um
XA DataSource pelo simples fato de delegar
o gerenciamento das transaes de banco
de dados para o JTA (Java Transaction API).
Alm disso, esse tipo de Data Source permite
utilizar todas as funcionalidades referentes a
transaes do Application Server, como por
exemplo, englobar uma transao com dois
ou mais recursos diferentes, como um banco
de dados e uma fila MQ.
Nesse momento voc dever configurar os
parmetros para a conexo com o MySQL em
Additional Properties. O GlassFish apresenta
de incio todas as propriedades configurveis
para um Data Source. Em um ambiente local,
no entanto, voc pode utilizar somente as
propriedades ServerName, Port, DatabaseNa-
me, User e Password. Portanto, configure-as
Figura 5. Primeiro passo da configurao de um JDBC Connection Pool conforme o seu ambiente.

34 Java Magazine Edio 127

java127.indb 34 15/04/2014 11:15:56


A Figura 6 apresenta a configurao
utilizada para esse artigo. Ao final desse
processo, clique no boto Finish.
Agora vamos criar nosso JDBC Resource,
que deve se chamar jdbc/MySQL. Para
isso, em JDBC > JDBC Resources, clique no
boto New... para criar um JDBC Resource.
Defina o JNDI Name como jdbc/MySQL
e selecione o JDBC Connection Pool criado
anteriormente na opo Pool Name, do mes-
mo modo como apresentado na Figura 7.
Por fim, clique no boto OK para encerrar
a configurao.

Configurando o Log4j
Para encerrarmos o processo de confi-
gurao do projeto, devemos configurar
as propriedades do Log4j para as men-
sagens de log de nossa aplicao. Sendo Figura 6. Segundo passo da configurao de um JDBC Connection Pool
assim, crie o arquivo log4j.properties dentro
de src/main/resources com o contedo da
Listagem 8.

Listagem 8. O arquivo de propriedades de


Logging, log4j.properties.

log4j.appender.stdout=org.apache.log4j.Console-
Appender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j
.PatternLayout
log4j.appender.stdout.layout.
ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L
- %m%n
log4j.rootLogger=DEBUG, stdout

Nas linhas 1 e 2 configuramos o Log4j


para direcionar as mensagens de log para
o console atravs do appender org.apache
.log4j.ConsoleAppender. O Log4j possui Figura 7. Configurando o JDBC Resource
suporte para trabalhar com arquivos de
texto, banco de dados (via JDBC), filas JMS Log4j, acesse o site oficial do projeto que Adicionar a funcionalidade para seleo
e outros recursos alm do console, utili- est contido na seo Links desse artigo. de mais de um sabor por pedido fica como
zando appenders da prpria biblioteca. exerccio para o leitor. A segunda funcio-
Nas linhas 3 e 4 configuramos o formato Botando a mo na massa! nalidade permitir s filiais a visualizao
da mensagem de log utilizando um layout A partir deste tpico, vamos elaborar dos novos pedidos recebidos e a mudana
baseado em padres, o org.apache.log4j um sistema que contemplar inicialmente de status dos pedidos existentes em tem-
.PatternLayout, e definindo o padro trs funcionalidades especficas: efetuar po real, para que com essa informao
atravs da propriedade log4j.appender pedidos, verificar o status deles e iniciar uma filial possa decidir se pode ou no
.stdout.layout.ConversionPattern. um atendimento. Na primeira delas, o atender um cliente em potencial. Como
E por fim, na linha 5, definimos o nvel cliente digitar suas informaes pessoais, requisito funcional, a pgina de status de
de log mnimo como DEBUG, utilizando escolher um sabor de pastel, a quantida- pedidos dever ser atualizada para todos
a propriedade log4j.rootLogger. Para mais de desejada e ir efetuar o pedido. Para os usurios conectados a ela assim que
informaes sobre os formatos das mensa- o exemplo, um cliente poder somente um novo pedido for recebido ou quando
gens ou os nveis de log suportados pelo selecionar um sabor de pastel por pedido. o atendimento de um pedido for iniciado.

Edio 127 Java Magazine 35

java127.indb 35 15/04/2014 11:15:57


Desenvolvendo aplicaes com Java EE 7

Para isso usaremos a tecnologia WebSockets (no vale utilizar o Listagem 11. A entidade Pedido.
setTimeout do JavaScript!). A terceira funcionalidade diz respeito
import java.io.Serializable;
ao registro de atendimento de um pedido por uma filial. Essa import java.util.*;
funcionalidade dever alterar o status para em atendimento e import javax.persistence.*;
@Entity
notificar todas as filiais que aquele pedido j est em processo @Table(name=pedido)
de atendimento. @NamedQueries(value={@NamedQuery(name=Pedido.buscarNovosEemAten-
dimento,
query= Select p From Pedido p inner join fetch p.itens
Elaborando a modelagem de domnio da aplicao Where p.status = :novo or p.status = :atendimento Order By
Antes de partirmos para as tecnologias aplicadas camada de p.id desc, p.status desc)})
public class Pedido implements Serializable {
apresentao do nosso sistema, precisamos desenvolver alguns
artefatos importantes que sero utilizados no decorrer do pro- private static final long serialVersionUID = 2753252136140066761L;
jeto. Dessa maneira, iniciaremos pela criao das classes que @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
definem o domnio do nosso projeto: as Entidades e os Objetos @Column(name=id, nullable=false)
de Valor. Seguindo as definies de Eric Evans, autor do livro private Long id;

Domain-Driven Design Atacando as Complexidades no Corao do @Temporal(TemporalType.TIMESTAMP)


Software, as entidades so objetos com uma identidade definida @Column(name=data_pedido, nullable=false)
private Date dataPedido = new Date();
e que geralmente esto relacionadas a um mapeamento de uma
tabela do banco de dados, enquanto os Objetos de Valor (VO @Column(name=nome_cliente, nullable=false, length=150)
private String cliente;
ou Value Objects) se referem a objetos que possuem apenas a
responsabilidade de trafegar valor entre diferentes camadas da @ElementCollection
@CollectionTable(name=item, joinColumns=@JoinColumn(name=item_id))
aplicao. Portanto, nossas primeiras classes sero o VO Status,
private List<Item> itens;
ilustrado na Listagem 9, as entidades Item e Pedido, que utili-
zam as anotaes do JPA para o mapeamento objeto-relacional, @Column(name=email, nullable=false, length=150)
private String email;
representadas nas Listagens 10 e 11, e mais outros dois Value
Objects: a classe Pedidos, que atua tipicamente como um agre- @Column(name=telefone, nullable=false, length=18)
private String telefone;
gador, definida na Listagem 12, e a classe Resultado, definida
na Listagem 13. @Column(name=atendente, nullable=true, length=50)
private String atendente;

Listagem 9. O value object Status. @Temporal(TemporalType.TIMESTAMP)


@Column(name=data_hora_atendimento, nullable=true)
public enum Status { private Date dataAtendimento;
N (Novo),
A (Em atendimento), @Enumerated(EnumType.STRING)
C (Cancelado),
@Column(name=status, length=1)
F (Finalizado);
private Status status = Status.N;

private String descricao;
// getters e setters
Status(final String descricao) { }
this.descricao = descricao;
} Listagem 12. Representao do value-object Pedidos.

public String toString() { import com.google.gson.Gson;
return descricao; import java.util.List;
} public class Pedidos {
} private List<Pedido> pedidos;

Listagem 10. A entidade Item.


public Pedidos(final List<Pedido> pedidos) {
this.pedidos = pedidos;
import java.io.Serializable;
import javax.persistence.*; }

@Embeddable public List<Pedido> getPedidos() {


public class Item implements Serializable { return pedidos;
}
private static final long serialVersionUID = -3778397739515571369L;
public String asJson() {
@Column(name=sabor, nullable=false, length=150)
final Gson gson = new Gson();
private String sabor;
final String json = gson.toJson(this);
@Column(name=quantidade, nullable=false) return json;
private Integer quantidade; }

//getters e setters }
}

36 Java Magazine Edio 127

java127.indb 36 15/04/2014 11:15:57


Listagem 13. Representao do value object Resultado. Listagem 14. Cdigo do EJB PedidoService.

import java.io.Serializable; import java.util.*;


import javax.*;
public class Resultado implements Serializable { import com.google.common.base.Preconditions;
// outros imports omitidos
private static final long serialVersionUID = 2170294401108447645L;
private boolean success; @Stateless
@TransactionAttribute(TransactionAttributeType.REQUIRED)
private String message;
public class PedidoService {
public Resultado() { @PersistenceContext
} private EntityManager entityManager;

public Resultado(boolean success, String message) { public void registrar(final Pedido pedido) {
this.success = success; entityManager.persist(pedido);
this.message = message; }
}
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public boolean isSuccess() { public Pedidos pendentes() {
return success; final TypedQuery<Pedido> namedQuery = entityManager.createNamedQuery(
} Pedido.buscarNovosEemAtendimento, Pedido.class);
namedQuery.setParameter(novo, Status.N);
public void setSuccess(boolean success) { namedQuery.setParameter(atendimento, Status.A);
this.success = success; final List<Pedido> pedidos = namedQuery.getResultList();
return new Pedidos(pedidos);
}
}
public String getMessage() { public void atender(final Long id, final String atendente) {
return message; final Pedido p = entityManager.find(Pedido.class, id);
} Preconditions.checkState(p.getStatus() == Status.N,
Pedido ja em atendimento);
public void setMessage(String message) { p.setStatus(Status.A);
this.message = message; p.setDataAtendimento(new Date());
} p.setAtendente(atendente);
}
} }

Desenvolvendo os servios Expondo a aplicao para o usurio final


Agora que dispomos das classes que representam nosso do- O foco deste artigo e, possivelmente, a maior preocupao
mnio, estamos aptos a escrever as classes que representam os durante o desenvolvimento de uma aplicao, a apresentao
servios que iro interagir com elas. Segundo o Domain-Driven do servio para o usurio final, ou seja, os elementos que vo
Design, um servio uma operao sem estado realizada possibilitar a interao dele com o nosso sistema. Como j temos
sobre um objeto do domnio. Nesse sentido, a manipulao as entidades e o servio que as manipula, resta agora construir
da entidade Pedido ser feita atravs de um Stateless Session os artefatos que vo expor a interao com o EJB e as pginas que
Bean chamado PedidoService, cujo contedo est exposto na faro uso desses artefatos. Desse modo, elegemos o processo de
Listagem 14. efetuar pedidos como a primeira funcionalidade a ser abordada
Observe na Listagem 13 que optamos por injetar o Entity- e, como ponto de partida, desenvolveremos o servlet EfetuarPe-
Manager diretamente no EJB, abdicando da utilizao de uma didoServlet, que deve ser salvo dentro do pacote br.com.dev-
camada de DAOs. Essa escolha foi feita para agilizar o processo media.pastelaria_online.presentation.servlet com o contedo
de desenvolvimento deste projeto e, principalmente, porque o da Listagem 15.
core do sistema no o foco deste artigo. De qualquer maneira, Analogamente aos demais Servlets que sero construdos no
analisando criticamente o EntityManager, possvel notar que decorrer do projeto, a responsabilidade deste ser, de maneira
ele j atua como um objeto de acesso a dados, j que encapsula o resumida, expor um endereo HTTP com propriedade de receber
cdigo de baixo nvel JDBC e SQL. Outro ponto de ateno dentro requisies com dados no formato JSON. Nesse contexto, o servlet
do EJB a chamada da funo Preconditions.checkState(), que faz recm-criado recebe a requisio e ento delega o processamento
parte da classe Preconditions, encontrada na biblioteca do Google para a classe PedidoService, injetada pelo container atravs da
Guava. Essa classe define algumas das condies mais comuns de anotao @EJB. Se esse fluxo ocorrer sem problemas, o cdigo
serem testadas durante o desenvolvimento de cdigo, como por retornar uma mensagem de sucesso (HTTP 200). Caso contr-
exemplo, verificaes booleanas, objetos no nulos e validao rio, ser retornada uma mensagem de erro interno no servidor
de ndices em listas. Isso significa que, com esse objeto auxiliar, (HTTP 500).
possvel realizar a verificao e, se necessrio, o lanamento de A mensagem quando chega ao servidor atravs do servlet no
excees, com poucas linhas de cdigo, tornando assim o cdigo est completamente pronta para ser enviada para outras classes
mais limpo. do nosso sistema. Isso acontece porque quando as informaes

Edio 127 Java Magazine 37

java127.indb 37 15/04/2014 11:15:57


Desenvolvendo aplicaes com Java EE 7

chegam ao HttpServlet elas ainda esto representadas como Elaborando as telas da aplicao
uma simples cadeia de bytes. Portanto, ao receber a requisio, A fim de permitir que um cliente efetue o pedido, elaboramos um
devemos obter o corpo da mensagem HTTP, o chamado payload, formulrio simples que conter os campos nome, e-mail, telefone,
atravs de um objeto InputStream obtido pela invocao do m- o sabor e a quantidade dos pastis dentro de um arquivo JSP que
todo HttpServletRequest.getInputStream(), realizar a converso chamaremos de efetuarPedido.jsp, conforme mostra a Listagem 16.
para JSON e ento para a classe que representa o mesmo JSON. S
assim poderemos delegar o processamento dos dados do payload Listagem 16. Formulrio de envio de um pedido.

para o servio que sabe lidar com a regra de negcio associada <%@ page language=java contentType=text/html; charset=UTF-8
ao mtodo doPost. pageEncoding=UTF-8%>
<%@ taglib prefix=c uri=http://java.sun.com/jstl/core_rt%>
<html>
Listagem 15. O servlet EfetuarPedidoServlet que atender os pedidos. <head>
<meta http-equiv=Content-Type content=text/html; charset=UTF-8>
@WebServlet(urlPatterns=/pedido, loadOnStartup=1) <title>Efetuar Pedido</title>
public class EfetuarPedidoServlet extends HttpServlet { <link rel=stylesheet type=text/css href=<c:url value=css/style.css />
media=all>
private static final long serialVersionUID = -3309626245848149687L; </head>
<body>
@EJB <div id=container>
private PedidoService pedidoService; <p align=center>
<table>
<thead>
@Override
<tr>
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
<th>Nome</th>
throws ServletException, IOException {
<th>E-mail</th>
resp.setContentType(application/json);
<th>Telefone</th>
resp.setCharacterEncoding(UTF-8);
<th>Sabor</th>
final Gson gson = new Gson();
<th>Quantidade</th>
try {
<th></th>
final InputStreamReader inputStreamReader = new InputStreamReader(
</tr>
req.getInputStream());
</thead>
final Pedido pedido = gson.fromJson(inputStreamReader, Pedido.class);
<tbody>
pedidoService.registrar(pedido);
<tr>
resp.setStatus(200);
<td>
resp.getWriter().write(gson.toJson(new Resultado(true,
<input type=text id=cliente maxlength=100 size=35>
Pedido realizado com exito)));
</td>
} catch (final RuntimeException e) {
<td>
resp.sendError(500, e.getMessage());
<input type=text id=email maxlength=100 size=40>
}
</td>
}
<td>
} <input type=text id=telefone maxlength=18 size=18>
</td>
<td>
<select id=sabor>
a que entra o Google GSON, uma biblioteca capaz de converter <option value=Carne>Carne</option>
JSON em uma classe Java. Na Listagem 15 utilizamos o mtodo <option value=Queijo>Queijo</option>
fromJson() passando como primeiro argumento um InputStre- <option value=Pizza>Pizza</option>
<option value=Quatro Queijos>Quatro Queijos</option>
amReader obtido a partir do InputStream encapsulado dentro </select>
do HttpServletRequest (parmetro req do mtodo doPost) e, </td>
<td>
como segundo argumento, a classe do objeto a ser mapeado que,
<input type=text id=quantidade maxlength=3 size=3>
no nosso caso, a classe Pedido. O resultado disso um objeto </td>
Pedido, preenchido com os valores do JSON recebido. <td>
<input type=button value=Enviar id=enviar>
O servlet EfetuarPedidoServlet ainda possui a anotao </td>
@WebServlet. Esta registra o servlet no contexto da aplicao e </tr>
define, por meio da propriedade urlPatterns, o padro de URL </tbody>
</table>
em que ele ir atender. Por exemplo, na Listagem 15, o parmetro </p>
urlPatterns define que o servlet atende no endereo /pedido. </div>
Tambm definimos a ordem de carregamento do Servlet como <script type=text/javascript src=<c:url
value=js/resources/jquery/jquery-2.0.3.min.js />>
1 por meio da propriedade loadOnStartup, para que ele seja </script>
carregado com maior prioridade pela aplicao em relao a outros <script type=text/javascript src=<c:url value=js/app/efetuarPedido.js>
</c:url>>
servlets. Observe que fizemos atravs de anotaes o que poderia
</script>
ter sido feito atravs das tags <servlet/> e <servlet-mapping/>, </body>
no web.xml. </html>

38 Java Magazine Edio 127

java127.indb 38 15/04/2014 11:15:58


Esses dados, quando preen-
chidos, sero enviados aps o
clique do boto Enviar, seguindo
uma abordagem REST que usa
Servlet, AJAX e JSON. A Figura 8
apresenta a tela de efetuar o
pedido.
Observe que na Listagem 16
utilizamos a tag <c:url /> do
Figura 8. Tela para efetuar pedido
JSTL core. Essa tag nos d a fle-
xibilidade de no depender de Listagem 17. Arquivo JavaScript da funcionalidade de realizao de um pedido.
contextos escritos mo (hardcoded) nos recursos estticos da
$(document).ready(function() {
nossa aplicao, o que significa que no precisaremos configurar
$(#enviar).click(function(){
manualmente o endereo desses recursos de acordo com o ambiente var json = {
em que a aplicao esteja implantada. Por exemplo, suponha que cliente : $(#cliente).val(),
itens: [
o GlassFish esteja rodando localmente e o contexto da aplicao
{sabor : $(#sabor).val(), quantidade : parseInt($(#quantidade).val())}
seja pastelaria-online. Como essa taglib foi empregada nos trechos ],email : $(#email).val(),
de cdigo que especificam a importao das bibliotecas JavaScript, telefone : $(#telefone).val()
};
o resultado final da interpretao do JSP ser a apresentao de
um HTML preenchido com o contexto completo da aplicao no $.ajax({
lugar delas e, no caso do jQuery, seria http://localhost:8080/pastelaria- url: ./pedido,
type: POST,
online/js/resources/jquery/jquery-2.0.3.min.js. Digamos agora que accepts: application/json,
a mesma aplicao ser implantada em outro servidor, com IP/ contentType: application/json; charset=utf-8,
Porta 192.168.0.7:9090, e que o contexto da aplicao nesse caso dataType: json,
data: JSON.stringify(json),
pastelaria. Nessa situao, o servidor interpretar a mesma tag timeout: 10000
<c:url /> e devolver o mesmo trecho HTML, mas apontando para }).done(function(data,textStatus, jqXHR){
alert(Pedido efetuado com sucesso);
a URL http://192.168.0.7:9090/pastelaria/js/resources/jquery/jquery-2
console.log(Pedido efetuado com sucesso);
.0.3.min.js, sem nenhuma interveno ou configurao adicional. }).fail(function(data,textStatus, jqXHR){
O cdigo JavaScript relacionado funcionalidade de realizao alert(Ocorreu um erro ao efetuar o pedido: + jqXHR);
console.log(Ocorreu um erro ao efetuar o pedido: + jqXHR);
do pedido que complementa o arquivo JSP efetuarPedido.jsp est
}).always(function(data,textStatus, jqXHR){
representado pela Listagem 17 e ser chamado de efetuarPedido.js. console.log(Finalizando efetuar pedido);
Repare que cada pgina do sistema ter um documento .js prprio });
});
com um nome igual ao JSP que ele complementa. Isso porque });
sabidamente conhecido que a melhor estratgia para organizar
os arquivos .js e .jsp dentro do projeto, permitindo maior modu-
larizao e facilidade de encontr-los, ter uma estrutura que Por ltimo, indicamos na propriedade url o endereo para o qual
os separem dessa maneira. Alm disso, tambm interessante os dados sero enviados durante a requisio.
aplicar ferramentas de compresso e ofuscao JavaScript, como Ao passo em que os pedidos so realizados pelos clientes, surge
a yuicompressor-maven-plugin. Estas comprimem todo o cdigo a necessidade de gerenci-los. Isso importante por que cada filial
.js da aplicao em nico arquivo, o que torna mais eficiente o precisa saber qual o status atual do pedido para que ela no atenda
carregamento da pgina pelo navegador, visto que apenas um algo que j foi atendido por outra. Portanto, a fim de atingir esse
documento compacto, com tamanho menor que o original, pre- objetivo, desenvolveremos agora a classe BuscarPedidosServlet,
cisar ser lido e mantido em cache no browser. exposta na Listagem 18, e a salvaremos dentro do mesmo pacote
Na Listagem 17 podemos notar o cdigo que ser vinculado ao do servlet criado anteriormente.
clique do boto Enviar apresentado na pgina da Figura 8. Nele Como j adiantamos, a responsabilidade do servlet BuscarPedi-
temos a criao de um JSON a partir dos valores enviados pelo dosServlet expor a listagem de todas as requisies j efetuadas.
usurio e a execuo assncrona de um HTTP POST via AJAX Essa listagem nada mais do que a representao em JSON dos
com o mtodo $.ajax do jQuery. No $.ajax, definimos por meio novos pedidos e daqueles que j esto sendo atendidos por alguma
das propriedades accepts, data, dataType e contentType que os dados pastelaria conveniada. Por sua vez, esses pedidos so recuperados
que sero trafegados so JSON e implementamos os callbacks a atravs de mais um mtodo do EJB PedidoService, o pendentes().
partir dos mtodos done(), fail() e always(), que correspondem s Com isso, a funcionalidade est quase completa, restando apenas
sintaxes mais recentes do jQuery e que substituram a maneira implementar a apresentao dela, o que ser feito em uma pgina
anterior que utilizava as funes success() e error() da biblioteca. JSP chamada pedidosPendentes.

Edio 127 Java Magazine 39

java127.indb 39 15/04/2014 11:15:58


Desenvolvendo aplicaes com Java EE 7

Portanto, crie o arquivo pedidosPendentes.jsp em src/main/webapp, tabela dentro da pgina que criamos. Acontece que se aplicarmos
conforme definido na Listagem 19. apenas essa biblioteca, vamos acabar misturando muito do cdigo
A pgina apresentada na Listagem 19 ainda no capaz de HTML com o JavaScript e isso degrada a organizao do cdigo, di-
mostrar a lista dos pedidos uma vez que o elemento div iden- minuindo a elegncia e manutenibilidade uma vez que essa mistura
tificado por container est em branco. Alm disso, levando tende a deix-lo mais confuso. Assim, para evitar que isso acontea,
em considerao que a listagem dos pedidos um contedo empregaremos a biblioteca underscore.js, que nos auxiliar, a partir
dinmico, precisamos de uma tabela capaz no somente de da criao de templates, a separar trechos de cdigo da linguagem
listar as pendncias, mas tambm de se auto atualizar conforme de marcao da linguagem de programao.
as novas solicitaes ocorrerem. E justamente por esse motivo
que utilizaremos JavaScript para manipular dinamicamente o Templating com underscore.js
contedo da pgina. Uma das grandes vantagens de se adotar o undescore.js que
os templates usados com ele podem receber como parmetro
Listagem 18. O servlet de busca de pedidos. um JSON. Essa capacidade nos fornece uma possibilidade muito
simples e inteligente de resolver o problema da aplicao de
@WebServlet(urlPatterns=/pedidos, loadOnStartup = 1)
public class BuscarPedidosServlet extends HttpServlet { cdigos HTML dentro de fontes JavaScript, solucionando com
elegncia o problema exposto anteriormente. Portanto, usare-
private static final long serialVersionUID = -5883823536178568608L;
mos no apenas funes bsicas da biblioteca, como tambm
@EJB estenderemos algumas delas a fim de disponibilizar mtodos
private PedidoService pedidoService; auxiliares que sero teis para a criao e formatao dos
@Override
valores. Dessa forma, comeando pela criao do JavaScript
protected void doGet(HttpServletRequest req, HttpServletResponse resp) que ser usado na apresentao dos pedidos pendentes, pre-
throws ServletException, IOException { encha o arquivo pedidosPendentes.js conforme apresentado na
resp.setContentType(application/json);
resp.setCharacterEncoding(UTF-8);
Listagem 20.
try { Aplicar o recurso de templates do underscore.js no nada
resp.setStatus(200); complicado e seu funcionamento bastante similar a scriptlets,
resp.getWriter().write(pedidoService.pendentes().asJson());
} catch (final RuntimeException e) { embora as tags lembrem um pouco as do ASP. Para us-lo, ne-
resp.sendError(500, e.getMessage()); cessrio criar uma String que representa o modelo e ento aplicar
} as tags e funes do underscore.js que preenchero essa estrutura
}
} conforme os valores, geralmente em forma de JSON, passados
a elas. Observe pela Listagem 20 que o template, representado
Listagem 19. Contedo do JSP que apresenta os pedidos pendentes.
pela varivel tabelaTemplate, usa as tags <%= e %> para definir
<%@ page language=java contentType=text/html; charset=UTF-8 os locais de incluso de valores de algum campo do JSON. Por
pageEncoding=UTF-8%> outro lado, o trecho com os elementos <% e %> tem uma caracte-
<%@ taglib prefix=c uri=http://java.sun.com/jstl/core_rt%>
<html>
rstica diferente e faz chamada para funes auxiliares como, por
<head> exemplo, o mtodo _.each(), que itera sobre elementos dentro do
<meta http-equiv=Content-Type content=text/html; charset=UTF-8> template. Em virtude disso, criaremos o JavaScript util.js, que con-
<title>Pedidos Pendentes</title>
<link rel=stylesheet type=text/css href=<c:url value=css/style.css /> ter a definio desses mtodo, conforme mostra a Listagem 21.
media=all> Com isso, o resultado final esperado para a tela dos pedidos
</head> pendentes dever ser semelhante ao apresentado na Figura 9.
<body>
<div id=container></div>
<script type=text/javascript
src=<c:url value=js/resources/jquery/jquery-2.0.3.min.js />></script>
<script type=text/javascript
src=<c:url value=js/resources/underscore/underscore.js />></script>
<script type=text/javascript src=<c:url value=js/app/util.js></c:url>>
</ script>
<script type=text/javascript
src=<c:url value=js/app/pedidosPendentes.js></c:url>></script>
</body>
Figura 9. Tela de pedidos pendentes
</html>

Agora nossa tabela j apresenta o status atual do pedido, mas


O JavaScript muito importante para permitir que elementos ainda no capaz de refletir as mudanas no estado dele. Para
HTML sejam manipulados diretamente no lado do cliente. Nesse que as novas informaes sejam visualizadas na tabela, precisa-
ponto, para o sistema que estamos elaborando, o jQuery se encai- mos usar uma funo JavaScript que remova os valores antigos
xaria perfeitamente e tornaria possvel a manipulao dinmica da e apresente os novos.

40 Java Magazine Edio 127

java127.indb 40 15/04/2014 11:15:58


Listagem 20. JavaScript dos pedidos pendentes.

var tabelaTemplate = <p align=center> + } else {


<table id=pedidos> + console.log(JSON invalido);
<thead> + }
<tr> + }
<th>ID do pedido</th> + function atender(id, status) {
<th>Data do pedido</th> + console.log(Iniciando o atendimento do pedido + id);
<th>Sabor</th> + $.ajax({
<th>Quantidade</th> + url: ./atendimento,
<th>Cliente</th> + type: POST,
<th>Telefone</th> + accepts: application/json,
<th>E-mail</th> + data: id= + id,
<th>Status</th> + timeout: 10000
<th>Atendente</th> + }).done(function(data,textStatus, jqXHR){
<th>Data do atendimento</th> + alert(Pedido em processo de atendimento);
</tr> + console.log(Pedido em processo de atendimento);
</thead> + }).fail(function(data,textStatus, jqXHR){
<tbody> + alert(Ocorreu um erro ao atender o pedido: + jqXHR);
<% _.each(pedidos, function(pedido) { %> + console.log(Ocorreu um erro ao atender o pedido: + jqXHR);
<tr> + }).always(function(data,textStatus, jqXHR){
<td><%= pedido.id %></td> + console.log(Finalizando requisio de atendimento do pedido);
<td><%= _.formatdate(pedido.dataPedido) %></td> + });
<td><%= pedido.itens[0].sabor %></td> + }
<td><%= pedido.itens[0].quantidade %></td> + $(document).ready(function() {
<td><%= pedido.cliente %></td> + (function() {
<td><%= pedido.telefone %></td> + $.ajax({
<td><%= pedido.email %></td> + url: ./pedidos,
<td><%= _.status(pedido.status) %></td> + type: GET,
<td><%= pedido.atendente %></td> + accepts: application/json,
<td><%= _.formatdate(pedido.dataAtendimento) %></td> + timeout: 10000
</tr> + }).done(function(data,textStatus, jqXHR){
<% }); %> + montarTabela(data);
</tbody> + console.log(Pedidos consultados com sucesso);
</table> + }).fail(function(data,textStatus, jqXHR){
</p>; console.log(Ocorreu um erro ao buscar os pedidos cadastrados: + jqXHR);
function montarTabela(json) { }).always(function(data,textStatus, jqXHR){
if (json) { console.log(Finalizando busca de pedidos);
$(#container).empty(); });
var template = _.template(tabelaTemplate, json); })();
$(#container).append(template); });

Listagem 21. Mtodos auxiliares de formatao e preenchimento de valores. Listagem 22. Gerando HTML atravs do template do underscore.js.

_.leadingzero = function(v) { $(#container).empty();


if (v < 10) { var template = _.template(tabelaTemplate, json);
return 0 + v; $(#container).append(template);
} else {
return v;
}
};
_.formatdate = function (v) { Esse o objetivo tratado pelo cdigo da Listagem 22. Neste cdi-
if (v) { go, aplicamos o mtodo do jQuery $.empty() na div identificada
var d = new Date(v);
var date = [ pelo id container para remover a listagem com as informaes
_.leadingzero(d.getDate()), antigas. Depois, com o underscore.js, geramos uma nova listagem
_.leadingzero(d.getMonth() + 1),
d.getFullYear() inserindo os valores do JSON ao template que estrutura a tabela.
]; Feito isso, anexamos o resultado desta operao na mesma div
var time = [
_.leadingzero(d.getHours()), container, concluindo assim o processo de atualizao dos
_.leadingzero(d.getMinutes()) dados.
];
return date.join(/) + + time.join(:); Para viabilizar esse processo utilizamos o mtodo _.template(),
} else { passando como primeiro argumento o template da tabela
return v;
} (nossa varivel tabelaTemplate) e como segundo argumento o
}; JSON recebido do servlet. Por fim, adicionamos o HTML gerado
_.status = function (v) {
switch(v) { pelo mtodo _.template() no prprio cdigo da pgina usando o
case A: return Em Atendimento; break; mtodo $.append().
case C: return Cancelado; break;
case F: return Finalizado; break;
case N: return Novo; break;
default: return v;
Registrando o atendimento de um pedido
} Outra importante funcionalidade do sistema proposto o
};
registro de atendimento ao pedido, pois precisamos fazer com

Edio 127 Java Magazine 41

java127.indb 41 15/04/2014 11:15:58


Desenvolvendo aplicaes com Java EE 7

que as pastelarias conveniadas saibam de antemo quando uma A alterao no template proporciona a criao de um boto que
solicitao j foi atendida. Para viabilizar isto, desenvolveremos dispara o atendimento. A Figura 10 apresenta a nova verso da tela
mais um servlet, apresentado na Listagem 23, que chamamos de pedidos pendentes. Ao passo em que o atendimento se inicia,
de AtendimentoServlet e que ser responsvel por registrar o o status do pedido alterado de Novo para Em atendimento
atendimento de um pedido. e o boto que foi clicado desabilitado atravs do atributo disa-
O AtendimentoServlet encarregado de receber um identifica- bled, que acrescentado ao input e que nos auxilia a evitar que
dor nico de pedido e, com isso, atualizar a nossa base de dados um segundo clique acontea. J a ao do clique est vinculada
para que o pedido fique registrado como em atendimento. ao mtodo JavaScript atender(), codificado na Listagem 25 e que
Guardaremos ainda a informao de data/hora da requisio, deve ser adicionado no arquivo js/app/pedidosPendentes.js. Esse
bem como quem o atendente que tem a responsabilidade sobre mtodo recebe como parmetros um nmero inteiro que define
a solicitao. Para no prolongarmos ainda mais este artigo, va- o identificador nico e o status do pedido.
mos considerar a identificao do atendente como sendo o ID da
prpria sesso do usurio na aplicao, uma vez que esse valor Comunicao web bidirecional com WebSockets
fcil de ser obtido de um objeto HttpSession. Um dos problemas mais frequentes durante a implementao
Com o servlet de alterao de status do pedido pronto, traba- da camada de apresentao de um sistema a maneira pela qual
lharemos agora na camada de apresentao, que ir interagir com conseguimos realizar a atualizao da informao em tempo
ele e que ser utilizada pelo usurio final. Para isso, realizaremos real para os nossos clientes. Atualmente, j existem inmeras
uma alterao simples no template da tabela dinmica que foi tcnicas, como por exemplo, o Ajax Reverso (tambm conhecido
construda durante a apresentao das Listagens 19, 20 e 21. como Ajax Push ou Comet), o uso de Applets Java, ou at Flash,
Portanto, abra o arquivo pedidosPendentes.js e o atualize, conforme que conseguem atender a esse objetivo de forma satisfatria, no
o contedo apresentado na Listagem 24. entanto, uma das maneiras mais elegantes com a implementao
de um WebSocket.
Listagem 23. O servlet de atendimento. A JSR-356, especificao da Java API for WebSocket, trata-se de
uma tecnologia capaz de permitir comunicao bidirecional entre
@WebServlet(urlPatterns=/atendimento, loadOnStartup = 1)
servidor e cliente, o que algo extremamente valioso quando
public class AtendimentoServlet extends HttpServlet {
o assunto aplicaes web. Contudo, essa caracterstica no
private static final long serialVersionUID = 7479025501206838360L; necessariamente um fruto da Java EE, visto que, antes mesmo
@EJB
do lanamento oficial da verso enterprise da linguagem, o Web
private PedidoService pedidoService; Container Jetty j possua uma implementao prpria dessa fun-
cionalidade. Nessa especificao, o JavaScript fundamental e a
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
implementao da API JavaScript essencial para o funcionamen-
throws ServletException, IOException { to dela. Em decorrncia disso, uma tecnologia que depende que
resp.setContentType(application/json); seu uso ocorra nos navegadores de Internet que a suportem.
resp.setCharacterEncoding(UTF-8);
final Gson gson = new Gson(); Voltando ao nosso projeto, vamos construir agora a classe
try { NotificacaoWebSocket, cujo cdigo apresentado na Listagem 26,
final Long id = Long.parseLong(req.getParameter(id)); dentro do pacote br.com.dev media.pastelar ia _online.
pedidoService.atender(id, req.getSession().getId());
resp.setStatus(200); present at ion.websocket. Nela aplicaremos a a notao
resp.getWriter().write(gson.toJson(new Resultado(true, @ServerEndpoint que representa o endpoint do nosso WebSo-
Atendimento registrado com sucesso)));
cket. Com isso seremos capazes de notificar todos os clientes que
} catch (final RuntimeException e) {
resp.sendError(500, e.getMessage()); esto conectados ao nosso sistema sobre qualquer mudana que
} acontea com um pedido.
}
}
Repare na classe que acabamos de criar e nas anotaes que
ela carrega. Indicamos que
ela se comporta como um
servidor WebSocket quan-
do atribumos a anotao
@ServerEndpoint, e ao in-
cluir a string /notificacao
como um parmetro dela,
definimos em qual ende-
reo ela atender as requi-
sies HTTP. Alm disso,
Figura 10. Nova verso da tela de pedidos pendentes com a anotao @Singleton

42 Java Magazine Edio 127

java127.indb 42 15/04/2014 11:15:59


especificamos que a classe um Session Bean que ser representa- sesses ativas atravs da varivel de instncia queue. Por sua vez,
do por uma instncia nica em todo o sistema. J com a anotao com as anotaes @OnClose e @OnError tratamos os eventos de
@OnOpen definimos a rotina que ser executada assim que uma finalizao da sesso e de captura de erros. No nosso projeto, para
comunicao via WebSocket for iniciada. Para o nosso sistema, ambas as ocorrncias a sesso removida das sesses ativas. Por
nesse momento que armazenamos a sesso aberta em uma lista de fim, o mtodo notificar() responsvel por iterar sobre todas as
sesses ativas dessa instncia enviando um texto JSON contendo o
ltimo estado dos pedidos pendentes. Assim, conseguimos enviar
Listagem 24. Template da tabela dinmica com incluso do boto de alterao
as alteraes dos pedidos para cada um dos nossos clientes.
do pedido.
A construo da classe NotificacaoWebSocket permite ao
var tabelaTemplate = <p align=center> + nosso servidor enviar notificaes para os clientes ativos no
<table id=pedidos> +
<thead> +
sistema, porm o navegador do lado do cliente ainda no capaz
<tr> + de interpretar essas informaes porque ainda no realizamos a
<th>ID do pedido</th> + devida implementao para trat-las. Portanto, para completar
<th>Data do pedido</th> +
<th>Sabor</th> + essa funcionalidade, precisamos adicionar cdigo JavaScript
<th>Quantidade</th> +
<th>Cliente</th> +
<th>Telefone</th> + Listagem 26. Cdigo do ServerEndpoint.
<th>E-mail</th> +
<th>Status</th> + import java.io.IOException;
<th>Atendente</th> + import java.util.Queue;
<th>Data do atendimento</th> + import java.util.concurrent.ConcurrentLinkedQueue;
<th></th> + import javax.ejb.*;
</tr> + import javax.websocket.*;
</thead> + import org.slf4j.Logger;
<tbody> + import org.slf4j.LoggerFactory;
<% _.each(pedidos, function(pedido) { %> + import br.com.devmedia.pastelaria_online.domain.service.PedidoService;
<tr> +
<td><%= pedido.id %></td> + @Singleton
<td><%= _.formatdate(pedido.dataPedido) %></td> + @ServerEndpoint(/notificacao)
<td><%= pedido.itens[0].sabor %></td> + public class NotificacaoWebSocket {
<td><%= pedido.itens[0].quantidade %></td> +
<td><%= pedido.cliente %></td> + private Queue<Session> queue = new ConcurrentLinkedQueue<Session>();
<td><%= pedido.telefone %></td> +
<td><%= pedido.email %></td> + private final Logger logger = LoggerFactory.getLogger(NotificacaoWebSocket.
<td><%= _.status(pedido.status) %></td> + class);
<td><%= pedido.atendente %></td> +
<td><%= _.formatdate(pedido.dataAtendimento) %></td> + @EJB
<td><input type=button value=Atender <%= ((pedido.status != \N\)? private PedidoService pedidoService;
disabled : undefined) %> onclick=atender(<%= pedido.id %>,
\<%= pedido.status %>\)</td> + public void notificar() {
</tr> + try {
<% }); %> + for (final Session s : queue) {
</tbody> + if (s.isOpen()) {
</table> + s.getBasicRemote().sendText(pedidoService.pendentes().asJson());
</p>; }
}
Listagem 25. Funo que inicia o atendimento, o mtodo atender(). } catch (final IOException e) {
logger.error(Ocorreu um erro ao enviar a mensagem:, e);
function atender(id, status) { }
console.log(Iniciando o atendimento do pedido + id + e status + status); }
$.ajax({
url: ./atendimento, @OnOpen
type: POST, public void open(final Session session) {
accepts: application/json, queue.add(session);
data: id= + id, }
timeout: 10000
}).done(function(data,textStatus, jqXHR){ @OnClose
alert(Pedido em processo de atendimento); public void close(final Session session) {
console.log(Pedido em processo de atendimento); queue.remove(session);
}).fail(function(data,textStatus, jqXHR){ }
alert(Ocorreu um erro ao atender o pedido: + jqXHR);
console.log(Ocorreu um erro ao atender o pedido: + jqXHR); @OnError
}).always(function(data,textStatus, jqXHR){ public void onError(final Session session, final Throwable cause) {
console.log(Finalizando requisicao de atendendimento do pedido); queue.remove(session);
}); }
} }

Edio 127 Java Magazine 43

java127.indb 43 15/04/2014 11:15:59


Desenvolvendo aplicaes com Java EE 7

capaz de interpretar as informaes em tempo real provenientes Atribuindo notificaes com WebSocket
do servidor. Vamos ento criar a instncia de um objeto do tipo Agora temos todo o arcabouo do nosso projeto montado e esta-
WebSocket, similar ao que j que expusemos anteriormente, no mos aptos para enviar notificaes para os nossos clientes. Para
carregamento da pgina. Para isso, abra o arquivo js/app/pedidos- finalizar o projeto, nos resta apenas alterar as funcionalidades
Pendentes.js e adicione o cdigo da Listagem 27. de efetuar pedido e atender pedido para que elas notifiquem
por meio do WebSocket as mudanas nos pedidos. Assim, as
Listagem 27. Instanciao do WebSocket no JavaScript. Listagens 30 e 31 apresentam as novas verses dos Servlets Efetuar-
PedidoServlet e AtendimentoServlet, que agora so capazes de
function init(endpoint, onMessage) { transmitir alteraes de estado para os nossos usurios utilizando
ws = new WebSocket(endpoint);
ws.onopen = function() {
o ServerEndpoint.
console.log(Conexo aberta com sucesso);
}; Listagem 30. Nova verso do servlet EdetuarPedidoServlet.
if (onMessage) {
ws.onmessage = onMessage; @WebServlet(urlPatterns=/pedido, loadOnStartup=1)
} public class EfetuarPedidoServlet extends HttpServlet {
ws.onclose = function() {
console.log(Conexo fechada com sucesso); private static final long serialVersionUID = -3309626245848149687L;
};
ws.onerror = function(evt) { @EJB
console.log(Ocorreu um erro no WebSocket + evt.data); private PedidoService pedidoService;
};
} @EJB
private NotificacaoWebSocket notificacaoWebSocket;

@Override
A funo init() recebe como argumentos o endereo do end-
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
point do WebSocket e a funo de callback que ser executada throws ServletException, IOException {
sempre que uma nova notificao for transmitida pelo servidor. resp.setContentType(application/json);
Por sua vez, o callback est definido na Listagem 28 pela funo resp.setCharacterEncoding(UTF-8);
atualizarTabela(), que recebe como parmetro a notificao do final Gson gson = new Gson();
try {
servidor. J a Listagem 29 apresenta a funo annima que
final InputStreamReader inputStreamReader = new
executada aps o carregamento da pgina e que adiciona o Event- InputStreamReader(req.getInputStream());
Listener load com o cdigo que inicia o WebSocket. Adicione final Pedido pedido = gson.fromJson(inputStreamReader, Pedido.class);
o cdigo das Listagens 28 e 29 no mesmo arquivo usado pela pedidoService.registrar(pedido);
Listagem 27, o pedidosPendentes,js. notificacaoWebSocket.notificar();

resp.setStatus(200);
Listagem 28. Funo de callback executada aps nova notificao. resp.getWriter().write(gson.toJson(new Resultado(true,
Pedido realizado com exito)));
function atualizarTabela(evt) { } catch (final RuntimeException e) {
if (evt.data) { resp.sendError(500, e.getMessage());
var json = $.parseJSON(evt.data); }
montarTabela(json); }
} else { }
console.log(WebSocket no retornou dados);
}
}

Listagem 29. Funo annima executada aps o carregamento da pgina de Implantando a aplicao
pedidos pendentes.
Com a primeira verso da aplicao desenvolvida, resta agora
$(document).ready(function() { implant-la no GlassFish 4 por meio do maven-glassfish-plugin.
(function(){ Para isso, utilize o Maven Goal glassfish:deploy. J para a instalao
window.addEventListener(load, init( de novas verses da nossa aplicao, podemos utilizar o Goal
ws://localhost:8080/pastelaria-online/notificacao, atualizarTabela), false);
glassfish:redeploy, que se encarrega do undeploy da verso antiga
})(); do WAR e do deploy da nova verso dele. A Listagem 32 apresenta
a instalao da nossa aplicao via linha de comando utilizando o
// cdigo apresentado nas listagens 16 e 19 omitido Maven, sendo que esse comando deve ser executado no diretrio
raiz da aplicao, onde se encontra o arquivo pom.xml. Certifique-
}
se tambm do GlassFish 4 estar em execuo.

44 Java Magazine Edio 127

java127.indb 44 15/04/2014 11:16:01


Listagem 31. Nova verso do Servlet de atendimento.
Vale lembrar que todos esses comandos podem ser executados
diretamente atravs do Eclipse. Para isso, clique no menu Run
@WebServlet(urlPatterns=/atendimento, loadOnStartup = 1) > Run As > Maven Build. Na tela Edit Configuration, basta digi-
public class AtendimentoServlet extends HttpServlet {
tar as metas do Maven na caixa de texto Goals, como mostra a
private static final long serialVersionUID = 7479025501206838360L;
Figura 11, e ento concluir o processo clicando no boto Apply.
Assim, ao clicar no boto run, o Eclipse vai executar o build do
@EJB Maven automaticamente.
private PedidoService pedidoService;
Nota
@EJB
private NotificacaoWebSocket notificacaoWebSocket; Os Goals (ou metas) so representaes de tarefas que podem ser associadas a um nmero
qualquer de fases do ciclo de vida do Maven. Isso , podem estar relacionadas fase de compilao,
@Override de realizao de testes ou outras quaisquer da ferramenta. Em geral, so especificadas como
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
argumentos na linha de comando e disponibilizadas por plug-ins instalados como, por exemplo,
throws ServletException, IOException {
resp.setContentType(application/json); o glassfish:deploy, que permite o deploy de uma aplicao no GlassFish, ou o jar:jar, que compila o
resp.setCharacterEncoding(UTF-8); atual projeto e gera um arquivo JAR. Comparativamente, podemos dizer que um Goal no Maven
final Gson gson = new Gson(); equivale aos targets do Ant.
try {
final Long id = Long.parseLong(req.getParameter(id));
pedidoService.atender(id, req.getSession().getId());
notificacaoWebSocket.notificar(); Como exerccio, deixamos para o leitor a implementao das
resp.setStatus(200); funcionalidades de finalizao e cancelamento de pedidos. Elas
resp.getWriter().write(gson.toJson(new Resultado(true, vo seguir o mesmo padro empregado pela funcionalidade de
Atendimento registrado com sucesso))); atendimento.
} catch (final RuntimeException e) {
O desenvolvimento de uma soluo corporativa no um
resp.sendError(500, e.getMessage());
} processo trivial e demanda um elevado grau de conhecimento
} de arquitetura de software e ferramentas que auxiliem na sua
} criao. medida que o sistema se torna mais crtico e uma
Listagem 32. Implantando a aplicao via linha de comando.
maior quantidade de usurios o utiliza, precisamos analisar
e ponderar com ainda mais cautela requisitos no funcionais
mvn clean install glassfish:deploy como velocidade de desenvolvimento, confiabilidade, eficincia,

Edio 127 Java Magazine 45

java127.indb 45 15/04/2014 11:16:02


Desenvolvendo aplicaes com Java EE 7

facilidade de manuteno e tolerncia a falhas. Em outras palavras, notificao, como foi o caso do projeto apresentado neste artigo e
quanto mais complexo o sistema, maior a necessidade que ele at mesmo jogos-online!
demanda para a adoo de padres, cdigo mais limpo e escolha Por fim, esperamos que o leitor tenha aproveitado ao mxi-
de frameworks aplicveis. Neste artigo, tivemos a oportunidade mo todo o contedo abordado neste artigo, proporcionando
de propor e produzir um projeto amplo que envolveu a escolha assim o conhecimento de novas tecnologias e tcnicas para o
de diferentes bibliotecas, frameworks, softwares e metodologias para desenvolvimento de aplicaes web mais robustas utilizando
atender o propsito final de desenvolver uma aplicao web capaz a plataforma Java.
de realizar atualizaes em tempo real para usurios conectados
a ela. Alm disso, destacamos em nosso estudo as vantagens que Autor
a tecnologia WebSockets, definida pela especificao da Java EE 7,
Alexandre Arcanjo de Queiroz
proporciona para os sistemas web.
alexandre.queiroz@live.com
tecnlogo em Anlise e Desenvolvimento de Sistemas pela Fa-
culdade de Tecnologia de So Paulo (FATEC-SP). Atua como revisor
tcnico em publicaes internacionais relacionadas ao desenvolvimento de
software. Tambm um usurio GNU/Linux.

Autor
Felipe Pierin
felipe.pierin@gmail.com
bacharel em Sistemas de Informao pela EACH/USP e futuro
mestre em Cincia da Computao pelo IME/USP. Analista De-
senvolvedor Java com considervel experincia no desenho e construo
de sistemas complexos e distribudos.

Links e Referncias:

Documentao da verso 7 da Java Enterprise Edition.


http://docs.oracle.com/javaee/7/tutorial/doc/home.htm

Documentao do container Java EE GlassFish.


https://glassfish.java.net/documentation.html

Lista dos navegadores que implementam WebSockets.


http://caniuse.com/websockets

Cdigo fonte do projeto construdo no artigo.


Figura 11. Criando um build para o Maven
https://github.com/alexandrearcanjodequeiroz/pastelaria-online

Site para download do GlassFish.


WebSockets uma soluo padronizada pela IETF que permite
https://glassfish.java.net/dowload.html
comunicao bidirecional baseando-se no protocolo TCP. uma
tecnologia projetada para ser implementada especialmente nos Livros
navegadores e servidores web que suportem HTML 5, embora Domain Driven Design: Atacando as Complexidades no Corao do Software, Eric
possa ser empregada por qualquer cliente ou aplicao servidora Evans, Alta Books, 2011
e surge como uma alternativa s tcnicas de Ajax Reverso, Ajax Abordagem sistemtica da modelagem de domnios a partir da aplicao de DDD.
Push ou Comet, resolvendo de maneira elegante um problema Cdigo Limpo: Habilidades Prticas do Agile Software, Robert C. Martin, Alta
recorrente dos sistemas web: a atualizao em tempo real de in- Books, 2009
formaes apresentadas nos aplicativos clientes. Princpio, padres e prticas de um cdigo limpo.
As vantagens da aplicao do WebSocket so relevantes e, por
essa razo, tal tecnologia deve ser ponderada e vista com bons
olhos durante a construo dos novos projetos. Com um poten- Voc gostou deste artigo?
cial considervel, pode ser aplicada em diversos sistemas que
demandem a atualizao de informaes em tempo real, como D seu voto em www.devmedia.com.br/javamagazine/feedback
por exemplo, a renovao de mensagens em redes sociais, atuali- Ajude-nos a manter a qualidade da revista!
zao de notcias, sistemas de conversao, e.g., chats, sistemas de

46 Java Magazine Edio 127

java127.indb 46 15/04/2014 11:16:06


Edio 127 Java Magazine 47

java127.indb 47 15/04/2014 11:16:10


Java EE 7: desenvolvendo
aplicaes batch Parte 2
Desenvolva aplicaes batch com a nova JSR
lanada com a plataforma Java EE 7

Este artigo faz parte de um curso Fique por dentro


A ltima parte deste artigo apresenta, de forma introdutria, a utilizao
dos principais componentes de software que fazem parte da JSR 352. A

N
o artigo anterior estudamos toda a parte partir do desenvolvimento de um estudo de caso simples, abordaremos
conceitual da JSR 352, disponibilizada em desde o monitoramento da execuo do processo batch at a visualizao
abril de 2013, pouco antes do lanamento da do resultado produzido pela sequncia dos passos desenvolvidos. Desta
nova verso da plataforma Java EE (a Java EE 7). Alm forma, ao final o leitor ter o conhecimento prtico necessrio para imple-
disso, foram apresentados conceitos genricos sobre os mentao de processos batch adotando este novo e importante recurso
dois principais tipos de processamento utilizados nas que a API Batch, lanada com a plataforma Java EE 7.
organizaes, Batch e On-Line. Neste artigo, aborda- Este tema til em situaes nas quais se faz necessrio o desenvol-
remos de uma forma mais tcnica a JSR 352, visando vimento de aplicaes batch visando automatizao de processos
complementar o contedo discutido no artigo anterior recorrentes e que no necessitam de intervenes manuais para serem
e tambm colocar em prtica as definies referentes ao realizados.
processamento batch, como Job e Step, por exemplo, j
analisadas na primeira parte desta srie.
Para isso, implementaremos um simples estudo de Viso geral da soluo Processos e Classes
caso para que possamos aplicar os principais conceitos Nas Figuras 1 e 2 podemos visualizar a soluo tcnica uti-
relacionados tecnologia foco do estudo. Este tem como lizada para implementao deste estudo de caso. Esta soluo
objetivo automatizar um processo para clculo de de- utiliza-se da notao BPMN para descrio do processo (Figura 1)
sempenho de alunos em uma escola que realizado a e do diagrama de classes da UML para descrio conceitual das
cada final de semestre. O processo ir resultar na gerao classes envolvidas na implementao (Figura 2). No decorrer da
de dois arquivos: parte prtica, desenvolveremos processos que fazem uso das
1. Arquivo que contm os dados dos alunos aprovados duas modalidades batch descritas no primeiro artigo, sendo
no semestre, com mdia igual ou superior a 6.0; elas Chunk-oriented (que visa quebrar um grande processo em
2. Arquivo que contm os dados dos alunos reprovados pequenos pedaos (chunks)) e Batchlet (que foca em apenas uma
no semestre, com mdia inferior a 6.0. nica tarefa).
A partir das vises apresentadas nas Figuras 1 e 2, podemos
Atravs deste pequeno estudo de caso, apresentaremos ter uma ideia do que ser necessrio desenvolvermos em prol
o desenvolvimento dos componentes de software que da automatizao do processo de clculo de notas e gerao dos
faro parte deste processo, bem como o desenvolvimento arquivos de desempenho dos alunos.
de uma pgina a ser utilizada para dar incio ao processo Antes de detalharmos cada um dos componentes de software
batch e possibilitar o monitoramento do status final de indicados na Figura 2, vamos entender a estrutura de pacotes exis-
cada uma das execues. Durante essa implementao, tente nesta nova JSR. Na Tabela 1 podemos verificar a finalidade
conheceremos as principais interfaces e classes perten- de cada um dos pacotes. Mais informaes sobre essa estrutura
centes ao pacote javax.batch. O objetivo deste estudo de podem ser encontradas no Javadoc da verso 7 da plataforma Java
caso trabalhar com as duas abordagens de desenvolvi- EE. O endereo para acesso ao Javadoc pode ser encontrado na
mento batch existentes na JSR 352: Chunk e Batchlet. seo Links deste artigo.

48 Java Magazine Edio 127

java127.indb 48 15/04/2014 11:16:11


Agora que temos uma viso geral da so-
luo de nosso estudo de caso, bem como
da estrutura de pacotes existentes na JSR
352, vamos partir para uma descrio mais
detalhada dos componentes desta API que
faremos uso. Essa descrio apresentada
na Tabela 2, onde indicamos o pacote e o
objetivo dos respectivos componentes. Nos
prximos tpicos detalharemos todos os
que sero implementados na soluo e a
finalidade de cada um deles.

Tecnologias utilizadas
Para que possamos implementar tudo o
que projetamos anteriormente, faremos
uso das seguintes ferramentas:
A IDE Eclipse Kepler;
O servidor de aplicaes GlassFish 4.0;
O framework web JavaServer Faces 2;
Engine para realizao da Injeo de
Dependncias (CDI);
A API Java Batch (implementao da
JSR 352).

Todas as informaes para download


do Eclipse, GlassFish e demais docu-
mentaes pertinentes a este artigo esto
disponveis na seo de Links.

Desenvolvimento da soluo
Para comearmos o nosso trabalho de
implementao, dividiremos nosso de-
senvolvimento em seis etapas, conforme
descrito a seguir:
1. Criao de um projeto web utilizando o
framework JSF. Para execuo deste projeto Figura 1. Viso macro do processo de gerao de arquivo com desempenho semestral dos alunos
utilizaremos o servidor de
aplicao GlassFish;
2. Adio das APIs necess-
rias para injeo de depen-
dncia (CDI) e processamen-
to batch (JSR 352);
3. Codificao do arquivo
com as definies batch uti-
lizando a linguagem JSL (Job
Specification Language). Esta
codificao utiliza o formato
XML para determinar o flu-
xo de execuo dos jobs e os
parmetros de execuo;
4. Desenvolvimento das
classes Java envolvidas na
soluo batch; Figura 2. Viso geral da soluo Modelo Conceitual

Edio 127 Java Magazine 49

java127.indb 49 15/04/2014 11:16:11


Java EE 7: desenvolvendo aplicaes batch Parte 2

Pacote Objetivo / Finalidade


javax.batch.api Pacote principal (raiz) da estrutura da API para processamentos batch da Java EE 7.
javax.batch.api.chunk Fornece APIs especficas a serem utilizadas apenas em steps do tipo Chunk.
javax.batch.api.chunk.listener Fornece APIs que definem listeners para interceptao de processos dentro de um step do tipo Chunk.
Fornece APIs que definem listeners que podem atuar tanto em nvel de job (agrupador de tarefas, que so executadas atravs de
javax.batch.api.listener
steps) quanto em nvel de step (tarefa especfica existente dentro de um job).
javax.batch.api.partition Fornece APIs para se trabalhar especificamente com steps, que so processados de forma particionada.
Fornece APIs que viabilizam a inicializao de jobs, gerenciamento e visualizao de resultados de execues batch, juntamente
javax.batch.operations
com suas excees, caso ocorram.
Fornece APIs para visualizao de detalhes referentes aos jobs, como suas execues, informaes dos jobs que esto em execu-
javax.batch.runtime
o, suas mtricas de execuo e status.
Fornece APIs que permitem o acesso ao contexto de um job ou step, viabilizando o compartilhamento de informaes prove-
javax.batch.runtime.context
nientes dos mesmos. Atua de forma similar ao javax.batch.runtime.

Tabela 1. Estrutura de pacotes da JSR 352

Pacote de Origem Componente Objetivo / Finalidade


Tem como finalidade trabalhar o processo de leitura de arquivos. Possui implementao
padro para os mtodos de abertura de arquivo, fechamento de arquivo e retorno de infor-
AbstractItemReader maes para checkpoint. Implementa a interface ItemReader e possui um mtodo abstrato
para que cada classe que a estenda implemente seu prprio mtodo de leitura de arquivos.
o primeiro passo de um chunk-step.
Interface que tem como finalidade disponibilizar um padro para implementao de classes
que realizam o processamento dos pedaos (chunks) de contedos lidos pelas classes que
javax.batch.api.chunk ItemProcessor estendem AbstractItemReader ou implementam a interface ItemReader. o segundo passo
de um chunk-step e tem como objetivo trabalhar as informaes e gerar uma sada de dados
que ser utilizada pela etapa de escrita (writer). Pode ser opcional dentro do step.
Tem como finalidade trabalhar o processo de gravao de arquivo. Possui implementa-
o padro para os mtodos de abertura de arquivo, fechamento de arquivo e retorno
AbstractItemWriter de informaes para checkpoint. Implementa a interface ItemWriter e possui um mtodo
abstrato para que cada classe que a estenda implemente seu prprio mtodo de gravao
de arquivos. o terceiro e ltimo passo de um chunk-step.
Interface que tem como finalidade prover o acesso ao contexto de um job. Atravs do
contexto pode-se obter informaes relativas ao processo batch, tais como o status da
javax.batch.runtime.context JobContext
execuo, obteno do ID da instncia, nome, suas propriedades e demais informaes
relacionadas ao Job.
Interface que tem como finalidade prover funcionalidades para a operao de jobs. Atravs
de JobOperator um job pode ser iniciado, reiniciado e parado. Alm disso, conseguimos
javax.batch.operations JobOperator
obter propriedades da execuo de um job, como o(s) nome(s) do(s) arquivo(s) de entrada, a
instncia a ser executada, entre outras informaes.
Classe que representa o ambiente de execuo batch da JSR 352. Tem como objetivo devol-
ver uma instncia da classe que implementa a interface JobOperator para realizar a gesto
BatchRuntime
da execuo de um job. Todo o processo comea a partir da solicitao de um JobOperator
realizada pela classe BatchRuntime.
Interface que tem como finalidade prover funcionalidades para obteno de informaes
JobExecution de jobs que esto sendo executados. Uma instncia de classe que implementa a interface
javax.batch.runtime JobExecution sempre obtida atravs de JobOperator.
Interface que tem como finalidade prover funcionalidades para obteno de informaes
referentes instncia de um job que j foi executado. Atravs desta interface possvel
apenas obter o ID da instncia do job executado e seu nome. Uma instncia de classe que
JobInstance
implementa a interface JobInstance sempre obtida atravs de JobOperator e o ID da
instncia de um job sempre retornado atravs dos processos de start ou restart de um Job,
que podem ser realizados tambm por JobOperator.
Tem como finalidade trabalhar em um step do tipo Batchlet. Implementa a interface
javax.batch.api AbstractBatchlet Batchlet e contm um mtodo abstrato para que cada classe que a estenda implemente seu
prprio mtodo de processamento de informaes/arquivos.

Tabela 2. Descrio dos componentes da API que sero utilizados

50 Java Magazine Edio 127

java127.indb 50 15/04/2014 11:16:12


5. Desenvolvimento dos componentes JSF (managed bean e pgina Ao clicar em Finish na janela de configurao do servidor de
XHTML para monitoramento dos jobs); aplicao, seremos redirecionados para a janela inicial do Wi-
6. Realizao dos testes e acompanhamento dos resultados das zard de criao do projeto. Assim, informe na lista de opes
execues. denominada Configuration, a opo JavaServer Faces v2.1 Project
(veja a Figura 5). Aps a seleo, clique no boto Finish para que
Para realizarmos o desenvolvimento, partimos da premissa que a criao do projeto web seja efetuada.
voc, leitor, j tem conhecimentos bsicos sobre a utilizao do
Eclipse com o servidor de aplicao GlassFish.

Criao do projeto JSF


Para criao do projeto JSF associado ao servidor GlassFish, den-
tro da ferramenta Eclipse, clique na opo File > New e, em seguida,
em Dynamic Web Project. Feito isto, ser aberta uma nova janela
para que sejam informados os dados pertinentes ao projeto.
Em Project Name vamos informar o nome do projeto e associar o
mesmo ao servidor de aplicaes GlassFish, atravs do clique no
boto denominado New Runtime. Aps o clique em New Runtime
ser aberta uma nova janela contendo os servidores web e de apli-
cao disponveis para utilizao. Esta tela carregada de acordo
com todos os plug-ins de servidores que se encontram instalados
no Eclipse. Nesta janela selecionaremos a opo Glass-Fish e, logo
aps, a verso 4.0, como indica a Figura 3.
Figura 4. Configurando o servidor GlassFish

Figura 3. Selecionando o servidor GlassFish

Caso voc no encontre a opo GlassFish, voc poder instalar


o plugin atravs do tutorial de instalao indicado na seo Links.
Para finalizarmos a configurao do servidor, necessrio clicar- Figura 5. Selecionando a opo JavaServer Faces v2.1 Project
mos em Next e informarmos no campo Glassfish Server Directory
o diretrio de instalao do servidor (ver Figura 4). Adio das APIs necessrias para CDI e processamento batch
Depois, basta clicar em Finish. Na seo Links o leitor tam- Para que possamos desenvolver sem problemas utilizando os
bm encontrar a URL para download do GlassFish em sua mecanismos de CDI e processamento Batch, precisamos adicionar
verso 4.0. ao nosso projeto algumas APIs. Elas ficam localizadas no diretrio

Edio 127 Java Magazine 51

java127.indb 51 15/04/2014 11:16:12


Java EE 7: desenvolvendo aplicaes batch Parte 2

de instalao do GlassFish, mais precisamente no diretrio de- <job>: Esta tag representa um Job. Para definir a nomenclatura
nominado modules. Os nomes das bibliotecas so: javax.batch-api do job, utilizamos o atributo id. Neste caso, temos um Job deno-
.jar e weld-integration.jar. minado JobDesempenho;
Para adicionarmos as bibliotecas citadas anteriormente, devemos <flow>: Esta tag realiza a definio de um fluxo de execuo que
clicar com o boto direito do mouse em cima do projeto criado pode conter 1 ou N steps. O atributo id inserido para definir o
(na view Project Explorer do Eclipse) e selecionar as opes Build nome do fluxo. Assim, este fluxo pode ser referenciado em uma
Path e, logo depois, Configure Build Path. Ao abrir a nova janela, tag <next> para determinar o prximo fluxo a ser executado;
selecione a aba denominada Libraries e clique no segundo boto, <step>: Esta tag realiza a definio de um step. O atributo id,
localizado direita da janela, chamado Add External Jars. A par- assim como nos casos anteriores, inserido para definir a nomen-
tir disso ser possvel informar o local onde as bibliotecas esto clatura do step. Da mesma forma que a tag <flow>, este id pode ser
localizadas, para que sejam adicionadas estrutura do projeto. referenciado em uma tag <next> para determinar o prximo step
Depois, basta clicar no boto OK. a ser executado. Alm disso, dentro do monitor de execues Batch
Por fim, para habilitarmos o CDI dentro do nosso projeto web, existente no servidor de aplicao GlassFish, possvel acompa-
se faz necessria a criao do arquivo beans.xml no diretrio Web- nhar o resultado de cada um dos steps executados por um job.
Content\WEB-INF\. Para o nosso projeto, este arquivo dever ser Neste caso, o id do step tambm utilizado. Em nosso exemplo,
criado sem nenhum contedo. temos dois steps: o primeiro, com a finalidade de processar um
arquivo de alunos com suas respectivas notas, calculando suas
Arquivo de configurao dos processos batch mdias e classificando-os como Aprovados ou Reprovados; e o
Para que a execuo dos Jobs seja possvel, necessrio que exista segundo e ltimo step, com a finalidade de segmentar os dados,
pelo menos um arquivo JSL (arquivo utilizado para configurao separando os alunos Aprovados dos alunos Reprovados;
da estrutura do Job, seus respectivos Steps e fluxos de execuo) <chunk>: Esta tag define um step como sendo do tipo chunk-
configurado no projeto. oriented. A propriedade denominada item-count indica que a
Por padro, todos os arquivos JSL devem ser armazenados no leitura-processamento-sada de informaes ocorrer em peda-
diretrio WebContent\WEB-INF\classes\META-INF\batch-jobs os (chunks) de 10 em 10 registros (valor default caso no seja
com a extenso do tipo .xml. A definio do nome do arquivo informado);
fica a cargo do desenvolvedor, porm deve-se ter em mente um <reader>: Esta tag define qual ser a classe que atuar como
padro de nomenclatura, uma vez que, quando o incio do Job for classe de leitura de informaes dentro do processo referente ao
realizado, o nome do arquivo JSL dever ser informado. step atual;
Dentro de um arquivo JSL podemos definir vrias propriedades <processor>: Esta tag define qual ser a classe que atuar como
a serem utilizadas pelos Jobs durante seu processamento, assim classe de tratamento das informaes referente ao step atual;
como vrios fluxos de execuo, definio da execuo de Jobs <writer>: Esta tag define qual ser a classe que atuar como
em paralelo e at mesmo critrios de continuao da execuo em classe de gravao de informaes dentro do processo referente
caso de falhas. Na Listagem 1 podemos verificar o arquivo JSL ao step atual;
que utilizaremos em nosso exemplo. <next>: Como j informado, essa tag utilizada para definir a
execuo do prximo step ou fluxo existente no job. A proprieda-
Listagem 1. Contedo do arquivo JSL denominado JobDesempenho.xml. de on indica qual o retorno esperado frente ao processamento
do step corrente para que o prximo step/fluxo seja executado.
<job id=JobDesempenho xmlns=http://xmlns.jcp.org/xml/ns/javaee
version=1.0 restartable=true>
A propriedade to indica qual ser o step/fluxo que ser executado.
<flow id=flow1> Os retornos esperados podem ser:
<step id=step1 > - ABANDONED;
<chunk item-count=10>
<reader ref=itemReaderDesempenho />
- COMPLETED;
<processor ref=itemProcessorDesempenho /> - FAILED;
<writer ref=itemWriterDesempenho /> - STARTED;
</chunk>
<next on=COMPLETED to=step2 /> - STARTING;
</step> - STOPPED;
<step id=step2> - STOPPING.
<batchlet ref=selecaoBatchlet />
</step> <batchlet>: Esta tag define o step como sendo do tipo Batchlet
</flow> e tambm indica qual ser a classe que atuar como classe de
</job>
processamento.

A seguir podemos visualizar todas as consideraes referentes Classes Java envolvidas no processo Batch
listagem anterior. Essas consideraes esto divididas por <tags> Na Listagem 1 especificamos todas as classes que participaro
para facilitar o entendimento: do processo batch. Todas elas foram definidas atravs das tags

52 Java Magazine Edio 127

java127.indb 52 15/04/2014 11:16:13


<reader>, <processor>, <writer> e <batchlet>. Agora, apresen- Podemos identificar na Listagem 2 a classe que implementa o
taremos o objetivo de cada uma delas: processo de leitura do arquivo de desempenho dos alunos. im-
ItemReaderDesempenho: Classe utilizada para realizar a leitura portante citar que todas as classes possuem nomes auto descritivos
do arquivo que contm os dados dos alunos e suas respectivas e comentrios para viabilizar o entendimento de cada linha de
notas. Obtm atravs do parmetro de execuo denominado cdigo. Quando executada a classe ItemReaderDesempenho, a
INPUT_DATA_FILENAME configurado antes da execuo primeira operao a ser acionada ser a de abertura do arquivo,
do processo batch o nome do arquivo que ser aberto e ter que feita atravs do mtodo open(Serializable prevCheckpoint-
seus dados lidos; Info). Dentro dele, invocamos o mtodo jobParameters.get() para
ItemProcessorDesempenho: Classe utilizada para calcular a obteno do parmetro denominado INPUT_DATA_FILENA-
mdia de cada aluno e definir qual foi o desempenho dos mes- ME, que corresponde ao nome do arquivo de entrada atribudo
mos com relao ao semestre, classificando-os como Aprovados ao job antes de sua execuo. Este nome ser utilizado no processo
ou Reprovados; de abertura.
ItemWriterDesempenho: Classe utilizada para gravao Aps esse processo, feita uma chamada ao mtodo readItem(),
dos dados de desempenho dos alunos lidos pela classe Item- o qual contm a implementao do processo de leitura e controle
ReaderDesempenho e processados pela classe ItemProces- dos registros lidos. Este controle realizado pelo acrscimo de
sorDesempenho. Esta classe far a persistncia do arquivo de um ao contador numeroRegistros a cada registro diferente de
desempenho; null, ou seja, a cada registro que possui contedo.
SelecaoBatchlet: Classe utilizada para segmentar o arquivo de A contabilizao dos registros ser feita apenas para que seja
desempenho dos alunos em dois, sendo o primeiro para armaze- possvel demonstrar a utilidade do parmetro item-count (confi-
nar os alunos aprovados e o seguindo para armazenar os dados gurado no XML de nosso Job, conforme descrito na Listagem 1)
dos alunos reprovados. na execuo de nosso processo batch. Este parmetro pode ser
inserido na definio de nosso Job para limitar a quantidade de
Para que essas classes funcionem com xito, definimos padres registros que sero lidos e processados por chunk (pedao). Por
de nomenclatura para todos os arquivos que faremos uso (arquivos fim, o mtodo close() realiza o fechamento do arquivo.
de entrada e sada trabalhados durante a execuo de todos os A finalidade do componente descrito na Listagem 2 apenas a de
steps), para o indicador de aprovao e reprovao e para o nome realizar a leitura registro a registro do arquivo de notas dos alunos,
do job que ser utilizado no momento da execuo. Todos esses obtendo insumos para a prxima fase, que ser realizada pela
padres sero mais bem descritos posteriormente. classe ItemProcessorDesempenho, apresentada na Listagem 3.
A seguir so apresentadas quatro classes para suporte, que O objetivo da classe ItemProcessorDesempenho processar o
sero utilizadas em todo o processo batch para o tratamento dos item lido por meio do mtodo padro processItem(Object obj),
dados existentes nos arquivos (arquivo de notas, desempenho estrutur-lo e defini-lo como um registro do tipo Aluno atravs do
e arquivo de alunos aprovados e reprovados) que participaro split (segmentao) das informaes de entrada, dando um signi-
dos processos de leitura, processamento e sada decorrentes da ficado aos dados. Alm disso, essa classe ainda calcula a mdia do
execuo batch. So elas: aluno para, por fim, definir sua situao (aprovado ou reprovado).
ParameterInterface: Interface criada para armazenamento das O processo de clculo da mdia, bem como o processo de definio
constantes que so utilizadas com frequncia dentro das demais da situao do aluno, realizado por intermdio de um mtodo
classes do projeto. Atravs dela temos a informao dos caminhos auxiliar, denominado calcularDesempenhoAluno(AlunoTO
de todos os arquivos que faremos uso durante o processo, bem alunoTO).
como sinalizadores de aprovao/reprovao e o nome do job Podemos verificar na Listagem 4 uma amostra do arquivo de
que ser executado; entrada que foi utilizado para este estudo de caso. Para distinguir
AlunoTO: Classe do tipo Transfer Object que ser trafegada nas as informaes existentes em sua estrutura, utilizado o ; como
diversas camadas do sistema. Contm os atributos que definem caractere delimitador. Sendo assim, temos na primeira coluna a
um aluno dentro da instituio, como matrcula, nome e as trs matrcula do aluno, na segunda o nome do aluno, na terceira a
notas do semestre. Esta classe representa a estrutura do arquivo nota um, na quarta a nota dois e na quinta coluna a nota trs.
de entrada a ser lido e processado posteriormente; Agora que j fizemos a leitura (ItemReaderDesempenho) e o
BoletimTO: Classe do tipo Transfer Object que ser trafegada processamento (ItemProcessorDesempenho) do desempenho
nas diversas camadas do sistema. Contm os dados de um alu- dos alunos, precisamos armazenar os registros resultantes no
no, alm de armazenar sua mdia final e situao (Aprovado ou arquivo de desempenho de alunos. Este o objetivo do compo-
Reprovado); nente denominado ItemWriterDesempenho e apresentado na
JobInformationTO: Classe do tipo Transfer Object que ser Listagem 5. A gravao dos registros realizada pelo mtodo
trafegada nas diversas camadas do sistema. Contm os dados do padro writeItems(@SupressWarnings(rawtypes) List list).
processamento de um job que utilizaremos em nossa pgina de Dentro dele, utilizamos o mtodo jobParameters.get() para obten-
monitoramento de execuo. o do parmetro denominado OUTPUT_DATA_FILENAME,

Edio 127 Java Magazine 53

java127.indb 53 15/04/2014 11:16:13


Java EE 7: desenvolvendo aplicaes batch Parte 2

Listagem 2. Cdigo da classe ItemReaderDesempenho.

package br.com.devmedia.batch.reader;
import java.io.BufferedReader; //Realiza o processo de abertura do arquivo.
import java.io.FileReader; fr = new FileReader(resourceName);
import java.io.Serializable; br = new BufferedReader(fr);
import java.util.Properties; System.out.println([ItemReaderDesempenho] Abertura do arquivo de
import javax.batch.api.chunk.AbstractItemReader; notas dos alunos a partir do registro: + numeroRegistros);
import javax.batch.operations.JobOperator; }
import javax.batch.runtime.BatchRuntime;
import javax.batch.runtime.context.JobContext; @Override
import javax.inject.Inject; //Mtodo executado logo aps o mtodo open(). Tem como objetivo
import javax.inject.Named; //realizar a leitura registro a registro.
public Object readItem() throws Exception {
@Named(itemReaderDesempenho)
public class ItemReaderDesempenho extends AbstractItemReader { //Realiza a leitura do arquivo.
line = br.readLine();
@Inject
private JobContext jobContext; //Valida se o registro diferente de null para que
private FileReader fr; //se possa realizar a contagem do mesmo.
private BufferedReader br; if (line != null) {
private String line; /* A partir do momento em que o registro lido com sucesso,
private int numeroRegistros; * incrementamos 1 ao nmero de registros.
*/
@Override numeroRegistros++;
//Mtodo com a funo de realizar a abertura do arquivo. }
public void open(Serializable prevCheckpointInfo) throws Exception { return line;
}
//JobOperator utilizado para se obter os parmetros de
//execuo definidos para o job. @Override
JobOperator jobOperator = BatchRuntime.getJobOperator(); //Mtodo de fechamento do arquivo, executado ao final da leitura
//de todos os registros.
//Obtm os parmetros de execuo existentes para este public void close() throws Exception {
//Job definidos em tempo de execuo. br.close();
Properties jobParameters = jobOperator.getParameters( fr.close();
jobContext.getExecutionId()); System.out.println([ItemReaderDesempenho] Mtodo de Fechamento
do Arquivo foi Executado. Total de Registros Lidos:
//Obtm o nome do arquivo de entrada, enviado como parmetro + numeroRegistros + ]]);
//no momento da execuo do Job. }
String resourceName = (String) jobParameters.get(INPUT_DATA_FILENAME); }

Listagem 3. Cdigo da classe ItemProcessorDesempenho.

package br.com.devmedia.batch.processor; alunoTO.setNome(campos[1]);


import javax.batch.api.chunk.ItemProcessor; alunoTO.setNota1(Double.parseDouble(campos[2]));
import javax.inject.Named; alunoTO.setNota2(Double.parseDouble(campos[3]));
import br.com.devmedia.batch.TO.AlunoTO; alunoTO.setNota3(Double.parseDouble(campos[4]));
import br.com.devmedia.batch.TO.BoletimTO; System.out.println([ItemProcessorDesempenho] Item Processado.);
import br.com.devmedia.batch.interfaces.ParameterInterface;
//Retorna objeto tratado e com desempenho calculado.
@Named(itemProcessorDesempenho) return calcularDesempenhoAluno(alunoTO);
public class ItemProcessorDesempenho implements ItemProcessor { }

@Override //Mtodo que tem como objetivo realizar o clculo da mdia dos alunos
//Mtodo executado para realizar a tratativa de cada um dos itens //e definir o desempenho dos mesmos.
//lidos pela etapa de ItemReader. private BoletimTO calcularDesempenhoAluno(AlunoTO alunoTO) {
public Object processItem(Object obj) throws Exception { BoletimTO boletimTO = new BoletimTO();
boletimTO.setAlunoTO(alunoTO);
//Instancia o objeto que representa o arquivo de entrada boletimTO.setMediaFinal((alunoTO.getNota1() + alunoTO.getNota2() +
//contendo os dados de notas dos alunos. alunoTO.getNota3()) / 3);
AlunoTO alunoTO = new AlunoTO(); if(boletimTO.getMediaFinal() < 6) {
boletimTO.setSituacao(ParameterInterface.DISAPPROVED);
//Realiza a diviso dos campos do arquivo utilizando como } else {
//delimitador o caractere ;. boletimTO.setSituacao(ParameterInterface.APPROVED);
String[] campos = obj.toString().split(;); }
return boletimTO;
//Realiza a formatao da entrada, estruturando a informao. }
alunoTO.setMatricula(Integer.parseInt(campos[0])); }

54 Java Magazine Edio 127

java127.indb 54 15/04/2014 11:16:13


que corresponde ao nome do arquivo de sada atribudo ao job O arquivo produzido pela classe ItemWriterDesempenho
antes de sua execuo. Este nome ser utilizado no processo de ser utilizado como entrada pelo Batchlet no step seguinte, para
escrita dos registros (arquivoDesempenhoAlunos.csv). Note que que o mesmo realize a segmentao desse arquivo, gerando
tambm utilizamos a classe DecimalFormat para realizarmos um novo arquivo com todos os alunos reprovados e outro com
a formatao dos valores numricos correspondentes s mdias todos os alunos aprovados. A estrutura do Batchlet pode ser
dos alunos. Alm disso, temos ainda uma estrutura de loop para visualizada na Listagem 6.
realizar a leitura de todos os registros. Como em nosso XML de A ideia de um componente do tipo Batchlet realizar uma
definio do Job (JSL) temos o atributo item-count definido como 10, tarefa especfica, diferente do estilo chunk, onde temos uma
a lista recebida por parmetro pelo mtodo writeItems() conter classe destinada leitura, uma destinada a processamento e
sempre 10 registros. outra destinada escrita. Devido a esse comportamento, o
Batchlet SelecaoSituacaoBatchlet possui um nico mtodo,
Listagem 4. Amostra do arquivo de entrada arquivoNotasAlunos.csv. denominado process(), que em nosso cenrio realiza o processo
de segmentao do arquivo de desempenho de alunos com
40201;Kaseem Potter;3.5;5;7
77351;Emery Morales;3.5;10;9 o auxlio dos mtodos: processarAprovados(), responsvel
75143;Lee Puckett;3.5;5;4 por realizar a leitura do arquivo de desempenho, identificar
55953;Tad Valencia;3.5;7;5
os alunos aprovados e gerar um arquivo resultante contendo
13435;Austin Roberts;7;8;7
15879;Axel Deleon;7;3;8 apenas esses alunos; processarReprovados(), responsvel por
56478;Bevis Black;7;3;0 realizar a leitura do arquivo de desempenho, identificar os alu-
13887;Armando Merrill;7;1;1
25044;Lewis Ayers;7;9;7
nos reprovados e gerar um arquivo resultante contendo apenas
59562;Vance Romero;7;5;5 esses alunos reprovados; e removerArquivoIntermediario(),

Listagem 5. Cdigo da classe ItemWriterDesempenho.

package br.com.devmedia.batch.writer; Properties jobParameters = jobOperator.getParameters(


jobContext.getExecutionId());
import java.io.FileWriter;
import java.io.PrintWriter; //Obtm nome do arquivo de sada, enviado como parmetro
import java.text.DecimalFormat; //no momento da execuo do Job.
import java.util.List; String resourceName = (String) jobParameters.get(OUTPUT_DATA_FILENAME);
import java.util.Properties;
//Realiza o processo de abertura do arquivo.
import javax.batch.api.chunk.AbstractItemWriter; fw = new FileWriter(resourceName, true);
import javax.batch.operations.JobOperator; pw = new PrintWriter(fw);
import javax.batch.runtime.BatchRuntime;
import javax.batch.runtime.context.JobContext; BoletimTO boletimTO = null;
import javax.inject.Inject;
import javax.inject.Named; String registro = null;

import br.com.devmedia.batch.TO.BoletimTO; //Prepara estrutura para formatao do valor numrico da mdia.
DecimalFormat df = new DecimalFormat(#0.00);
@Named(itemWriterDesempenho)
public class ItemWriterDesempenho extends AbstractItemWriter { //Realiza a gravao de todos os itens recebidos como parmetro.
for (int i=0 ; i < list.size() ; i++) {
@Inject
private JobContext jobContext; Object obj = list.get(i);

private FileWriter fw = null; boletimTO = (BoletimTO) obj;
private PrintWriter pw = null;
registro = (boletimTO.getAlunoTO().getMatricula() + ; +
@Override boletimTO.getAlunoTO().getNome() + ; + df.format(boletimTO.getMediaFinal())
//Mtodo com a funo de realizar a gravao das informaes recebidas. + ; + boletimTO.getSituacao());
//Como o item-count 10, a persistncia sempre ser com uma
//lista de 10 registros. pw.println(registro);
public void writeItems(@SuppressWarnings(rawtypes) List list) }
throws Exception {
//Realiza o processo de fechamento do arquivo.
//JobOperator utilizado para se obter os parmetros de execuo fw.close();
//definidos para o job. pw.close();
JobOperator jobOperator = BatchRuntime.getJobOperator();
System.out.println([ItemWriterDesempenho] Itens Gravados.);
//Obtm os parmetros de execuo existentes para este Job }
//definidos em tempo de execuo. }

Edio 127 Java Magazine 55

java127.indb 55 15/04/2014 11:16:13


Java EE 7: desenvolvendo aplicaes batch Parte 2

Listagem 6. Cdigo da classe SelecaoSituacaoBatchlet.

package br.com.devmedia.batch.batchlet; System.out.println(Erro na Gravao do Arquivo de Aprovados: + e.getMessage());


retorno = NOK;
import java.io.BufferedReader;
import java.io.File; //Bloco finally para liberao dos recursos utilizados.
import java.io.FileReader; } finally {
import java.io.FileWriter; try {
import java.io.IOException; pw.close();
import java.io.PrintWriter; fw.close();
br.close();
import javax.batch.api.AbstractBatchlet; fr.close();
import javax.inject.Named; } catch (IOException e) {
System.out.println(Erro no Fechamento dos Arquivos: + e.getMessage());
import br.com.devmedia.batch.interfaces.ParameterInterface; retorno = NOK;
}
@Named(selecaoBatchlet) }
public class SelecaoSituacaoBatchlet extends AbstractBatchlet { }

private FileWriter fw; private void processarReprovados() {


private FileReader fr;
private PrintWriter pw; try {
private BufferedReader br; // Configura o arquivo para leitura gerado durante o processamento do step 1.
private String retorno; fr = new FileReader(ParameterInterface.OUTPUT_DATA_FILENAME);
br = new BufferedReader(fr);
@Override
//Mtodo executado para realizar o processo de segmentao. // Configura o arquivo para gravao dos alunos reprovados.
public String process() throws Exception { fw = new FileWriter(ParameterInterface.OUTPUT_DISAPPROVED_DATA_FILENAME);
pw = new PrintWriter(fw);
//Mtodo criado para realizar o armazenamento apenas dos alunos Aprovados.
processarAprovados(); String registro = null;
String[] campos = null;
//Mtodo criado para realizar o armazenamento apenas dos alunos Reprovados.
processarReprovados(); while((registro = br.readLine()) != null) {
campos = registro.split(;);
//Mtodo criado para remover o arquivo intermedirio gerado pelo step 1. //Obtm o ltimo campo do registro.
removerArquivoIntermediario(); //Campo corresponde situao do aluno (Aprovado ou Reprovado).
if(campos[campos.length-1].equals(ParameterInterface.DISAPPROVED)) {
System.out.println([SelecaoSituacaoBatchlet] Arquivos de pw.println(registro);
Aprovados e Reprovados Gravados.); }
}
//Devolve um cdigo de retorno, que poder ser utilizado para verificar
//se o Batchlet Step foi executado com sucesso. retorno = OK;
return retorno;
} //Bloco catch para realizar o tratamento de exceo.
} catch (IOException e) {
private void processarAprovados() { System.out.println(Erro na Gravao do Arquivo de Aprovados:
+ e.getMessage());
try { retorno = NOK;
//Configura o arquivo gerado durante o processamento do step 1 para leitura.
fr = new FileReader(ParameterInterface.OUTPUT_DATA_FILENAME); //Bloco finally para liberao dos recursos utilizados.
br = new BufferedReader(fr); } finally {
try {
//Configura o arquivo para gravao dos alunos aprovados. pw.close();
fw = new FileWriter(ParameterInterface.OUTPUT_APPROVED_DATA_FILENAME); fw.close();
pw = new PrintWriter(fw); br.close();
fr.close();
String registro = null; } catch (IOException e) {
String[] campos = null; System.out.println(Erro no Fechamento dos Arquivos: + e.getMessage());
retorno = NOK;
while((registro = br.readLine()) != null) { }
campos = registro.split(;); }
//Obtm o ltimo campo do registro. }
//Campo que corresponde situao do aluno (Aprovado ou Reprovado).
if(campos[campos.length-1].equals(ParameterInterface.APPROVED)) { private void removerArquivoIntermediario() {
pw.println(registro);
} File arquivoIntermediario = new File(ParameterInterface.OUTPUT_DATA_FILENAME);
}
//Remove o arquivo intermedirio, gerado pelo step 1.
retorno = OK; arquivoIntermediario.delete();
//Bloco catch para realizar o tratamento de exceo. }
} catch (IOException e) { }

56 Java Magazine Edio 127

java127.indb 56 15/04/2014 11:16:14


responsvel por realizar a remoo do arquivo de desempenho, Listagem 8. Cdigo da classe AlunoTO.
anteriormente lido pelos mtodos processarAprovados() e
package br.com.devmedia.batch.TO;
processarReprovados().
Aps a implementao das classes principais, podemos visua- import java.io.Serializable;
lizar na Listagem 7 a estrutura da interface ParameterInterface,
public class AlunoTO implements Serializable {
criada para centralizarmos o contedo de todos os parmetros
que utilizamos durante a execuo do processo batch. Essa orga- private static final long serialVersionUID = 3025227029929852437L;
nizao facilitar futuras manutenes (caso sejam necessrias),
public AlunoTO() {}
como a mudana do nome do arquivo de entrada, no sendo
preciso alterarmos o nome do arquivo em todas as classes que o private int matricula;
utilizam. Os parmetros existentes nesta interface so: private String nome;
private double nota1;
JOB_NAME Responsvel por armazenar o nome do Job; private double nota2;
INPUT_DATA_FILENAME Responsvel por armazenar o private double nota3;
nome do arquivo de entrada utilizado pelo step 1. Este arquivo
//Mtodos get e set omitidos...
contm o nome dos alunos e suas respectivas notas; }
OUTPUT_DATA_FILENAME Responsvel por armazenar o
Listagem 9. Cdigo da classe BoletimTO.
nome do arquivo de sada gerado pelo step 1. Este arquivo contm
a mdia final dos alunos e respectivas classificaes (Aprovado package br.com.devmedia.batch.TO;
ou Reprovado);
import java.io.Serializable;
OUTPUT_APPROVED_DATA_FILENAME Responsvel por
armazenar o nome do arquivo de sada gerado pelo step 2 con- public class BoletimTO implements Serializable {
tendo todos os alunos aprovados;
private static final long serialVersionUID = -6319246099223322428L;
OUTPUT_DISAPPROVED_DATA_FILENAME Responsvel
por armazenar o nome do arquivo de sada gerado pelo step 2 public BoletimTO() { }
contendo todos os alunos reprovados;
private AlunoTO alunoTO;
APPROVED Constante que representa o aluno Aprovado; private double mediaFinal;
DISAPPROVED Constante que representa o aluno Reprovado. private String situacao;

//Mtodos get e set omitidos...
Listagem 7. Cdigo da interface ParameterInterface. }

package br.com.devmedia.batch.interfaces; Listagem 10. Cdigo da classe JobInformationTO.


public interface ParameterInterface {
package br.com.devmedia.batch.TO;
//Representa o nome do arquivo XML JSL sem a extenso .xml.
public static final String JOB_NAME = JobDesempenho; import java.io.Serializable;
import java.util.Date;
//Esse bloco de cdigo representa os arquivos de entrada e sada.
public static final String INPUT_DATA_FILENAME = public class JobInformationTO implements Serializable {
D:/arquivo-desempenho/arquivoNotasAlunos.csv;
public static final String OUTPUT_DATA_FILENAME = private static final long serialVersionUID = 8430944292564663080L;
D:/arquivo-desempenho/arquivoDesempenhoAlunos.csv; public JobInformationTO() {}
public static final String OUTPUT_APPROVED_DATA_FILENAME = private long instanceID;
D:/arquivo-desempenho/arquivoAlunosAprovados.csv; private long executionID;
public static final String OUTPUT_DISAPPROVED_DATA_FILENAME = private String jobName;
D:/arquivo-desempenho/arquivoAlunosReprovados.csv; private Date startTime;
private Date endTime;
//Constantes que representam a situao dos alunos. private String batchStatus;
public static final String APPROVED = Aprovado;
public static final String DISAPPROVED = Reprovado; //Mtodos get e set omitidos...
} }

Como vimos, essa interface adotada em diversas classes analisa- Criando a interface web para iniciar o processo
das anteriormente e tambm estar presente no managed bean que Para possibilitarmos a execuo do processo batch, criaremos
codificaremos no tpico Desenvolvendo dos componentes JSF. uma interface web que oferecer ao usurio uma opo que,
Por fim, nas Listagens 8, 9 e 10 encontraremos a definio das quando ativada, ir gerar o desempenho dos alunos. Esta pgi-
classes auxiliares descritas no incio deste tpico, que possuem um na possuir ainda um simples monitor de execues, para que
papel importante na estruturao das informaes, dando uma seja possvel acompanhar todos os resultados decorrentes dos
semntica para cada dado lido, processado e armazenado. processos batch.

Edio 127 Java Magazine 57

java127.indb 57 15/04/2014 11:16:14


Java EE 7: desenvolvendo aplicaes batch Parte 2

Nas Listagens 11 e 12 so apresentados os cdigos do nosso o job. Este ltimo mtodo inclui todos os parmetros que sero
managed bean e da pgina XHTML, analisados a seguir. transmitidos ao job antes de sua execuo, realizada atravs do
Na Listagem 11 podemos ver a estr ut urao de nos- mtodo start() existente na classe JobOperator. A incluso dos
so managed bean, dividido em trs mtodos principais: parmetros neste momento fundamental para que os compo-
calcularDesempenho(ActionEvent ae), responsvel pelo acio- nentes ItemReaderDesempenho, ItemWriterDesempenho e
namento do mtodo startNewBatchJob() e pelo armazenamento SelecaoSituacaoBatchlet possam utiliz-los.
das informaes iniciais da execuo do processo batch; atuali Atravs da Listagem 12 podemos visualizar o cdigo referente
zarStatusExecucao(ActionEvent ae), responsvel por atualizar nossa pgina, que possui uma tabela, utilizada para apresentao
a lista de jobs executados, atualizao esta que refletida em dos status dos Jobs, e dois botes: um denominado Calcular De-
nossa pgina JSF; e startNewBatchJob(), responsvel por invocar sempenho, cuja funo acionar o mtodo calcularDesempenho();

Listagem 11. Cdigo do managed bean ExecutorMB.

package br.com.devmedia.batch.MB;
//Adiciona as informaes de execuo na lista para que seja exibida para o usurio.
// imports omitidos... jobs.add(job);

@ManagedBean } catch (Exception e) {


@ViewScoped e.printStackTrace();
public class ExecutorMB implements Serializable { }
}
private static final long serialVersionUID = -8810535252811164653L;
//Mtodo desenvolvido para atualizar a lista de jobs executados.
public ExecutorMB() { } //Visa obter o status final de execuo dos jobs que esto em andamento.
public void atualizarStatusExecucao(ActionEvent ae) {
//Lista que conter todos os jobs executados, utilizada para monitoramento.
private List<JobInformationTO> jobs; List<JobInformationTO> jobsNew = new ArrayList<JobInformationTO>();

@PostConstruct //Recupera a data de finalizao do Job e seu cdigo de retorno.
//Inicializa a lista de jobs. for(JobInformationTO ji : jobs) {
private void init() {
jobs = new ArrayList<JobInformationTO>(); if(ji.getEndTime() == null && ji.getBatchStatus() == null) {
} ji.setEndTime(BatchRuntime.getJobOperator().getJobExecution
(ji.getExecutionID()).getEndTime());
public List<JobInformationTO> getJobs() { ji.setBatchStatus(BatchRuntime.getJobOperator().getJobExecution
return jobs; (ji.getExecutionID()).getBatchStatus().name());
} }

public void setJobs(List<JobInformationTO> jobs) { jobsNew.add(ji);
this.jobs = jobs; }
}
jobs = jobsNew;
//Mtodo desenvolvido para realizar a inicializao do Job }
//que realizar o clculo de desempenho dos alunos.
public void calcularDesempenho(ActionEvent ae) { //Mtodo desenvolvido para realizar a inicializao do Job.
private long startNewBatchJob() throws Exception {
try {
//Obtm o id da execuo do job. O id retornado pelo //Obtm o JobOperator para posteriormente realizar a execuo de um Job.
//mtodo de inicializao do job. JobOperator jobOperator = BatchRuntime.getJobOperator();
long jobExecutionID = startNewBatchJob();
//Cria um arquivo de propriedades contendo todos os parmetros
//Obtm o JobOperator para posteriormente obter informaes de execuo. //que sero passados para os steps que sero executados.
JobOperator jobOperator = BatchRuntime.getJobOperator(); Properties props = new Properties();
props.setProperty(INPUT_DATA_FILENAME, ParameterInterface.
//Obtm o JobExecution para armazenar na estrutura JobInformationTO INPUT_DATA_FILENAME);
//as informaes de execuo do Job iniciado. props.setProperty(OUTPUT_DATA_FILENAME, ParameterInterface.
JobExecution jobExecution = jobOperator.getJobExecution(jobExecutionID); OUTPUT_DATA_FILENAME);
props.setProperty(OUTPUT_APPROVED_DATA_FILENAME,
JobInformationTO job = new JobInformationTO(); ParameterInterface.OUTPUT_APPROVED_DATA_FILENAME);
props.setProperty(OUTPUT_DISAPPROVED_DATA_FILENAME,
//Armazena na estrutura JobInformationTO as informaes pertinentes ParameterInterface.OUTPUT_DISAPPROVED_DATA_FILENAME);
//execuo do Job.
job.setInstanceID(jobOperator.getJobInstance(jobExecutionID).getInstanceId()); //Retorna o ID de execuo do Job
job.setExecutionID(jobExecutionID); return jobOperator.start(ParameterInterface.JOB_NAME, props);
job.setJobName(jobExecution.getJobName()); }
job.setStartTime(jobExecution.getStartTime()); }

58 Java Magazine Edio 127

java127.indb 58 15/04/2014 11:16:15


e outro denominado Atualizar, cuja funo acionar o mtodo Testes e resultados das execues
atualizarStatusExecucao(), com a finalidade de carregar os status Para finalizar, demonstraremos neste tpico o funcionamento de
finais das execues dos Jobs. todos os componentes descritos anteriormente. Para que isso seja
possvel, precisamos inicializar o servidor GlassFish e acessar a
Listagem 12. Cdigo da pgina execucaoBatch.xhtml. pgina execucaoBatch.xhtml. Feito isto, visualizaremos algo similar
tela da Figura 6.
<!DOCTYPE html PUBLIC -//W3C//DTD XHTML 1.0 Transitional//EN
Neste momento, ao clicarmos no boto Calcular Desempenho, o
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd>
primeiro processo iniciado e, atravs de Ajax, so carregadas as
<html xmlns=http://www.w3.org/1999/xhtml informaes iniciais da execuo (id da instncia do job executada,
xmlns:ui=http://java.sun.com/jsf/facelets
id da execuo, nome do job e data de incio da execuo), como
xmlns:h=http://java.sun.com/jsf/html
xmlns:f=http://java.sun.com/jsf/core> pode ser observado na Figura 7.
<f:view> Como em todo o cdigo inclumos alguns sysouts para rastrea-
<h:head>
<h3><h:outputText id=title1 value=Java Magazine /></h3>
bilidade, conseguiremos, atravs do console do Eclipse, visualizar
<h3><h:outputText id=title2 value=Aplicao Batch Simples Demonstrando a todo o processo de execuo (ver Figura 8). Ou seja, por meio deste
Utilizao da JSR 352 /></h3> console seremos capazes de analisar os registros processados, o
</h:head>
<h:body> momento em que eles so gravados e a execuo do batchlet.
<h:form id=batchForm>
<h:outputText
value=Para realizar o clculo de desempenho dos alunos,
clicar no boto Calcular Desempenho. />
<br />
<h:panelGrid columns=3 id=inputInformation>
<h:commandButton value=Calcular Desempenho
actionListener=#{executorMB.calcularDesempenho}>
<f:ajax render=batchForm:outputInformation></f:ajax>
</h:commandButton>
<h:commandButton value=Atualizar
actionListener=#{executorMB.atualizarStatusExecucao}>
<f:ajax render=batchForm:outputInformation></f:ajax>
</h:commandButton>
</h:panelGrid>
<br /> Figura 6. Pgina inicial para execuo do processo batch
<br />
<h:outputText value=Status dos Jobs Executados: />
<h:dataTable id=outputInformation value=#{executorMB.jobs}
var=j border=1>
<h:column>
<f:facet name=header><h:outputLabel value=Instance ID /></f:facet>
<h:outputText value=#{j.instanceID} />
</h:column>
<h:column>
<f:facet name=header><h:outputLabel value=Execution ID /></f:facet>
<h:outputText value=#{j.executionID} />
</h:column>
<h:column>
<f:facet name=header><h:outputLabel value=Job Name /></f:facet>
<h:outputText value=#{j.jobName} />
</h:column> Figura 7. Resultado da pgina aps a primeira execuo do Clculo de Desempenho
<h:column>
<f:facet name=header><h:outputLabel value=Start Time /></f:facet>
<h:outputText value=#{j.startTime} />
</h:column>
<h:column>
<f:facet name=header><h:outputLabel value=End Time /></f:facet>
<h:outputText value=#{j.endTime} />
</h:column>
<h:column>
<f:facet name=header><h:outputLabel value=Batch Status /></f:facet>
<h:outputText value=#{j.batchStatus} />
</h:column> Figura 8. Log de rastreabilidade incio e fim da execuo do processo batch
</h:dataTable>
</h:form>
</h:body> Podemos observar ainda que os itens so sempre processados de
</f:view> 10 em 10 (o que faz valer nossa propriedade item-count, inserida
</html>
na tag <chunk> conforme a Listagem 1), alm de ser possvel

Edio 127 Java Magazine 59

java127.indb 59 15/04/2014 11:16:16


Java EE 7: desenvolvendo aplicaes batch Parte 2

verificarmos o encerramento da execuo (atravs da penltima Para finalizarmos, vlido informar que tambm podemos visu-
linha exibida na Figura 8). alizar a execuo dos processos batch utilizando o console nativo
Para que possamos visualizar as informaes das colunas End do GlassFish. Para isto, devemos executar os seguintes passos:
Time (data do fim da execuo) e Batch Status (Status da execuo Acessar a URL: http://caminhoServidor:4848;
do job) existentes em nossa pgina JSF, precisamos clicar no boto Feito isso, ser apresentada ao usurio a pgina inicial de
Atualizar, para que os status sejam verificados e atualizados de administrao do GlassFish. Atravs desta pgina, clique no
acordo com a implementao de nosso managed bean, descrito link localizado ao lado esquerdo da pgina, denominado server
na Listagem 11. A Figura 9 mostra o resultado da execuo desse (Admin Server);
processo de atualizao. Aps acessar a seo de Admin Server, clique na aba denominada
Caso ocorra alguma falha na execuo do job por conta de al- Batch;
guma exceo lanada, ser possvel verificar o status FAILED
na coluna Batch Status. Neste caso o erro dever ser averiguado Na prxima pgina possvel verificar a execuo de todos os
pelo desenvolvedor. Para facilitar o processo de identificao dos processos batch. Para termos acesso a um nvel maior de granu-
erros, recomenda-se que seja implementado um mecanismo de laridade do resultado, devemos clicar no link existente na coluna
log utilizando APIs como a Log4J, ao invs dos nossos sysouts Execution ID. Ele nos levar a uma tela de detalhamento inicial do
criados durante o desenvolvimento. job selecionado (Figura 10), na qual ser possvel visualizarmos os
parmetros que foram transmi-
tidos na execuo, o status final
da execuo (Batch Status), a hora
de incio (Start Time) e fim (End
Time) e a quantidade de steps
(Step Count).
Para visualizarmos os steps
executados dentro do job, bem
como o retorno de cada um dos
steps e at mesmo o nome dos
mesmos, clique na aba Execution
Steps. Assim, ser apresentada
a pgina de detalhamento dos
steps (veja a Figura 11). Nesta
Figura 9. Resultado da atualizao do status de execuo pgina podemos encontrar o
nome de cada um dos steps, no-
mes estes que foram definidos em
nosso arquivo de configurao
e que so importantes quando
queremos identificar com maior
facilidade o ponto de erro. Alm
disso, possvel verificarmos
seus respectivos status de execu-
o, data de incio e fim e mtri-
cas padres, como a quantidade
de leituras realizadas.
Este artigo abordou de forma
simples as principais classes e
interfaces existentes na JSR 352.
Para isso, apresentamos um
estudo de caso e desenvolvemos
componentes de software que,
em conjunto, realizam tarefas
objetivas que visam automatizar
processos, como, por exemplo, o
processo de clculo de desem-
Figura 10. Tela de detalhamento da execuo de um job penho. Alm disso, vimos como

60 Java Magazine Edio 127

java127.indb 60 15/04/2014 11:16:17


Edio 127 Java Magazine 61

java127.indb 61 15/04/2014 11:16:17


Java EE 7: desenvolvendo aplicaes batch Parte 2

Figura 11. Tela de visualizao dos steps de um job

realizada a configurao e organizao de todos os elementos Links:


que envolvem um processo batch, e atravs de um mecanismo
simples de monitoramento, visualizamos e detalhamos o resultado Site oficial da JSR 352 Batch Applications for the Java Platform.
da execuo do processo, por meio da interface administrativa do http://jcp.org/en/jsr/detail?id=352
servidor GlassFish. An Overview of Batch Processing in Java EE 7.0.
Para encerrar, importante frisar que em grandes sistemas a http://www.oracle.com/technetwork/articles/java/batch-1965499.html
execuo de processos batch normalmente intermediada por
Javadoc da especificao da Java EE 7.
softwares e APIs que implementam mecanismos de agendamento,
https://javaee-spec.java.net/nonav/javadocs/overview-summary.html
tornando o processo mais automatizado e menos propenso a erros
por conta da interao humana. Link para Download da IDE Eclipse Kepler.
http://www.eclipse.org/kepler/

Autor Link para Download do Servidor de Aplicao Glassfish 4.0.


https://glassfish.java.net/download.html
Jefferson S. de Araujo
jefferson.santos.araujo@gmail.com Link para instalao do GlassFish no Eclipse Kepler.
Mestrando em Engenharia de Software pela Universidade http://pablonobrega.wordpress.com/2013/10/16/configurando-o-glassfish-4-no-eclipse-
de Liverpool (Inglaterra). Trabalha com TI h seis anos, tendo kepler/
atuado com tecnologias de alta plataforma focada em Mainframe, baixa
plataforma (Java e .Net), ERP (SAP), passando por reas como anlise e Voc gostou deste artigo?
desenvolvimento de sistemas, arquitetura funcional e arquitetura de solues. Atualmente
Gerente de Projetos na empresa Grupo Conectt e tambm professor em cursos de
graduao e ps-graduao na Faculdade de Informtica e Administrao Paulista (FIAP). D seu voto em www.devmedia.com.br/javamagazine/feedback
Possui as certificaes: COBIT Foundations 4.1, ITIL Foundations V3, OCA Java SE 5/SE 6, Ajude-nos a manter a qualidade da revista!
EXIN (CLOUDF) Cloud Computing Foundation e ISO/IEC 20000.

62 Java Magazine Edio 127

java127.indb 62 15/04/2014 11:16:19


Edio 127 Java Magazine 63

java127.indb 63 15/04/2014 11:16:19


Introduo a linguagem
Clojure
Saiba por que essa linguagem est sendo to
falada

A Fique por dentro


linguagem Clojure foi criada por Rich Hickey
e apresentada ao pblico pela primeira vez em
2007. Ela foi desenvolvida visando constru- Este artigo apresenta Clojure, uma linguagem funcional com a qual voc
o de aplicaes multitarefa de forma mais simples e pode criar aplicaes concorrentes que rodam sobre a Mquina Virtual
concisa, aproveitando a maturidade da mquina virtual do Java de modo simples e elegante, e voltado a desenvolvedores de
Java e eliminando parte da resistncia das empresas software que tenham interesse em conhecer programao funcional,
por novas tecnologias, uma vez que praticamente toda aprender novas linguagens ou precisem escrever aplicaes para a m-
a base de cdigo existente hoje, escrita originalmente quina virtual do Java aproveitando a base de cdigo que j existe hoje.
em Java, pode ser aproveitada e facilmente integrada a
uma aplicao escrita em Clojure.
Por ser uma linguagem funcional, a menor unidade Instalando o Clojure
de trabalho passa a ser uma funo. Isto significa que, A melhor ferramenta para comearmos a explorar o Clojure
enquanto em Java estamos acostumados a criar classes e oLeiningen. Esta ferramenta gerencia dependncias e automatiza
objetos especializados, fazendo com que eles trabalhem a compilao, execuo de testes e gerao de pacotes binrios,
em conjunto para resolver problemas, numa linguagem alm de permitir o uso de uma grande quantidade de plugins. Se
funcional ns criamos funes altamente especializadas voc j est familiarizado com o Maven no Java ou com o Rake no
que trabalham em conjunto para resolver praticamente Ruby, no vai ter dificuldades em usar o Leiningen.
os mesmos tipos de problemas. No site do Leiningen, indicado na seo Links, podem ser bai-
Assim como numa linguagem orientada a objetos xados os scripts de instalao para Linux/Mac e para Windows.
normal que voc passe objetos como parmetros ou re- Seguindo as instrues disponveis, copie o script para algum
ceba objetos como resultado, na programao funcional diretrio que esteja definido na varivel de ambientePATHe, caso
fazemos a mesma coisa com funes. voc no esteja utilizando o Windows, marque o script como exe-
Outra caracterstica que pode causar estranheza no cutvel. Por fim, execute o comando a seguir, que exibe a verso
incio que, por padro, os valores em Clojure so atual da ferramenta:
imutveis. Para quem vem do Java, como se todos
os valores fossem declarados usando a clusula final. lein -v
Na prtica isso faz com que voc no tenha problemas
quando duas threads precisarem acessar o mesmo valor, Nota
o que uma das maiores dificuldades quando lidamos Para quem quiser instalar o Leiningen no Windows, crie um diretrio sem espaos no nome e
com aplicaes concorrentes. informe-o na varivel de ambienteLEIN_HOME. Por padro, a ferramenta instalada no diretrio
Clojure permite tambm que voc altere a lingua- do usurio, mas voc no conseguiria atualizar o Leiningen para uma prxima verso por conta do
gempara que ela se adeque s suas necessidades. Isto diretrioDocuments and settings. Nos demais sistemas operacionais isso no acontece.
possvel graas criao demacros. Criar uma macro o
equivalente a poder adicionar construes novas na lin-
guagem. um recurso to poderoso que existem livros Nas primeiras vezes que o Leiningen for utilizado, ser feito o
inteiros apenas sobre esse assunto. No decorrer do artigo download de algumas bibliotecas para seu computador, do mes-
voc vai aprender os conceitos bsicos da linguagem e mo modo que acontece com o Maven. Uma vez que determinada
at mesmo a criar sua primeira aplicao. biblioteca seja baixada para a sua mquina, ela ficar disponvel

64 Java Magazine Edio 127

java127.indb 64 15/04/2014 11:16:19


num repositrio local, eliminando a necessidade de um novo como os sinais +, *, -, / e combinaes entre eles. Isso possibilita
download. A verso do Leiningen empregada durante a escrita a criao de operadores exclusivos para as nossas necessidades,
desse artigo foi a2.3.4, e esse nmero pode variar sem prejuzo conforme elas forem aparecendo.
algum ao entendimento do artigo. A funo + recebe uma lista de argumentos e devolve a soma de
Um dos principais recursos que o Leiningen oferece chamado todos eles, o que exatamente o que se espera de uma funo com
REPL, o qual usaremos em boa parte deste artigo. O REPL um esse nome. Nesse momento vale ressaltar um detalhe importante:
prompt para a linguagem no qual podemos definir valores, exe- possvel que voc passe ilimitados argumentos para essa funo,
cutar comandos e testar trechos de nossas aplicaes de forma enquanto na notao infixa, a que usamos no Java, s possvel
interativa. O nome REPL um acrnimo paraRead-Eval-Print- somar dois valores de cada vez. Em Clojure podemos efetuar uma
Loopou, em Portugus, Leia-Avalie-Imprima-Repita. soma com vrios parmetros, como (+ 1 2 3 4), enquanto em Java
teramos que usar o operador+mais de uma vez (por exemplo:1
Primeiros passos + 2 + 3 + 4).
Aps instalar o Leiningen, abra o seu terminal ou o prompt de Dissemos tambm que uma lista pode conter outras listas.
comando, dependendo do sistema operacional que voc esteja Nestes casos, as expresses so avaliadas de dentro para fora,
usando, e digiteleinsem parmetros para ver as opes dispon- comeando com a que estiver mais aninhada e terminando com
veis. Existem diversos plugins para o Leiningen que acrescentam a mais externa. Fica mais fcil de entender esse comportamento
itens a essa lista de opes. quando mostramos o seguinte cdigo:
Usando o Leiningen, vamos abrir o REPL para fazermos nossos
primeiros experimentos com a linguagem Clojure. Para isso, de- (+ 2 (* 3 4))
vemos chamar a oporepl executando o comando a seguir: ; 14

lein repl Internamente o Clojure resolve primeiro a multiplicao e


depois a soma. Note que dessa forma no existe precedncia de
Em Clojure, o cdigo fonte formado por uma srie de listas operadores. Tanto+quanto*so funes e, para o Clojure, tm a
aninhadas contendo expresses delimitadas por parnteses. mesma importncia.
Estas expresses so interpretadas e executadas assim que a O que, primeira vista, pode parecer uma deficincia, na verdade
aplicao escrita em Clojure executada ou quando voc digita uma caracterstica que colateralmente faz com que o cdigo fique
o seu cdigo no REPL. Como em Clojure os tempos de interpre- mais organizado e menos confuso. Imagine o seguinte cdigo
tao, compilao e execuo frequentemente se sobrepem, ao numa linguagem qualquer:
contrrio da linguagem Java (que tem esses passos muito bem
definidos), esse conjunto de passos chamado genericamente 2+3*4
deavaliao.
O primeiro item de uma lista um operador, e os demais so S sabemos que a multiplicao ocorre antes da adio porque
chamados de operandos, argumentos ou parmetros. Essa nota- isso nos foi ensinado no comecinho do primeiro grau. Mas, e se
o, com o operador na frente dos argumentos, chamada depr- algum compilador especfico resolver mandar as regras da ma-
fixada, ou polonesa, e uma das caractersticas que mais chama a temtica s favas e definir que a adio tem precedncia sobre a
ateno de quem est comeando no Clojure. multiplicao?
Uma vez que o REPL esteja pronto, geralmente exibindo o Nesse nosso exemplo, teramos como resultado o nmero 20,
textouser=>, vamos colocar a mo na massa para entendermos ao invs de 14. Num sistema maior, isso seria uma fonte de erros
melhor o que foi dito at agora. Deste modo, digite o cdigo a difcil de encontrar.
seguir e veja o que acontece, lembrando que o texto aps o sinal Por mais absurda que essa situao possa parecer, assim que
; o resultado e no precisa ser digitado: nosso crebro funciona, processando as informaes passo a
passo. Com isso seremos naturalmente induzidos ao erro de
(+ 2 3) calcular primeiro a adio e, depois, a multiplicao. Para evitar
;5 isso, usamos parnteses, como nos casos da Listagem 1.

O sinal de ;indica um comentrio, e por no ter valor para o


Listagem 1. Forando a ordem dos clculos.
REPL, ser utilizado para exibir os resultados das expresses, para
que voc possa acompanhar conforme for lendo o artigo. 2 + (3 * 4)
Perceba que temos nesse primeiro exemplo uma expresso de- // 14

limitada por parnteses contendo uma lista de trs itens. O sinal (2 + 3) * 4


de+, na verdade, uma funo. Em Clojure permitido nomear // 20
funes com caracteres que so considerados especiais no Java,

Edio 127 Java Magazine 65

java127.indb 65 15/04/2014 11:16:19


Introduo a linguagem Clojure

Quando utilizamos a notao prefixa, comeamos a perceber firmemente na imutabilidade, que o princpio em que um valor
que os parnteses tm uma razo lgica para existir e, com o no se altera at o final da execuo do cdigo, e a transformao
tempo, comearo a ser naturais para quem est comeando com de dados, onde uma funo recebe um valor ou um conjunto de
a linguagem. Em Clojure, os mesmos clculos acima ficariam valores e devolve um novo valor ou conjunto de valores.
conforme apresenta a Listagem 2. Baseando-se nisso, vamos armazenar os valores de 1 a 10 em uma
sequncia ao invs de alterar o valor de uma varivel dez vezes.
Listagem 2. Os mesmos clculos, em Clojure. Para armazenar essa sequncia, vamos usar a palavra chavedef,
que internamente cria uma estrutura chamada Var. Apesar do
(+ 2 (* 3 4))
Clojure no ter variveis, vamos pensar noVarcomo sendo uma
; 14
varivel global. Para o que vamos fazer, essa analogia mais do
(* (+ 2 3) 4) que suficiente. Dito isso, execute o seguinte cdigo no REPL:
; 20

(def nums [1 2 3 4 5 6 7 8 9 10])


Pensando de forma funcional
Clojure uma linguagem funcional. Num primeiro momento, OVarnums umarraycontendo nmeros de 1 a 10. Note que
isso significa que a unidade bsica do seu cdigo a funo, vrgulas so opcionais em Clojure e dificilmente so usadas, mas
enquanto no Java podemos dizer que as unidades bsicas so fique a vontade para separar os nmeros com vrgulas como forma
classes e objetos. de experimentar a linguagem.
Assim como na Programao Orientada a Objetos podemos dizer A linguagem Clojure tem uma grande quantidade de funes
que objetos especializados trabalham juntos para que problemas em sua API padro que nos permite transformar e verificar da-
sejam resolvidos, na Programao Funcional ns combinamos dos de inmeras formas. Para reproduzirmos a verificao que
funes para que dados sejam transformados at que o problema fizemos dentro doifdo cdigo Java, por exemplo, vamos usar a
seja solucionado. funoeven?,que retornatrueoufalsecaso o nmero informado
Mais do que simplesmente trocar objetos por funes, desenvol- seja par ou impar, respectivamente. Como demonstrao desse
ver aplicaes utilizando programao funcional exige que voc recurso, veja o cdigo da Listagem 4.
pense de forma diferente, enriquecendo o seu arsenal de tcnicas
para resolver problemas. Listagem 4. Demonstrando a funo even?
Para comearmos a entender as diferenas entre a programao
(even? 4)
imperativa, que usamos no Java, e a programao funcional, uti- ; true
lizada no Clojure, vamos usar um exemplo simples calculando a
soma dos nmeros inteiros pares de 1 a 10. (even? 51)
; false
Em Java, resolveramos com o cdigo exibido na Listagem 3.

Listagem 3. Exemplo de cdigo imperativo. Dentro da biblioteca padro do Clojure existe tambm uma
funo chamadafilter, que recebe como parmetros uma funo,
int soma = 0;
que vamos chamar depredicado, e uma lista de dados, retornando
for (int num = 1; num <= 10; num++) { uma nova lista contendo somente os itens que fizerem o predicado
if(num % 2 == 0) { retornartrue.
soma += num;
} Tanto em programao quanto na matemtica, chamamos
} de predicado uma funo que retorna true ou false de acordo
com o parmetro recebido. Neste exemplo, a funo even? o
System.out.println(soma);
nosso predicado.
Perceba no cdigo da Listagem 5 como a funofilternos retorna
Perceba que os valores de soma e num mudam vrias vezes somente os nmeros pares, e no altera o valor original denums.
durante a execuo do programa. Linguagens imperativas como Esse um dos fundamentos da imutabilidade: os dados permane-
o Java se apoiam namutabilidadedos dados, enquanto linguagens cem intactos e uma nova sequncia criada com os dados novos.
funcionais, como o caso do Clojure, preferem utilizar deimuta- Voltando ao Java, como a classe Stringfunciona, retornando um
bilidade, retornando dados transformados e mantendo os dados novo objetoStringa cada operao e mantendo o valor original
originais intactos. do objeto que invocou o mtodo.
No cdigo imperativo, a base do nosso cdigo ofore os valores A API padro nos fornece ainda uma funo chamadareduce,
que vo de 1 a 10. O for trabalha alterando o valor da varivel que tambm recebe uma funo e uma lista como parmetros. Essa
de controle, no nosso caso o num, tantas vezes quantas forem funo, qual chamamos decallback, aplicada cumulativamente
necessrias. J o paradigma funcional de programao apoia-se aos itens da lista, de dois em dois, at que tenhamos um nico

66 Java Magazine Edio 127

java127.indb 66 15/04/2014 11:16:20


valor agregado. Vamos demonstrar seu funcionamento com um Tivemos at aqui um bom exemplo de como utilizar as funes
exemplo simples, como pode ser visto no cdigo: padro do Clojure para resolver nossos problemas. Obviamente,
tambm podemos criar funes, uma vez que apenas as funes
(reduce + [1 2 3]) prontas no so suficientes para resolvermos todos os problemas
;6 que poderemos encontrar.
Em um teste de uma grande empresa, foi pedido para cal-
Listagem 5. Obtm lista de nmeros pares sem alterar nums. cular a quantidade de nmeros divisveis por3 entre1e100.
Como no existe nenhuma funo na biblioteca padro que
(filter even? nums)
; (2 4 6 8 10)
nos traga os nmeros mltiplos de trs, vamos criar uma para
esse fim.
nums Podemos resolver esse problema da mesma forma que o ante-
; [1 2 3 4 5 6 7 8 9 10]
rior, mas ao invs de declararmos um array escrevendo todos os
nmeros que queremos, um por um, vamos usar a funorange.
Internamente, a funo+recebeu dois parmetros: o valor0, que Esta funo recebe como parmetros um valor inicial A e um valor
o valor da soma antes que qualquer coisa tenha sido somada, e final B, retornando uma lista com os nmeros inteiros maiores
o primeiro item da lista, que 1. ou igual a A e menores que B. Vamos armazenar essa lista de 1 a
Pegamos o resultado dessa operao, que 1, e somamos com o 100 num Varchamadolista:
prximo item da lista, que 2, e o resultado3ser somado ao item
seguinte da lista, que tambm 3, totalizando6. Como no existem (def lista (range 1 101))
mais itens na lista, a funoreduceretorna6como resultado.
Para demonstrar esse recurso, vamos utiliz-lo para somar a Agora que temos a lista com os valores a serem filtrados, preci-
lista de nmeros pares que encontramos comfilter (veja o cdigo samos de um predicado que vai nos dizer se o nmero ou no
da Listagem 6). mltiplo de 3. Para isso, vamos criar uma funo chamadatriplo?,
que recebe um nmero inteiro, de nomenum, e retornatruecaso
seja divisvel por3. Este ser o nosso predicado. Para esse clculo,
Listagem 6. Cdigo completo usando reduce.
faremos uso da funomod, que calcula o resto de diviso. Veja
(def nums [1 2 3 4 5 6 7 8 9 10]) o cdigo resultante at aqui:

(reduce + (filter even? nums))


; 30 (defn triplo? [num]
(= (mod num 3) 0))

Uma segunda forma de resolver esse problema consiste em Perceba que em Clojure no existereturn, sendo considerado
usar a funoapply no lugar da funo reduce. A funoapply como o retorno da funo o ltimo valor avaliado, de modo pare-
tambm recebe uma funo como callback e uma lista como pa- cido com o Ruby. J o sinal de interrogao no nome da funo
rmetros, mas transforma todos os itens da lista em parmetros uma mera conveno para indicar que a funo um predicado.
para o callback. Adicionar esse smbolo ao final do nome da funo serve como
Exemplificando, o cdigo(apply + [1 2 3])gera internamente o uma boa prtica, mas no obrigatrio.
cdigo(+ 1 2 3). Tanto uma forma como a outra fazem exatamente o Em seguida vamos usar a funo filter, que j utilizamos
mesmo. Assim sendo, basta substituirreduce porapply e teremos anteriormente, para retornar uma lista contendo somente os
a mesma resposta, como demonstra o cdigo da Listagem 7. nmeros mltiplos de trs. Para sabermos o tamanho dessa lista,
faremos uso da funo count, que tambm pertence biblioteca
padro do Clojure. Separando os nmeros triplos e contando
Listagem 7. Cdigo completo usando apply.
quanto eles so, chegamos ao resultado do nosso problema (veja
(def lista [1 2 3 4 5 6 7 8 9 10]) a Listagem 8).
(apply + (filter even? lista))
; 30
Listagem 8. Cdigo completo usando a funo triplo?.

(def lista (range 1 101))


Isso no significa quereduce eapply fazem a mesma coisa, mas,
neste caso, do o mesmo resultado por formas diferentes. (defn triplo? [num]
Mesmo sem cair no erro de avaliar uma linguagem pela quan- (= (mod num 3) 0))

tidade de linhas de cdigo, compare a simplicidade do cdigo (count (filter triplo? lista))
completo em Clojure com a verso escrita em Java.

Edio 127 Java Magazine 67

java127.indb 67 15/04/2014 11:16:20


Introduo a linguagem Clojure

Ao criar a funotriplo?, voc deu um nome a ela, permitindo Operadores -> e ->>
que ela seja usada tantas vezes quantas forem necessrias. S que Como vimos com as funes annimas, a biblioteca padro do
existem casos em que voc s vai utilizar a funo uma nica vez, Clojure prov uma srie de construes alternativas para que o
tornando desnecessrio que ela fique disponvel para o restante da cdigo fique mais limpo e simples de entender.
aplicao e, por conta disso, no havendo a necessidade de nome- Dentre essas formas, a linguagem oferece tambm dois opera-
la. A entram as chamadasfunes annimas, ou funes lambda. dores,->>e->, que tornam o cdigo mais legvel, mas que podem
Para criar uma funo annima, temos que usar a formafn. A parecer confusos primeira vista. Esses operadores so chamados
sintaxe parecida com a da forma defnque vimos antes, mas aqui de threads macros, arrows ou setas, dependendo do autor. O uso
voc no informa o nome da funo. Optando por uma funo desses operadores permite que o fluxo de transformao dos
annima, nosso cdigo ficar assim: dados se torne explcito e facilmente compreendido para quem
ler o cdigo.
(count (filter (fn [num] (= (mod num 3) 0)) lista)) Vamos comear pelo operador->>, chamado dethread last. Este
recebe uma lista de parmetros, pega a primeira expresso da
Caso voc ache que o cdigo ficou difcil de ler, recomenda-se lista e passa como ltimo parmetro da expresso seguinte.
que ele seja formatado alinhando os argumentos das funes um O resultado dessa operao passado como parmetro para a
embaixo do outro, como exposto na Listagem 9. prxima expresso, e assim por diante. Os demais parmetros,
se existirem, so mantidos em suas respectivas posies. Por
Listagem 9. Cdigo formatado usando uma funo annima.
exemplo, o cdigo (->> 3 (+ 1 2)) o equivalente a (+ 1 2 3), como
veremos com maiores detalhes a seguir.
(count Para explicar na prtica como esse operador funciona, vamos
(filter
(fn [num] voltar ao nosso exemplo dos mltiplos de 3 e reescrev-lo usando
(= (mod num 3) 0)) o ->>. Para facilitar, leve em considerao que, ao utilizar este
lista)) operador, voc precisa ler a sua expresso do final para o comeo.
Isso acontece porque o Clojure avalia as expresses de dentro
Apenas a ttulo de curiosidade, quando utilizamos a for- para fora, como j dissemos no incio do artigo: a expresso que
ma defn para darmos um nome a uma funo, o Clojure in- estiver mais aninhada ser avaliada primeiro, em seguida a ime-
ternamente usadefefnem conjunto, ou seja, quando criamos diatamente menos aninhada e assim sucessivamente at que toda
uma funodobrousando (defn dobro [x] (* x 2)), o compilador a expresso tenha sido avaliada.
converte para algo como(def dobro (fn [x] (* x 2))). Usando a Listagem 8 como exemplo, veremos que a expresso mais
Em alguns casos, declarar uma funo annima dessa forma aninhada do nosso cdigo (filter triplo? lista). Assim, pegamos o
pode tornar o cdigo difcil de ler. Por isso, existe ainda outra ltimo parmetro,lista, e passamos para o restante da expresso,
forma de declararmos uma funo annima, que empregando a que (filter triplo?). Feito isso nosso cdigo vai ser modificado para
forma reduzida#( ), que funciona como ofn, mas sem especificar (->> lista (filter triplo?)). O resultado dessa expresso passado por
os parmetros. Caso a funo receba um parmetro, o utiliza- parmetro para a funocount, que vai para a ltima posio da lista
remos atravs do smbolo%. Caso a funo receba mais de um de argumentos do operador ->>, ficando (->> lista (filter triplo?)
parmetro, usaremos%1para o primeiro parmetro,%2para o count). Apresentando esse cdigo de maneira formatada, temos:
segundo, e assim por diante. A forma#( ) meramente uma-
car sinttico, que internamente vai ser interpretado como se voc (->> lista
tivesse usadofn. Portanto, fique a vontade para usar a forma que (filter triplo?)
for mais conveniente. (count))
Com essa forma reduzida, temos o cdigo da Listagem 10.
Recapitulando, dessa vez no sentido oposto, pegamos lista e
passamos como o ltimo parmetro da expresso seguinte, que
Listagem 10. Cdigo formatado usando funo annima em sua forma reduzida.
(filter triplo?). A expresso vai ser entendida pelo compilador
(count como (filter triplo? lista), e o resultado, vai ser passado como
(filter
#(= (mod % 3) 0)
ltimo parmetro para a expresso seguinte, que (count), sendo
lista)) avaliado como(count (filter triplo? lista)), que exatamente o
cdigo original que usamos como exemplo, que est completo
na Listagem 8.
Perceba como o cdigo ficou mais limpo. Como j foi dito, J o operador->trabalha quase da mesma forma, mas passando
tantofnquanto#( )vo gerar exatamente o mesmo cdigo in- o resultado da expresso anterior como o primeiro parmetro da
ternamente, sendo a escolha de um ou de outro uma questo de expresso seguinte. Vamos usar um clculo simples para entender
preferncia de acordo com a situao. a diferena. Veja a Listagem 11.

68 Java Magazine Edio 127

java127.indb 68 15/04/2014 11:16:22


Listagem 11. Exemplo de cdigo usando o operador ->. houvesobrecargada funo, ou overloading, dependendo do autor.
Na Listagem 12 temos um exemplo de como isso funciona. Assim
(-> 10
(- 4)
como nos outros exemplos, o texto escrito aps o smbolo; indica
(/ 3) o retorno da funo.
(* 21))

Listagem 12. Exemplo de sobrecarga de funo.


Com esse cdigo, pegamos o valor10e passamos como o primei-
(defn aridade
ro parmetro da prxima expresso, que (- 4), sendo avaliada
([]
(- 10 4), o que retorna 6como resultado. Pegamos ento o valor6e Nenhum parmetro)
passamos como o primeiro argumento para a expresso seguinte, ([a]
que (/ 3), sendo avaliada(/ 6 3), o que vai nos retornar2e, final- Um parmetro)
([a b]
mente, multiplicado2por21, totalizando42. Dois parmetros))
Esse cdigo que usamos como exemplo poderia ser escrito
como(* (/ (- 10 4) 3) 21), o que com certeza mais difcil de ler. (aridade)
; Nenhum parmetro

Sobrecarga de funes (aridade 1)


Ainda falando sobre funes, em Clojure podemos declarar uma ; Um parmetro
mesma funo com vriasaridades diferentes. Aridade o termo
(aridade 1 2)
para a quantidade de argumentos ou parmetros que uma funo ; Dois parmetros
pode receber. Uma funof(x)tem aridade 1, enquantog(x, y)tem
aridade 2. Uma funo que no recebe parmetros tem aridade 0. (aridade 1 2 3)
; ArityException Wrong number of args (3) passed to: user$aridade
Quando cdigos diferentes so executados usando o mesmo nome
de funo, mas quantidades diferentes de parmetros, dizemos

Edio 127 Java Magazine 69

java127.indb 69 15/04/2014 11:16:24


Introduo a linguagem Clojure

No nosso exemplo, caso a funo no receba nenhum argumento, Listagem 14. Usando def dentro de uma funo.
retornar o textoNenhum parmetro. Caso receba apenas um
(def x 10)
parmetro, retornarUm parmetroe, caso receba dois par-
metros, retornar o texto Dois parmetros. Porm, ao usar trs (defn errado []
ou mais parmetros, voc receber uma mensagem de erro. Caso (def x 40)
voc s precise de at dois parmetros, nossa funo suficiente, (println O valor de x dentro x))

mas se precisar de um nmero indefinido de argumentos, se torna (println O valor de x antes x)


invivel voc sobrecarregar a funo com todas as possibilidades
possveis. Para evitar que voc precise escrever inmeras assina- (errado)
turas de funo com aridades diferentes, a linguagem permite que
(println O valor de x depois x)
voc receba uma quantidade varivel e indefinida de parmetros
usando o smbolo&. ; O valor de x antes 10
Na Listagem 13 os valores1e2so armazenados nos parme-
; O valor de x dentro 40
trosaeb, respectivamente, enquantocrecebe um array contendo
todos os demais parmetros que forem passados para a funo. ; O valor de x depois 40

Listagem 13. Sobrecarga com quantidade varivel de argumentos.


Perceba que o valor dex alterado dentro da funo e isso mo-
(defn aridade
difica o valor doxque foi declarado fora da funo. Justamente
([a b]
Dois parmetros) pelo fato do Var ser visvel em todo o escopo da aplicao, a
([a b & c] atribuio do valor no respeitou o escopo da funo e, numa
Trs ou mais parmetros)) aplicao de verdade, isso pode nos trazer srios problemas. Esse
(aridade 1 2)
comportamento o equivalente ao de uma varivel global, e deve
; Dois parmetros ser evitado sempre que possvel.
Para resolver isso, existe a formalet, que cria um escopo local
(aridade 1 2 3)
e atribui valores a smbolos. Assim como as variveis locais
; Trs ou mais parmetros
criadas com ofinaldo Java, esse tipo de atribuio no pode ser
(aridade 1 2 3 4) modificado aps a sua declarao. Um smbolo em Clojure pode
; Trs ou mais parmetros ser entendido como umnome de varivelno Java. Essa atribuio
de um valor a um smbolo chamada de binding.
Caso voc faa umbindingpara um smbolo que tenha o mesmo
Escopo local nome de outro smbolo j existente e que tenha sido declarado em
Como j vimos no incio do artigo, a formadefcria umVarque outro escopo, o valor do binding mais antigo ser mantido, e voc
funciona como, apesar de no ser, uma varivel global dentro s enxergar o que foi recm-criado. Isso chamado deshadowinge
do nosso cdigo. O operador def existe para criarmos funes acontece tambm no Java, quando criamos uma varivel local com
ou quaisquer valores que sero acessados em outros pontos da o mesmo nome de uma varivel global.
sua aplicao. Vamos escrever um pequeno trecho de cdigo que demonstra
Enquanto estivermos demonstrando funcionalidades e escre- como podemos usar a formaletpara criarmos um escopo local,
vendo cdigos simples como esses do artigo, def acaba sendo mantendo assim o valor do Var inalterado. Veja o cdigo na
suficiente. Mas, ao escrevermos cdigo com um pouco mais de Listagem 15.
complexidade, passamos a sentir falta do que em linguagens O primeiro parmetro de let um vetor contendo pares de
imperativas chamamos devariveis locais. smbolos e valores. O primeiro valor ser atribudo ao primeiro
Para entendermos melhor o que um Var, e como funcio- smbolo, o segundo valor ao segundo smbolo e assim por diante.
na def, primeiro voc precisa saber que, em Clojure, toda Na Listagem 14 tivemos apenas um par de smbolo e valor, in-
funo faz parte de um namespace. Podemos entender dicando que atribumos o nmero40ao smbolox. Podemos ter
um namespace como um package do Java. Ao utilizarmos tantas atribuies quantas forem necessrias dentro de umlet.
o operador def dentro de uma funo, estaremos criando Isso nos ajuda a organizar o cdigo em passos lgicos, tornando-o
umVarque tem escopo global dentro do namespace. Caso j mais fcil de entender.
exista umVarcom o mesmo nome, dentro do mesmo names- Vamos demonstrar esses recursos criando uma funo que re-
pace, um novoVarser criado sobrescrevendo o anterior e o cebe uma frase qualquer, converte a primeira letra em maiscula
novo valor atribudo a ele ser visvel para qualquer membro e o resto do texto em minsculas. Para isso, vamos importar para
donamespacee mesmo para toda a aplicao. Vamos demons- o nosso cdigo as funeslower-caseeupper-case do namespa-
trar na Listagem 14 como isso funciona. ceclojure.string. Veja a Listagem 16.

70 Java Magazine Edio 127

java127.indb 70 15/04/2014 11:16:24


Listagem 15. Usando let dentro de uma funo. no cdigo, movendo a expresso crescida para dentro de uma
funo s dela.
(def x 10)

(defn certo []
Macros
(let [x 40] Macros so construes que permitem que voc estenda o
(println O valor de x dentro x))) compilador do Clojure escrevendo cdigo em Clojure. Visual-
mente elas se parecem com funes, tendo um nome, uma lista
(println O valor de x antes x)
de argumentos e um corpo que ser executado, assim como uma
(certo) funo, mas na prtica tm propsitos diferentes. Imagine se voc
pudesse criar novas palavras chaves ou construes em Java que
(println O valor de x depois x) fossem compiladas e executadas exatamente da mesma forma
que as construes nativas. Enquanto em Java isso no possvel,
; O valor de x antes 10
macros permitem que voc faa isso em Clojure.
; O valor de x dentro 40
Listagem 17. Demonstrando o uso das funes rest, str e apply.
; O valor de x depois 10
(def texto cebola)
Listagem 16. Usando let para organizar o cdigo em passos lgicos.
(rest texto)
(use [clojure.string :only [lower-case upper-case]]) ; (\e \b \o \l \a)

(defn capitalize-first [text] (str (rest texto))


(let [first-letter (first text) ; (\\e \\b \\o \\l \\a)
upper-first (upper-case first-letter)
rest-of-text (->> text (apply str (rest texto))
rest ; ebola
(apply str)
lower-case)]
(str upper-first rest-of-text))) A linguagem Ruby tem uma construo chamadaunlessque,
basicamente, o inverso doif. Clojure tambm tem algo assim,
(capitalize-first FRASE EM MAIUSCULAS)
; Frase em maiusculas chamadoif-not, mas para demonstrar como criar macros, vamos
criar nosso prpriounlessem Clojure.
Em Java, temos que nos contentar com o if que a linguagem
Em Clojure, um texto uma sequncia de caracteres e, por fornece.
conta dessa caracterstica, podemos tratar qualquer texto como A primeira coisa que precisamos saber sobre Clojure antes de
se fosse uma lista. A biblioteca padro da linguagem nos oferece escrevemos nossa primeira macro que a linguagem homoic-
uma grande variedade de funes para manipulao de listas, nica. Essa palavra bonita significa que a linguagem trata dados e
como por exemplo,first, que retorna o primeiro item, erest, que cdigo da mesma forma, podendo converter cdigo em dados e
retorna os demais. vice-versa conforme a nossa necessidade. Se voc voltar ao incio
Como rest retorna uma lista de caracteres, usamos a fun- do texto, vai lembrar que um cdigo Clojure formado por listas
ostrpara transformar o contedo novamente em texto e, para aninhadas contendo expresses. Essas listas nada mais so do que
isso, usamos o nosso j conhecido apply. A funo str recebe estruturas de dados nativas da linguagem. Ou seja, o seu cdigo
como parmetros textos e caracteres, que so concatenados em fonte uma estrutura de dados que transformada em cdigo
uma nica String. Qualquer outro valor que no seja um texto durante o processo de avaliao.
ou um caractere ser convertido para o tipoStringpara depois A segunda coisa que precisamos saber, dessa vez sobre macros,
ser concatenado. Dessa forma, caso voc passe a lista retornada que os parmetros no so avaliados automaticamente, como
porrestdiretamente como parmetro parastr, no teremos um acontece nas funes. Isso permite que os parmetros de uma
texto contendo apenas os caracteres que queremos, mas sim uma macro s sejam executados no momento que quisermos, e se
representao em formato texto da lista, como podemos ver no quisermos.
segundo resultado da Listagem 17. a que entra a funoapply, Para entender a diferena, lembre-se que ao passarmos expres-
fazendo com que cada um dos caracteres da lista seja passada por ses como parmetros para uma funo, essas expresses so
parmetro para a funostr. Demonstramos essa implementao avaliadas e s ento seus respectivos resultados so passados
no ltimo exemplo da Listagem 17. como parmetro. Vamos criar uma funounlesspara ver como
Voltando Listagem 16, note que voc pode escrever expres- esse processo funciona na prtica.
ses inteiras dentro dolet, sendo que, em momentos em que a No nosso exemplo, caso o usurio tenha acesso, o que represen-
expresso cresce demais, voc deve considerar uma simplificao tado na Listagem 18 pelo parmetro access? sendo true, um banco

Edio 127 Java Magazine 71

java127.indb 71 15/04/2014 11:16:25


Introduo a linguagem Clojure

de dados fictcio ser apagado. Caso contrrio, o usurio receber nomes completos, contendo inclusive os namespaces de origem.
uma mensagem de erro e nada ser apagado. Criamos tambm a Isso serve para garantir que o compilador vai usar exatamente
funounlesse a executamos passando respectivamente false e a funo ou o operador que precisa, sem se preocupar com fun-
depois true como parmetros, para que possamos testar as duas es que tenham o mesmo nome, mas que esto em namespaces
condies possveis. diferentes. Vamos ver como osyntax quotefunciona na prtica na
Listagem 20.
Listagem 18. Criando a construo unless como uma funo.
Listagem 20. Usando syntax quote.
(defn unless [condition falsey truey]
(if-not condition `(if-not true 0 1)
falsey ; (clojure.core/if-not true 0 1)
truey))

(defn drop-database [access?]


J o caractere~, chamado deunquote, o que vai dizer ao compila-
(unless access?
(println Acesso negado) dor que, caso a execuo do cdigo passe por ali, aquela expresso
(println O banco foi apagado))) deve ser avaliada, transformando os dados contidos emcondition,
trueyoufalseyem cdigo. a que est o pulo do gato.
(drop-database false)
; Acesso negado
A biblioteca padro do Clojure nos fornece uma forma que
; O banco foi apagado permite ver como o cdigo fica instantes antes de ser avaliado.
Essa forma chama-se macroexpand e de grande utilidade para
(drop-database true)
quando estivermos escrevendo nossas prprias macros, tornando
; Acesso negado
; O banco foi apagado possvel sabermos se a macro vai fazer o que queremos antes mes-
mo de execut-la. Para que voc possa ver como a macro funciona
internamente, vamos usar o mesmo cdigo que est dentro da
Nos exemplos apresentado, independentemente do valor passa- nossa funodrop-database.
do como parmetro, os dados foram apagados e o usurio recebeu Observando a Listagem 21, note o smboloque est antes do
uma mensagem de Acesso negado. Isso acontece porque, ao utili- cdigo que vai ser expandido. Esse operador transforma cdigo
zarmos funes, a mensagem de acesso negado e o comando para em uma lista, fazendo o oposto do operador ~. Dessa forma,
apagar o banco de dados so executadosantesdoif-notter sido podemos passar cdigo como parmetro para um operador sem
avaliado. Num sistema de verdade isso seria um desastre. A nica que ele seja avaliado.
forma de avaliarmos somente a expresso desejada, de acordo com Lembra que falamos que, em Clojure, cdigo e dados so tratados
o valor passado por parmetro para unless, fazendo com que da mesma forma? Pois os operadores~ecuidam dessa transfor-
o prprio compilador siga esse desvio, avaliando as expresses mao de dados em cdigo e cdigo em dados, respectivamente.
condicionalmente conforme as nossas necessidades. Na Listagem 21 demonstrado o que acontece quando usa-
Para que isso seja possvel, vamos reescrever unless como mosmacroexpandpara inspecionar o cdigo.
uma macro, ao invs de usarmos uma funo. Como podemos
constatar no cdigo da Listagem 19, precisamos dar algumas Listagem 21. Exemplo de uso de macroexpand.
dicas ao compilador para que ele saiba exatamente o que deve ser
avaliado imediatamente e o que ele deve aguardar para avaliar (macroexpand (unless access?
(println Acesso negado)
condicionalmente.
(println O banco foi apagado)))

Listagem 19. Escrevendo unless em forma de macro. ; (if (clojure.core/not access?)


; (println Acesso negado)
(defmacro unless [condition falsey truey] ; (println O banco foi apagado))
`(if-not ~condition
~falsey
~truey)) Internamente, nossa macro se transformou em um if comum,
negando o acesso caso a condio no seja verdadeira e apagando o
banco de dados caso seja. Exatamente o que queremos que ela faa.
Apesar de parecido, o cdigo usando macros tem algumas Na Listagem 22 finalmente temos o nosso exemplo funcionando
diferenas importantes. Antes do if-not, perceba que estamos como esperado.
usando o smbolo`, conhecido porsyntax quote, que informa ao O recurso de macros constitui uma parte importante e complexa
compilador que aquele cdigo no deve ser avaliado at que a da linguagem. Na prtica voc est escrevendo cdigo que vai ge-
macro seja executada. Mais do que isso, internamente o compi- rar cdigo, o que facilita muito na hora de criar formas que tornem
lador substitui todas as macros e funes pelos seus respectivos o cdigo mais fcil de entender e mais prximo ao problema que

72 Java Magazine Edio 127

java127.indb 72 15/04/2014 11:16:26


Edio 127 Java Magazine 73

java127.indb 73 15/04/2014 11:16:29


P

Introduo a linguagem Clojure

voc precisa resolver. Por consequncia, faz com que a criao A brincadeira no termina aqui
de linguagens especficas de domnio seja algo completamente Neste artigo foi apresentada apenas uma pequena parte dessa
transparente. linguagem to poderosa chamada Clojure. Existem inmeros
Existe um livro, chamadoLet Over Lambda, que explica de manei- recursos que pretendemos abordar aqui naJava Magazinecomo,
ra profunda o funcionamento e a criao de macros em Common por exemplo, integrao com Java, programao concorrente,
LISP, que uma linguagem prima do Clojure e parecida em vrios criao de projetos para Web, recurso, lazy sequences, list
aspectos. Assim que voc j estiver familiarizado com os demais comprehension, memorization e mais uma lista enorme de
recursos que foram apresentados neste artigo, recomenda-se uma coisas bacanas.
lida nos captulos abertos disponveis no site do livro para que De qualquer maneira, o que foi abordado mais do que su-
voc tenha uma boa ideia do quo poderoso esse recurso. ficiente para que voc comece a brincar e se acostumar com a
cara diferente da linguagem. Com o tempo, voc vai notar que
Listagem 22. Cdigo completo usando macro. se tornou um desenvolvedor melhor na sua linguagem favorita
simplesmente pelo fato de ter aprendido novas formas de pensar
(defmacro unless [condition falsey truey]
`(if-not ~condition e de resolver problemas.
~falsey
~truey))
Links:
(drop-database false)
; Acesso negado Site oficial da linguagem Clojure.
http://clojure.org
(drop-database true)
; O banco foi apagado Site do Leiningen.
http://leiningen.org/ C

M
Site do livro Let Over Lambda.
http://letoverlambda.com/
Autor
Y

Gists com cdigos em Clojure.


CM

Plnio Balduino
https://gist.github.com/pbalduino MY
pbalduino@gmail.com
desenvolvedor h 18 anos, autor do livro Dominando JavaScript CY

com jQuery, pela editora Casa do Cdigo e do curso Ruby on Rails Voc gostou deste artigo? CMY

do comeo ao Fim, pelo iMasters Pro. Criou a linguagem Lucio para fins
de aprendizado e colaborou com a engine JavaScript dynjs. Atualmente
K

organiza os encontros do (clj-sp), Grupo de Usurios Clojure de So Paulo, administrador D seu voto em www.devmedia.com.br/javamagazine/feedback
da lista Clojure Brasil, membro ativo das listas Clojure Portugal, da lista oficial da linguagem Ajude-nos a manter a qualidade da revista!
e apresenta palestras e coding dojos sobre Clojure pelo pas.

74 Java Magazine Edio 127

java127.indb 74 15/04/2014 11:16:32


Porta80_AnINst.pdf 1 18/09/2011 14:58:37

CM

MY

CY

CMY

Edio 127 Java Magazine 75

java127.indb 75 15/04/2014 11:16:34


POR_CA_vert_cm8.pdf 1 3/25/14 10:12 AM

Introduo a linguagem Clojure

CM

MY

CY

CMY

76 Java Magazine Edio 127

java127.indb 76 15/04/2014 11:16:36

Potrebbero piacerti anche