Java-Magazine 140 Zoovzveb
Java-Magazine 140 Zoovzveb
Java-Magazine 140 Zoovzveb
Distribuição
FC Comercial e Distribuidora S.A Java, o logotipo da xícara de café Java e todas as marcas e logotipos
Rua Teodoro da Silva, 907, Grajaú - RJ baseados em/ ou referentes a Java são marcas comerciais ou
marcas registradas da Sun Microsystems, Inc. nos Estados Unidos e
CEP 20563-900, (21) 3879-7766 - (21) 2577-6362
em outros países.
Sumário
Artigo no estilo Curso
sobre e
34 – Elaborando projetos com a Arquitetura Orientada a Eventos
[ Ualter Azambuja Junior ]
s
ta
edição
Conteúdo sobre Novidades, Artigo no estilo Solução Completa Dê seu voto sobre esta edição, artigo por artigo, atra-
vés do link:
62 – Big Data: MapReduce na prática www.devmedia.com.br/javamagazine/feedback
[ Cláudio Martins e Wesley Louzeiro Mota ]
Criando uma aplicação
corporativa em Java –
Parte 4
Conheça mais recursos do JSF e desenvolva as
regras de negócio da aplicação
N
este quarto e último artigo da série desenvol- Como um dos destaques, citamos a análise sobre as fases do ciclo de
veremos as duas principais funcionalidades do vida do processamento de uma requisição a uma página JSF. Além
sistema gerenciador de bibliotecas: o emprésti- disso, vamos demonstrar como adotar a nova API de Data e Hora na
mo e a devolução de livros. Citamos tais funcionalidades prática, recurso lançado com a versão 8 do Java e que já começa a
como as mais importantes porque são elas que agregam ser amplamente adotado pelo mercado. A partir de todo o conteúdo
maior valor a um software destinado a bibliotecas. exposto nesta série, o leitor terá um ótimo material de referência para
A implementação desses dois itens será feita por meio iniciar o desenvolvimento de suas aplicações e se aprofundar ainda
de páginas JSF e managed beans. mais no assunto.
Ainda nesse artigo, serão explicadas as fases do ciclo
de vida do processamento de uma requisição feita a uma
página JSF. Para complementar esse tópico, também do suas informações no banco de dados. Para isso, é possível no
mostraremos um exemplo prático de como implementar formulário escolher o livro que será emprestado e para qual leitor
a interface PhaseListener, da API do JavaServer Faces, o é este empréstimo.
que é bastante útil quando precisamos executar algum Para apresentar todos os livros disponíveis para empréstimo,
código antes ou depois de alguma fase do ciclo de vida usamos os componentes h:selectOneMenu, que irá gerar um
da requisição. elemento HTML do tipo select, e f:selectItems, que irá gerar
Por fim, também é válido citar que passaremos rapi- elementos HTML do tipo option. Através do componente
damente pela nova API de data e hora do Java 8, a qual h:selectOneMenu conseguimos associar o valor do item sele-
será adotada para simplificar a manipulação de datas cionado com uma propriedade do managed bean (nesse caso, a
em todas as funcionalidades relacionadas da nossa propriedade idLivro). Com relação ao componente f:selectItems,
aplicação. para que o leitor entenda como ele foi utilizado no código da pá-
gina emprestimo.xhtml, veremos para que servem seus atributos,
Criando a funcionalidade para empréstimo de livros que são explicados a seguir:
Após toda a construção realizada até aqui, o nosso • value – colocamos nesse atributo a EL #{emprestimoBean
próximo passo é implementar a funcionalidade para em- .livrosDisponiveis}, para indicar que os livros que devem ser
préstimo de livros. Iniciaremos pela página emprestimo listados devem ser provindos da propriedade livrosDisponiveis
.xhtml, que tem seu código apresentado na Listagem 1. do managed bean. Em outras palavras, o método getLivros-
Tal página, apresentada na Figura 1, é composta por Disponiveis() – presente no bean – será invocado e irá retornar
um formulário que irá registrar o empréstimo, salvan- uma lista de livros, que serão exibidos na tela;
import java.util.List;
Listagem 1. Código da página emprestimo.xhtml. import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
<?xml version=”1.0” encoding=”ISO-8859-1” ?> import javax.inject.Named;
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” import br.com.javamagazine.dao.LeitorDao;
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> import br.com.javamagazine.entidades.Leitor;
<html xmlns=”http://www.w3.org/1999/xhtml” import br.com.javamagazine.interceptadores.Transacional;
xmlns:f=”http://java.sun.com/jsf/core”
xmlns:h=”http://java.sun.com/jsf/html” @Named
xmlns:ui=”http://java.sun.com/jsf/facelets”> @RequestScoped
<ui:composition template=”/WEB-INF/_template.xhtml”> public class LeitorBean {
<ui:define name=”corpo”>
<h:form> //restante da implementação omitida
<h2>
<h:outputText value=”Empréstimo” /> @Inject
</h2> private LeitorDao leitorDao;
<fieldset> private List<Leitor> leitores;
<legend>Dados do Empréstimo</legend>
@Transacional
<h:outputLabel value=”Escolher livro a emprestar:” for=”livro” /> public List<Leitor> getLeitores(){
<h:selectOneMenu id=”livro” value=”#{emprestimoBean.idLivro}”> if(leitores == null){
<f:selectItems value=”#{emprestimoBean.livrosDisponiveis}” leitores = leitorDao.listarLeitores();
var=”livro” itemValue=”#{livro.id}” itemLabel=”#{livro.nome}” /> }
</h:selectOneMenu>
<h:outputLabel value=”Leitor:” for=”leitor” /> return leitores;
<h:selectOneMenu id=”leitor” value=”#{emprestimoBean.idLeitor}”> }
<f:selectItems value=”#{leitorBean.leitores}”
var=”leitor” itemValue=”#{leitor.id}” itemLabel=”#{leitor.nome}” /> //restante da implementação omitida
</h:selectOneMenu> }
<h:outputLabel value=”Data do empréstimo:” for=”dataEmprestimo” />
<h:inputText id=”dataEmprestimo” value=”#{emprestimoBean
.dataEmprestimoFormatada}” disabled=”true” /> Como podemos verificar na Listagem 1, é utilizada a tag
<h:outputLabel value=”Data prevista para devolução:” for=”dataPrevista” /> f:selectItems para que sejam exibidos os nomes dos leitores e no
<h:inputText id=”dataPrevista” value=”#{emprestimoBean
.dataPrevistaFormatada}” disabled=”true” />
atributo value dessa tag é colocada a EL #{leitorBean.leitores}.
<h:commandButton value=”Realizar Empréstimo” action= Então, para que essa página funcione, o managed bean Leitor-
”#{emprestimoBean.realizarEmprestimo}” /> Bean precisa ser criado. O código desse bean pode ser visto na
</fieldset>
</h:form> Listagem 2. Note que deixamos essa classe com o mínimo de
</ui:define> código necessário, permanecendo apenas o método getLeitores(),
</ui:composition> que irá recuperar a lista com todos os leitores do banco de dados
</html>
através de uma classe DAO.
Analisaremos agora o código do managed bean EmprestimoBean, Listagem 3. Código do managed bean EmprestimoBean.
presente na Listagem 3. No construtor dessa classe são invocados
package br.com.javamagazine.mb;
os métodos setters no objeto que representa o empréstimo para
que sejam definidas a data do empréstimo e a data prevista para import java.time.LocalDate;
devolução do livro. A data do empréstimo é a data atual e a data import java.time.format.DateTimeFormatter;
prevista para devolução é obtida a partir da adição de sete dias à import java.util.List;
import javax.enterprise.context.RequestScoped;
data atual. Com o intuito de simplificar o nosso trabalho, observe import javax.inject.Inject;
que estamos usando a nova API de data e hora do Java 8. Com import javax.inject.Named;
ela fica bem mais simples somar sete dias à data atual, pois a // restante dos imports omitidos
@Transacional
Criando a funcionalidade para devolução de livros public List<Livro> getLivrosDisponiveis() {
Uma das partes mais importantes do sistema, sem dúvidas, é a if(livrosDisponiveis == null){
livrosDisponiveis = livroDao.listarLivrosDisponiveisParaEmprestimo();
funcionalidade para devolução de livros. A página devolucao.xhtml }
(vide Listagem 4) juntamente com o managed bean Devolucao- return livrosDisponiveis;
Bean (vide Listagem 5) dão forma a esta funcionalidade. }
Explicaremos essa parte específica do sistema em duas etapas.
//demais métodos get e set omitidos...
Primeiro, iremos analisar os pontos mais importantes da página
devolucao.xhtml, e depois, passaremos à análise da classe Devo- }
lucaoBean.
Listagem 5. Código do managed bean DevolucaoBean. Caso exista mais de um empréstimo vinculado ao livro, o que
vale é aquele que ainda não tem data de devolução, pois essa data
package br.com.javamagazine.mb;
só será atualizada quando a devolução for concretizada. Ainda
import java.math.BigDecimal; nesse método são recuperadas a data do empréstimo e a data
import java.time.LocalDate; prevista para devolução, formatadas e atribuídas às variáveis de
import java.time.format.DateTimeFormatter; instância dataEmprestimoFormatada e dataPrevistaFormatada,
import java.util.List;
import javax.enterprise.context.RequestScoped; para que possam ser exibidas na página. Em seguida é avaliado
import javax.inject.Inject; se a data atual é maior que a data prevista para o empréstimo,
import javax.inject.Named; e nesse caso é gerada uma multa de R$ 15,00. Para mantermos o
// restante dos imports omitidos
sistema simples, a multa não será persistida. Apenas será mostrada
@Named na tela para que seja cobrado algum valor pelo atraso.
@RequestScoped
public class DevolucaoBean {
@Inject Listagem 6. Código do método alteradoLivroSelecionado().
private LivroDao livroDao;
private Integer idLivro; public void alteradoLivroSelecionado(){
private List<Livro> livrosEmprestados; if(idLivro != null){
@Inject setarEmprestimo();
private EmprestimoDao emprestimoDao; DateTimeFormatter formatador = DateTimeFormatter.ofPattern(“dd/MM/yyyy”);
dataEmprestimoFormatada = emprestimo.getDataEmprestimo()
private Emprestimo emprestimo = new Emprestimo();
.format(formatador);
private String dataEmprestimoFormatada;
dataPrevistaFormatada = emprestimo.getDataPrevista().format(formatador);
private String dataPrevistaFormatada;
LocalDate dataAtual = LocalDate.now();
if(dataAtual.isAfter(emprestimo.getDataPrevista())){
private String multa = FormatadorDeNumeros.
multa = FormatadorDeNumeros.formatarBigDecimalComoMoeda
formatarBigDecimalComoMoeda(new BigDecimal(0.0d));
(new BigDecimal(15.0d));
}
//Código do método alteradoLivroSelecionado(), apresentado na Listagem 6 }else{
limparCampos();
//Código do método realizarDevolucao(), apresentado na Listagem 7 }
}
//Código do método setarEmprestimo(), apresentado na Listagem 8
//Código do método getLivroSelecionado(), apresentado na Listagem 9 O método principal da classe DevolucaoBean é o realizarDe-
volucao(), exposto na Listagem 7. Esse método primeiramente
//Código do método getEmprestimoDoLivroSelecionado(), apresentado
//na Listagem 10 invoca o setarEmprestimo(), que recupera o empréstimo asso-
ciado ao livro que foi selecionado para ser devolvido e o atribui à
//Código do método limparCampos(), apresentado na Listagem 11
variável de instância emprestimo. Nesse momento é atualizada
//Código do método getLivrosEmprestados(), apresentado na Listagem 12 a data de devolução desse empréstimo com a data atual. Em
seguida essa alteração no empréstimo é persistida no banco
//Métodos de acesso omitidos de dados através de uma classe DAO. Na sequência, a lista dos
} livros emprestados é atualizada para que o livro que acabou de
ser devolvido não apareça mais nela. Por fim, os campos são
limpos através de uma chamada ao método limparCampos()
Esse bean possui alguns métodos importantes que foram extra- e é adicionada uma mensagem informando ao usuário que a
ídos e serão explicados à parte. devolução foi realizada com sucesso. Repare também que o
Começaremos as explicações pelo método alteradoLivroSele- método é marcado com a anotação @Transacional, que criamos
cionado(), presente na Listagem 6. Este método será invocado no segundo artigo da série.
quando o usuário do sistema escolher algum livro na lista de Dando continuidade às explicações dos métodos, veremos a seguir
livros emprestados, que aparece na página devolucao.xhtml. Em como o setarEmprestimo() foi implementado – vide Listagem 8.
outras palavras, o método é chamado toda vez que alteramos a No início ocorre uma chamada a getLivroSelecionado(), que re-
seleção, ou seja, quando mudamos o livro que está selecionado. torna um objeto que representa o livro selecionado pelo usuário
Dentro dele é verificado se o conteúdo da variável idLivro está do sistema. Na próxima linha de código, o livro selecionado é
preenchido, o que significa que algum livro foi selecionado. Se o passado como argumento para o método getEmprestimoDoLi-
conteúdo da variável for nulo, significa que a opção em branco vroSelecionado(), que faz exatamente o que seu nome sugere e
da lista foi selecionada. Se algum livro foi selecionado, o método retorna o objeto empréstimo vinculado ao livro. Antes do método
setarEmprestimo() é chamado para atribuir o empréstimo vigente terminar, o objeto empréstimo que foi retornado é atribuído à
– representado por um objeto – relacionado àquele livro à variável variável membro emprestimo, para que possa ser utilizado em
de instância emprestimo. outros lugares da classe.
Listagem 13. Código da classe para formatação de números. dastro de funcionários e usuários, mas por enquanto ele consegue.
Para resolver esse problema dos acessos indevidos, vamos criar
package br.com.javamagazine.util;
uma classe que implemente a interface PhaseListener, da API
import java.math.BigDecimal; do JSF. Antes, no entanto, precisamos saber quais são as fases do
import java.text.DecimalFormat;
ciclo de vida do processamento de uma requisição feita a uma
import java.text.DecimalFormatSymbols;
import java.util.Locale; página JSF e entender cada uma delas, já que o uso da interface
public class FormatadorDeNumeros { PhaseListener tem relação direta com essas fases.
A Figura 3 mostra as seis fases do ciclo de vida. São elas:
public static String formatarBigDecimalComoMoeda(BigDecimal numero){
Locale localeBrasil = new Locale(“pt”,”BR”); • Restore view: é nessa fase que a árvore de componentes UI,
DecimalFormatSymbols moedaReal = new DecimalFormatSymbols chamada de view, é criada. No entanto, ela só será criada caso seja
(localeBrasil);
DecimalFormat df = new DecimalFormat(“¤ ###,###,##0.00”, moedaReal);
a requisição inicial à página JSF, caso contrário a view já existirá
return df.format(numero); e apenas será restaurada;
} • Apply request values: nessa fase os valores vindos por parâme-
}
tros na requisição são atribuídos a cada componente da árvore;
• Process validations: é nesta etapa que são processados todos os
validadores associados aos componentes da árvore;
Evitando acessos indevidos com PhaseListener • Update model values: se não ocorreu nenhum erro de validação
A aplicação está quase finalizada, porém, nesse momento, ela tem nem conversão nas etapas anteriores, isso significa que os valores
um problema que precisa ser corrigido. Da forma que está, ela per- são válidos. Então são passados dos componentes para as proprie-
mite que o usuário acesse qualquer página através da URL mesmo dades do managed bean, o que ocorre neste momento;
sem estar logado. E mesmo que o usuário tenha se logado, se ele • Invoke application: depois do modelo ter sido atualizado na
não for administrador, não deveria poder acessar a página de ca- fase anterior, nessa fase são tratados os eventos disparados pelo
coleções e coleções não são Maps. Maps são pares de chave-valor, -- Output:
que não se encaixam na definição de coleções, que são constituí- Key set: [A, J, F, B, M]
das por elementos simples. No entanto, ao mesmo tempo, podem Entry set: [A=Alex, J=José, F=Fernando, B=Bruno, M=Miguel]
ser vistas como coleções de chaves/valores, ou pares, e este fato Values: [Alex, José, Fernando, Bruno, Miguel]
Este problema de linguagem acontece porque no inglês existem de balanceamento, o que faz com que a ordem de inserção não
palavras que significam coisas distintas, mas que são traduzidas seja mantida. Desta forma, é feita uma nova ordenação dos seus
da mesma forma para o português. No caso do nosso problema, dados, o que chamaremos de classificação dos elementos. Assim,
as palavras são ordered e sorted. Ambas podem ser traduzidas vamos denominar este tipo de estrutura de dados de estruturas
para ordenado em português. De forma a tentarmos resolver ordenadas/classificadas. No caso do exemplo, a classificação/
este conflito, vamos traduzir ordered para ordenado e sorted para ordenação dos elementos foi feita por ordem alfabética porque
classificado, para entendermos o que cada uma significa. criamos uma TreeSet de Strings (Set<String>), mas podemos
Quando dissermos que uma estrutura é ordenada, é porque implementar uma árvore com qualquer tipo de classificação/
a ordem de inserção dos elementos é mantida, como acontece ordenação que pretendermos.
nas listas e arrays. Quando dissermos que uma estrutura é
classificada, queremos dizer que um elemento será inserido Implementações das coleções
numa determinada posição da estrutura de acordo com a regra Agora que já vimos as interfaces do framework Collections, va-
de ordenação da mesma, por exemplo: por ordem (classificação) mos conhecer as classes que implementam estas interfaces no Java.
alfabética, numérica, etc. Como você poderá notar, temos disponíveis diversas implementa-
Vejamos um exemplo para ajudar na compreensão destas dife- ções e essas classes tipicamente têm os nomes no formato <estilo de
renças. A Listagem 2 mostra a iteração sobre os elementos de uma implementação><interface>. Por exemplo, a classe ArrayList possui
lista, sobre os elementos de um HashSet e sobre os elementos de o estilo de implementação em Array e implementa a interface List.
um Set. No fim, é mostrado o output dos testes realizados para A Tabela 1 sumariza estas classes.
validarmos os resultados. As implementações de uso geral, ou seja, aquelas que respondem
Como podemos ver pela saída gerada ao executarmos esse códi- melhor a um maior número de casos de uso, que podemos ver na
go, a lista manteve a ordem dos elementos que foram inseridos no Tabela 1, suportam todas as operações opcionais das interfaces e
input; logo, é uma estrutura de dados ordenada, ou que mantém não têm restrições sobre os tipos de elementos que podem conter
a ordem. O HashSet, por sua vez, não é uma estrutura ordena- (vamos falar sobre este assunto mais à frente, no tópico sobre Ge-
da. Isso quer dizer que se fizermos uma iteração sobre os dados nerics). Estas implementações não são sincronizadas, ou seja, não
que foram inseridos, não teremos como garantir a sua ordem, suportam o uso concorrente por mais que uma thread de forma
como visto no exemplo, pois esta depende do valor do hash de segura, mas a classe Collections contém wrappers de sincroni-
cada objeto, assim como do tamanho do array de dispersão. Por zação que podem ser usados para torná-las sincronizadas, caso
último, TreeSet ordena os dados segundo um algoritmo binário seja necessário (veremos um exemplo mais à frente).
Isso quer dizer que, no caso de acesso concorrente por mais que sobre LinkedList, pelos mesmos motivos que foram explicados no
uma thread, devemos optar por uma implementação que garanta caso da escolha da melhor estrutura que implementa a interface
esse acesso com segurança ou, como informado anteriormente, List. ArrayDeque é uma estrutura baseada em array (mais efi-
utilizar um wrapper de sincronização fornecido pela classe ciente) e a LinkedList é uma estrutura de lista duplamente ligada
Collections na estrutura de lista escolhida, como nos mostra o (mais dinâmica, mas menos eficiente).
código a seguir:
Que implementação de Map escolher?
List<String> l = Collections.synchronizedList(new ArrayList<String>()); Do mesmo modo que foi explicado nas implementações de
Queue, o caso da interface Map é muito semelhante ao da escolha
Um wrapper nos permite adotar uma estrutura que já conhece- da implementação de estruturas Set, como podemos verificar na
mos, e com isso evita o trabalho de termos que procurar por uma Tabela 1. Deste modo, para estruturas de dados do tipo Map,
nova implementação para um caso específico. a melhor implementação para uso geral é a LinkedHashMap,
por ser mais flexível que uma HashMap e mais rápida que uma
Que implementação de Set escolher? TreeMap. Para mais detalhes sobre esta explicação, veja o tópico
No caso da estrutura de dados do tipo Set, a melhor implemen- “Que implementação de Set escolher?”.
tação para uso geral é a LinkedHashSet, por ser mais flexível que
uma HashSet e mais rápida que uma TreeSet. Ordenação de coleções de dados
No entanto, vamos começar nossa análise pela HashSet. Esta Uma das ações mais comuns que temos que fazer com uma cole-
collection é implementada usando uma Hashtable e pode ser ção de dados é ordenar os elementos nela contidos. Para nos ajudar
vista, basicamente, como uma Hashtable onde só existem chaves. neste sentido, a linguagem Java disponibiliza duas opções para
Logo, não existem elementos duplicados. Por ser baseada nesta ordenarmos uma coleção: através da implementação da interface
estrutura de dados que é organizada por um array de dispersão, Comparable, por parte dos objetos a serem guardados numa cole-
os seus elementos não são ordenados. Ademais, os métodos add(), ção, ou através da implementação da interface Comparator, num
remove() e contains() são de complexidade constante O(1), o que objeto de comparação que serve apenas para este fim.
é o melhor caso em termos de eficiência. A interface Comparable transmite ordenação natural para
Os objetos do tipo TreeSet são implementados numa estrutura classes que a implementam. Esta interface especifica uma re-
de árvore binária balanceada, utilizando o algoritmo de árvores lação de ordem, que também pode ser usada para substituir a
preta-vermelha, para ser mais preciso. Os seus elementos são ordenação natural.
ordenados e, por ser uma árvore ordenada binária, significa Vejamos um exemplo para ilustrar essa informação. Para isso,
que os métodos básicos add(), remove() e contains() possuem suponha que precisamos de uma classe para representar um
complexidade logarítmica O(log(n)), o que é pior que o caso de estudante, onde apenas necessitamos saber qual o seu nome, a
complexidade constante O(1), mas melhor que o caso de comple- idade e uma nota que o estudante tenha no estabelecimento de
xidade linear O(N). Apesar de ter uma maior complexidade que ensino. Neste cenário, sabemos que iremos manusear coleções de
a estrutura anterior, ela nos oferece funcionalidades para lidar estudantes e que precisaremos listar os estudantes destas coleções
com conjuntos classificados, como retornar o primeiro ou último pela ordem alfabética dos seus nomes. Dito isso, a Listagem 3
elemento da classificação e listar os dados de forma classificada, apresenta um exemplo de implementação desta classe.
da maneira que acharmos mais conveniente através da implemen- Como podemos verificar, a classe Student implementa a interface
tação da interface Comparable ou através de um Comparator. Comparable. Sendo assim, deve implementar o método compare-
Por fim, uma LinkedHashSet é implementada com uma Hashta- To(), que é quem viabiliza a ordenação natural a instâncias desta
ble e uma lista duplamente ligada (LinkedList) sobre os elementos classe. Neste exemplo, este método apenas usa o compareTo() da
contidos em sua Hashtable. Isso quer dizer que temos a vantagem classe String, já que esta classe também implementa a interface
de ter a complexidade constante na utilização dos métodos básicos, Comparable. Logo, temos que a ordenação natural de um estu-
como temos na HashSet (com um ligeiro comprometimento de- dante é estabelecida pela ordem alfabética do seu nome.
vido à adição da lista ligada), mas ao mesmo tempo conseguimos Vamos ver o que isso quer dizer observando o resultado desta
manter a ordem de inserção dos objetos através da ligação feita implementação através de um método de testes que cria uma
pela LinkedList. Por estar num meio termo entre a eficiência e a coleção ordenada e depois itera sobre seus elementos. Para isso,
flexibilidade de utilização, esta é a implementação que geralmente adotamos a opção TreeSet, já que precisamos apenas ordenar e
satisfaz mais casos de uso para estruturas do tipo Set. iterar sobre a lista. A Listagem 4 mostra o nosso código de teste
e o respectivo output.
Que implementação de Queue escolher? Ótimo! Como podemos constatar, inserimos os alunos aleato-
O caso da Queue é semelhante ao da List, como podemos ver riamente, com os nomes não ordenados e o output nos mostra a
pela Tabela 1. Assim sendo, a melhor implementação para uso lista de alunos ordenados alfabeticamente pelos nomes, mas o
geral de filas é a ArrayDeque, devido a sua melhor performance que aconteceu com os alunos com o mesmo nome?
número positivo, ou seja, este aluno virá depois que o aluno com- Listagem 7. Exemplo de utilização da classe para comparação da idade dos
parado. Explicando de outra maneira, se o resultado do método alunos.
compareTo() for igual a 0, isso significa que os dois objetos são 01 public static void testComparable() {
iguais. Se o resultado for menor do que 0, significa que o objeto 02 Comparator<Object> csa = new CompareStudentAge();
é “menor” (vem antes) que o objeto comparado, e se o resultado 03 Set<Student> set = new TreeSet<>(csa);
04
for maior do que 0, significa que o objeto é “maior” (vem depois)
05 set.add(new Student(“José”, 34, 100));
que o objeto comparado. 06 set.add(new Student(“Miguel”, 32, 94));
Caso analisemos com atenção a comparação da idade do aluno, 07 set.add(new Student(“Bruno”, 36, 56));
vamos notar que invertemos a ordem de comparação para con- 08 set.add(new Student(“Bruno”, 30, 56));
09 set.add(new Student(“Bruno”, 26, 82));
seguirmos um resultado por ordem crescente, ou seja, se a idade 10 set.add(new Student(“Fernando”, 30, 80));
do aluno atual for menor que a idade do aluno a ser comparado, 11 set.add(new Student(“Alex”, 28, 78));
então o resultado será negativo, o que quer dizer que o aluno 12
13 Iterator<Student> it = set.iterator();
atual é “menor” (vem antes) que o aluno a ser comparado. Para
14 while (it.hasNext()) {
alterarmos a ordem de crescente para decrescente, basta modificar 15 System.out.println(it.next());
a ordem da subtração, assim como foi feito para a comparação da 16 }
idade dos alunos. 17 }
Desta vez, criamos uma lista de inteiros. Ao fazer isso, o com- Listagem 13. Código exemplo utilizando a classe Box.
pilador Java sabe que só é possível inserir e retornar inteiros
01 public static void testBox() {
da lista. Deste modo, a tentativa de inserir uma String, como
02 Box box = new Box();
acontece na linha 4, gera o erro de compilação apresentado no 03
fim da listagem, informando que o método add() para uma lista 04 box.set(1324);
de inteiros não é aplicável a argumentos do tipo String. Como 05 Object o = box.get();
o compilador nos garante que só existirão inteiros na lista, não 06 if (o instanceof Integer) {
07 Integer i = (Integer) o;
existe a necessidade de casts no momento da leitura dos seus
08 System.out.println(“Box has: “ + i);
valores, como podemos confirmar nas linhas 7 e 10. Assim, não 09 }
temos que remediar o problema perguntando os tipos de elemen- 10
tos que estão na lista antes de os ler. Em vez disso, impedimos 11 box.set(“Another object”);
12 o = box.get();
que o problema possa acontecer, criando uma restrição sobre o
13 if (o instanceof String) {
que pode ser inserido na lista. 14 String s = (String) o;
15 System.out.println(“Box has: “ + s);
Como escrever uma classe genérica? 16 }
17 }
Agora que já sabemos quais as vantagens de se utilizar classes
genéricas, vamos aprender como escrever uma. Para isso, cria- -- Output
remos primeiro uma classe da maneira “tradicional” e depois Box has: 1324
vamos transformá-la numa classe genérica para entendermos Box has: Another object
quais as diferenças. Listagem 14. Código da classe Box na sua forma genérica – GenericBox.
Seja Box uma classe que representa uma caixa que pode guardar
apenas um objeto de cada vez e da qual podemos retornar o objeto 01 public class GenericBox<T> {
que está dentro dela, vejamos como escrever um possível código 02 private T t;
03
para esta classe na sua forma tradicional (vide Listagem 12).
04 public void set(T t) {
05 this.t = t;
Listagem 12. Classe que representa uma caixa que guarda um objeto. 06 }
07
01 public class Box { 08 public T get() {
02 private Object object;
09 return t;
03
10 }
04 public void set(Object object) {
05 this.object = object; 11 }
06 }
07
08 public Object get() {
09 return object;
Comparando a classe Box com GenericBox, podemos notar que
10 } todas as ocorrências de Object foram substituídas pela variável
11 } de tipo T, que poderá ser especificada com qualquer tipo não
primitivo que desejarmos. Esta mesma técnica pode ser usada
Como podemos verificar, uma caixa pode conter um objeto de para criar interfaces genéricas.
qualquer tipo. Isso quer dizer que quando retornarmos o objeto da Para compreendermos o resultado desta alteração, vejamos como
caixa, teremos que perguntar que tipo de objeto ela contém antes podemos utilizar a classe GenericBox na Listagem 15.
de fazermos o cast para o seu tipo específico e assim podermos Com a nova classe, somos obrigados a identificar, no momento
trabalhar com ele. Para entendermos melhor este fato, analisemos da sua instanciação, que tipo de objeto ela (GenericBox) pode
a Listagem 13, que nos mostra um exemplo de utilização segura guardar. Deste modo seremos impedidos de guardar objetos
da classe Box e o output gerado pelo código. que sejam de um tipo diferente do previamente acordado e, se
Para verificarmos as diferenças entre as implementações no tentarmos burlar esse acordo, teremos um erro de compilação e
modelo tradicional e com genéricos, vamos reescrever a classe Box não conseguiremos executar o programa. Portanto, ganhamos a
de forma a transformá-la numa classe genérica e assim podermos segurança do “type safety” e não temos mais que nos preocupar
usufruir da segurança do type safety. A Listagem 14 nos mostra a com casts aos objetos guardados pela caixa.
implementação dessa classe, a qual chamamos de GenericBox.
Por regra, uma classe genérica é definida no formato Convenções de nomeação para parâmetros de classes genéricas
NomeDaClasse<T1, T2, T3, ..., Tn>, onde T representa um tipo Uma ressalva importante é que T não é uma palavra-chave do
de parâmetro da classe. Em GenericBox, introduzimos apenas Java. Portanto, a classe GenericBox poderia ser escrita da mesma
uma variável do tipo T, que será o parâmetro recebido pela classe forma como se encontra na Listagem 16, sem qualquer tipo de
e que irá determinar o tipo de objeto que ela poderá usar. problema.
Quanto mais complexo é um projeto de software, mais difícil Você gostou deste artigo?
torna-se encontrar os seus bugs. Sendo assim, um planejamento
cuidadoso e uma série de testes bem executados podem ajudar a Dê seu voto em www.devmedia.com.br/javamagazine/feedback
reduzir o número destes, mas não nos garante a sua eliminação Ajude-nos a manter a qualidade da revista!
por completo.
P
rojetar e desenvolver um software de qualidade problemas durante o desenvolvimento de novas funcionalidades e
não é uma tarefa trivial. Muitos softwares levam manutenção de um software. Neste contexto, este artigo é útil para
incontáveis meses para serem desenvolvidos, a identificação de códigos mal escritos, que podem trazer problemas
gerando altos custos e jamais chegam a ser entregues. em tempo de manutenção, e também para a aplicação de refatoração
Um dos fatores mais comuns para o insucesso na criação nestes códigos, utilizando para isso alguns padrões de projetos e
de softwares é o código mal escrito, também conhecido boas práticas.
como code smell.
Por isso, é importante que a equipe de desenvolvimen-
to se preocupe desde o início do projeto em entregar Diante disso, o objetivo deste artigo é mostrar como identificar
um software de qualidade, de modo a evitar futuros códigos mal escritos e como refatorar estes códigos utilizando
problemas. No entanto, se um software é entregue padrões de projetos e boas práticas de programação. Deste modo,
com trechos de código mal escritos, ele fatalmente durante o artigo serão mostrados exemplos de códigos mal
apresentará problemas em tempo de manutenção e até escritos, a identificação do mesmo e a aplicação da refatoração,
mesmo para o incremento de novas funcionalidades. apresentando, por fim, os ganhos obtidos.
Neste ponto, o software pode tornar-se inviável, pois
um código ruim tende a gerar mais código ruim, além Padrões de Projeto
de diminuir a produtividade no desenvolvimento. Este O foco principal deste artigo não é esmiuçar em detalhes os
é o momento de identificar os pontos de código mal conceitos dos padrões de projetos. No entanto, é importante ter
escrito e refatorá-los. conhecimento das características dos padrões utilizados no de-
Para auxiliar e orientar os desenvolvedores neste pro- correr do artigo, de modo que se possa tomar a decisão de qual
cesso, existem diversos padrões de projetos e boas práti- padrão utilizar em determinadas situações.
cas de programação que podem ser aplicados como base Um padrão é composto basicamente por uma solução robusta
para a refatoração de códigos mal escritos. No entanto, para problemas comuns. Em outras palavras, um padrão é uma
antes de iniciar a refatoração de um código, deve-se levar solução que pode ser facilmente adaptada para resolver problemas
em consideração qual a característica que torna o código semelhantes, em diferentes contextos.
ruim e avaliar qual o padrão ou boa prática que melhor Existem basicamente duas maneiras de se utilizar padrões em
se encaixa para refatoração de tal código. um projeto de software. Uma delas é a utilização dos padrões já
Em 1994 os autores Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides publicaram o
Boas práticas
famoso livro Design Patterns: Elements of Reusable Object-Oriented Software (Padrões de Projeto:
Escrever um código limpo e legível não é fácil. O código deve
Soluções Reutilizáveis de Software Orientado a Objetos) com o objetivo de conceituar alguns
ser frequentemente revisado e refatorado. Para isso, o desenvol-
padrões de projeto para o desenvolvimento de softwares orientados a objetos. Depois disso, estes
vedor deve partir do princípio de que um código sempre pode
autores ficaram conhecidos como Gang of Four, ou ainda, GoF.
melhorar, por mais limpo e bem escrito que possa parecer. Com
O livro busca documentar soluções recorrentes para tipos de problemas parecidos, que se agrupam
este objetivo, existem diversas boas práticas de programação
em contextos semelhantes. Os vinte e três padrões abordados no livro estão divididos em três
que, se utilizadas, podem melhorar muito a legibilidade do
categorias: padrões criacionais, estruturais e comportamentais.
código de um projeto, facilitando a manutenção e a evolução
do mesmo. Lembre-se: um código fácil de ler é um código fácil
Padrões de Projeto Criacionais de manter e evoluir.
Os padrões de projeto criacionais, como o próprio nome indica, O famoso livro Clean Code, do autor Robert C. Martin, também
são padrões utilizados para a criação de objetos. A ideia é separar conhecido como Uncle Bob, é uma leitura ótima neste sentido,
a lógica de criação de modo que a mesma possa ser encapsulada e e apresenta conceitos e exemplos interessantíssimos no que diz
reaproveitada facilmente. A seguir são listados alguns exemplos respeito a código limpo e legível e sua importância para o sucesso
de padrões de projeto criacionais: dos projetos. O livro também faz uma abordagem interessante
• Abstract Factory; sobre os princípios S.O.L.I.D (vide BOX 2).
• Builder; Veremos agora alguns exemplos de boas práticas de progra-
• Factory Method; mação e como elas podem ajudar na refatoração de códigos mal
• Prototype; escritos.
• Singleton.
Box 2. Princípios S.O.L.I.D
Padrões de Projeto Estruturais Os Princípios SOLID representam cinco princípios de design de softwares orientados a objetos.
Os padrões de projeto estruturais focam na organização das Abordados inicialmente por Robert C. Martin, são considerados um conjunto de boas práticas
classes e objetos de um sistema. O propósito é evitar o alto acopla- de programação que visa separar responsabilidades e diminuir o acoplamento entre classes,
mento entre as classes, modularizando o sistema em componentes melhorando a legibilidade e a organização do código. A seguir, apresentamos um breve resumo de
com responsabilidades específicas. A seguir são listados alguns cada um dos cinco princípios:
exemplos de padrões de projeto estruturais: 1. Single Responsibility Principle: cada classe ou método de um projeto deve ter apenas uma
• Adapter; responsabilidade, ou seja, deve fazer apenas uma coisa. E deve fazer da melhor forma possível;
• Bridge; 2. Open Closed Principle: princípio do aberto/fechado. Diz que as classes do projeto podem ter seu
• Composite; comportamento facilmente estendido, seja por herança, interfaces ou composição. Por outro lado,
• Decorator; as classes não devem ser abertas para pequenas modificações;
• Facade; 3. Liskov Substitution Principle: é uma extensão do princípio aberto/fechado e diz que uma classe
• Flyweight; base deve poder ser substituída por qualquer uma de suas classes derivadas;
• Proxy. 4. Interface Segregation Principle: define que uma interface não pode fazer com que uma
classe implemente métodos que não sejam dela. Além disso, as interfaces devem ter poucos
Padrões de Projeto Comportamentais comportamentos, evitando o alto acoplamento e procurando manter a coesão;
Os padrões de projeto comportamentais definem como as res- 5. Dependency Inversion Principle: este princípio diz que as classes de um nível mais alto de abstração
ponsabilidades são atribuídas entre as classes do sistema, ou seja, não podem depender de seus detalhes de implementação, ou seja, devem ser independentes.
atuam sobre o comportamento das classes.
Nomes Significativos deve ser agregada uma nova funcionalidade no sistema e aquele
Para o desenvolvimento de um projeto, utilizamos vários trecho de código será impactado. Por fim, um terceiro desenvol-
pacotes, classes, métodos e variáveis. São esses elementos que vedor recebe a demanda e se depara com um código confuso e
compõem um projeto e o desenvolvedor é o responsável por dar que faz uma coisa diferente do que diz o comentário.
nome a eles. Diante disso, é muito importante que o desenvolvedor No entanto, existem alguns tipos de comentários que podem
escolha nomes que expressem realmente o que aquela classe ou ser utilizados. É o caso dos Javadocs, que podem ser definidos
variável representa dentro do contexto do projeto, com o objetivo em métodos públicos e classes que sejam disponibilizadas como
de facilitar a leitura do código. bibliotecas para uso em outros módulos ou projetos.
Infelizmente, não é difícil encontrar classes com incontáveis Analisando as vantagens e desvantagens, o ideal é evitar os
linhas de código, com métodos gigantescos e se deparar com comentários. Afinal, um código bem escrito e fácil de entender
nomes de variáveis que não fazem sentido algum. Isso dificulta, não precisa de comentários.
e muito, a manutenção do código, pois consome um tempo bem
maior de leitura e interpretação para realmente compreender o Refatorando na prática
que determinada variável ou método faz, ou deveria fazer. Por Para demonstrar na prática a refatoração utilizando padrões de
isso, é preferível perder um pouco de tempo definindo bons no- projeto e boas práticas de programação, será utilizado um exem-
mes para suas classes, variáveis e métodos, do que perder muito plo de um projeto que requer uma refatoração de código, com o
tempo depois tentando entender o que cada um destes elementos objetivo de melhorar a legibilidade do mesmo e facilitar as futuras
significa dentro do contexto do projeto. manutenções e evoluções que se façam necessárias.
Métodos com poucos parâmetros As regras para o cálculo do valor dessa conta são as seguintes:
Procure sempre criar métodos que recebam poucos parâmetros. • Para os veículos que permanecerem no estacionamento por até
De preferência, nenhum, e no máximo, três. Mais que isso pode meia hora, o valor da conta será correspondente ao da linha Até
ser um indício de que seu método está fazendo mais coisas do que ½ hora da referida tabela;
deveria fazer, quebrando o princípio da responsabilidade única. • Para os veículos que permanecerem no estacionamento por mais
Métodos com muitos parâmetros são também mais difíceis de de meia hora, até uma hora, o valor da conta será correspondente
serem reutilizados. ao da linha Até 1 hora;
Caso seja necessário criar métodos com mais de três parâmetros, • Para os veículos que permanecerem no estacionamento por mais
avalie a possibilidade de encapsular estes parâmetros em um de uma hora, as regras são as seguintes:
objeto, aumentando assim a possibilidade de reuso. - Para os veículos que permanecerem no estacionamento até
seis horas, o valor da conta será o valor da linha Até 1 hora,
Comentários mais o valor da linha Hora Adicional da Tabela 1, multiplicado
Comentários devem ser evitados. Uncle Bob diz que um comen- pelo número de horas adicionais de utilização;
tário é uma tentativa de explicar um código confuso e mal escrito. - Para os veículos que permanecerem no estacionamento por
Além disso, existe um problema maior ainda com comentários. mais de seis horas, o valor da conta será correspondente ao
Imagine que um desenvolvedor escreveu um código confuso para valor da linha Diária;
implementar uma regra de negócio e utilizou um comentário para • Para os veículos do tipo carro de passeio, devem ser utilizados
explicar o que o código faz. Pouco tempo depois, a regra de negócio os valores da coluna Carro de Passeio;
muda e outro desenvolvedor recebe a tarefa de aplicar a alteração • Para veículos do tipo caminhonete, devem ser utilizados os
no código. Ele o faz, porém, não altera o comentário. Mais tarde, valores da coluna Caminhonete;
A Figura 1 representa o diagrama de Aplicando boas práticas na refatoração confusa para obter o período que o veículo
classes da versão atual do módulo de Feita a análise e a identificação dos pro- utilizou o estacionamento. No entanto, o
cálculo da conta de utilização do estacio- blemas no código, o desenvolvedor deve ideal seria eliminar o comentário, extrair
namento. Com o intuito de facilitar a com- agora começar a refatoração. O primeiro a lógica para obter o período de utilização
preensão do conteúdo apresentado a partir passo é aplicar boas práticas para eliminar em um método privado e renomear a va-
deste ponto, conforme o código for sendo alguns problemas levantados. Como é riável para indicar que o período armaze-
refatorado, o diagrama será evoluído, para possível observar na Listagem 1, o método nado é em minutos.
ilustrar a utilização de alguns padrões de responsável por gerar a conta do estaciona- Outro ponto a se observar, é que o méto-
projetos e boas práticas na refatoração. mento começa com uma linha um pouco do pode retornar null, o que nunca é uma
boa ideia. Analisando o código, pode-se
Listagem 4. Aplicando boas práticas na refatoração. constatar que o método só retorna null se
o veículo não tiver nenhum valor defini-
01 public Double gerarConta() { do para o atributo que representa o tipo
02
03 long periodoEmMinutos = obtemPeriodoDeUtilizacaoEmMinutos(); de veículo ou se o valor for diferente de
04 CAMINHONETE ou CARRO_PASSEIO.
05 //lógica omitida... Neste caso, o método pode lançar uma
06
07 throw new IllegalStateException(“Tipo de veículo indefinido”); exceção indicando que o estado do objeto
08 } é inválido. A Listagem 4 mostra como fica
09
o código após a refatoração.
10 private long obtemPeriodoDeUtilizacaoEmMinutos() {
11 return (saida - entrada) / 1000 / 60; Note que o código pertinente à lógica de
12 } geração da conta do estacionamento foi
omitido dessa listagem, pois neste ponto
da refatoração ele ainda não foi alterado.
Aplicando o padrão Template Method na refatoração Dito isso, a seguir serão apresentadas as subclasses criadas
Finalmente, será demonstrada a aplicação do padrão Template para implementar as regras de cada tarifa. A Listagem 9 ilustra
Method, para reduzir o restante da lógica condicional da classe que a subclasse responsável por calcular a tarifa de veículos que
gera a conta do estacionamento. Este padrão consiste na utilização permanecem no estacionamento até meia hora. A Listagem 10
de herança para variar partes de um algoritmo. Basicamente, a apresenta a classe para veículos que permanecem de meia hora
classe principal possui uma lógica genérica, onde apenas algu- até uma hora. A Listagem 11 mostra a classe com o cálculo para
mas partes mudam. E esses pontos do algoritmo que variam são veículos que ficam até seis horas, onde temos o cálculo de horas
especializados nas subclasses. adicionais de utilização.
No exemplo da geração da conta do estacionamento, temos uma
lógica genérica que obtém o tempo de utilização e, de acordo Listagem 9. Classe que representa o cálculo de tarifa para até meia hora de utilização.
com as tarifas do tipo do veículo, calcula o valor da conta sobre 01 public class ContaAteMeiaHora extends Conta {
o tempo que o veículo permaneceu no estacionamento. Porém, 02
03 @Override
a tarifa varia para cada faixa de tempo de utilização. Com essa
04 protected double calcularContaDeUtilizacao(long periodoEmMinutos) {
leitura, pode-se delegar o cálculo para subclasses da classe Conta, 05 return getVeiculo().tarifaAteMeiaHora();
permitindo o isolamento da lógica do cálculo para classes espe- 06 }
07 }
cíficas, facilitando a manutenção e melhorando a legibilidade e
organização do código. Para tanto, a classe Conta deve passar a ser Listagem 10. Classe que representa o cálculo de tarifa para mais de meia hora, até
uma hora de utilização.
abstrata, forçando a implementação do cálculo do valor da conta
nas subclasses específicas para cada faixa de tempo. 01 public class ContaAteUmaHora extends Conta {
02
A Figura 3 ilustra como fica o diagrama de classes do módulo de
03 @Override
cálculo do valor da conta de utilização do estacionamento com a 04 protected double calcularContaDeUtilizacao(long periodoEmMinutos) {
aplicação do Template Method após a refatoração do código. 05 return getVeiculo().tarifaAteUmaHora();
06 }
A partir dessa refatoração, além da eliminação dos condicionais 07
no código para gerar a conta de utilização do estacionamento, 08 }
resolvemos também o problema do crescimento da complexidade Listagem 11. Classe que representa o cálculo de tarifa para mais de uma hora, até
ciclomática do código, em caso da adição de novas regras de tarifa seis horas de utilização.
como, por exemplo, valores para pernoites e mensalidades.
01 public class ContaHoraAdicional extends Conta {
Para a utilização do Template Method, foram criadas quatro 02
subclasses para a classe Conta, uma para cada tarifa entre as faixas 03 @Override
04 protected double calcularContaDeUtilizacao(long periodoEmMinutos) {
de valores definidas na Tabela 1. Assim, caso alguma nova tarifa 05 long horasAdicionais = (periodoEmMinutos - 1) / 60;
seja criada, basta criar uma nova subclasse e implementar nela 06 return horasAdicionais * getVeiculo().tarifaHoraAdicional() + getVeiculo()
a regra representando a tarifa. Com isso, o código fica isolado e .tarifaAteUmaHora();
07 }
cada classe tem apenas a responsabilidade de implementar a regra 08
específica daquela tarifa. 09 }
U
ma das maneiras de tentar estabelecer uma Fique por dentro
comunicação organizada e controlada entre
os componentes que formam o conjunto de Neste artigo apresentaremos um exemplo prático para demonstrar
sistemas de uma empresa/departamento seria utilizar o que é e como pode ser concebida uma Arquitetura Orientada a
a ocorrência de um fato, ou seja, um evento, pois desta Eventos. Para isso, implementaremos todas as partes relevantes de
forma teríamos a comunicação entre as partes mantida uma arquitetura completa de integração, incluindo componentes
com um baixo acoplamento, onde emissores e assinantes que compõem a Arquitetura Orientada Serviços (SOA). Em seguida,
estariam ligados apenas pela criação destes eventos. complementaremos nosso estudo abordando algumas ferramentas
Além dos eventos definirem o momento em que a que ajudam a conceber a EDA (Event Driven Architecture).
integração pode ocorrer, possuem as informações ne- Portanto, este artigo é útil para profissionais envolvidos em ambien-
cessárias que serão trocadas entre as peças, efetivando tes nos quais há necessidade de comunicação entre os participantes
as integrações. No acontecimento de um evento, ações (sistemas e componentes) que formam todo o conjunto de sistemas
podem ser tomadas pelos interessados sem a necessi- de software da empresa.
dade de qualquer tipo de atuação por parte do gerador
do evento.
Mas dentro do nosso contexto de tecnologia: o que
são estes eventos? A resposta é: depende! Apesar de ou a quantidade de execuções de um serviço no mês. Porém, na
a resposta parecer uma tentativa de ludibriar alguém maioria dos cenários, ao estabelecer uma arquitetura orientada
que você gostaria de convencer, ela realmente depende a eventos, a prioridade fica para a definição e captura de eventos
do contexto envolvido, ou seja, do modelo do domínio do negócio em questão, para que a empresa tenha mais benefícios
de negócio que estamos nos referindo. O evento é algo sobre o orçamento investido em tecnologia. Os eventos de natu-
relevante para o negócio da empresa, podendo ser: a reza técnica (como tempo de resposta) ganham maior prioridade
ocorrência de uma venda, o cadastro de um novo cliente, quando afetam diretamente os negócios da empresa.
a desistência de uma assinatura de revista, saques em Em uma arquitetura orientada a eventos, colocamos obviamente
locais geograficamente distantes do mesmo correntista, como ator principal o evento, estabelecendo assim o elo que cau-
ou o valor acumulado de pedidos realizados dentro de sará a integração entre os sistemas e componentes. Vale ressaltar
um período. ainda que um evento é autocontido, isto é, carrega consigo a
A definição do que pode ser considerado um evento informação de quando está preparado para ser disparado e quais
está muito mais orientada ao negócio da empresa do informações deve conter.
que a decisões técnicas. É claro que é possível também O que de fato a Arquitetura Orientada a Eventos (EDA – Event
estabelecer eventos de natureza técnica como, por Driven Architecture) deve promover é uma estrutura que possa
exemplo: o tempo médio da execução de transações estabelecer o alicerce de apoio à integração entre os sistemas e/ou
A característica comum de um ambiente nante (publish/subscriber). A outra opção informações desde o início do cenário tran-
SOA é a existência de consumidores e pro- seria utilizar ferramentas especializadas sacional, passando pelo sistema de vendas,
vedores, ao passo que em EDA possuímos no gerenciamento e tratamento de eventos, barramento de serviços, processamento de
assinantes e emissores. Como um dife- como o Esper, que iremos analisar em se- eventos, chegando até a visualização do
rencial, na arquitetura EDA os emissores guida no nosso cenário de exemplo. monitoramento das atividades de negócio.
não sabem quem são os seus assinantes, Desta maneira, podemos ter uma melhor
viabilizando assim uma integração de Arquitetura Orientada a Eventos na demonstração prática do papel dos eventos
acoplamento baixo. prática dentro desta arquitetura.
Na implementação de uma Arquitetura O nosso exemplo de Arquitetura Orienta- A visão lógica e completa da arquitetura
Orientada a Eventos, uma das opções mais da a Eventos provém de um cenário simples implementada nesta solução pode ser ob-
simples seria utilizar filas de mensagerias de e-commerce, onde uma loja on-line servada na Figura 3.
(normalmente disponíveis em ferramentas fornece seus produtos. Apesar de ser um Nesta solução temos os clientes realizan-
ESB), através de tópicos, colocando em cenário simples de compras via internet, do os pedidos no site da empresa ou com
ação o padrão de comunicação editor/assi- implementamos o fluxo de integração das representantes de vendas via telefone, o
que é ilustrado nos quadros número um e
dois. As compras são registradas através
do serviço Registar Pedido, que está expos-
to no Barramento de Serviços Corporativos
(ESB) da empresa (vide quadro número
três). O barramento realiza o roteamento
do request para o sistema atual, responsá-
vel na empresa por manusear e gerenciar
as transações de vendas (vide quadro
número quatro).
Até este momento observamos que não
temos nada muito diferente de uma Ar-
quitetura Orientada a Serviços, onde cada
sistema realiza a sua tarefa (motivo pelo
qual foi criado) e as integrações ocorrem
de forma controlada pelo Barramento
de Serviços. Neste são tratados todos os
aspectos de integração, desonerando os
sistemas de tarefas como: transformações,
enriquecimento, mapeamento, roteamen-
to, orquestração, protocolos e adaptadores
de tecnologia.
Os pedidos irão para o sistema deno-
Figura 2. Integração entre sistemas utilizando eventos com o apoio de um barramento de serviços (SOA)
minado SalesCenter, que após confirmar
com sucesso o registro da compra, eventos
de interesse da empresa nesta transação
de registro de compra serão capturados
e lançados pelo ESB para o componente
que realiza o processamento de eventos
(vide quadro número cinco). Aqueles que
tenham alguma relevância informacional
serão, também, armazenados em uma
base de dados (vide quadro número seis).
O sistema de monitoramento, denominado
MiniJS-BAM, faz uso destes dados para
apresentar informações das atividades de
negócio que estão ocorrendo na empresa
simultaneamente à ocorrência dos fatos
Figura 3. Visão holística da arquitetura lógica da solução de vendas on-line (vide quadros de número sete e oito).
Clientes Executivos
Compras()
Registrar Compras()
Lançar Ev entos()
Armazenar Eventos()
Monitoração()
Ler Eventos()
Possui a função de prover o Barramento de Serviço, conhecido pelo acrônimo em inglês ESB (Enterprise Service Bus). Este componente é
Mule ESB responsável por estabelecer o barramento dos fluxos de mensagens de integrações entre consumidores e provedores de serviços. A versão
(Anypoint Studio) utilizada é a Community Edition 4.1.1, representada nesta solução pelo IDE Anypoint Studio, que possui uma versão reduzida do Mule ESB
embutido para a execução dos fluxos.
Servidor de aplicação Java certificado para o Web Profile Java EE 6 da Apache, com alguns adicionais, por exemplo, em vez de termos dispo-
nível o EJB Lite, como diz a especificação Web Profile Java EE, temos uma implementação completa da API EJB. Utilizaremos este servidor de
Apache TomEE+
aplicação para fazer a implantação das nossas soluções Java, o SalesCenter e o MiniJS-BAM. O Apache TomEE+ é praticamente o OpenEJB
com “esteroides”. Ele nasceu e cresceu deste projeto. A versão utilizada na solução é a 1.7.1.
Banco de dados utilizado como repositório das informações capturadas durante o processamento dos eventos pelo Esper. Assim, mais tarde,
ferramentas e/ou aplicações interessadas em mostrar estas informações em uma perspectiva analítica, podem fazê-lo. Nesta base de dados
MySQL
armazenamos o resultado final do processamento dos eventos, apenas para que nossa solução MiniJS-BAM possa usufruir e montar seu
cockpit. Veremos mais informações sobre isso no nosso exemplo.
Componente de software para trabalhar com o processamento do fluxo de eventos, criado pela empresa EsperTech. Iremos analisar em mais
Esper
detalhes esta ferramenta a seguir.
A apresentação provida pelo sistema de monitoramento ocorre das e o seu propósito básico perante a implementação desta
na forma de gráficos que, juntos, caracterizam uma espécie de solução.
cockpit. A partir dele, os executivos da empresa podem ter a visão A representação da arquitetura desta solução, agora traduzindo
do que acontece nos negócios de sua corporação – neste caso em do conceito para a tecnologia implementada, pode ser observada
tempo real –, o que pode trazer mais segurança e precisão para a na Figura 5. Nesta tradução, temos cada um dos elementos que
tomada de decisões, pois imagine como seria difícil pilotar um utilizamos na composição do cenário.
Boeing 777-200 sem nenhum painel de informações: sem saber A visão da implantação final de nossa solução pode ser vista no
a altitude, velocidade vertical, velocidade em relação ao solo, Diagrama de Deployment na Figura 6. Este digrama é muito útil
velocidade do ar, inclinação, posição dos flaps e a proa, e ainda para conciliar de forma organizada os dois mundos: software e
assim ter que conduzir um “negócio” de U$ 269.500.000,00. infraestrutura. Os nossos componentes de software (WAR e EAR,
Na Figura 4 podemos verificar em uma visão mais conven- neste caso), com as necessidades de infraestrutura (Apache TomEE,
cional, utilizando UML, a dinâmica de funcionamento desta Mule ESB e o MySQL). Infelizmente é pouco usado na prática.
solução. Observe que o fluxo das mensagens inicia no momento Um detalhe importante que podemos verificar nesse modelo
em que o cliente realiza a compra. Em um dado momento após de implantação e que passa despercebido em nossos modelos
a realização da compra, mais tarde ou mesmo simultaneamente, dinâmicos apresentados é a presença do JAR com o nome de Do-
os interessados em analisar as informações coletadas pelos main Model – Canonical. Neste componente estão presentes todos
eventos acessam via Intranet o MiniJS-BAM. os objetos que representam as entidades, mensagens e eventos
Para implantar o nosso exemplo de arquitetura orientada a comuns do modelo de domínio da empresa em questão. Portanto,
eventos, na Tabela 1 temos a lista das tecnologias seleciona- desta forma, mantemos o modelo canônico como um componente
o qual todos os demais podem (na verdade, deveriam) utilizar em devem ser utilizados pelos mecanismos de persistência e serializa-
suas soluções como dependência externa. ção/deserialização REST/XML. Como estas anotações não são intru-
Contido neste componente canônico, dentre outros, há dois sivas, no final as classes continuam sendo simples objetos POJO que
elementos comuns que ultrapassam a fronteira das aplicações da podem ser utilizados por qualquer classe Java, sem a necessidade
empresa, que são os objetos Order e Customer. Estes, respecti- da presença de um container, como um EJB para JPA.
vamente, representam as entidades Pedido de Compra e Cliente, Para controle dos projetos da organização, utilizamos a ferra-
pertencentes ao domínio de negócio da corporação. Na Listagem 1 menta Maven. Assim, este componente do modelo canônico estará
são demonstrados estes objetos. Perceba que eles possuem anota- presente no repositório Maven para ser utilizado pelos demais
ções JPA e JAXB, pois assim levam consigo as definições de como projetos de forma organizada e centralizada.
Mule ESB
Apache Tomee
«Services»
Message Flow s
«EAR» depends
«Customers Simulation» «use» SalesCenter
Front End
«use» depends
«JAR»
SoapUI depends :Domain Model
- Canonical
«JAR»
Domain Model - «WAR»
Canonical MiniJS-BAM
«JAR» depends
Esper Ev ent
Processing
«use»
Persistence
«Database»
«use»
MySQL
«use»
Vale ressaltar novamente a grande utilidade que o conceito podemos padronizar eventos (por exemplo: Quantidade Total
de Canônico pode trazer. Para criar um padrão de comunicação de Ordem de Compra no dia), pois tais eventos também serão de
entre os sistemas da empresa, entidades e mensagens podem ser uso corporativo, ou seja, não pertencem apenas a uma aplicação,
padronizadas dentro do barramento de serviços, onde os sistemas e sim à corporação.
se integram “falando” a mesma língua. E da mesma maneira que Em nossa solução é importante observar que o Esper atua como
podemos padronizar entidades comuns (por exemplo: Cliente), um filtro, coletando apenas informações que são alvos de interes-
se. Apenas estes dados, no formato de eventos, são direcionados
para o armazenamento na base de dados MySQL. Portanto, hipote-
Listagem 2. Exemplos de declarações da linguagem específica de domínio EPL da ticamente, se 100 milhões de transações passarem pelo Barramento
ferramenta Esper.
de Serviços, apenas uma fração destes dados, já processados no
// Busca o símbolo, o preço e o total do volume dos preços de movimentações da formato de eventos, serão selecionados e lançados pelo Esper como
// ação da IBM dentro objeto de análise. A quantidade de eventos, é claro, dependerá
// dos últimos 60 segundos
do desenho concebido para a solução em questão, o que definirá
select symbol, price, sum(volume) from StockTickEvent(symbol=’IBM’).win:time(60 sec)
também a existência da característica de integração por eventos
// Busca a média e o total do preço nas movimentações da ação da IBM dentro da arquitetura orientada a eventos. A Figura 7 representa uma
// dos últimos 10 elementos
ilustração do fluxo de dados deste cenário.
select avg(price) as avgPrice, sum(price) as sumPrice from
StockTickEvent(symbol=’IBM’).win:length(10) where symbol=’IBM’
Implementação da solução de Arquitetura Orientada a Eventos
// Busca o total das movimentações de ações dentro dos últimos 30 segundos, A partir de agora iremos abordar a implementação, características
// quando o volume for maior que 100
select count(*) from StockTickEvent.win:time(30 sec) where volume > 100 group e principal propósito de cada uma das camadas e componentes que
by symbol fazem parte da nossa solução exemplo de Arquitetura Orientada
a Eventos. Dentro de cada tópico, todo o código fonte relevante à
// Busca a cada 60 segundos o total dos preços dos Pedidos
// nos últimos 30 minutos principal função do componente será apresentado e explicado.
select sum(price) from OrderEvent.win:time(30 min) output snapshot every
60 seconds Front End – SoapUI
// Busca a cada 1.5 minutos as informações das movimentações de ações Para representar o cliente navegando no site e realizando as
// dentro das últimas cinco movimentações ocorridas compras, utilizamos a ferramenta SoapUI, que simula a execução
select * from StockTickEvent.win:length(5) output every 1.5 minutes destas chamadas. Como adendo, podemos aproveitar alguns
Listagem 3. Objetos POJO que representam para o Esper as entidades de eventos. recursos como, por exemplo, simular uma quantidade ininter-
rupta de chamadas ao nosso sistema de vendas. Esta ferramenta
public class StockTickEvent {
irá implementar a ação de enviar pedidos para o Barramento de
private String symbol;
private float price; Serviços Corporativos (ESB) da empresa.
private int volume; Na ferramenta SoapUI criamos um caso de teste onde são orga-
// setters e getters
nizados os requests que simulam as chamadas de clientes. Estes
}
requests se apresentam no formato JSON e possuem todas as
public class OrderEvent { informações de um registro de compra realizado por um cliente.
private float price;
// setters e getters
Utilizamos o formato JSON porque o Mule ESB (Barramento de
} Serviços) fornece um Serviço de Registro de Pedido via proto-
colo REST. Na Listagem 4 podemos conferir o conteúdo desta
mensagem enviada para o ESB.
Para garantir que as execuções dos requests
do caso de teste ocorram com sucesso, cria-
mos assertions no SoapUI para estes requests,
conferindo informações básicas que deveriam
estar presentes na resposta. Nestes assertions
validamos ao menos:
• Que o status do código de retorno HTTP seja
200 – OK;
• Que o conteúdo da resposta contenha o texto
“orderNumber”;
• Utilizamos uma declaração XPath procuran-
do pelo orderNumber na resposta e conferindo
Figura 7. Cenário do fluxo de dados e a filtragem dos eventos pelo Esper que seu valor seja maior que zero.
Esta URL é o endereço utilizado pelo caso de teste do SoapUI O componente de número quatro é uma referência a um subflow,
explicado no tópico anterior. um outro fluxo de mensagem que deve ser chamado neste momen-
Na sequência do fluxo temos o componente de número três, de to. O objetivo deste subflow é realizar a chamada ao provedor do
nome “Save Order to Variable”. Neste elemento temos uma classe serviço, o sistema que registra os pedidos de compra.
Java a qual o Mule ESB executa durante o fluxo da mensagem, Entrando no subflow, de nome SendOrderToSalesCenter, temos
passando para ela todo o contexto de informações. Isto permite o componente de número cinco, que realiza a chamada HTTP/
a realização de algum tratamento dos dados sendo trafegados REST ao sistema SalesCenter. Ele executa o request ao endereço
utilizando código Java. A função deste componente, neste caso, é http://localhost:8083/SalesCenter-Web/rest/order/register/, no qual
apenas armazenar em memória o objeto Order (Pedido de Com- está exposto o serviço de Registro de Compras no protocolo
pra) original que foi enviado no request executado pelo cliente. REST. Este web service RESTful encontra-se em um Servidor de
Para isso, ele converte a String JSON recebida para o objeto do Aplicação Java EE Apache TomEE+, o qual iremos verificar du-
tipo Order e, em seguida, o armazena como uma variável do rante a análise da aplicação SalesCenter nos tópicos a seguir. No
Fluxo de Mensagem. O código da classe SaveOrderToVariable é momento, desempenhando o papel de barramento de serviços,
apresentado na Listagem 6. precisamos entender apenas que este é o provedor do serviço
que registra a ordem de compra. Este serviço recebe o Pedido
de Compra (Order) no protocolo REST e nos retorna um número
Listagem 6. Classe Java executada pelo componente “Save Order to Variable” no
fluxo de mensagem do ESB. gerado caso a transação tenha sido executada com sucesso.
Continuando no subflow SendOrderToSalesCenter, temos o
public class SaveOrderToVariable implements Callable {
componente de número seis, nomeado “Save Order Response to
@Override Variable”, outra classe Java que tem a oportunidade de interagir
public Object onCall(MuleEventContext eventContext) throws Exception { com a mensagem. Neste caso, não alteramos em nada a mensagem.
String restOrder = eventContext.getMessage().getPayloadAsString();
ObjectMapper om = new ObjectMapper(); Apenas a interceptamos para armazenar a reposta do SalesCenter
om.configure(Feature.UNWRAP_ROOT_VALUE, true); em variáveis do fluxo de mensagem do Mule ESB. Na Listagem 7
Order order = om.readValue(restOrder, Order.class); podemos observar o código desta classe.
eventContext.getMessage().setInvocationProperty(“Order”, order);
return eventContext.getMessage().getPayload(); As informações retornadas após a execução da transação do
} pedido de compras estão encapsuladas no objeto Java de nome
ResponseSaveOrder, que pertence ao nosso modelo canônico,
}
presente no JAR mencionado anteriormente.
Listagem 7. Classe Java executada pelo componente “Save Order Response to @Override
Variable” no fluxo de mensagem do ESB.
public Object transformMessage(MuleMessage message, String outputEncoding)
throws TransformerException {
public class SaveOrderResponseToVariables implements Callable{
String uuid = “”;
@Override
public Object onCall(MuleEventContext eventContext) throws Exception { String orderNumber = “”;
String date = “”;
String jsonString = eventContext.getMessageAsString(); try {
ObjectMapper mapper = new ObjectMapper(); uuid = message.getPayloadAsString();
mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true); orderNumber = message.getInvocationProperty(“orderNumber”);
ResponseSaveOrder responseSaveOrder = mapper.readValue date = message.getInvocationProperty(“date”);
(jsonString, ResponseSaveOrder.class); } catch (Exception e) {
e.printStackTrace();
Order order = (Order)eventContext.getMessage().getInvocationProperty(“order”); }
order.setDateTime(responseSaveOrder.getDate()); return new MessageResponse(new Integer(orderNumber),uuid,date);
}
eventContext.getMessage().setInvocationProperty(“responseSaveOrder”, }
responseSaveOrder);
eventContext.getMessage().setInvocationProperty(“orderNumber”,
String.valueOf(responseSaveOrder.getNumber()));
eventContext.getMessage().setInvocationProperty Antes de retornar a resposta, no entanto, iremos realizar um
(“date”, responseSaveOrder.getDate().toString());
passo que não impacta mais no término transacional deste fluxo
return eventContext.getMessage().getPayload(); de mensagens, que é lançar o evento ocorrido para a nossa ins-
} tância do Esper. Isto é realizado através de um conector específico
} feito para a comunicação com o Esper. Estamos nos referindo ao
componente de número nove, denominado Esper.
Este componente de conexão com o Esper não está presente na
O componente de número sete trata-se de um script Groovy, versão livre do Anypoint (Mule ESB) disponível para a comu-
que gera um código aleatório, “praticamente” único, utilizando nidade. Sendo assim, é necessária a instalação deste conector
o padrão de identificação UUID. Retornamos este número ao executando os seguintes passos:
consumidor do serviço de Registro de Pedido com uma espécie • No Anypoint Studio, navegue pelo menu seguindo o caminho:
de Ticket que ele pode utilizar para referenciar mais tarde um Help > Install New Software;
determinado request feito ao serviço fornecido pelo Mule ESB, • Na janela seguinte, escolha a opção Anypoint Connectors Update
caso seja necessário. Rotulamos este componente de “Generate Site no campo Work with;
UUID for Request”. • Procure pelo módulo Mule Esper Module Mule Studio Extension
Seguindo para o componente de número oito, rotulado de “Build nas opções de Community.
Message Response”, encontramos um componente transformador • Na Figura 10 podemos verificar o conector que deve ser instalado.
de informações de mensagens, implementado com uma classe
Java. Neste ponto, todas as informações trafegando pelo fluxo de Após a instalação, precisamos criar uma configuração para o
mensagem serão passadas para uma classe Java, que irá reunir conector no Fluxo de Mensagem, a qual será utilizada por todas
todas as informações consideradas relevantes que coletamos as instâncias do componente Esper, seja para lançar eventos ou
durante todo o fluxo para compor a mensagem de resposta para para interceptá-los. Para isto, criamos um arquivo XML de nome
o nosso consumidor. Na Listagem 8 temos o código da classe esper-config.xml, apresentado na Listagem 9, e adicionamos este
CompoundMessageResponseObject. Observe que no final re- arquivo ao fluxo de mensagem como um Global Element.
tornamos o objeto MessageResponse, criado especialmente para Nas primeiras linhas desta configuração informamos ao Esper
ser a reposta deste fluxo de mensagens, fazendo parte do contrato quais são os tipos de eventos que poderão existir, e como men-
do serviço exposto. cionado anteriormente, são apenas classes POJO Java. Em nosso
Neste ponto da análise do fluxo chegamos ao último componente exemplo, informamos que as seguintes classes são tipos de eventos
do nosso subflow SendOrderToSalesCenter. Este realiza uma tarefa que poderão ser lançados ou interceptados:
importante para a nossa arquitetura orientada a eventos. No en- • OrderPedido;
tanto, do ponto de vista transacional, já temos tudo finalizado: o • TotalOrderByCity;
pedido foi gravado com sucesso no sistema SalesCenter e temos • TotalOrderByDate;
todas as informações para retornar ao consumidor. • TotalOrderByTimeFrame.
Listagem 9. Configuração da instância do Esper para os conectores do Mule ESB lançar ou interceptar eventos.
Figura 13. Componente Esper configurado para leitura dos eventos realizando o processamento das informações
(JAX-RS) com o objeto Order no formato JSON. Em seguida, com interessados em receber nesta injeção contém um qualificador
a referência a outro EJB, SalesCenterFacade, executa o registro que deve ser considerado no processamento desta dependência.
do pedido e retorna a resposta ao cliente. Nas Listagens 13 e 14 temos, respectivamente, o código fonte da
interface ISalesRepository e da classe SaleRepositoryQualifiers.
Listagem 10. Interface de definição do contrato de um serviço do SalesCenter.
Esta última possui os qualificadores criados para identificação
precisa da classe alvo de nosso interesse que será instanciada para
public interface ISalesCenterService { a injeção da dependência. Esta classe injetada é a que realiza o
public ResponseSaveOrder placeOrder(Order order); contrato de ISalesRepository.
Listagem 11. EJB Stateless que realiza o contrato de um serviço do SalesCenter. @Stateless
public class SalesCenterFacade {
@Stateless
public class SalesCenterService implements ISalesCenterService { @Inject @MySQL
ISaleRepository repository;
private final static Logger LOGGER = LoggerFactory.getLogger
(SalesCenterService.class); public Order registerSale(Order order) {
return repository.save(order);
@EJB }
private SalesCenterFacade facade;
public boolean getStatusSale(Order order) {
@POST return repository.checkStatus(order);
@Path(“/order/register/”) }
@Consumes(MediaType.APPLICATION_JSON)
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) public boolean cancelSale(Order order) {
public ResponseSaveOrder placeOrder(Order order) { return repository.cancel(order);
LOGGER.info(“Receiving order from customer: \”{}\””,order.getCustomer() }
.getName());
facade.registerSale(order); }
Integer orderNumber = order.getNumber();
Date date = order.getDateTime(); Listagem 13. Interface que estabelece as funções de persistência do SalesCenter.
LOGGER.info(“Generated order \”#{}\””,orderNumber.toString());
return new ResponseSaveOrder(orderNumber,date); public interface ISaleRepository {
}
public abstract Order save(Order sale);
@GET public abstract boolean cancel(Order sale);
@Path(“/hello/{nome}”) public abstract boolean checkStatus(Order sale);
public String ping(@PathParam(“nome”) String name) {
return “Hello there, “ + name + “!”; }
}
Listagem 14. Qualificadores criados para classificação de classes que implemen-
} tam a interface ISalesRepository.
private final static Logger LOGGER = LoggerFactory.getLogger(MySQLDatabase.class); private void checkPersistenceProduct(Order sale, EntityManager em) {
List<OrderItem> ordersItens = new ArrayList<OrderItem>();
public Order save(Order sale) { for(Iterator i = sale.getItens().iterator(); i.hasNext();) {
EntityManagerFactory factory = null; OrderItem item = (OrderItem)i.next();
try { if ( item.getProduct().getId() != null ) {
factory = Persistence.createEntityManagerFactory(“SalesCenterJPA”); Product product = em.find(Product.class, item.getProduct().getId());
EntityManager em = factory.createEntityManager(); if ( product == null ) {
product = item.getProduct();
em.getTransaction().begin(); product.setId(null);
/** em.persist(product);
* Persist Customer }
*/ item.setProduct(product);
checkPersistenceCustomer(sale, em); } else {
/** Product product = item.getProduct();
* Persist Products em.persist(product);
*/ item.setProduct(product);
checkPersistenceProduct(sale, em); }
sale.setDateTime(Calendar.getInstance().getTime()); ordersItens.add(item);
em.persist(sale); }
em.getTransaction().commit(); sale.setItens(ordersItens);
}
} catch (Exception e) {
LOGGER.error(e.getMessage(), e); private static void checkPersistenceCustomer(Order sale, EntityManager em) {
throw new RuntimeException(e); if (sale.getCustomer().getId() != null) {
} finally { Customer c = em.find(Customer.class, sale.getCustomer().getId());
factory.close(); if (c == null) {
} c = sale.getCustomer();
return sale; c.setId(null);
} em.persist(c);
}
public boolean cancel(Order sale) { sale.setCustomer(c);
return false; }
} }
Podemos observar que além de implementar e cumprir o contra- É importante mencionar que esta mini aplicação de monito-
to da interface ISalesRepository, MySQLDatabase contém a ano- ramento de atividades de negócio não foi testada em ambiente
tação do qualificador @MySQL. Esta classe grava as informações produtivo com alta escala e sob alto estresse de carga. Como pre-
do pedido que chegam via o objeto Order utilizando a API JPA. viamente apontado, é uma aplicação ilustrativa para demonstrar
Lembre-se que a classe Order provém do componente (JAR) que o resultado de nossa solução, criada apenas para este contexto.
compõe o modelo canônico de nossa corporação, e estas classes Para ambientes mais robustos e complexos, existem no mercado
estão com as configurações de anotações para uso do JPA. soluções prontas que são coesas às famílias de produtos de seus
Com isso chegamos ao final da análise da parte transacional de fabricantes.
nossa solução. No próximo tópico vamos analisar um pequeno Por exemplo: para a IBM Integration Bus (outrora conhecida
exemplo de monitoramento das atividades de negócio. Para isso, como IBM WebSphere Message Broker) ferramenta ESB da
utilizaremos uma simples solução web – que faz o uso de Java- IBM, temos a opção de utilizar o IBM Business Monitor como
Script como Front-End – para ilustrar em tempo real a captura de ferramenta de monitoração de atividades de negócio. As duas
eventos durante as transações. ferramentas possuem recursos que permitem a integração entre
elas provida pelo fabricante. Assim, na maioria das vezes, temos
Monitoramento – MiniJS-BAM apenas o trabalho de configurar a comunicação entre estas fer-
Criamos esta simples aplicação web/JavaScript somente para ramentas e, é claro, talvez a parte mais difícil, implementar uma
tornar mais dinâmica e interessante a demonstração de nossa solução de arquitetura que faça uso intensivo destes Monitores
solução baseada em arquitetura orientada a eventos. De fato, de Negócio.
é nessa parte que reside o “marketing” de todo o esforço, pois A aplicação MiniJS-BAM é composta por apenas um arquivo
esta é a “janela” que é apresentada aos senhores de negócio, que WAR (Web Archive) Java EE, que possui como principais compo-
investem seu orçamento em tecnologia. nentes os seguintes itens:
• MiniJsBamController: Servlet que recebe as requisições via a comunicação com a base de dados, executando as queries de
protocolo HTTP, no formato REST, realiza as consultas e retorna consulta nas tabelas em que foram gravadas as informações
os resultados no mesmo formato; durante a captura dos eventos pelo Esper, e para isto ela utiliza
• MiniJSBamServices: Classe Java comum (POJO) que executa a API JDBC.
as consultas na base de dados MySQL via JDBC, respondendo as
requisições enviadas pelo Servlet Controller. Solução em Ação
Com todas as peças de nossa solução ligadas e funcionando,
O código fonte do MiniJsBamController é apresentado na podemos ver em ação o monitoramento executando enquanto os
Listagem 16. Esta classe desempenha o papel de controlador requests de pedidos chegam ao nosso barramento de serviços.
principal, que recebe as requisições dos clientes que desejam Os componentes e as soluções neles implantadas que devem
informações sobre eventos já capturados durante a execução de estar funcionando são:
transações de negócio. Este servlet é disponibilizado com quatro • Mule ESB – Responsável pela disponibilização do Serviço Re-
diferentes padrões de URL para receber as chamadas dos clientes gistro de Pedidos;
(podemos observar o atributo urlPatterns na anotação @Web- • Apache TomEE+ – Responsável por prover o ambiente de
Servlet). Assim, conforme o padrão de URL utilizado pelo cliente execução Java EE ao Sistema de gerenciamento de pedidos (Sa-
no request, decidimos quais informações devemos disponibilizar lesCenter);
na resposta. • MySQL – Base de dados responsável pelo registro dos eventos
No método principal do servlet recepcionamos as requisições capturados pelo Esper no Mule ESB.
via GET, analisamos a URI fornecida e, através dela, definimos
qual ação do serviço de consulta deve ser executada. Para iniciar a simulação, abrimos o SoapUI, responsável por
Como o foco de nosso artigo é Arquitetura, o código fonte da executar os requests dos clientes conforme explicado nos tópicos
classe MiniJsBamServices não é tão relevante para ser apre- anteriores. Uma vez iniciado o cenário de testes na ferramenta,
sentado. Por isso seu conteúdo não está presente na forma de simultaneamente abrimos no navegador a nossa aplicação de
listagem, mas está disponível para download junto com toda a monitoramento MiniJS-BAM. Desta forma, em tempo real, pode-
solução exemplo. A classe MiniJsBamServices apenas realiza mos acompanhar as informações capturadas nos eventos durante
@Named break;
@WebServlet(name=”MiniJsBamController”, default:
urlPatterns = {“/totalOrderByCities”,”/totalOrderByDate”,” break;
/totalOrderByTimeFrame”,”/ping”}, loadOnStartup = 1) }
public class MiniJsBamController extends HttpServlet { resp.getWriter().write( jsonContent );
}
private static final long serialVersionUID = 1L;
@Override
private MiniJsBamServices services = new MiniJsBamServices(); protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
@Override resp.getWriter().write(“Sorry! We are working only with GET for the sake of
protected void doGet(HttpServletRequest req, HttpServletResponse resp) simplicity, this is not a real project.”);
throws IOException { }
String path = req.getServletPath();
private String transformToJson(Object obj) {
String jsonContent = “”; String result = “”;
switch (path) { ObjectMapper mapper = new ObjectMapper();
case “/totalOrderByCities”: mapper.setSerializationInclusion(Include.NON_NULL);
jsonContent = this.transformToJson ( this.services.getTotalOrderByCity() ); mapper.enable(SerializationFeature.INDENT_OUTPUT);
break; mapper.enable(SerializationFeature.CLOSE_CLOSEABLE);
case “/totalOrderByDate”: mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, false);
int dateRequest = Integer.parseInt(req.getParameter(“date”));
jsonContent = this.transformToJson ( this.services.getTotalOrderByDate try {
(dateRequest)); result = mapper.writeValueAsString(obj);
break; } catch (JsonProcessingException e) {
case “/totalOrderByTimeFrame”: throw new RuntimeException(e);
String timeFrameRequest = req.getParameter(“timeFrame”); }
jsonContent = this.transformToJson return result;
( this.services.getTotalOrderByTimeFrame(timeFrameRequest)); }
break;
case “/ping”: }
jsonContent = this.ping();
O groupId (especificado através da tag <groupId>) representa, Logo após, na tag <artifactId>, informamos o nome da biblioteca
na maioria dos casos, a empresa ou projeto do qual a biblioteca em em si e, na tag <version>, a versão da mesma. Em nosso caso,
questão faz parte. No caso de nosso projeto, como estamos usando determinamos os seguintes valores: commons-lang3 e 3.3.2,
uma biblioteca do projeto Apache Commons, nosso groupId ficou respectivamente.
definido como org.apache.commons.
Nota
Listagem 1. Exemplo de pom.xml para definir as dependências do Maven. Para auxiliar os desenvolvedores a encontrar as bibliotecas que necessitam, os repositórios
remotos do Maven normalmente disponibilizam uma interface web que permite a busca de
<project xmlns=”http://maven.apache.org/POM/4.0.0”
bibliotecas e devolve, como retorno, as configurações do pom.xml para adicionar a mesma em
xmlns:xsi=”http://www.w3.org/2001/XMLSchema- instance”
xsi:schemaLocation=”http://maven.apache.org/POM/4.0.0 seu projeto. Na seção Links adicionamos o caminho para o site Maven repository, que possui
http://maven.apache.org/xsd/maven-4.0.0.xsd”> uma interface desse tipo.
<modelVersion>4.0.0</modelVersion>
<groupId>br.com.javamagazine</groupId>
<artifactId>mavenProject</artifactId>
<version>0.0.1-SNAPSHOT</version>
Agora que entendemos as partes do arquivo pom.xml, vamos criar
<packaging>jar</packaging> nosso projeto que utiliza o Maven como gerenciador de depen-
<build> dências. Para isso, com o plugin do Maven instalado no Eclipse,
<sourceDirectory>src</sourceDirectory>
<plugins> basta criarmos um projeto do tipo Maven Project. Na Figura 2
<plugin> apresentamos as telas de criação para nosso projeto exemplo e os
<artifactId>maven-compiler-plugin</artifactId> passos que devemos seguir.
<version>3.1</version>
<configuration> Conforme podemos verificar, a criação de um projeto Maven
<source>1.8</source> é dividida em um wizard composto por duas telas principais.
<target>1.8</target>
A primeira, localizada à esquerda, apresenta algumas configu-
</configuration>
</plugin> rações básicas como a localização de nosso projeto (workspace)
</plugins> e, também, se desejamos utilizar algum tipo de Working set do
</build>
Eclipse. É importante notar ainda que selecionamos o primeiro
<dependencies>
<dependency> checkbox dessa tela, que indica que não queremos usar nenhum
<groupId>org.apache.commons</groupId> archetype do Maven para criar nosso projeto.
<artifactId>commons-lang3</artifactId>
<version>3.3.2</version>
Os chamados archetypes são espécies de templates que nos ajudam
</dependency> a criar um esqueleto de nosso projeto, com as source folders e configu-
</dependencies> rações iniciais a partir de diversos modelos pré-definidos (aplicações
</project>
web, EARs, aplicações desktop, etc.). Como em nosso exemplo iremos
criar o projeto sozinhos, optamos por não os utilizar.
public static void main(String[] args) { A segunda mudança, indicada pelas tags <modules> e <mo-
System.out.println(StringUtils.capitalize(“usando a dependência do Apache
Commons!”));
dule>, serve para, explicitamente, indicar onde está localizado
} nosso módulo. Como ainda não criamos esse módulo, para que
essa configuração funcione, podemos seguir os mesmos procedi-
}
mentos para criação de um projeto Maven e criar um novo, com
o nome moduloMaven.
Múltiplos módulos Uma vez criado esse projeto, precisamos apenas modificar seu
Além dos archetypes e do gerenciamento de dependências, o pom.xml para referenciarmos o projeto pai do qual esse subprojeto
Maven também permite a criação de módulos dentro de seus pro- depende – em nosso exemplo, o projeto JavaMagazineMaven.
jetos. Um módulo funciona como uma dependência, mas, dentro Na Listagem 4 apresentamos o pom.xml do projeto recém-criado
do workspace, é representado por um projeto Java tradicional. indicando esse relacionamento.
Para trabalhar com módulos, basta adicionar essa informação Nesse código podemos ver que o relacionamento de um pro-
em nosso pom.xml. Visando demonstrar esse recurso, vamos al- jeto com seu “pai” é indicado pela tag <parent>. Dentro dessa
terar esse arquivo em nosso projeto Maven adicionando algumas tag, relacionamos todos os dados do projeto pai, inclusive seu
linhas. Na Listagem 3 apresentamos o novo pom.xml, já com as caminho relativo.
informações de nosso módulo. Assim que os dois projetos estiverem configurados, ao rodar o
Foram realizadas duas alterações para a inclusão do novo mó- comando mvn install no projeto pai, veremos que todos os projetos
dulo. A primeira, relacionada à tag <packaging>, é a modificação declarados na seção de módulos serão compilados e seus pacotes
do tipo de empacotamento de jar para pom. Essa mudança é finais adicionados à pasta target de cada um.
necessária para projetos Maven que utilizam módulos em sua
construção e irá, automaticamente, disparar o build e empacota- Vantagens e desvantagens do Maven
mento dos subprojetos declarados no pom.xml. Agora que completamos nosso primeiro exemplo utilizando o
É importante observar que, ao escolher o tipo de empacotamento Maven, vamos analisar os pontos positivos e negativos de usar
pom, nosso projeto pai deixa de ser compilado e passa a servir essa tecnologia.
somente de referência para que os projetos declarados na seção Conforme dissemos no início do artigo, a tarefa de gerenciamen-
de módulos sejam empacotados to de dependências é extremamente complexa.
Listagem 4. Pom.xml indicando o relacionamento entre o módulo e o projeto pai. Apache Ivy
Com o intuito de possibilitar uma maior liberdade para o de-
<project xmlns=”http://maven.apache.org/POM/4.0.0”
senvolvedor construir seus projetos, a Apache desenvolveu outro
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://maven.apache.org/POM/4.0.0 projeto, implementado em paralelo ao Apache Maven, chamado
http://maven.apache.org/xsd/maven-4.0.0.xsd”> Apache Ivy. Essa ferramenta tem como objetivo propiciar as van-
<parent> tagens do gerenciamento de dependências do Maven integradas
<groupId>JavaMagazineMaven</groupId>
com um processo de build mais dinâmico.
<artifactId>JavaMagazineMaven</artifactId>
<version>0.0.1-SNAPSHOT</version> Para isso, esse processo utiliza outra solução da Apache, o
<relativePath>../JavaMagazineMaven/pom.xml</relativePath> chamado Apache Ant. Para quem não está familiarizado, o Ant
</parent> permite que se especifique instruções para a construção de um
<modelVersion>4.0.0</modelVersion>
<groupId>br.com.javamag</groupId> projeto, tais como cópia de arquivos e execução de comandos shell,
<artifactId>moduloMaven</artifactId> através de um arquivo XML denominado build.xml. Você pode
<version>0.0.1-SNAPSHOT</version> encontrar a URL oficial do projeto na seção Links.
<name>moduloMaven</name>
As dependências do Ivy, por sua vez, são gerenciadas pelo pró-
</project>
prio Ivy, em um arquivo denominado ivy.xml. Sua configuração
é bastante semelhante ao pom.xml do Maven e um exemplo desse
Com a capacidade de baixar automaticamente as dependências arquivo pode ser visto na Listagem 5.
de repositórios online, o Maven traz como grande vantagem a
praticidade e simplicidade na configuração dessas bibliotecas Listagem 5. Exemplo de arquivo ivy.xml.
e, como ponto extra, também permite a criação de esqueletos
<ivy-module version=”2.0”>
de aplicações, através dos archetypes. Essa automação, se <info organisation=”java-mag” module=”ivy”/>
comparada com a configuração manual dos projetos, traz um <dependencies>
ganho enorme tanto em manutenção, ao evitar a verificação e <dependency org=”commons-lang” name=”commons-lang” rev=”2.0”/>
<dependency org=”commons-cli” name=”commons-cli” rev=”1.0”/>
atualização das bibliotecas manualmente, como em velocidade
</dependencies>
de desenvolvimento, diminuindo bastante a complexidade do </ivy-module>
gerenciamento das dependências.
Além disso, ao especificar seu projeto com os mesmos atributos
que definem uma dependência (criando o artifactId, groupId e Como podemos verificar, a sintaxe do Ivy é muito semelhante
version), o Maven permite que, facilmente, possa se compartilhar à do Maven, com apenas algumas pequenas diferenças. As bi-
o projeto como dependência de outros projetos. Essa facilidade de bliotecas declaradas como dependências são, da mesma forma
uso é um dos pontos mais vantajosos do Maven e, devido a isso, que no Maven, especificadas dentro da tag <dependencies>,
podemos dizer que essa ferramenta é uma das mais utilizadas porém, em vez de possuírem os parâmetros groupId, artifactId
por desenvolvedores em todo o mundo. e version, característicos do Maven, possuem os atributos org,
No entanto, também existem alguns pontos negativos com a name e rev que, em conjunto, identificam a dependência que
utilização dessa ferramenta. O primeiro deles, relacionado ao queremos utilizar.
gerenciamento de dependências, é a dificuldade de se eliminar Ainda de modo semelhante ao Maven, onde utilizamos a tag
conflitos de dependências, ou seja, quando temos duas depen- project, declaramos na tag info do Ivy as informações de nosso
dências iguais (mas de versões diferentes) no mesmo projeto. projeto. E dentro dessa tag também declaramos o atributo or-
Apesar de ser possível excluir bibliotecas, quando temos uma ganisation e module. É importante notar que, ao contrário do
árvore muito grande de dependências, a tarefa de encontrar e re- Maven, o Ivy não possui instruções de build dentro do ivy.xml.
mover as bibliotecas em conflito se torna bastante complicada. Estas instruções, conforme dissemos anteriormente, ficam em um
Ainda como ponto negativo, pelo fato do Maven se basear em arquivo à parte, chamado build.xml, e são rodadas pelo Ant.
XMLs para sua configuração, a configuração do processo de Para conseguirmos executar as tarefas de baixar as dependên-
build se torna limitada a instruções pré-definidas, não dando cias e construir nosso projeto localizadas nesse arquivo, temos
muito espaço para customização. Para conseguir realizar ma- que instalar mais alguns plug-ins no Eclipse. Isso pode ser feito
nipulações mais complexas dentro do processo de build, como escolhendo a opção Help > Install New Software na interface de sua
usar alguma lógica de programação, é necessária a adoção de IDE e, na próxima janela, buscar pelo plugin Apache Ivy Ant Tasks
outra ferramenta, como o Apache Ant, que já abre mais espaço dentro dos softwares disponíveis para download. Na Figura 3
para builds customizados. mostramos qual a biblioteca que deve ser baixada para conse-
Por fim, os próprios archetypes, implementados com o objetivo guirmos prosseguir com o nosso exemplo.
de servir como forma de criar esqueletos de projetos, também Uma vez instalado, basta adicionarmos o JAR desse plugin ao
não são passíveis de customização, deixando o desenvolvedor nosso processo de build do Ant. Isso pode ser feito nas preferên-
engessado nos padrões adotados pelo Maven. cias do Eclipse, escolhendo, nas opções da tela de preferências,
todas as dependências do arquivo ivy.xml. Dessa forma, conse- <target name=”dist” depends=”compile” description=”generate the distribution”>
guimos unir o processo de build do Ant com o gerenciamento de <mkdir dir=”${dist}/lib” />
dependências do Ivy. <jar jarfile=”${dist}/lib/MyProject-${DSTAMP}.jar” basedir=”${build}” />
</target>
Por fim, para testarmos todas as nossas configurações, basta
criarmos um projeto Java simples em nossa IDE e adicionar, no <target name=”clean” description=”clean up”>
diretório raiz do projeto, os arquivos ivy.xml e build.xml apresen- <delete dir=”${build}” />
tados nas Listagens 5 e 6. Uma vez adicionados, podemos clicar <delete dir=”${dist}” />
</target>
com o botão direito em cima do arquivo build.xml e escolher a </project>
opção Run As > Ant Build para baixar as
dependências e construir nosso projeto.
Apesar do leque de instruções ser bem maior que a oferecida Um exemplo de arquivo settings.gradle pode ser visto a seguir,
pelo Maven, tarefas fora do padrão podem se tornar extremamen- onde expomos a configuração de nosso multi-project exemplo:
te complexas de implementar devido à limitação mencionada,
ainda mais quando precisamos de algo em nossos processos que include “:java_mag1”,”:java_mag2”
exige lógicas extensas ou tarefas fora do padrão do Ant como,
por exemplo, o uso de lógica de programação para configurar Neste caso, especificamos que nosso multiprojeto será composto
nossos pacotes. por dois subprojetos: um chamado java_mag1 e outro chamado
de java_mag2.
Gradle Para testarmos o funcionamento do Gradle, vamos mover esse
Com a solução desses problemas em mente, foi desenvolvida a arquivo para uma pasta dentro do nosso diretório definido como
ferramenta para gerenciamento de dependências Gradle. Elimi- Workspace. No nosso caso, criamos uma pasta denominada
nando o uso de XMLs e aplicando, dentro do processo de compila- java_mag e adicionamos, dentro dela, o arquivo settings.gradle.
ção e empacotamento, o uso de uma linguagem de programação, o É válido observar que ainda não criamos nosso projeto na IDE.
Gradle disponibiliza uma ferramenta completa de gerenciamento Feito isso, vamos ao próximo passo: criar o build.gradle. Nesse
e automatização de builds. momento, vamos criar apenas um arquivo de texto em branco (com
Seu princípio fundamental é o uso da linguagem Groovy nos o nome build.gradle) no mesmo diretório de nosso settings.gradle.
scripts de build permitindo, assim, que qualquer tarefa seja pro- Além disso, também precisamos criar os diretórios java_mag1
gramável, utilizando recursos de linguagens de programação e java_mag2, na mesma pasta em que adicionamos os arquivos
como a criação de funções, loops e ifs. Ademais, o Gradle dis- build.gradle e settings.gradle, permitindo que o Eclipse possa criar
ponibiliza uma sintaxe simples para declarar as dependências, esses subprojetos em seu workspace assim que importamos o
semelhante ao modo de declaração do Maven e, ainda, possibilita projeto principal.
a invocação de scripts Ant. Com a estrutura de diretórios pronta, já é possível para o plugin
Portanto, podemos dizer que o Gradle é uma ferramenta ex- do Eclipse reconhecer a existência de um projeto Gradle e seus
tremamente poderosa, principalmente quando precisamos criar projetos filhos, através do arquivo settings.gradle (onde declara-
scripts de build um pouco mais complexos. Inclusive, diversos mos o nome dos subprojetos), e criar os mesmos dentro de nossa
projetos importantes, como o SDK do Android e o Hibernate, já IDE. Para isso, basta escolher a opção Import > Gradle Project na
utilizam o Gradle em seus desenvolvimentos, mostrando a con- IDE e, na tela que surgir, selecionar o diretório em que criamos
fiabilidade e qualidade desta solução. os arquivos e pastas e clicar no botão Build Model. Na Figura 4
apresentamos essa tela e como ela deve ficar após seguir os passos
Criando um projeto com o Gradle supracitados.
Iniciando nosso exemplo sobre o Gradle, o processo de cons- Como podemos verificar, no Eclipse o nosso projeto Gradle foi
trução de um projeto que utiliza essa ferramenta se baseia nas quebrado em três: o projeto principal, denominado java_mag e,
instruções declaradas no arquivo build.gradle. Assim como no logo em seguida, os dois subprojetos (java_mag1 e java_mag2),
Ivy ou no Maven, esse arquivo serve para explicitarmos nos- conforme declarado no settings.gradle. Para importá-los, basta
so processo de build e, também, para especificarmos nossas selecionar o checkbox à esquerda do nome do projeto e clicar
dependências. em Finish.
Como diferença, no Gradle esse arquivo não se baseia mais Outro ponto importante nessa configuração multi-project está
em um documento XML e, sim, na linguagem de programação relacionado à herança das dependências. No Gradle todos os
Groovy. Além desse arquivo, o Gradle também utiliza o arquivo subprojetos irão herdar todas as dependências do projeto pai
settings.gradle, responsável por definir algumas propriedades de automaticamente e podem, através de configurações no build
build do projeto e, principalmente, gerenciar a criação dos cha- .gralde, herdar dependências de outros subprojetos.
mados multi-projects. É importante ressaltar que, para cada projeto, o arquivo build.
Esse tipo de projeto (multi-projects) é bastante útil para desen- gradle localizado na sua raiz irá identificar quais são as suas
volvedores que pensam em melhorar a organização de seu código, dependências. Em casos de subprojetos, se o arquivo build.gradle
quebrando um sistema grande em subprojetos menores, cada um não existir (como no nosso exemplo), esses projetos irão assumir
com suas respectivas dependências. Uma abordagem como essa somente as dependências do projeto principal.
permite maior desacoplamento entre as partes do projeto e, no Portanto, se quisermos adicionar dependências no projeto
caso de alguma das partes precisar ser reutilizada, fica muito java_mag, basta alterar o arquivo de configuração em sua raiz
mais simples o uso somente do que é necessário. (build.gradle) e caso seja necessário incluir diferentes dependências
Outro grande atrativo do Gradle é a facilidade de criação e em java_mag1 ou java_mag2, basta adicionar as configurações
gerenciamento desse tipo de abordagem (multiprojetos). Assim, dentro de um arquivo build.gradle em cada um deles.
pretendemos, nesse tópico, apresentar como podemos aliar essa Deste modo, temos flexibilidade para configurar nossas depen-
funcionalidade ao gerenciamento de dependências. dências exatamente onde quisermos.
Listagem 8. Sobrescrevendo a tarefa de geração do JAR no arquivo build.gradle. Deste modo, ao rodarmos a task jar, o arquivo será gerado na
pasta pacote, em vez de na pasta default.
apply plugin: ‘java’
Outro plugin para geração de pacotes é o plugin para criação de
repositories { arquivos do tipo WAR. Assim como a task de geração de arquivos
mavenCentral()
JAR, essa task gera o pacote automaticamente em um diretório
flatDir {
dirs ‘libs’ padrão, mas também abre espaço para customização de algumas
} propriedades.
}
Na Listagem 9 apresentamos um exemplo de uso do plugin war
jar { do Gradle para gerar um pacote desse tipo, também adotando
destinationDir= file(“pacote”)
algumas customizações.
}
dependencies { A sobrescrita dessa task, como podemos verificar, é muito seme-
compile ‘org.apache.commons:commons-lang3:3.3.2’ lhante à que fizemos com a task jar. A diferença é que, neste caso,
}
sobrescrevemos a localização do arquivo web.xml, indicando onde
Listagem 9. Utilizando e sobrescrevendo a tarefa de geração do war no arquivo desejamos que o Gradle o busque e o insira no pacote.
build.gradle.
Feito isso, para testar essa tarefa, precisamos criar um arquivo
apply plugin: ‘java’ com o nome web.xml e adicioná-lo na raiz de nosso projeto. Para
apply plugin: ‘war’ auxiliar o leitor, apresentamos na Listagem 10 um web.xml vazio,
repositories { que pode ser utilizado neste exemplo.
mavenCentral()
flatDir {
dirs ‘libs’
} Listagem 10. Arquivo web.xml exemplo para nosso projeto.
}
<?xml version=”1.0” encoding=”UTF-8”?>
war { <web-app xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
webXml = file(‘web.xml’) xmlns=”http://java.sun.com/xml/ns/javaee”
} xmlns:web=”http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd”
jar { xsi:schemaLocation=”http://java.sun.com/xml/ns/javaee
destinationDir= file(“pacote”) http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd”
}
dependencies { id=”WebApp_ID” version=”2.5”>
compile ‘org.apache.commons:commons-lang3:3.3.2’
} </web-app>
C
onjuntos de dados têm como solução ideal o Fique por dentro
uso de um modelo de processamento paralelo
e distribuído que se adapta a qualquer volume Este artigo aborda o uso do MapReduce na solução de um proble-
e grau de complexidade. Esse é o caso do MapReduce, ma complexo, que é melhor resolvido com a técnica de algoritmos
uma técnica que abstrai os detalhes de paralelização e genéticos (AG). Apesar da natureza iterativa dos AGs, com algumas
distribuição do processamento de dados, que pode ser adaptações é possível construir aplicações que necessitem de gran-
utilizada em aplicações que necessitem dessas carac- de poder de processamento e que podem ser executadas de forma
terísticas, como ocorre com algumas abordagens não paralela e distribuída no modelo MapReduce. Entre os softwares que
triviais, a exemplo do tratamento do “big data” e do podem ser beneficiados, há algoritmos de inteligência computacional,
processamento de algoritmos de alta complexidade e como AGs e redes neurais, e qualquer problema de otimização que
escalabilidade. necessite encontrar boas respostas em situações que normalmente são
Tais algoritmos podem ser empregados em diversas si- difíceis de serem resolvidas com aplicações tradicionais em uma única
tuações que envolvam resolver problemas de busca e oti- máquina. Por exemplo, sistemas para identificar padrões em biometria,
mização, normalmente presentes em sistemas de tomada formulação de regras em jogos de estratégia, na busca de melhores
de decisão e descoberta de conhecimento. Por exemplo, rotas (caminhos) a serem percorridas em um conjunto de cidades,
escolher qual a melhor empresa para investir o capital como é o caso do exemplo tratado neste artigo, entre outros.
(quantia) na bolsa de valores; solucionar problemas de
agendamento e planejamento de recursos; auxiliar na
organização e alocação de turmas a professores, presente resumido e agregado na saída, sendo cada função executada em
na definição de grades horárias de trabalho; e qualquer uma etapa distinta. Na primeira etapa, Map, uma função de ma-
situação que necessite uma boa solução (entre tantas), peamento distribui os dados em diversos nós de processamento e
considerando as regras específicas para o domínio do armazenamento. Na segunda etapa, Reduce, uma função agrega
problema a fim de alcançar o melhor resultado possível. e sumariza os resultados obtidos no mapeamento, para gerar um
Como podemos notar, são problemas complexos, muitas resultado final.
vezes de difícil solução e que envolvem significativas A técnica MapReduce pode ser aplicada em vários campos,
reduções de custos, melhorias dos tempos de processos como o agrupamento de dados, aprendizado de máquina e visão
e/ou melhor alocação dos recursos em atividade. computacional. Outro exemplo de campo que pode adotar essa
Como o MapReduce pode ajudar na solução desses técnica é o da Inteligência Computacional, em especial o dos Al-
problemas? Em essência, a resposta está na capacidade goritmos Genéticos, que devem tratar uma grande base de dados
do poder de processamento paralelo e distribuído da (a chamada população de indivíduos) para localizar valores para
técnica, fatores que permitem alta escalabilidade à uma tomada de decisão. Tais algoritmos exigem um alto custo de
solução. processamento para ser executado em uma única máquina, o que
O MapReduce é baseado no paradigma de programa- torna a técnica MapReduce ideal para ser adotada.
ção funcional, adotando duas funções que dão nome Com base nisso, este artigo demonstra o uso combinado da
ao modelo: a função map e a função reduce. Esse modelo abordagem AG com MapReduce para resolver um tipo especial
estabelece uma abstração que permite construir aplica- de problema complexo, chamado de “caixeiro viajante” (ou PCV).
ções com operações simples, escondendo os detalhes da Este problema busca identificar os melhores caminhos para se
paralelização. Em resumo, tais funções transformam um percorrer um conjunto de cidades, visitando pelo menos uma vez
grande volume de dados de entrada em um conjunto cada cidade em um determinado percurso. O PCV envolve um
Representação do problema
No PCV, o conceito de indivíduo é aplicado à representação do
caminho a ser percorrido entre as cidades, e a função objetivo
Figura 1. Exemplo de mapa para dez cidades do PCVS
para calcular a aptidão do indivíduo é determinada pela distân-
cia obtida para esse caminho. Assim, uma forma de estruturar
a codificação do indivíduo é representar o caminho de cidades
usando um grafo não direcionado com pesos nas arestas, como
será explicado a seguir. Por se inspirar na matemática que lida
com a geometria dos planos, essa representação é chamada PCV
Simétrico Euclidiano (PCVS).
Considere, por exemplo, um conjunto de dez cidades posiciona-
das em um plano bidimensional, com dois eixos (x e y) iniciados
Figura 2. Equação para calcular a distância entre cidades
em zero, como visto na Figura 1. No PCVS para este caso (dez
cidades), os caminhos são identificados por uma sequência de Cidade X Y
caracteres, onde cada caractere representa uma cidade utilizan- A 1 2
do uma letra do alfabeto (A, B, ..., Z). No plano, cada letra é uma B 7 9
coordenada x,y. Para simplificar a demonstração da solução,
C 3 3
optou-se por empregar um conjunto de dez cidades, que segue a
D 1 5
sequência de “A” até “J”.
Inicialmente, para resolver o PCVS é necessário conhecer as E 4 3
distâncias entre todas as cidades. Considerando que uma cidade F 5 6
(C) está localizada em um ponto (x, y) do plano (mapa), como G 2 4
visto na Figura 1, o processo para calcular a distância total (D) H 8 3
entre uma cidade origem (C1) e a n-ésima (Cn) cidade (dentro de
I 9 2
um caminho válido para n cidades) é determinado pela soma
J 6 9
das distâncias (d) das coordenadas (x, y) das cidades que fazem
parte desse caminho, aplicando a fórmula vista na Figura 2. Em Tabela 1. Coordenadas relativas das cidades no plano euclidiano
outras palavras, o que estamos fazendo é calcular o perímetro
do polígono formado pela sequência de cidades pertencentes ao Dessa forma, aplicando a equação da Figura 2 calcula-se a
caminho válido. distância percorrida em um caminho. Por exemplo, no cálculo do
A título de exemplo, adotando-se as coordenadas relativas (x, y) caminho “ABCDEFGHIJ” chega-se ao valor aproximado de 53.3,
para cálculo das distâncias entre as cidades (como visto na Figura 1), conforme demonstra a Figura 3.
encontramos os valores disponíveis na Tabela 1. Para esse proble-
ma, como mencionado anteriormente, as cidades são identificadas Modelagem no MapReduce
por letras, e um caminho é representado pela sequência das letras A implementação de um Algoritmo Genético no paradigma do
destas cidades, como no caso “ABCDEFGHIJ”. MapReduce apresenta alguns desafios. O maior deles é adaptar
Para simplificar o exemplo, cada letra presente na cadeia de strin- Entrada | map |
gs do indivíduo possui uma correspondência com uma cidade ABCDEIGHFJ ( 0, ABCDEIGHFJ) ( 53, <ABCDEFGHIJ, 53.3475288096413>)
GIJABEFCDH ( 1, GIJABEFCDH) ( 51, <GHIJABEFCD, 50,6532924781218>)
no mapa, e as coordenadas (x, y) de cada cidade está registrada BEFGHIJACD ( 2, BEFGHIJACD) ( 53, <EBFHGIJACD, 52.8074130652778>)
EBGFHCDIJA ( 3, EBGFHCDIJA) ( 51, <EBHFCDIJGA, 51.4288322764095>)
na aplicação. A seguir temos um exemplo de quatro linhas de
indivíduos:
| shuffle
(51, [<GHIJABEFCD, 50,6532924781218>, <EBHFCDIJGA, 51.4288322764095>])
ABCDEIGHFJ (53, [<ABCDEFGHIJ, 53.3475288096413>, <EBFHGIJACD, 52.8074130652778>])
GIJABEFCDH
BEFGHIJACD reduce > Saída
EBGFHCDIJA (27.866056254812328, ACEHIBJFDG) 27.866056254812328, ACEHIBJFDG
(28.014106081769356, GFJBHIECAD) 28.014106081769356, GFJBHIECAD
Codificando o Indivíduo
A Listagem 1 apresenta o código
da classe Individuo, que instanciará
os objetos (dados) trabalhados nas
funções map e reduce. Como esses
objetos não seguem os tipos padrões
do MapReduce (que são Integer, Float,
Text, etc.), foi necessário implementar Figura 7. Criação de um projeto MapReduce
a interface Writable, que permite per-
Nota
sonalizar a estrutura de dados com dois atributos: o cromossomo do
indivíduo, representado por uma cadeia de caracteres (String), e sua O Hadoop disponibiliza ao programador a interface Writable, solução que permite criar objetos para
aptidão, que é um número real (double). O termo “cromossomo” foi o processamento distribuído do MapReduce. Para isso, basta criar uma classe que a implemente. Tal
usado porque no linguajar dos algoritmos genéticos cromossomo é a interface especifica dois métodos que devem ser construídos: o write(), para gravar os objetos na
própria representação do indivíduo; neste caso, a cadeia de caracteres saída (DataOutput), que normalmente é um arquivo texto; e readFields(), para a leitura dos objetos
que especifica o caminho das cidades percorridas. Já a aptidão é o obtidos no fluxo de entrada (DataInput), que normalmente são obtidos de um arquivo texto. No projeto
resultado do cálculo da distância total desse caminho. Em resumo, deste artigo, a classe Individuo implementa Writable, mas na maioria das vezes, customizar a interface
os objetos instanciados dessa classe são usados em todo o processa- Writable é desnecessário, pois o Hadoop disponibiliza estruturas de dados prontas para objetos em
mento do MapReduce. forma de arrays, mapas e todos os tipos primitivos da linguagem Java (com exceção ao char).
Listagem 1. Código da classe Individuo. Context context. Os dois primeiros são correspondentes ao par
chave/valor de entrada. Já o último parâmetro, o objeto Context,
// imports omitidos...
representa o objeto que permite que a aplicação se comunique e
public class Individuo implements Writable { envie dados para o framework Hadoop a serem processados na
fase reduce.
private String cromossomo;
private double aptidao;
Listagem 2. Código da função map – classe CaixeiroViajanteMapper.
@Override
public void write(DataOutput out) throws IOException { // imports omitidos...
out.writeUTF(cromossomo);
out.writeDouble(aptidao); public class CaixeiroViajanteMapper extends
} Mapper<Object, Text, IntWritable, Individuo> {
saída (IntWritable e Individuo, respectivamente). public void geracaoLocal(Context context) throws IOException,
Para cumprir a função de mapeamento, CaixeiroViajante- InterruptedException {
Mapper possui os atributos populacao e operacaoGenetica que int CondicaoDeParada = 10;
int geracao = 0;
são, respectivamente, uma lista do tipo Individuo e o objeto que while (CondicaoDeParada != geracao) {
encapsula todas as operações do algoritmo genético (cruzamento, populacao = operacaoGenetica.selecao(populacao);
populacao = operacaoGenetica.cruzamento(populacao);
mutação, etc.). Portanto, tais campos (atributos) são usados nas ta-
populacao = operacaoGenetica.mutacao(populacao);
refas de criação dos grupos de indivíduos, no armazenamento do populacao = operacaoGenetica.calcularAptidao(populacao);
melhor indivíduo e para fornecer as operações ao processo AG. geracao++;
}
populacao.add(operacaoGenetica.getMelhorIndividuo());
Nota int Aptidao;
for (Individuo individuo : populacao) {
No Hadoop, a função map é representada por uma classe derivada da classe abstrata Mapper, um
Aptidao = (int) Math.round(individuo.getAptidao());
tipo genérico que permite tratar os dados definidos no par chave/valor. Mapper trabalha com context.write(new IntWritable(Aptidao), individuo);
quatro parâmetros que especificam a chave de entrada, o valor de entrada, a chave de saída e o }
populacao.clear();
valor de saída. Além disso, essa classe declara quatro métodos (setup(), map(), cleanup() e run())
}
que podem ser usados pela aplicação do usuário. Dentre eles, map() é o único método que é @Override
obrigatório implementar. Na ordem de chamadas, o primeiro método a ser executado é o setup(), protected void cleanup(Context context) throws IOException,
InterruptedException {
que realiza algum procedimento de inicialização da tarefa (job) de mapeamento. O método run()
geracaoLocal(context);
permite que o programador altere alguns dos parâmetros de configuração do job, que normalmente }
são especificados na classe principal da aplicação. O método map(), por sua vez, realiza a lógica de
}
processamento do mapeamento. Por fim, o método cleanup() pode ser implementado para executar
algum código no fim da tarefa de mapeamento, por exemplo, fechamento de conexões com o banco
de dados e reinicialização de variáveis. O método geracaoLocal() foi criado para modularizar o proces-
so AG, na função de mapeamento. Este método é chamado para
atuar em um conjunto de 100 indivíduos, onde são aplicadas as
A classe CaixeiroViajanteMapper ainda possui dois métodos operações genéticas de seleção, cruzamento e mutação sobre 5% da
herdados da classe Mapper: map() e cleanup(), e um método au- população. Essas operações são executadas 10 vezes (gerações), em
xiliar, denominado geracaoLocal(). O map() é o método principal um laço while cuja condição de parada ocorre quando a variável
da classe e trabalha com três parâmetros: Object key, Text value, CondicaoDeParada for igual a 10.
A função reduce é representada por uma classe derivada da classe abstrata Reducer, um tipo genérico }
semelhante ao Mapper. O Reducer possui quatro parâmetros que são usados para especificar os
tipos (classe) de objetos manipulados na função de redução. Em essência, eles definem dois pares de
objetos chave/valor, sendo um par para entrada e outro para saída. Da mesma forma que Mapper, Em resumo, a finalidade do método reduce() é recuperar a lista
a classe Reducer fornece quatro métodos (setup(), reduce(), cleanup() e run()) que podem ser de indivíduos de entrada e processar o AG nessa população.
implementados na classe de redução. Na prática, apenas a implementação do método reduce() é O AG é executado pelo atributo operacaoGenetica, que realiza
obrigatória, pois é ele quem realiza a função de redução. Para os outros métodos (setup(), cleanup() dez vezes (10 gerações) as operações genéticas de seleção, cruza-
e run()), o framework os trata da mesma maneira como na classe Mapper. mento e mutação, além de calcular a aptidão para cada indivíduo
da população. Ao final, acrescenta-se à população já processada
o melhor indivíduo, pela chamada ao método operacaoGenetica
O método reduce() trabalha com três parâmetros. Os dois pri- .getMelhorIndividuo(), tratamento esse chamado de elitismo.
meiros (key e values) são referentes ao par chave/valor de entrada, Finalizando, o método reduce() gera na saída a população conten-
que correspondem aos tipos de saída da fase de mapeamento (um do os melhores indivíduos, gravando os dados na pasta /Output
inteiro representando a aptidão e uma lista de indivíduos que (como veremos a seguir) pela chamada ao método write() do
pertencem àquela aptidão). O último parâmetro (o objeto context) objeto context.
é responsável pelo resultado final da tarefa de redução, gerando
os dados de saída. A classe da lógica AG: OperacaoGenetica
No trecho operacaoGenetica.setTaxaMutacao(0.9), a taxa de muta- Na classe OperacaoGenetica encontra-se toda a inteligência do
ção é definida em 90%, mais alta que no mapeamento (de apenas 5%), algoritmo genético. Esta disponibiliza todas as operações previstas
na técnica dos AGs, que são: seleção, cruzamento, mutação, cálculo quickSort() garante uma lista de indivíduos ordenada de forma
de aptidão e elitismo. Tais operações são traduzidas em métodos descendente, do que possui a maior aptidão para o de menor apti-
da classe e são explicados a seguir. dão. Com o objetivo de reduzir a quantidade de indivíduos menos
Na Listagem 4 vê-se a definição dos atributos da classe Operacao- aptos que podem estar presentes na população, uma quantidade
Genetica: melhorIndividuo, tamanhoIndividuo, taxaMutacao, dos melhores indivíduos é novamente adicionada à população.
alfabeto e distancias. O atributo melhorIndividuo tem o objetivo Isso é feito limpando a lista, via populacao.clear(), e adicionando
de guardar o melhor indivíduo encontrado durante o processo AG. a ela os 70% dos melhores indivíduos do ranking de classificação
O atributo tamanhoIndividuo, por sua vez, define o tamanho da e depois outros 30% também dos melhores indivíduos. Mesmo
cadeia de cidades (neste caso, 20). Já o atributo alfabeto representa duplicados, tais indivíduos sofrerão um processo de mutação na
as vinte cidades, onde cada cidade é representada por uma letra do etapa seguinte. Dessa forma garantimos o retorno de uma nova
alfabeto, enquanto distancias representa uma matriz contendo as população com os indivíduos de maior aptidão.
coordenadas de cada cidade (letra) no plano cartesiano. Por fim, Na operação genética de seleção é necessário utilizar algum
o atributo taxaMutacao define a taxa (5%) que será aplicada na método de ranqueamento para a escolha dos indivíduos mais
operação de mutação da população. aptos. Assim, foi implementado o método quickSort(), exposto na
Listagem 7 e que realiza a ordenação de uma lista de indivíduos
Listagem 4. Código resumido da classe auxiliar OperacaoGenetica. (população), para ser usado no método selecao().
// imports omitidos...
Listagem 5. Código do método calcularAptidao().
public class OperacaoGenetica {
private Individuo melhorIndividuo = new Individuo(); public ArrayList<Individuo> calcularAptidao(ArrayList<Individuo> populacao)
private int tamanhoIndividuo = 20; throws IOException {
private double taxaMutacao = 0.05; ArrayList<Individuo> popula = new ArrayList<Individuo>();
private String alfabeto = “ABCDEFGHIJKLMNOPQRSTUVWXYZ”; double aptidao =0;
private int[][] distancias = { int x1, x2, y1, y2, pos;
{ 1, 8 }, { 1,10 }, { 2,13 }, { 3,15 }, { 5,16 }, String[] indString;
{ 8,17 }, { 10,17}, { 13,16}, { 15,15}, { 16,13}, for (Individuo indi : populacao) {
{ 17,10}, { 17,8 }, { 16,5 }, { 15,3 }, { 13,2 }, indString = new String[tamanhoIndividuo];
{ 10,1 }, { 8, 1 }, { 5, 2 }, { 3, 3 }, { 2, 5 } }; indString = indi.getIndArray();
for (int j = 0; j < tamanhoIndividuo; j++) {
public ArrayList<Individuo> calcularAptidao(ArrayList<Individuo> populacao) {...};
public ArrayList<Individuo> selecao(ArrayList<Individuo> populacao) {...}; pos = alfabeto.indexOf(indString[j]);
private Individuo[] quickSort(Individuo[] pop, int inicio, int fim) {...}; x1 = distancias[pos][0];
public ArrayList<Individuo> cruzamento(ArrayList<Individuo> populacaoA) {...}; y1 = distancias[pos][1];
pos = alfabeto.indexOf(indString[(j + 1)%tamanhoIndividuo]);
public ArrayList<Individuo> mutacao(ArrayList<Individuo> populacao) {...}; x2 = distancias[pos][0];
public Individuo getMelhorIndividuo() {...} y2 = distancias[pos][1];
public void setMelhorIndividuo(Individuo ind) {...} aptidao += Math.sqrt(Math.pow(x1 - x2, 2)
public double getTaxaMutacao(){...} + Math.pow(y1 - y2, 2));
public void setTaxaMutacao(double taxa) {...} }
} indi.setAptidao(aptidao);
setMelhorIndividuo(indi);
popula.add(indi);
aptidao = 0;
Como o próprio nome indica, o método calcularAptidao(), visto }
na Listagem 5, serve para calcular a aptidão da função objetivo do return popula;
}
problema em questão, que se resume em determinar a distância a
ser percorrida na roda de navegação entre as cidades especificadas Listagem 6. Código do método selecao().
em cada indivíduo. Para isso, o método localiza as coordenadas
public ArrayList<Individuo> selecao( ArrayList<Individuo> populacao) throws
das cidades (no atributo alfabeto) para determinar o valor do IOException {
atributo distancias. Em seguida, aplica o cálculo da distância int tamanhoPop = populacao.size();
euclidiana e acumula o valor na variável aptidao, registrando o Individuo[] popula = new Individuo[tamanhoPop];
popula = populacao.toArray(popula);
resultado no atributo correspondente daquele indivíduo (usando popula = quickSort(popula, 0, tamanhoPop - 1);
o método setAptidao()). Depois é chamado o método setMelhor- populacao.clear();
Individuo() para identificar o melhor individuo (com a menor
for(int i=0; i < (popula.length*(0.7)); i++){
distância), para ser utilizado na operação de elitismo. No fim, o populacao.add(popula[i]);
método calcularAptidao() retorna a lista (população) de indiví- }
duos e suas aptidões calculadas. for(int i=0; i < (popula.length*0.3); i++){
populacao.add(popula[i]);
Já o método selecao(), cujo código é apresentado na Listagem 6, }
realiza a operação de seleção dos indivíduos mais aptos. Para return populacao;
}
escolher os melhores, uma chamada ao método de classificação
public ArrayList<Individuo> mutacao(ArrayList<Individuo> populacao) throws Listagem 10. Código dos métodos getMelhorIndividuo() e setMelhorIndividuo().
IOException {
public Individuo getMelhorIndividuo() {
int tamanhoPop = populacao.size();
return melhorIndividuo;
String cidade;
}
String[] indString;
Individuo indi;
public void setMelhorIndividuo(Individuo ind) {
Random indRandom = new Random();
if (getMelhorIndividuo().getAptidao() == 0
int ri, rc1, rc2;
|| getMelhorIndividuo().getAptidao() > ind.getAptidao()) {
if(tamanhoPop == 1){
Individuo melhorInd = new Individuo();
taxaMutacao = 1;
melhorInd.setCromossomo(ind.getCromossomo());
}
melhorInd.setAptidao(ind.getAptidao());
this.melhorIndividuo = melhorInd;
for (int i = 0; i < tamanhoPop * taxaMutacao; i++) {
}
}
ri = indRandom.nextInt(tamanhoPop);
indi = populacao.get(ri);
Listagem 11. Código dos métodos getTaxaMutacao() e setTaxaMutacao().
indString = indi.getIndArray();
rc1 = indRandom.nextInt(tamanhoIndividuo); public double getTaxaMutacao(){
rc2 = indRandom.nextInt(tamanhoIndividuo); return this.taxaMutacao;
cidade = indString[rc1]; }
indString[rc1] = indString[rc2];
indString[rc2] = cidade; public void setTaxaMutacao(double taxa){
indi.setIndArray(indString); this.taxaMutacao = taxa;
}
populacao.set(ri, indi);
}
return populacao;
} Classe principal da aplicação: CaixeiroViajante
A classe que é considerada a principal da aplicação chama-se
Em seguida, com um subconjunto da população (gerado a partir CaixeiroViajante e é apresentada na Listagem 12. Ela representa
da taxaMutacao), um indivíduo é escolhido aleatoriamente (vari- a aplicação propriamente dita, isto é, é a classe que tem o papel
ável indi, na posição ri da lista). Desse indivíduo são selecionadas de executar o MapReduce. Para isso, a partir do método estático
duas cidades, cujas posições na cadeia que formam o indivíduo main(), toda a configuração para o controle da execução das tare-
também são escolhidas de forma aleatória (e atribuídas nas variá- fas é declarada. Por exemplo, as definições das pastas dos dados
veis rc1 e rc2). Em seguida, estas cidades são permutadas entre si. de entrada e saída são informadas às classes FileInputFormat e
O resultado desse processo é a substituição do indivíduo original FileOutputFormat. Também são informadas quais classes execu-
da população pelo “mutante” (indi) via instrução populacao tarão as funções map e reduce, o que é feito via o objeto job e as
.set(ri, indi). Ao final de sua execução, o método mutacao() retorna chamadas aos métodos setMapperClass() e setReducerClass().
uma população com alguns indivíduos modificados. Além disso, são declarados os tipos de dados dos pares chave/
O método setMelhorIndividuo() cumpre o papel da operação valor de entrada e saída, obrigatórios nas classes que represen-
genética de elitismo. Seu código (visto na Listagem 10) realiza tam as funções map e reduce. No final do método main(), há
Autor
Cláudio Martins
[email protected]
Mestre em Computação pela Universidade Federal do Rio
Grande do Sul (UFRGS), professor do Instituto Federal do Pará
(IFPA) e analista de sistemas da Companhia de Informática de Belém
(Cinbesa). Trabalha há dez anos com a plataforma Java.
Autor
Figura 10. Resultados obtidos e comparação com o menor caminho (melhor aptidão)
Wesley Louzeiro Mota
Melhor Indivíduo Pior Indivíduo [email protected]
É graduado em Tecnologia em Análise e Desenvolvimento de
Sistemas pelo IFPA, em 2015. Possui experiência em progra-
mação Java desde 2012. Seu trabalho de conclusão de curso abordou
o uso combinado de MapReduce e algoritmo genético na solução do
Problema do Caixeiro Viajante.
Links:
Figura 11. Melhor e pior indivíduo encontrados Apostila “Algoritmos Genéticos: uma Introdução”, de Diogo C. Lucas e Luiz O.
Alvares, Inf/UFRGS, 2002.
Para que você possa reproduzir esses testes, as cinco bases de http://www.inf.ufrgs.br/~alvares/INF01048IA/ApostilaAlgoritmosGeneticos.pdf
dados estão disponíveis para download (veja a seção Links). Cada Link reduzido para download do plugin para uso do Hadoop no Eclipse.
base é um arquivo contendo um conjunto de linhas de texto, onde http://bit.ly/1dTezxt
cada linha representa um indivíduo, ou seja, uma sequência não
Link reduzido para download do projeto da aplicação (Eclipse) e os arquivos de
repetida de cidades (letras).
entrada com as populações iniciais.
Há muitos exemplos de casos reais do uso de AGs que podem ser http://bit.ly/1HiOJ4e
adaptados ao MapReduce. Entre eles, podemos citar os sistemas
de classificação de imagens de satélites, utilizados para identificar Livros
áreas florestais, queimadas, etc. Normalmente, aplicações dessa Hadoop: The Definitive Guide - 3rd Edition. Tom White. O’Reilly. 2012.
natureza trabalham com bancos de imagens que manipulam Livro que aborda o Hadoop de forma didática e atualizada em sua atual versão (2.x), apresen-
bilhões de dados em forma de pixels. tando estudos de caso usados para resolver problemas no modelo mapreduce.
Além desse estudo, que mostrou a viabilidade do uso de MapRe- Sistemas inteligentes: fundamentos e aplicações. Solange Oliveira Rezende.
duce no processamento de um algoritmo genético, muitas outras Manole Ltda. 2003.
aplicações podem se beneficiar dessa técnica. Em destaque, apli- Traz os avanços de metodologias e técnicas utilizadas no desenvolvimento de Sistemas
cações que envolvam carga e tratamento de um grande volume de Inteligentes, mostrando os princípios e aplicações práticas das técnicas.
dados, que manipulem fontes com formatos diversificados e não
estruturados, ou necessitem de eficiência de processamento em
Você gostou deste artigo?
larga escala, em situações que a solução tradicional não resolve.
METODISTA
Estudando Sistemas de
Informação na Metodista
eu me sinto desafiado
a desenvolver minha
capacidade dentro
da sala de aula e no
mercado de trabalho.
Com os cursos da área de Exatas e Tecnologia da
Metodista, o Sérgio tem contato com laboratórios
e ferramentas de última geração. Além disso,
desenvolve seu lado profissional por meio de
Projetos de Ação Profissional, simulando na
prática a resolução de problemas reais.
Sérgio
Aluno de Sistemas de Informação
• Análise e Desenvolvimento de • Engenharia Ambiental e Sanitária (Presencial) • Gestão da Tecnologia da Informação (Presencial)
Sistemas (Presencial e EAD) • Engenharia de Produção (Presencial) • Sistemas de Informação (Presencial)
• Automação Industrial (Presencial)
MEC
DESDE 2005
Portaria MEC
processoseletivo.metodista.br
244, 31/05/13
EDUCAÇÃO A
DISTÂNCIA
76 Java Magazine
Grande • Edição
São Paulo: 140 5000 | Outras localidades: 0800 889 2222 | facebook.com/universidade.metodista | twitter.com/metodista
11 4366