Você está na página 1de 76

ISSN 1676836-1

9 771676 836002

00150

Assine agora e tenha acesso a


todo o contedo da DevMedia:
www.devmedia.com.br/mvp
Edio 150 2016 ISSN 1676-8361

EXPEDIENTE
Editor
Eduardo Spnola (eduspinola@gmail.com)
Consultor Tcnico Diogo Souza (diogosouzac@gmail.com)

Produo
Jornalista Responsvel Kaline Dolabella - JP24185
Capa e Diagramao Romulo Araujo

Distribuio
FC Comercial e Distribuidora S.A
Rua Teodoro da Silva, 907, Graja - RJ
CEP 20563-900, (21) 3879-7766 - (21) 2577-6362

Atendimento ao leitor

Fale com o Editor!

A DevMedia possui uma Central de Atendimento on-line, onde voc


pode tirar suas dvidas sobre servios, enviar crticas e sugestes e
falar com um de nossos atendentes. Atravs da nossa central tambm
possvel alterar dados cadastrais, consultar o status de assinaturas
e conferir a data de envio de suas revistas. Acesse www.devmedia.
com.br/central, ou se preferir entre em contato conosco atravs do
telefone 21 3382-5038.

muito importante para a equipe saber o que voc est achando da revista:
que tipo de artigo voc gostaria de ler, que artigo voc mais gostou e qual
artigo voc menos gostou. Fique a vontade para entrar em contato com os
editores e dar a sua sugesto!
Se voc estiver interessado em publicar um artigo na revista ou no site Java
Magazine, entre em contato com o editor, informando o ttulo e mini-resumo
do tema que voc gostaria de publicar:

Publicidade
publicidade@devmedia.com.br 21 3382-5038
Anncios Anunciando nas publicaes e nos sites do Grupo DevMedia,
voc divulga sua marca ou produto para mais de 100 mil desenvolvedores
de todo o Brasil, em mais de 200 cidades. Solicite nossos Media Kits, com
detalhes sobre preos e formatos de anncios.

Java, o logotipo da xcara de caf Java e todas as marcas e logotipos


baseados em/ ou referentes a Java so marcas comerciais ou
marcas registradas da Sun Microsystems, Inc. nos Estados Unidos e
em outros pases.

Eduardo Oliveira Spnola


eduspinola.wordpress.com
@eduspinola / @Java_Magazine

Sumrio
Artigo no estilo Curso

06 Por dentro do banco de dados NoSQL Couchbase Parte 2


[ Fernando Henrique Fernandes de Camargo ]

Artigo no estilo Curso

18 Como usar o Apache Cassandra em aplicaes Java EE Parte 2

Contedo sobre Novidades

Destaque - Vanguarda

Feedback
eu

[ Jos Guilherme Macedo Vieira ]

sobre e
s

32 Introduo ao Java 9: Conhea os novos recursos

D
s

[ Marlon Patrick ]

edio
ta
Contedo sobre Novidades

48 Simplificando o desenvolvimento de microsservios com o WildFly Swarm


[ Joel Backschat ]

Artigo no tipo Mentoring

A Java Magazine tem que ser feita ao seu gosto.


Para isso, precisamos saber o que voc, leitor, acha
da revista!
Destaque - Reflexo

60 DevOps: Como adequar seu processo de CI a essa nova cultura


[ Pedro E. Cunha Brigatto ]

D seu feedback sobre esta edio!

D seu voto sobre esta edio, artigo por artigo, atravs do link:
www.devmedia.com.br/javamagazine/feedback

Por dentro do banco


de dados NoSQL
Couchbase Parte 2
Veja neste artigo como desenvolver aplicaes
escalveis com Couchbase
Este artigo faz parte de um curso

Fique por dentro


Este artigo apresenta a parte prtica de um banco de dados NoSQL
com estrutura de dados baseada em documentos: o Couchbase. Aqui

ser exposta a continuao do artigo que analisou a teoria do mesmo.

uito tem se falado de NoSQL nos ltimos anos.


De forma simples, podemos descrev-lo como
o movimento que norteou a criao de bancos de dados bem especializados em certos problemas
e alguns de uso mais geral. O que todos eles tm em
comum a capacidade de serem utilizados em grandes
aplicaes e conseguirem escalonar o suficiente para que
as mesmas continuem com boa performance mesmo com
grande fluxo de dados e usurios simultneos.
Dentre as opes disponveis, destacam-se aquelas
cuja estrutura de dados baseia-se em documentos. Isso
porque eles podem servir tanto para aplicaes de uso
geral, como tambm para aplicaes mais especficas,
que exigem flexibilidade nos dados armazenados.
O MongoDB e Couchbase so os conhecidos dessa
categoria. O primeiro , atualmente, o mais utilizado,
enquanto o segundo vem ganhando destaque e apresentando timos resultados em benchmarks que provam
sua superioridade em performance.
O Couchbase, banco de dados apresentado na primeira
parte deste artigo, se mostra muito til a seus usurios.
No s pela sua grande performance e tima escalabilidade, mas tambm pela sua capacidade de sincronizao
com dispositivos mveis, uma nova funcionalidade que
ainda no existe em seu concorrente. Com esse recurso,
viabiliza-se a facilidade da sincronizao de dados entre
dispositivos mveis, que podero operar sem acesso
internet, e o servidor, que ser o centro de dados.

Dessa vez, no entanto, ser vista a configurao e implementao


de um cliente Java que utiliza o Couchbase. Assim, esse contedo
til para desenvolvedores que procuram um banco de dados com
grande potencial de escalabilidade e/ou uma estrutura de dados
bem flexvel.

Com suas vantagens analisadas, foi vista a teoria sobre seu


funcionamento, desde o gerenciamento do cluster e a conexo
entre seus elementos, at o funcionamento interno de cada n,
o que faz com que toda essa performance proposta seja garantida. Dentre os conceitos relacionados a seu funcionamento,
vale ressaltar a ausncia de hierarquia entre os ns do cluster.
Com o intuito de evitar um nico ponto de falha, no se utiliza
a tradicional arquitetura mestre-escravo. Ao invs disso, todos
os ns se conhecem e a aplicao cliente poder se conectar com
qualquer um deles, sendo informada da existncia de todos os
outros automaticamente.
Esses ns, individualmente, garantem tempos de respostas na
ordem de milissegundos atravs de um cache em memria, que
armazena os ltimos documentos acessados. Alm disso, as operaes de insero e alterao so realizadas primeiro na memria,
o que possibilita ainda mais agilidade.
Para implementar o escalonamento horizontal, os dados so distribudos entre esses ns com o recurso de auto-sharding. E para se
prevenir de desastres, uma rplica de cada documento guardada
em outro n. Assim, caso um dos ns venha a ficar fora do ar, um

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Figura 1. Tela de configurao dos Data Buckets


outro assume seu lugar e um rebalanceamento dos dados acontece
a fim de redistribuir os dados e criar novas rplicas.
Com o funcionamento do Couchbase detalhadamente abordado,
foram apresentados os campos reservados de seus documentos,
o comportamento de cada um deles, bem como a capacidade de
armazenar outros valores que no sejam documentos.
Por fim, uma tcnica foi apresentada para que seja feito o versionamento de esquemas dos documentos de forma que sejam
atualizados, quando cada documento for acessado naturalmente
pela aplicao. Utilizando essa tcnica, veremos a seguir o desenvolvimento de uma aplicao Java que se comunicar com um
banco de dados Couchbase implementando as operaes bsicas
e o mapeamento entre objetos e documentos.

Desenvolvimento com o Java SDK


Antes de iniciar a implementao com o cliente Java do Couchbase precisamos instal-lo e configur-lo. Para isso, est disponvel na seo Links a URL com as instrues de download e
instalao da verso gratuita, alm de como executar o servio
do Couchbase. Esses detalhes variam de acordo com o sistema
operacional do usurio.

Concluda essa etapa, acesse a URL http://localhost:8091. Nesse


momento uma tela ser apresentada para que seja feita a configurao inicial do banco de dados. Nessa tela pode-se registrar
o administrador como desejar e, por ser apenas um banco usado
para desenvolvimento, no necessrio mais de 1GB de memria
em sua configurao. Alm disso, tambm ser questionada a
quantidade de memria para o bucket padro, que o primeiro
bucket criado no Couchbase. Como um outro bucket ser criado
para o exemplo, o padro no tem importncia e pode at mesmo
ser deletado aps essa configurao inicial. Ento, recomenda-se
colocar a quantidade mnima de memria para ele. Para esclarecer,
bucket , para o Couchbase, o equivalente a um schema do MySQL.
Normalmente, ser criado um para cada aplicao.
Aps essas definies, o painel de controle do Couchbase ser
exibido, como mostra a Figura 1. Nesse painel, na aba Data Buckets,
crie um novo bucket com o nome Exemplo e tipo Couchbase, visto
que ser utilizado para o armazenamento de documentos (o tipo
membase serve para armazenar apenas chave-valor). Feito isso,
as opes desse bucket ficaro visveis ao clicar na seta exibida
ao lado de seu nome. Clique, ento, nessa seta, depois em Edit e
digite exemplo no campo de password.

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

Por dentro do banco de dados NoSQL Couchbase Parte 2

Esta ser a senha usada no exemplo de acesso ao banco de


dados.
Em um projeto Java, seja ele Maven ou Gradle, deve-se adicionar
o seguinte artefato referente ao SDK do Couchbase: com.couchbase.
client:java-client:2.1.4. Caso seja criado um projeto Java comum
Java, deve-se baixar o arquivo JAR desse cliente, cujo endereo
tambm encontra-se na seo Links. Outra biblioteca utilizada
aqui ser a Joda Time, que muito conhecida como substituta
da API nativa de gerenciamento de data e tempo do Java. Neste
exemplo, ela ser utilizada para salvar informaes importantes
no documento, como o momento em que foi criado e o momento de
sua ltima modificao. Para o Maven e o Gradle, basta adicionar
o seguinte artefato: joda-time:joda-time:2.8.1.
Com o projeto criado e as dependncias satisfeitas, o prximo
passo estabelecer uma conexo com o banco de dados, o que
pode ser feito em uma classe contendo um mtodo main() como
o cdigo da Listagem 1.
Nesse cdigo demonstramos a simples tarefa de abrir e fechar
uma conexo com o banco de dados. Note que comeamos
criando uma conexo com um cluster passando uma lista de
IPs conhecidos. Nesse caso, por ser um nico servidor local,
sem qualquer cluster, podemos chamar o mtodo create() sem
qualquer argumento.
Listagem 1. Conexo com o Couchbase.
Public class Exemplo {
static Cluster cluster;
static Bucket bucket;
public static void main(String[] args){
cluster = CouchbaseCluster.create(new String[]{127.0.0.1});
bucket = cluster.openBucket(Exemplo, exemplo);
// Uso do bucket
cluster.disconnect();
}
}

8 Java Magazine Edio 150

Porm, se tivssemos um cluster com vrios servidores conectados, deveramos passar uma lista com os IPs de alguns
deles na chamada desse mtodo.
Com uma conexo aberta para o cluster, deve-se conectar
ao bucket criado para nossa aplicao. Para isso, chama-se o
mtodo openBucket() do cluster passando como argumento
o nome e a senha do bucket. Por fim, aps seu uso, fechamos
a conexo com o cluster, o que possibilita a liberao dos
recursos utilizados e desencadeia no fechamento da conexo
do bucket.
A partir do momento em que uma conexo aberta, o objeto
que ser utilizado por todo o sistema para acessar o banco
ser o bucket. Deste modo, esse pode ser configurado como
singleton no sistema, possibilitando que seja injetado pelo
Spring ou outro framework de injeo de dependncias. Por
outro lado, o cluster usado apenas para abrir conexes com
buckets e desconectar de todos eles de uma s vez atravs do
disconnect(), caso o sistema utilize mais de um. Portanto, o
objeto cluster ter esse mtodo invocado no shutdown do
sistema para que os recursos sejam liberados.
A seguir, os principais mtodos da classe Bucket so explicados:
AsyncBucket async(): retorna uma verso assncrona do
Bucket. Com essa verso, todos os mtodos comuns do Bucket
estaro disponveis, porm, eles retornam um Observable do
RxJava, biblioteca que vem crescendo em adoo e cujo uso
recomendado. Como est fora do escopo deste artigo explic-la,
sero mostrados aqui apenas os mtodos sncronos;
JsonDocument get(String id): retorna o documento cujo
identificador seja igual ao passado como parmetro. Caso tal
documento no exista, null ser retornado;
JsonDocument getAndLock(String id, int lockTime): funciona da mesma maneira que o mtodo anterior, contudo, esse
mtodo utilizado para a concorrncia pessimista, explicada
anteriormente. Assim, caso o documento seja encontrado, ele
ser retornado e bloqueado. Caso esse mtodo seja invocado
novamente enquanto o documento estiver bloqueado, uma

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

exceo ser lanada. Apesar de permitir ajustar a quantidade de


segundos que o documento ficar bloqueado, caso esse no seja
desbloqueado manualmente, o valor mximo que o documento
permanecer bloqueado de 30 segundos, mesmo que seja passado um valor maior;
<D extends Document<?>> Boolean unlock(D document):
desbloqueia manualmente um documento;
JsonDocument getAndTouch(String id, int expiry): funciona de
maneira simular ao mtodo get(String id), com a diferena que o
tempo de expirao do documento reajustado com o valor passado
como parmetro. Esse mtodo pode ser utilizado para acessar a
sesso de um usurio logado em um sistema web, por exemplo;
Boolean touch(String id, int expiry): tambm utilizado para
atualizar o tempo de expirao de um documento, mas sem
retorn-lo;
<D extends Document<?>> D insert(D document): insere
um documento no banco de dados caso ainda no exista. Caso
contrrio, um erro ser lanado. Em caso de sucesso, o mesmo
documento ser retornado com o valor de CAS atualizado. Assim,
novas atualizaes podem ser feitas no mesmo com concorrncia
otimista;
<D extends Document<?>> D upsert(D document): funciona
da mesma maneira que o mtodo anterior, com a diferena que,
caso o documento j exista, ele ser substitudo;
<D extends Document<?>> D replace(D document): mais uma
variante dos dois mtodos anteriores. Diferentemente desses, esse
ter sucesso apenas se o documento j existir. Caso contrrio, um
erro indicar que o mesmo no existe;
<D extends Document<?>> D remove(D document): remove
o documento do servidor. O documento retornado ter todas as
propriedades vazias, restando apenas o identificador e o CAS;
JsonDocument remove(String id): funcionamento idntico ao
mtodo anterior. Pode ser utilizado quando apenas o identificador
do documento estiver disponvel;
ViewResult query(ViewQuery query): faz uma busca por
documentos atravs da query passada como parmetro. O funcionamento desse tipo de busca ser explorado mais adiante;
JsonLongDocument counter(String id, long delta, long initial):
cria ou atualiza um contador. Contudo, esse mtodo armazena um
nmero Long como valor da chave, ao invs de um documento
JSON. Caso o identificador no exista, o contador receber o valor
inicial. Caso contrrio, ter seu valor somado de delta. Essa operao atmica, o que a torna segura para concorrncia.

Comeando pela interface, essa possui apenas quatro mtodos,


sendo todos eles do tipo getter para as propriedades bsicas:
id(), content(), cas() e expiry(). Seus nomes so sugestivos sobre
o que cada um retorna, porm, vale analisar melhor o mtodo
content().
Para entender o que o mtodo getter para o contedo de
um documento retorna, devemos observar que a interface
Document<T> faz uso de generics para especificar o tipo de
contedo. Essa interface tem como principal implementao a
classe JsonDocument, que especifica JsonObject como tipo de
contedo, o qual nada mais do que um wrapper para Map que
valida suas chaves e valores para assegurar a compatibilidade
com um documento JSON. Alm desta, outras implementaes esto disponveis, como a j citada JsonLongDocument,
que armazena um Long, e outras equivalentes para cada tipo
primitivo, como JsonDoubleDocument, JsonBooleanDocument
e JsonStringDocument. Para armazenar contedo binrio, existe
ainda a BinaryDocument.
Sobre o tipo de documento mais comum, JsonDocument, importante observar seus factory methods, os quais sero utilizados
para criar instncias do mesmo. Esses mtodos possuem o nome
create() e algumas combinaes de argumentos, sendo eles: id,
content, cas e expiry. Alm desses, tambm existem mtodos
que permitem a cpia de documentos definidos com o nome
from() e os mesmos argumentos anteriores, com a adio de
JsonDocument. A funo desses mtodos permitir a criao de
documentos clones, naturalmente com identificadores diferentes,
o que pode ser til para permitir a cpia de registros pelo usurio
para que esse faa as modificaes que desejar.
Com essa simples API j possvel realizar todas as operaes
bsicas com documentos. No entanto, ainda falta o mtodo de
converso de entidades para documentos JSON.

Vale notar que todas as operaes que modificam um documento, vistas anteriormente, utilizam de concorrncia otimista
caso o valor de CAS esteja presente. Se houver diferena entre
o valor de CAS passado pelo cliente e o valor do servidor, assume-se que tal documento foi alterado em paralelo e a exceo
CASMismatchException lanada para indicar o ocorrido.
Com a viso bsica dos principais mtodos de Bucket, o prximo
passo explorar a interface Document e sua principal implementao, JsonDocument, a qual usaremos na maior parte do tempo.

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

Por dentro do banco de dados NoSQL Couchbase Parte 2

Como mencionado na discusso sobre modelagem de documentos, ser utilizado um esquema de mapeamento com controle de
verso. Vejamos a explicao e o cdigo de exemplo desse mtodo
no tpico a seguir.

Listagem 2. Interface que especifica os mtodos de um mapeador, bem como


especifica nomes de campos para os metadados.
public interface DocumentMapper<E extends Entity> {
String TYPE = type;
String TYPE_VERSION = typeVersion;
String CREATED_AT = createdAt;
String LAST_MODIFIED = lastModified;

Mapeadores de documentos
O mapeamento de objetos se dar atravs de classes simples,
expostas adiante. Antes de mostrar esse cdigo, no entanto, sero apresentadas as entidades de exemplo. Vale notar que todas
elas implementaro uma classe abstrata, chamada Entity, com o
intuito de tornar disponvel em todas as entidades as seguintes
propriedades: String id, DateTime createdAt, DateTime lastModified e Long revision. Com elas, armazenamos o identificador do
documento, o momento em que o mesmo foi inserido, o momento
em que foi modificado pela ltima vez e o valor do CAS.
Para exemplificar, sero criadas apenas duas entidades: Task e
Category. A entidade Task representar uma tarefa a ser realizada e Category a encaixar em determinada categoria de tarefas.
Tal aplicao ser uma simples TODO List. Para torn-la o mais
simples possvel, ambas as entidades tero somente um campo,
chamado name, para armazenar o nome da tarefa e da categoria,
e a tarefa possuir ainda uma referncia para sua categoria, o que
configura uma relao de um para muitos.
Com as entidades prontas, deve-se criar os mapeadores de
documentos para as mesmas. A Listagem 2 mostra a interface
implementada por todos os mapeadores. Essa utiliza generics para
especificar qual tipo de entidade tal mapeador tratar, especificando seus dois mtodos de converso. Alm disso, quatro campos
que estaro presentes em todos os documentos so especificados
atravs de constantes. E para facilitar o trabalho das implementaes dessa interface, a classe AbstractDocumentMapper,
mostrada na Listagem 3, implementa os mtodos do mapeador de
forma a tratar os metadados do documento, como o identificador,
momento de criao e ltima modificao e o valor do CAS.
O primeiro ponto a se notar no cdigo de AbstractDocumentMapper o uso da classe ISODateTimeFormat, da biblioteca Joda
Time. O objetivo com isso que todos os timestamps sejam salvos
no formato padro definido pela ISSO 8601. Tal formato tem a vantagem de ter um ordenamento natural das datas, de forma que uma
ordenao simples de Strings colocaria os timestamps em ordem.

10

E getEntity(JsonDocument document);
JsonDocument getDocument(E entity);
}
Listagem 3. Classe abstrata criada com o propsito de ser superclasse de todos os
mapeadores.
public abstract class AbstractDocumentMapper<E extends Entity> implements
DocumentMapper<E> {
protected DateTimeFormatter dateTimeFormatter;
protected AbstractDocumentMapper() {
this.dateTimeFormatter = ISODateTimeFormat.dateTime();
}
@Override
public JsonDocument getDocument(E entity) {
JsonObject properties = JsonObject.create();
String id = entity.getId();
Long rev = entity.getRevision();
properties.put(CREATED_AT, entity.getCreatedAt().
toString(dateTimeFormatter));
properties.put(LAST_MODIFIED, entity.getLastModified().
toString(dateTimeFormatter));
return JsonDocument.create(id, properties, rev != null ? rev : 0);
}
protected void fillMetadata(E entity,JsonDocument document){
entity.setId(document.id());
entity.setRevision(document.cas());
JsonObject properties = document.content();
entity.setCreatedAt(dateTimeFormatter.parseDateTime((String)
properties.get(CREATED_AT)));
entity.setLastModified(dateTimeFormatter.parseDateTime((String)
properties.get(LAST_MODIFIED)));
}
}

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Tambm vale notar que foi utilizada a verso do mtodo create()


da classe JsonDocument que espera um valor de CAS. Quando
tal valor no est disponvel, normalmente utilizaramos outra
verso desse mtodo. Porm, para facilitar, simplesmente passamos 0 quando o CAS for nulo, visto que esse o valor padro do
Couchbase, equivalente a nulo. O restante da implementao dessa
classe clara e no necessita de maiores explicaes.
No exemplo abordado, sero duas classes que herdam de
AbstractDocumentMapper: TaskDocumentMapperV1 e CategoryDocumentMapperV1. O sufixo V1 indica que essas classes
so responsveis por mapear a primeira verso de seus respectivos documentos. A lgica de versionamento ser mostrada
mais adiante. As Listagens 4 e 5 mostram essas duas classes
mapeadoras.
Como pode-se verificar, o cdigo dos mapeadores bem simples.
Eles utilizam de mtodos criados na superclasse para preencher
e obter os metadados dos documentos e manipulam diretamente
o contedo desses, que armazenado em uma instncia de JsonObject. E assim como feito em um JSON comum, as propriedades
so armazenadas nos pares chave-valor, tendo seus nomes como
chaves. Dessa forma, utilizamos o mtodo put() para preencher
cada propriedade.
Deve-se notar o ajuste das propriedades TYPE e TYPE_VERSION, as quais so usadas para armazenar o tipo de documento
e a verso do esquema. Na Listagem 5, o tipo ajustado com o
valor category, definido como constante na classe CategoryDocumentMapper, que ainda ser mostrada. E por se tratar da primeira verso, ajustamos o campo de verso com o nmero inteiro
1. Tambm pode-se notar na Listagem 4 que o identificador da
categoria de determinada tarefa armazenado em um campo
do documento, caso a categoria exista. Isso cria o mapeamento
de um para muitos, de maneira parecida com o mtodo adotado
em bancos de dados relacionais.
Para finalizar a lgica dos mapeadores, as Listagens 6 e 7
mostram as implementaes das classes TaskDocumentMapper e CategoryDocumentMapper. Ambas foram utilizadas
anteriormente nas Listagens 4 e 5, onde suas constantes
DOC_TYPE foram usadas para armazenar o tipo correto de
seus documentos.
A lgica de versionamento implementada por essas classes
simples. Elas possuem um mapa cuja chave especifica uma verso do documento e o valor um mapeador para determinada
verso. Ao mapear um documento para uma entidade, a verso
do documento verificada para se obter o mapeador correto.
O caminho contrrio, em que a entidade ser convertida em
documento, sempre feito com a verso atual. Quando uma
nova verso de um documento criada, a constante que especifica a verso atual ser atualizada, e um mapeador, para tal
verso, ser criado para a mesma e adicionado ao mapa. Para
manter a compatibilidade, verses anteriores do mapeador sero
atualizadas. Isso porque esses devero ler um documento em
verso antiga e mape-lo para a verso mais nova da entidade,
que poder ter seus campos modificados.

Listagem 4. Classe mapeadora dos documentos de tarefa de verso 1.


public class TaskDocumentMapperV1 extends AbstractDocumentMapper<Task> {
public static final String NAME = name;
public static final String CATEGORY_ID = categoryId;
@Override
public Task getEntity(JsonDocument document) {
JsonObject properties = document.content();
String name = (String) properties.get(NAME);
Task task = new Task();
task.setName(name);
String categoryId = (String) properties.get(CATEGORY_ID);
if(categoryId != null){
task.setCategory(new Category(categoryId));
}
fillMetadata(task, document);
}

return task;

@Override
public JsonDocument getDocument(Task task) {
JsonDocument document = super.getDocument(task);
JsonObject properties = document.content();
properties.put(TYPE, TaskDocumentMapper.DOC_TYPE);
properties.put(TYPE_VERSION, 1L);
properties.put(NAME, task.getName());
Category category = task.getCategory();
if(category != null){
properties.put(CATEGORY_ID, category.getId());
}

return document;

Listagem 5. Classe mapeadora dos documentos de categoria de verso 1.


public class CategoryDocumentMapperV1 extends AbstractDocumentMapper
<Category> {
public static final String NAME = name;
@Override
public Category getEntity(JsonDocument document) {
JsonObject properties = document.content();
String name = (String) properties.get(NAME);
Category category = new Category();
category.setName(name);
fillMetadata(category, document);
}

return category;

@Override
public JsonDocument getDocument(Category category) {
JsonDocument document = super.getDocument(category);
JsonObject properties = document.content();
properties.put(TYPE, CategoryDocumentMapper.DOC_TYPE);
properties.put(TYPE_VERSION, 1L);
properties.put(NAME, category.getName());

return document;

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

11

11

Por dentro do banco de dados NoSQL Couchbase Parte 2

Listagem 6. Implementao da lgica de versionamento dos mapeadores dos


documentos de tarefa.

Listagem 7. Implementao da lgica de versionamento dos mapeadores dos


documentos de categoria.

public class TaskDocumentMapper implements DocumentMapper<Task> {

public class CategoryDocumentMapper implements


DocumentMapper<Category> {

public static final String DOC_TYPE = task;

public static final String DOC_TYPE = category;

public static final Integer CURRENT_VERSION = 1;

public static final Integer CURRENT_VERSION = 1;

private Map<Integer, DocumentMapper<Task>> mappers;

private Map<Integer, DocumentMapper<Category>> mappers;

public TaskDocumentMapper(TaskDocumentMapperV1
taskDocumentMapperV1) {
this.mappers = new HashMap<>();
this.mappers.put(CURRENT_VERSION, taskDocumentMapperV1);
}

public CategoryDocumentMapper(CategoryDocumentMapperV1
categoryDocumentMapperV1) {
this.mappers = new HashMap<>();
this.mappers.put(CURRENT_VERSION, categoryDocumentMapperV1);
}

@Override
public Task getEntity(JsonDocument document) {
return mappers.get(((Number)document.content().get(TYPE_VERSION))
.intValue()).getEntity(document);
}

@Override
public Category getEntity(JsonDocument document) {
return mappers.get(((Number)document.content().get(TYPE_VERSION)).
intValue()).getEntity(document);
}

@Override
public JsonDocument getDocument(Task task) {
return mappers.get(CURRENT_VERSION).getDocument(task);
}

@Override
public JsonDocument getDocument(Category category) {
return mappers.get(CURRENT_VERSION).getDocument(category);
}
}

Tambm vale observar que esses mapeadores, responsveis


pelo versionamento de mapeadores, no implementam a classe
AbstractDocumentMapper, mas sim a interface DocumentMapper. O objetivo deixar transparente ao resto do sistema
o mecanismo de mapeamento de documentos. Normalmente,
essas instncias sero criadas atravs de injeo de dependncias
e os utilizadores das mesmas no precisaro se preocupar com
detalhes do versionamento.
Antes de partirmos para a demonstrao do uso destas classes,
preciso entender que elas devem ser combinadas para se obter
objetos completos. Ao observarmos a Listagem 4, por exemplo,

12 Java Magazine Edio 150

12

notamos que o objeto criado a partir de um documento de tarefa


possui uma instncia vazia de Category, com apenas o identificador preenchido. Para preencher esse objeto com uma categoria
completa, deve-se pesquisar por seu documento, utilizar o
mapeador de categoria e ento ajust-la com um objeto completo.
A responsabilidade de fazer essa combinao geralmente fica
por conta de um DAO, que ir interagir com documentos e usar
os mapeadores para transform-los em objetos.
Finalizando essa seo sobre mapeamento de documentos, a
Listagem 8 mostra o mtodo de insero e atualizao de entidades. Esse mtodo utilizado na Listagem 9, a qual demonstra

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Listagem 8. Cdigo de insero/atualizao de entidades. Normalmente estaria


em um DAO.
private static <T extends Entity> void upsert(DocumentMapper<T>
documentMapper, T entity) {
if(entity.getCreatedAt() == null){
entity.setCreatedAt(new DateTime());
}
entity.setLastModified(new DateTime());
JsonDocument document = documentMapper.getDocument(entity);
JsonDocument updatedDocument = bucket.upsert(document);
entity.setRevision(updatedDocument.cas());
}
Listagem 9. Cdigo que demonstra o uso bsico do Couchbase.
public static void main(String[] args){
// Abre conexo com o banco de dados
cluster = CouchbaseCluster.create(new String[]{127.0.0.1});
bucket = cluster.openBucket(Exemplo, exemplo);
// Instancia os mapeadores
CategoryDocumentMapper categoryDocumentMapper = new
CategoryDocumentMapper(new CategoryDocumentMapperV1());
TaskDocumentMapper taskDocumentMapper = new
TaskDocumentMapper(new TaskDocumentMapperV1());
// Cria as entidades com identificadores UUID aleatrios
Category category = new Category();
category.setId(UUID.randomUUID().toString());
category.setName(Categoria 1);
Task task = new Task();
task.setId(UUID.randomUUID().toString());
task.setName(Tarefa 1);
task.setCategory(category);
// Insere as entidades recm criadas
upsert(categoryDocumentMapper, category);
upsert(taskDocumentMapper, task);
// Realiza alteraes nas entidades
category.setName(Categoria 1 modificada);
task.setName(Tarefa 1 modificada);
// Persiste as alteraes no banco
upsert(categoryDocumentMapper, category);
upsert(taskDocumentMapper, task);
// Recupera o objeto completo de tarefa com sua respectiva categoria
JsonDocument taskDocument = bucket.get(task.getId());
task = taskDocumentMapper.getEntity(taskDocument);
JsonDocument categoryDocument = bucket.get(task.getCategory().getId());
category = categoryDocumentMapper.getEntity(categoryDocument);
task.setCategory(category);
// Remove os registros do banco
bucket.remove(category.getId());
bucket.remove(task.getId());
// Finaliza a conexo
cluster.disconnect();
}

todo o processo de insero, modificao, atualizao, obteno


completa e, por fim, remoo das entidades de categoria e tarefa.
O cdigo da Listagem 8 bem simples. Ele verifica se a entidade j possui uma data de criao e, caso no possua, cria uma
com a data atual. Por outro lado, a data de modificao sempre
atualizada. Depois disso, o mapeador utilizado para converter
o objeto em documento e utiliza-se o mtodo upset(), que insere
ou atualiza um documento. Assim, o documento retornado por
esse mtodo tem sua propriedade de CAS extrada e ajustada na
entidade. Isso permite que futuras atualizaes da entidade sejam
possveis evitando um CASMismatchException.
Da mesma forma, o cdigo da Listagem 9 no possui complicaes. Deve-se notar a combinao dos mapeadores para se obter
um objeto completo. Primeiramente, o documento de tarefa
obtido e convertido em objeto. Ento, busca-se sua categoria
utilizando o identificador do objeto incompleto de categoria. Por
fim, o documento de categoria convertido em sua entidade e
passado para o objeto de tarefa. Isso algo que normalmente estaria dentro de um DAO, mas para fins de facilitar a visualizao,
foi colocado aqui. O restante do cdigo autoexplicativo. Basicamente, altera-se as entidades, as armazena no banco utilizando
o mtodo apresentado na Listagem 8 e depois as remove atravs
do mtodo remove(), que recebe o identificador do documento
como parmetro.
Com o cdigo demonstrado possvel fazer todas as operaes
bsicas com documentos, desde que se tenha seu identificador.
Mas normalmente essa pr-condio no satisfeita. Ao invs
disso, um sistema normalmente lista entidades de acordo com
determinadas condies para que o usurio escolha o que fazer
com alguma dessas entidades. Em um banco de dados relacional,
isso feito com queries SQL. A seguir, veremos como buscar
registros no Couchbase.

Utilizando Views
A forma de busca por dados utilizada no Couchbase algo novo
e completamente diferente das tradicionais consultas SQL. Assim,
ao invs de utilizar uma linguagem de busca de dados como em
bancos relacionais, o Couchbase utiliza da lgica de MapReduce
para indexar os dados em Views, as quais so uma forma de
organizao de um index de busca criado no banco e permitem
extrao, filtro, agregao e busca de informao.
O processo de criao de uma View se d pela configurao de
uma ou duas funes. A primeira a funo de map, que filtra
entradas e pode extrair informao. Seu resultado final uma lista
ordenada de chave-valor, chamada de index, que armazenada
em disco e atualizada de forma incremental medida que os
documentos so atualizados. Opcionalmente, pode-se prover uma
segunda funo, chamada reduce, que tem o objetivo de somar,
agregar ou realizar outros clculos sobre as informaes.
Quando nos referimos a funes, estamos mencionando rotinas
programadas em JavaScript, as quais sero executadas em cada
um dos documentos. Essas rotinas de map e reduce das Views
so armazenadas como strings em documentos especiais JSON,

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

13

13

Por dentro do banco de dados NoSQL Couchbase Parte 2

conhecidos como design documents, e ento esses so associados


ao bucket. Vale ressaltar que cada design document pode armazenar uma ou mais Views.
Depois que a View criada, pode-se utiliz-la para buscar
informaes. Isso feito atravs de seu index, o qual ordena de
forma crescente as chaves emitidas para cada documento. Assim,
possvel fazer buscas por chaves iguais a determinado valor,
que iniciem com algum valor, entre outros. Essas chaves podem
ser emitidas com qualquer campo de um documento. Ento, se
for desejada uma busca por contatos cujo nmero de telefone se
inicie com o DDD 62, uma View ser criada filtrando apenas documentos de contatos e usando o nmero de telefone como chave.
Por fim, uma busca nessa View com chaves que iniciem com 62
ser executada e os contatos sero obtidos.
Para entender melhor o funcionamento desse tipo de busca,
podemos comear criando uma View para buscar todas as tarefas
do exemplo anterior. Para isso, entre no console web do Couchbase
novamente, abra a aba de Views e selecione o bucket Exemplo,
criado anteriormente. Nessa tela, nota-se que existem duas abas:
Development Views e Production Views. Essa diferena existe para
que, ao criar Views no banco de dados, esse no tenha quedas de
performance enquanto o processo de index feito. Portanto, uma
Development View trabalhar apenas com um subconjunto dos
dados, a no ser que seja requisitado o contrrio.
Como estamos no momento de criao da View, devemos utilizar
a aba Development Views e clicar em Create Development View. Em
seguida, preencha tanto o campo Design Document Name quanto o
campo View Name com all_tasks e clique em Save. Note que o Design Document Name prefixado com _design/dev_, sem opo de
mudana, visto que um padro fixado pelo Couchbase. Com os
campos preenchidos como especificado, o design document ser
criado com a linguagem JavaScript e uma View chamada all_tasks
ser criada junto a ele. Clicando em Edit na View recm-criada, uma
tela apresentar o cdigo da View em duas partes: Map e Reduce.
Em Map est presente um cdigo simples, sendo essa uma
funo JavaScript que recebe como parmetro o documento e os

14 Java Magazine Edio 150

14

metadados do mesmo. Vemos tambm uma chamada para o


mtodo emit(), que basicamente adiciona ao index uma chave
e valor. Nesse cdigo de exemplo, apenas o identificador do
documento emitido com um valor nulo. Isso porque o valor
aqui utilizado apenas pela funo Reduce, que se encontra
vazia, visto que opcional.
Caso essa View seja mantida dessa forma, ela simplesmente
buscar por todos os documentos e os ordenar pelo identificador. Alm de no ser o que desejamos, tambm no faz sentido
essa ordenao, visto que geramos os identificadores com UUID
aleatrio no cdigo da Listagem 9. Uma ordenao mais eficaz,
por exemplo, seria pela data de criao.
Deste modo, como desejamos listar todas as tarefas e orden-las
por data de criao, utilizaremos o cdigo mostrado na Listagem 10
como funo Map.
Listagem 10. Funo Map para listar todas tarefas ordenadas por data de criao.
function (doc, meta) {
if(meta.type == json && doc.type == task){
emit(doc.createdAt);
}
}

O funcionamento dessa funo simples. Primeiro, certificase que o documento atual do tipo JSON, lembrando que
ele poderia ser binrio, armazenar Strings, ou outros tipos
mencionados anteriormente. Ento, verificamos se o tipo
do documento, armazenado no campo type, igual a task,
lembrando que esse campo foi criado em nossos mapeadores.
Por fim, emitimos o campo createdAt, que tem como valor
a data de criao em formato ISO. Como no ser utilizada
uma funo de Reduce, o valor passado para a funo emit()
nulo. Em JavaScript, um argumento nulo pode simplesmente
ser ignorado.
Para testar essa View pode-se executar parte do cdigo mostrado na Listagem 9, retirando o trecho que remove os documentos

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

inseridos. Assim, ao clicar em Show Results, ser apresentada uma


tabela com a Key igual data de criao e o Value igual a null. Cada
linha apresenta ainda um link para visualizarmos o documento.
Com nossa primeira View criada e testada, pode-se voltar aba
Development Views, localizar o design document all_tasks e clicar
em Publish. Feito isso, esse passar para a aba de produo com
o nome _design/all_tasks. Observe que o prefixo _design/dev_ foi
alterado automaticamente.
Para demonstrar a utilizao dessa View pelo sistema, foi criada a Listagem 11. Nela, implementamos uma ViewQuery que
ordenar as chaves de forma decrescente, pular os primeiros 0
registros e limitar em 10 os registros retornados, demonstrando
assim o uso de paginao com os mtodos skip() e limit(). Traduzindo essa query, obteremos as dez ltimas tarefas criadas no
sistema, sendo elas ordenadas em ordem decrescente de criao,
ou seja, as mais novas so apresentadas primeiro.
Ainda observando esse cdigo, percebemos que a classe
ViewResult retornada pelo mtodo query() do bucket um
iterator que contm as linhas obtidas como resultado. Cada
uma dessas linhas tem o identificador do documento, a chave e
valor emitidos, alm do documento representado. E assim como
feito antes, o documento convertido em objeto utilizando um
mapeador.
Dessa forma, para obter a categoria de cada tarefa deve-se fazer
uma busca pelo identificador de cada uma. Para que isso se torne
desnecessrio, ser criada outra View que far algo parecido com
o conhecido JOIN do SQL.
O processo de criao dessa View ser o mesmo, mas a chamaremos de tasks_with_categories. O cdigo da funo Map dela
apresentado na Listagem 12.
Listagem 11. Cdigo que demonstra o uso da View all_tasks para listar as ltimas
dez tarefas.
ViewQuery viewQuery = ViewQuery.from(all_tasks, all_tasks)
.descending()
.skip(5)
.limit(10);
ViewResult viewResult = bucket.query(viewQuery);
for(ViewRow row : viewResult){
JsonDocument document = row.document();
task = taskDocumentMapper.getEntity(document);
}
Listagem 12. Funo Map para listar todas tarefas ordenadas por data de criao,
com suas categorias.
function (doc, meta) {
if(meta.type == json){
if(doc.type == category){
emit([meta.id]);
}
if(doc.type == task){
emit([doc.categoryId, doc.createdAt]);
}
}
}

A parte a se notar dessa tcnica a seguinte: chaves tambm podem ser emitidas como arrays, como pode ser visto nas invocaes
de emit(). Quando isso feito, as chaves continuaro sendo ordenadas naturalmente, mas utilizando todos os elementos do array
de forma que o elemento seguinte considerado apenas em caso
de empate na ordenao baseada no elemento anterior. Voltando
View criada, essa emitir todos os documentos de categoria e
tarefa. Os documentos de categoria tero como chave um array de
elemento nico com o identificador de cada documento, enquanto
os documentos de tarefa tero um array com o identificador da
categoria e a data de criao da tarefa como chave.
Quando essas chaves so ordenadas, as categorias e suas tarefas
ficam juntas, visto que suas chaves comeam com o identificador
da categoria. Com essas chaves emitidas, o registro da categoria
aparece antes das tarefas, j que sua chave possui apenas um
elemento, e logo aps a categoria, todas as tarefas relacionadas
so dispostas, ordenadas por sua data de criao.
Com essa ordenao, a responsabilidade do cdigo Java que
utilizar essa View ser de diferenciar os documentos de categoria
dos de tarefa. Assim, ao iterar pelas linhas obtidas como resultado,
sempre que uma categoria for encontrada ela ser guardada em
uma varivel temporria e todas as tarefas subsequentes sero
admitidas como da categoria corrente e tero seu valor de categoria
ajustado com ela. O cdigo com essa lgica implementada pode
ser visualizado na Listagem 13.
Outro uso para essa View seria a busca por todas as tarefas de
uma categoria j conhecida. Isso poderia ser feito com os mtodos startKey() e endKey() de ViewQuery, como mostrado na
Listagem 14. Esses mtodos so usados para especificar um intervalo de chaves dos documentos que sero encontrados. Assim,
uma chave que, em ordenao natural, seja superior startKey()
e inferior endKey() ter seu documento listado.
No entanto, para que a tcnica de listagem das tarefas de determinada categoria funcione, devemos saber que um elemento
vazio de um array toma precedncia sobre qualquer valor,
quando tratamos de ordenao natural. Ento, utiliza-se como chave inicial um array com um nico elemento, a chave da categoria
procurada, e a chave final ser um array cujo primeiro elemento
a chave da categoria e o segundo a String especial \uefff.
Essa String representa um nico caractere UTF-8, que grande o
suficiente para garantir que nenhum valor do segundo elemento
das chaves seja maior em uma ordenao alfabtica. Traduzindo
tudo isso, buscamos por chaves que comecem com o identificador
da categoria e aceitamos qualquer valor como segundo elemento
da chave, desde vazio at um valor qualquer.
Essas tcnicas podem ser combinadas com outras para se buscar
registros atravs de Views. Para isso, no entanto, deve ser feito
um bom planejamento com o intuito de reutilizar essas Views
na medida do possvel, pois uma criao excessiva delas pode
gerar um impacto bastante negativo na performance do banco
de dados.
A outra funo de uma View fazer resumo de dados. Para isso,
recomenda-se o uso da funo Reduce, que no ser apresentada

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

15

15

Por dentro do banco de dados NoSQL Couchbase Parte 2

neste artigo, mas que simples de ser implementada. Na seo


Links esto disponveis guias para um estudo mais aprofundado
sobre Views e isso inclui sua funo Reduce.
Listagem 13. Cdigo utilizado para se obter todas as tarefas com categorias.
ViewQuery viewQuery = ViewQuery.from(tasks_with_categories, tasks_with
_categories);
ViewResult viewResult = bucket.query(viewQuery);
Category currentCategory = null;
List<Task> tasks = new ArrayList<>();
for(ViewRow row : viewResult){
JsonDocument document = row.document();
String docType = (String) document.content().get(DocumentMapper.TYPE);
if(CategoryDocumentMapper.DOC_TYPE.equals(docType)){
currentCategory = categoryDocumentMapper.getEntity(document);
}
else if(TaskDocumentMapper.DOC_TYPE.equals(docType)){
Task currentTask = taskDocumentMapper.getEntity(document);
currentTask.setCategory(currentCategory);
tasks.add(currentTask);
}
}
Listagem 14. Parmetros de busca para se obter tarefas de determinada categoria.
viewQuery.startKey(JsonArray.from(categoryId));
viewQuery.endKey(JsonArray.from(categoryId, \uefff));

Uma boa notcia para aqueles que ficaram receosos com essa
forma de pesquisar dados, em verses mais novas do Couchbase
encontrada uma nova ferramenta: a N1QL. Conhecida como o
SQL para JSON, possui uma linguagem praticamente idntica
ao SQL. Ela no foi abordada neste artigo porque relativamente
nova e vem evoluindo nas ltimas verses do Couchbase.
Para suprir as necessidades de grandes aplicaes como o
Facebook e as solues do Google, diversas opes de bancos de
dados foram surgindo, resultando na criao do acrnimo NoSQL
para defini-las. Dentre elas, bancos como o Apache Cassandra e
BigTable foram criados e por fim abertos ao pblico.
Nessa linha de novos bancos, o primeiro a se destacar com estrutura de dados baseada em documentos foi o MongoDB, o qual
permanece sendo o mais utilizado nos dias de hoje. Porm, com a
fuso do CouchDB com o Membase surgiu um forte concorrente,
que foi apresentado neste artigo: o Couchbase.
Alm de demonstrar melhor performance que a concorrncia
em testes de benchmark, o Couchbase facilita a criao de novos
tipos de aplicao: aquelas que requerem funcionamento offline. Um grande exemplo disso so aplicaes mobile que tm o
contedo fornecido por um servidor, mas que devero trabalhar
frequentemente sem acesso internet. Para essas existe a combinao do Couchbase Server com o Couchbase Lite interligados
pelo Sync Gateway. Pelo lado do servidor, deve-se simplesmente
configurar o Sync Gateway para criar um meio de acesso ao
Couchbase Server. J pelo lado da aplicao mobile, utiliza-se
o Couchbase Lite para armazenar, recuperar e sincronizar os
dados com o servidor. Sua programao muito semelhante

16 Java Magazine Edio 150

16

encontrada neste artigo e os dados so sincronizados sem grande


esforo do desenvolvedor.
Mesmo quando aplicaes mobile no esto em discusso, o
Couchbase prova ser um timo banco de dados para diversas
situaes. Um exemplo muito interessante para ele seriam as
lojas virtuais, afinal, em pocas de grandes promoes, como a
famosa Black Friday, podemos notar que grande parte dessas lojas
acaba no aguentando o fluxo de usurios, chegando a cair ou
apresentar problemas. Com o uso de Couchbase torna-se possvel
um escalonamento horizontal temporrio na camada de banco de
dados. Assim, a loja virtual poderia operar com um nico n em
datas comuns e fazer a adio de ns quando um grande fluxo
de acesso for previsto.
Enfim, no s o Couchbase, mas as solues NoSQL em geral
ampliam o horizonte de possibilidades. E algumas delas foram
apresentadas neste artigo, o qual tambm teve como objetivo ensinar o leitor a utilizar esse banco de dados de grande potencial.

Autor
Fernando Henrique Fernandes de Camargo
fernando.camargo.ti@gmail.com
desenvolvedor Java EE, Android e Grails. Atualmente mestrando em Engenharia de Computao na UFG. Desenvolve em
Java desde 2009 e para Android desde 2012. Possui a certificao OCJP6,
artigos publicados na Easy Java Magazine e na Java Magazine, alm de
palestras e minicursos apresentados em eventos.
Links:
Artigo sobre NoSQL.
www.couchbase.com/nosql-resources/what-is-no-sql
Comparao entre Couchbase e CouchDB.
www.couchbase.com/couchbase-vs-couchdb
Guia de desenvolvimento do Couchbase.
docs.couchbase.com/developer/dev-guide-3.0
Artigo sobre concorrncia otimista e pessimista.
blog.couchbase.com/optimistic-or-pessimistic-locking-which-one-should-you-pick
Instrues de instalao do Couchbase.
docs.couchbase.com/admin/admin/install-intro.html
Guia de desenvolvimento do Java SDK.
docs.couchbase.com/developer/java-2.1/java-intro.html
JAR com SDK do Couchbase.
mvnrepository.com/artifact/com.couchbase.client/java-client/2.1.4

Voc gostou deste artigo?


D seu voto em www.devmedia.com.br/javamagazine/feedback
Ajude-nos a manter a qualidade da revista!

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Programador Java:
Por onde comear?
Descubra nesse vdeo como entrar
na carreira Java com o p direito!

DEVMEDIA
http://www.devmedia.com.br/programador-java-por-onde-comecar/33638
Edio 150 Java Magazine
Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

17

17

Como usar o Apache


Cassandra em
aplicaes Java EE Parte 2
Aprenda neste artigo como implementar e
executar comandos no Cassandra seguindo as
melhores prticas atravs de uma aplicao Java EE
Este artigo faz parte de um curso

Fique por dentro


Veremos neste artigo uma viso prtica da utilizao do Apache
Cassandra seguindo as melhores recomendaes do mercado. Assim,
para quem conhece o Cassandra num nvel apenas terico, poder

aqui colocar a mo na massa e dar os primeiros passos neste que

om a exploso da Internet que vivenciamos atualmente, muitos novos desafios esto surgindo
para a indstria de software, desde a preocupao com a escalabilidade das aplicaes, que agora
possuem milhes de usurios, at mesmo a melhoria
contnua da usabilidade desses sistemas, dado que os
usurios se tornam cada vez mais exigentes e desejam
mais facilidades.
Um desses desafios trata-se do armazenamento e
processamento da imensa massa de dados gerada pelos
diversos servios disponveis. Por conta dela, novas
tecnologias de banco de dados emergiram nos ltimos
anos para atender uma srie de requisitos que o modelo mais tradicional (Relacional) no conseguiu suprir.
A esse novo movimento de tecnologias de bancos de
dados deu-se o nome de NoSQL.
Diante da relevncia do tema, apresentamos na primeira parte desse artigo um dos bancos NoSQL mais
renomados desse ecossistema, o Apache Cassandra. Para

18

um dos bancos de dados NoSQL mais empregados.


Ademais, tudo feito dentro do contexto de uma aplicao Java EE,
demonstrando como essas duas tecnologias podem ser integradas.
A aplicao de exemplo utilizar o driver da DataStax para se comunicar com o Cassandra, WildFly 9, PrimeFaces 5.3, Cassandra 2.2, alm
de outras tecnologias.

isso, foram abordados em detalhes vrios aspectos da arquitetura


do Cassandra, fornecendo ao leitor um embasamento terico fundamental para o aprendizado prtico desse banco de dados. Alm
disso, demos incio implementao de uma aplicao Java EE,
onde detalhamos a preparao do ambiente de desenvolvimento,
a configurao do projeto, a apresentao do driver Cassandra,
uma introduo ao DevCenter e o desenvolvimento inicial da
integrao entre Cassandra e Java EE.
Nesta segunda etapa do artigo, iremos aprofundar a parte prtica
e evoluir a aplicao de modo que ela se torne funcional j com
quase todas as funcionalidades propostas. Ao final o leitor ser

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

capaz de executar comandos no Cassandra via Java, tanto usando


CQL diretamente quanto usando a API object-mapping. O leitor
tambm ir adquirir um entendimento bsico sobre modelagem
de dados no Cassandra, que como dito na primeira parte do artigo,
bem diferente da modelagem relacional.
Para tanto, evoluiremos nossa aplicao adicionando funcionalidades para executar comandos CQL de forma a seguir as
recomendaes do driver Cassandra, bem como demonstraremos
a maneira adequada de utilizar tais funcionalidades. Alm disso,
sero abordados os novos recursos do PrimeFaces para criar pginas responsivas, a API de validao client-side dessa biblioteca,
algumas features do CDI, como o uso de qualifiers e eventos,
alguns recursos do DeltaSpike, entre outros. Todas essas opes
sero utilizadas de forma a facilitar a implementao da aplicao
de exemplo, bem como demonstrar a integrao de todas essas
tecnologias.

Evoluindo o WebShelf
Na primeira parte do artigo iniciamos o desenvolvimento do
WebShelf: uma aplicao que possibilita aos seus usurios manter uma prateleira de livros online ao estilo do Amazon Shelfari.
At o momento, as principais configuraes do projeto j foram
demonstradas e explicadas, o que nos possibilita agora focar mais
nas funcionalidades da aplicao.

Elaborando a pgina responsiva de Cadastro de Usurio com PrimeFaces


Como na primeira parte foi criado o template padro (default
.xhtml) das pginas JSF para a aplicao WebShelf, podemos
agora criar a tela de Cadastro de Usurio. A Listagem 1 traz essa
implementao.
De modo simples, essa pgina define os dois parmetros que o
template precisa atravs da tag ui:param. Em seguida, adiciona
o seu contedo no template atravs da tag ui:define.
Para deixar a tela mais responsiva envolvemos todo o contedo
da pgina numa div que tem a classe ui-fluid do PrimeFaces.
Atravs dessa classe diversos componentes deste framework so
renderizados de forma responsiva.
Ainda pensando na responsividade, utilizamos o componente
p:panelGrid (no confundir com o componente padro do JSF
h:panelGrid) em conjunto com o Grid CSS, ambos do PrimeFaces.
O Grid CSS um layout leve que produz uma interface responsiva
para celulares, tablets e desktops e est presente no PrimeFaces
desde sua verso 5.1, possibilitando ao desenvolvedor dividir a
tela em 12 colunas de mesmo tamanho.
O p:panelGrid, ao ser configurado com o layout grid, ir fazer
uso do Grid CSS para definir o tamanho de cada uma das suas
colunas. Isso configurado atravs do atributo columnClasses,
que define uma classe CSS indicando quantas colunas do Grid
CSS elas devem ocupar (ui-grid-col-1, ui-grid-col-4, ui-gridcol-7) totalizando as 12 colunas que preenchem a tela. No nosso
exemplo, a primeira coluna do p:panelGrid ir ocupar 1 coluna
do Grid CSS (ui-grid-col-1), a segunda coluna ir ocupar 4 colunas do Grid CSS (ui-grid-col-4) e a terceira 7 colunas do Grid
CSS. Caso voc precise fazer um arranjo diferente, interessante
saber que existem 12 classes ui-grid-col-*, indo de ui-grid-col-1
at ui-grid-col-12.

Listagem 1. Tela de cadastro de Usurio (public/insertUser.xhtml).


<?xml version=1.0 encoding=UTF-8 ?>
<!DOCTYPE html PUBLIC -//W3C//DTD XHTML 1.0 Transitional//EN
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd>
<ui:composition xmlns=http://www.w3.org/1999/xhtml
xmlns:ui=http://java.sun.com/jsf/facelets
xmlns:h=http://java.sun.com/jsf/html
xmlns:p=http://primefaces.org/ui
template=/WEB-INF/templates/default.xhtml>
<ui:param name=mainContentTitle value=Cadastro de Usurio />
<ui:param name=renderedMenuBar value=false />
<ui:define name=mainContent>
<h:form prependId=false>
<div class=ui-fluid>
<p:panelGrid columns=3 layout=grid
columnClasses=ui-grid-col-1,ui-grid-col-4,ui-grid-col-7
styleClass=ui-panelgrid-blank>
<p:outputLabel for=userName value=Nome />
<p:inputText id=userName value=#{userController.user.name} />
<p:message for=userName />
<p:outputLabel for=userLogin value=Login />
<p:inputText id=userLogin autocomplete=false
value=#{userController.user.login} />
<p:message for=userLogin />
<p:outputLabel for=userPassword value=Senha />
<p:password id=userPassword autocomplete=false
match=userPasswordConfirmation
value=#{userController.password} />
<p:message for=userPassword />
<p:outputLabel for=userPasswordConfirmation
value=Confirmar Senha />
<p:password id=userPasswordConfirmation autocomplete=false
value=#{userController.password} />
<p:message for=userPasswordConfirmation />
<p:commandButton value=Salvar action=#{userController.insert}
ajax=false validateClient=true />
</p:panelGrid>
</div>
</h:form>
</ui:define>
</ui:composition>

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

19

Como usar o Apache Cassandra em aplicaes Java EE - Parte 2

Para encerrar, temos um p:commandButton para invocar o


mtodo que ir gravar o usurio no banco de dados. Esse boto
ir usar a validao no lado do cliente oferecida pelo PimeFaces
(validateClient=true). Dessa forma, erros simples de validao
como tamanho mximo do campo e campo obrigatrio podem ser
alertados antes de qualquer requisio ser enviada para o servidor,
evitando roundtrips desnecessrios e melhorando a performance
da aplicao. Para que essa feature seja usada, necessrio ativ-la
atravs do parmetro primefaces.CLIENT_SIDE_VALIDATION
no web.xml, como pode ser visto na Listagem 5 da parte 1, publicada na edio anterior.

Desenvolvendo o controller de Usurio


Implementada a pgina web do Cadastro de Usurio, na
Listagem 2 criaremos o controller que ir intermediar a camada
de negcio com a UI.
Como podemos notar, UserController um bean CDI com
escopo View. Esse escopo obtido atravs da classe javax.faces
.view.ViewScoped, presente a partir do JSF 2.2, e no deve ser
confundida com a antiga classe javax.faces.bean.ViewScope,
que tem vrios problemas, como pode ser visto em The benefits
and pitfalls of ViewScoped (veja a seo Links).
A anotao @Named permite que o bean possa ser acessado
por um nome especfico, por exemplo, em telas JSF atravs de
Expression Language. Como no foi especificado nenhum nome
no atributo value, ento o nome do bean passa a ser o nome da
classe com a primeira letra minscula: userController.
J o atributo messages usa a classe JsfMessage do DeltaSpike,
uma biblioteca que fornece diversas extenses para se trabalhar
com Java EE e que nasceu da juno de outros players (JBoss
Seam e Apache CODI). A classe JsfMessage fornece uma maneira
elegante e simples de adicionar mensagens JSF no contexto atual.
Para isso, basta criar uma interface como na Listagem 3.

Listagem 2. Cdigo do controller de usurio (webshelf-web).


package br.com.devmedia.webshelf.controller;
import java.io.Serializable;
import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;
import org.apache.deltaspike.jsf.api.message.JsfMessage;
import org.hibernate.validator.constraints.NotBlank;
import br.com.devmedia.webshelf.model.User;
import br.com.devmedia.webshelf.service.UserBean;
import br.com.devmedia.webshelf.util.Messages;
@Named
@ViewScoped
public class UserController implements Serializable {
private static final long serialVersionUID = 1L;
@Inject
private UserBean userBean;

@Inject
private JsfMessage<Messages> messages;
private User user = new User();
@NotBlank(message=Senha: no pode est em branco.)
private String password;
public User getUser() {
return user;
k! }
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}

Preparando CassandraCluster para suportar object-mapping


Antes de implementar a lgica de negcio, ser necessrio
adicionar alguns mtodos na classe CassandraCluster para que
possamos, de fato, executar comandos no Cassandra, visto que
anteriormente havamos definido apenas alguns atributos. O primeiro deles o mtodo mapper(), apresentado a seguir. Sua funo
ser criar uma instncia da classe Mapper que ir possibilitar o
uso da API object-mapping do driver DataStax.

public String insert() {


user.setClearPassword(password);
this.userBean.insertUser(this.user);
messages.addInfo().insertUserSuccess();
return login.xhtml?faces-redirect=true;
}
}
Listagem 3. Classe de mensagens (webshelf-business).
package br.com.devmedia.webshelf.util;
import org.apache.deltaspike.core.api.message.MessageBundle;
import org.apache.deltaspike.core.api.message.MessageTemplate;

public <E> Mapper<E> mapper(Class<E> entityClazz){


return mappingManager.mapper(entityClazz);
}

@MessageBundle
public interface Messages {

Este mtodo simplesmente delega sua execuo para o mtodo


MappingManager.mapper() e como parmetro recebe qualquer
classe anotada com @Table, como o caso de User.
O seu retorno uma instncia da classe Mapper parametrizada
com o mesmo tipo da classe do parmetro entityClazz, e dessa
forma, ir prover operaes como busca, insero e deleo de

20 Java Magazine Edio 150

20

@MessageTemplate(Agora s informar os dados de login e iniciar a sua


prateleira de livros online!)
String insertUserSuccess();

@MessageTemplate(Login/Senha invlido.)
String invalidCredentials();
}

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

registros na tabela correspondente da entidade sem que seja


necessrio escrever o CQL diretamente.

Implementando o cache de PreparedStatements em CassandraCluster


Como dito na seo Regras de utilizao do driver DataStax,
se voc perceber que ir executar um statement de forma repetida,
recomenda-se que essas instrues sejam feitas atravs de PreparedStatement. Alm disso, as instncias dessa classe precisam ser
mantidas num cache para evitar que o mesmo CQL seja preparado
mais de uma vez, o que pode gerar problemas de performance.
Para implementar tais recomendaes utilizamos no nosso
exemplo um Map de PreparedStatements, onde o Map ser instanciado uma nica vez dentro da classe CassandraCluster e com
isso se encarregar de fazer o cache desses objetos (vide BOX 1).
A ideia tornar a prpria String que representa o comando CQL
(passada como parmetro) a chave do Map. Dessa forma, para cada
instruo CQL passada nesse mtodo haver apenas um PreparedStatement. Essa estratgia pode ser vista atravs do mtodo
CassandraCluster.prepare(), apresentado na Listagem 4.
Listagem 4. Cache de PreparedStatement em CassandraCluster.
private BoundStatement prepare(String cql){
if(!preparedStatementCache.containsKey(cql)){
preparedStatementCache.put(cql, session.prepare(cql));
}

return preparedStatementCache.get(cql).bind();
}

O ganho com PreparedStatements verificado quando uma instruo executada repetidas vezes, pois com esse tipo de statement
o parse acontece uma nica vez em cada n que for execut-lo.
Nas execues subsequentes, apenas o ID do statement e os valores dos parmetros so enviados pela rede. Considerando isso
em um ambiente distribudo com vrios ns, uma melhora de
performance significativa pode ser obtida.
Por fim, saiba que o mtodo prepare() sempre retornar uma nova
instncia de BoundStatement, a qual ser utilizada pelos clientes
para fazer o bind dos parmetros contidos na instruo CQL.

Listagem 5. Mtodo para execuo de CQL em CassandraCluster.


@Lock(LockType.READ)
public ResultSet execute(Statement stmt){
return session.execute(stmt);
}

BOX 1. Caches
Como j demonstrado na implementao de CassandraCluster, a utilizao de caches ao se
trabalhar com Cassandra de fundamental importncia. Portanto, vale a pena estudar mais a
fundo as estratgias de cache a fim de identificar a que melhor se adequa sua realidade. Apesar
de ser possvel utilizar maps para esse intuito, como fizemos aqui, esta no a nica maneira e,
principalmente, no a melhor abordagem para grandes aplicaes. Por exemplo, voc pode
precisar de um cache que expira itens (mais antigos, menos utilizados, etc.) ou caso contrrio ir
acumular uma grande quantidade de objetos e poder ter problemas de estouro de memria.

A parte interessante nesse mtodo a presena da anotao


@Lock. Essa anotao faz parte da especificao EJB e deve ser
usada em conjunto com EJBs singleton, como o caso de CassandraCluster. Um EJB singleton, por padro, no permite que
mais de uma thread invoque um mtodo de negcio. Nesse caso,
entenda-se mtodo de negcio como qualquer mtodo de classe
pblico, como o caso de execute().
Assim, um cliente ter que esperar o outro terminar para que
ento seu pedido seja atendido. Por exemplo, suponha que a
thread A chamou execute() passando um statement que vai
demorar cinco minutos para finalizar sua execuo. Quando
a chamada de A estava com 1 minuto de execuo, a thread B
tambm invocou execute() com outro statement, que nesse caso
ir levar apenas 1 segundo para executar. No entanto, como A
iniciou a execuo primeiro, a thread B s ser atendida quando
A terminar, ou seja, o comando de B que deveria levar apenas 1
segundo ir levar 4 minutos (tempo restante para completar a
execuo de A) e 1 segundo.

Adicionando mtodo para execuo de comandos no CassandraCluster


Para que a classe CassandraCluster esteja com todas as principais funcionalidades disponveis, agora falta incluir o mtodo
execute(), apresentado na Listagem 5. Como o prprio nome j
diz, esse mtodo ser o responsvel por executar instrues CQL
no Cassandra.
Apesar desse cdigo ser pequeno interessante prestar bem
ateno para entender o que acontece. Basicamente, ele recebe um
Statement e delega a chamada para o mtodo Session.execute()
que, por sua vez, ir executar o comando no Cassandra.
Ao finalizar a execuo, o mtodo retorna um ResultSet que
pode ser utilizado para obter os dados de uma consulta (no caso
do comando ser uma consulta).

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

21

Como usar o Apache Cassandra em aplicaes Java EE - Parte 2

Para que isso no acontea, utilizamos @Lock(LockType.READ)


para sinalizar ao container EJB que esse mtodo, especificamente, no deve funcionar como da forma descrita anteriormente.
Assim, no haver lock no mtodo execute() e qualquer cliente
que invoc-lo ser atendido de imediato.
Nesse momento os leitores mais experientes podem se perguntar:
Mais o que acontece quando diversas threads chamam esse mtodo simultaneamente? No h perigo de uma chamada atrapalhar
a outra?. A resposta que no h problema algum. Como dito
na primeira parte deste artigo, a classe Session thread-safe e,
portanto, pode ser utilizada num ambiente multithreading sem
qualquer perigo.
Outro mtodo que poderia sofrer esse mesmo problema seria o
prepare(). Isso porque ele tambm pode ter execues que vo levar
um tempo considervel e, por isso, ficar aguardando sua finalizao
geraria um grande gargalo. No nosso cenrio isso no ir acontecer
porque esse mtodo privado e, sendo assim, no sofre o lock do
container, pois no considerado um mtodo de negcio. No entanto, se sua aplicao precisar expor esse mtodo, teria tambm
que remover o lock do container EJB. Caso voc necessite usar o
controle de lock constantemente, isso pode indicar que preciso
repensar a implementao, como explicado no BOX 2.
BOX 2. Exposio de Session
Assim como os mtodos execute() e prepare(), caso outros mtodos da classe Session precisem
ser expostos em CassandraCluster, uma outra estratgia seria criar uma classe encapsuladora;
CassandraSession, por exemplo. Essa nova classe teria uma instncia de Session encapsulada e s
seriam expostos os mtodos que fossem convenientes, evitando-se expor mtodos de configurao
da classe Session para toda a aplicao. Na classe CassandraCluster, ento, seria criado um mtodo
getCassandraSession() que retornaria esse novo objeto. Dessa forma, a classe Session se manteria
privada a um nico ponto da aplicao, bem como a preocupao com locks seria eliminada,
j que os mtodos de longa durao seriam chamados fora do contexto EJB: cassandraCluster.
getCassandraSession().execute().

verificado na Listagem 7. O sufixo Bean refere-se conveno


de nomes de EJB.
Listagem 6. CQL para inserir usurio (CassandraCluster).
public BoundStatement boundInsertUser(){
return prepare(INSERT INTO webshelf.user(login,name,password)
VALUES (?,?,?) IF NOT EXISTS;);
}
Listagem 7. Cdigo do bean de usurio (webshelf-business).
package br.com.devmedia.webshelf.service;
import java.util.Set;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.inject.Inject;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
import com.datastax.driver.core.BoundStatement;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.mapping.Mapper;
import br.com.devmedia.webshelf.data.CassandraCluster;
import br.com.devmedia.webshelf.exception.BusinessRuleException;
import br.com.devmedia.webshelf.model.User;
@Stateless
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class UserBean {
@Inject
private CassandraCluster cassandra;
@Inject
private Validator validator;
public User findUserByLogin(String login) {
Mapper<User> mapper = cassandra.mapper(User.class);
return mapper.get(login);
}
public void deleteUser(User user) {
Mapper<User> mapper = cassandra.mapper(User.class);
mapper.delete(user);
}

Criando o CQL para inserir Usurio


Agora que o mtodo prepare() est implementado, pode-se
criar algumas instrues, como a de insero de um usurio,
demonstrada na Listagem 6. O intuito desse mtodo disponibilizar para o cliente uma instncia de BoundStatement com o
comando necessrio para inserir um usurio no Cassandra. De
posse do statement o desenvolvedor poder setar os valores dos
parmetros (identificados pelo caractere ?) e ento fazer a execuo do comando.
Note que a sintaxe do CQL nesse exemplo bem semelhante do
SQL. A principal diferena est no uso da instruo IF NOT EXISTS,
que ser melhor explicada na seo Usando Lightweight Transactions (LWT) para garantir a unicidade, logo mais frente.

public void insertUser(User user) {


executeBeanValidation(user);

BoundStatement insertUser = cassandra.boundInsertUser();
insertUser.bind(user.getLogin(), user.getName(), user.getPassword());

ResultSet result = cassandra.execute(insertUser);
if (!result.wasApplied()) {
throw new BusinessRuleException(Login j existente.);
}
}
private void executeBeanValidation(User user) {
Set<ConstraintViolation<User>> constraintViolations = validator.validate(user);

Enfim, o bean de Usurio


Feitas as melhorias em CasandraCluster, agora possvel
criar o bean da entidade usurio, UserBean. Trata-se de um EJB
Stateless que usa CDI para injetar algumas dependncias, como

22 Java Magazine Edio 150

22

if (!constraintViolations.isEmpty()) {
throw new ConstraintViolationException(constraintViolations);
}
}
}

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Como pode ser visto no mtodo executeBeanValidation(), nesta classe injetamos um Validator para
invocar as validaes da API Bean Validation. Alm
disso, injetado um CassandraCluster para realizar
a comunicao com o Cassandra.
Ainda analisando este cdigo, o mtodo findUserByLogin() obtm um Mapper<User> e ento
faz a consulta pelo login, que nesse caso tambm
a primary key da tabela. Essa consulta realizada
atravs do mtodo Mapper.get(), que aceita uma
lista de parmetros correspondente primary key da
tabela na ordem declarada na sua criao. O retorno
um objeto com os campos devidamente preenchidos Figura 1. Exemplo de race condition Fonte: FinishJUG
graas ao mapeamento feito na classe User. Operao
semelhante acontece no mtodo deleteUser(), que tambm faz uso
inicial da aplicao, caso contrrio, uma mensagem de erro dever
de Mapper para remover o registro.
ser mostrada. Para quem ainda no tem cadastro, ser disponibilizado um boto que levar o usurio para a tela de Cadastro
Usando Lightweight Transactions (LWT) para garantir a unicidade
de Usurio.
Por fim, tem-se o mtodo insertUser(), o qual cadastra o usurio
no Cassandra. Apesar da classe Mapper possuir um mtodo save()
Elaborando pgina responsiva de login com PrimeFaces
que poderia ser usado aqui, foi necessrio criar um statement
A tela de login tem quatro componentes principais, conforme
manualmente atravs do mtodo CassandraCluster.boundInpode ser visto na Listagem 8: um input de login, outro da senha,
sertUser() para que fosse possvel usar lightweight transactions, j
um boto para efetuar o login e outro para se cadastrar.
que o Mapper no oferece (ainda) essa possibilidade.
Observe que o parmetro renderedMenuBar, logo no incio do
Como j informado, no WebShelf o login do usurio nico. Para
cdigo, foi setado para false, j que no deve ser exibido nenhum
garantir essa regra em bancos de dados relacionais, normalmente
menu enquanto o usurio no se logar.
usamos uma transao, na qual executada uma consulta para
Com o intuito de deixar a tela responsiva, utilizamos os mesmos
verificar a existncia do login. Caso ele no exista, executado o
recursos apresentados na Listagem 1. Assim, todos os compocomando de insert e finalmente feito o commit da operao.
nentes visuais da pgina foram englobados por uma div que
No Cassandra, por sua vez, no possvel proceder dessa forma
tem a classe ui-fluid e os inputs foram organizados dentro de
devido ausncia de transaes ACID. Desse modo, se voc tentar
componentes p:panelGrid configurados para usar o Grid CSS
fazer isso, poder cair numa race condition, como exemplificado
(layout=grid). Alm disso, os p:panelGrid definiram o tamanho
na Figura 1. Para contornar esse problema foi criado o conceito de
de suas colunas em funo das 12 colunas que o Grid CSS usa para
LWT, que no caso da insero do usurio se caracteriza pelo uso
dividir a tela responsivamente (ui-grid-col-*).
da seguinte condio ao fim do insert: IF NOT EXISTS. Ou seja,
ao invs de fazer uma consulta para verificar se o login existe ou
no, no ato do insert j ser feito essa checagem sem que outras
requisies interfiram na operao.
A execuo dessa instruo retorna um ResultSet a partir do
qual possvel checar se o insert foi aplicado ou no atravs do
mtodo wasApplied(). Caso este retorne false, significa que o
login ja existe.
Contudo, como dito na primeira parte deste tutorial, essa feature deve ser usada com moderao, pois impacta fortemente
na performance. Nesse exemplo, optou-se por fazer uso de LWT
porque a regra de unicidade de usurio considerada crtica para
a aplicao.

Implementao do Login
Como em quase todas as aplicaes web, tambm iremos desenvolver um mecanismo de login. O funcionamento deste ser
simples: o usurio ter que informar o seu login e sua senha e,
caso tudo esteja correto, dever ser redirecionado para a pgina

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

23

Como usar o Apache Cassandra em aplicaes Java EE - Parte 2

Uma coisa a observar que nessa pgina temos dois p:panelGrid.


Isso foi necessrio porque, para gerar uma melhor visualizao da
tela, as configuraes dos tamanhos das colunas so diferentes
para a rea dos campos texto (Login e Senha) e a rea dos botes
(Entrar e Cadastrar-se). Enquanto a primeira rea usa os tamanhos 1,
4 e 7 (columnClasses=ui-grid-col-1,ui-grid-col-4,ui-grid-col-7)
para suas trs colunas, a segunda rea usa os tamanhos 1, 2 e 2
(columnClasses=ui-grid-col-1,ui-grid-col-2,ui-grid-col-2).
Ainda com relao aos botes, o primeiro (Entrar) ser utilizado
para fazer o login e o segundo (Cadastrar-se) servir para redirecionar o usurio para a tela de cadastro de usurio, caso o mesmo
ainda no tenha se registrado no site.
As Figuras 2 e 3 demonstram o comportamento responsivo da
tela de login.

Figura 2. Tela de login visualizada em um computador

Desenvolvendo controller de login


Aps criarmos o XHTML do login, podemos desenvolver a classe que efetuar as principais aes dessa tela: LoginController.
Basicamente, essa classe ter a responsabilidade de executar as
funcionalidades de login e logout da aplicao e pode ser visualizada na Listagem 9.
Listagem 8. Cdigo da tela de login (public/login.xhtml).
<ui:composition xmlns=http://www.w3.org/1999/xhtml
xmlns:ui=http://java.sun.com/jsf/facelets
xmlns:f=http://java.sun.com/jsf/core
xmlns:h=http://java.sun.com/jsf/html
xmlns:p=http://primefaces.org/ui
template=/WEB-INF/templates/default.xhtml>
<ui:param name=mainContentTitle value=WebShelf />
<ui:param name=renderedMenuBar value=false />
<ui:define name=mainContent>
<h:form prependId=false>
<div class=ui-fluid>
<p:panelGrid columns=3 layout=grid
columnClasses=ui-grid-col-1,ui-grid-col-4,ui-grid-col-7
styleClass=ui-panelgrid-blank>

<p:outputLabel for=userLogin value=Login />
<p:inputText id=userLogin autocomplete=false
value=#{loginController.login} />
<p:message for=userLogin />
<p:outputLabel for=userPassword value=Senha />
<p:password id=userPassword autocomplete=false
value=#{loginController.password} />
<p:message for=userPassword />
</p:panelGrid>
<p:panelGrid columns=3 layout=grid
columnClasses=ui-grid-col-1,ui-grid-col-2,ui-grid-col-2
styleClass=ui-panelgrid-blank>
<h:outputText />
<p:commandButton value=Entrar
action=#{loginController.doLogin()} ajax=false
validateClient=true />
<p:button value=Cadastrar-se outcome=insertUser />
</p:panelGrid>
</div>
</h:form>
</ui:define>
</ui:composition>

24 Java Magazine Edio 150

24

Figura 3. Tela de login visualizada em um celular


Visto que no precisar guardar nenhum estado entre requests, esta classe um bean CDI com escopo de requisio
(@RequestScoped). E como ela ser acessada nos XHTMLs
(login.xhtml e default.xhtml) atravs de Expression Language, tambm foi anotada com @Named.
Dentre os atributos da classe, os campos de login e senha (utilizados na pgina de login) so devidamente validados com o
auxlio da API Bean Validation atravs da anotao @NotBlank.
Dessa forma, ambos so obrigatrios. Alm desses dois, existem
mais trs campos na classe, todos injetados via CDI:
HttpServletRequest: utilizado para obter acesso a HttpSession;
JsfMessage: serve para adicionar mensagens ao contexto JSF; e
UserBean: utilizado para consultar o usurio.
A principal funcionalidade provida por LoginController o
login do usurio, que realizado atravs do mtodo doLogin().
Este faz a pesquisa do usurio pelo seu login e caso o mesmo
exista e sua senha seja igual senha informada, ento considerado um login vlido e assim o objeto usurio armazenado na
sesso. Caso no exista o usurio ou sua senha seja diferente do
inputado, uma mensagem de erro retornada.
Por fim, o mtodo logout() simplesmente remove o usurio da
sesso e invalida-a, bem como redireciona o usurio para a tela
de login.

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Listagem 9. Controller do login (webshelf-web)


package br.com.devmedia.webshelf.controller;

oportuno salientar que a maneira utilizada aqui para fazer o


controle de login do sistema muito simples e, portanto, deve ser
evitada em produo, conforme comentado no BOX 3.

// imports omitidos...
@Named
@RequestScoped
public class LoginController implements Serializable {
private static final long serialVersionUID = 1L;

@Inject
private HttpServletRequest request;

@Inject
private UserBean userBean;
@Inject
private JsfMessage<Messages> messages;

@NotBlank(message=Senha: no pode est em branco.)
private String password;
@NotBlank(message=Login: no pode est em branco.)
private String login;
public String getPassword() {
return this.password;
}
public String getLogin() {
return this.login;
}

BOX 3. Cassandra vs JAAS


Diferentemente de bancos de dados relacionais, no existe uma forma out-of-box para implementar
um secutiry-domain no WildFly com o Cassandra. Por isso, nessa aplicao de exemplo utilizamos
um mecanismo prprio e bem simples de controle de acesso, ao invs de usar o JAAS (Java
Authentication and Authorization Service). No entanto, num ambiente de produo recomenda-se
investir tempo para criar um mdulo de login que possa utilizar o Cassandra em conjunto com o
JAAS ou a utilizao de algum framework de segurana como o Apache Shiro.

Disponibilizando o usurio logado como bean CDI


Obter o usurio logado uma das tarefas mais comuns a qualquer
aplicao web. Para isso, iremos criar a classe ResourceProducer,
que far uso das facilidades do CDI a fim de tornar essa tarefa
mais simples dentro do projeto WebShelf. Essa implementao
mostrada na Listagem 10.
Na classe ResourceProducer temos apenas um mtodo: getLoggedInUser(). Este produz beans CDI (@Produces) com escopo
de sesso (@SessionScoped) e que so acessveis via Expression
Language (@Named) atravs do nome loggedInUser. A outra
anotao (@LoggedInUser) um qualifier CDI para denotar que
o bean aqui produzido equivale ao usurio logado. Sua implementao e explicao sero apresentadas com a Listagem 11.

public void setPassword(String senha) {


this.password = senha;
}

Listagem 10. Cdigo da classe ResourceProducer (webshelf-web)

public void setLogin(String usuario) {


this.login = usuario;
}

import javax.enterprise.context.SessionScoped;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.http.HttpSession;

public String doLogin() {


User user = userBean.findUserByLogin(login);
String encryptedPassword = User.encryptPassword(password);

if(user==null || !user.getPassword().equals(encryptedPassword)){
messages.addWarn().invalidCredentials();
return null;
}

package br.com.devmedia.webshelf.util;

import br.com.devmedia.webshelf.model.User;
public class ResourceProducer {
@Inject
private HttpSession session;

@Produces
@LoggedInUser
@SessionScoped
@Named(loggedInUser)
protected User getLoggedInUser() {
User loggedInUser = (User)session.getAttribute(loggedInUser);

if(request.getSession(Boolean.FALSE) != null){
request.getSession(Boolean.FALSE).invalidate();
}

request.getSession().setAttribute(loggedInUser, user);
return /private/home.xhtml?faces-redirect=true;

if (loggedInUser == null) {
loggedInUser = new User();
}

}
public String logout() throws ServletException {
request.getSession().removeAttribute(loggedInUser);
request.getSession().invalidate();
return /public/login.xhtml?faces-redirect=true;
}
}

return loggedInUser;
}
}

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

25

25

Como usar o Apache Cassandra em aplicaes Java EE - Parte 2

Como podemos observar, a implementao do mtodo getLoggedInUser() bem simples. Ele retorna o usurio logado que
est na sesso caso exista um. Vale lembrar que o usurio logado
colocado na sesso atravs do mtodo LoginController.doLogin(). Se no houver nenhum usurio logado, ento ele devolve
um objeto User vazio. Isso porque no permitido retornar
nulo em mtodos produtores no CDI.
Listagem 11. Qualifier CDI para Usurio logado
package br.com.devmedia.webshelf.util;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

A questo que fica : como informar ao CDI que desejamos


obter o objeto gerado pela classe ResourceProducer, ao invs do
objeto gerado pelo construtor default? nesse contexto que entra
o qualifier. atravs dele que o CDI saber qual dos produtores
usar. Veja o exemplo na Listagem 12.
Nesse caso, como o atributo loggedInUser est anotado com
@LoggedInUser, ele ser produzido pelo mtodo ResourceProducer.getLoggedInUser(), visto que somente este mtodo produz
usurios com tal qualificador. J o atributo dummyUser, por no
especificar nenhum qualifier, ser gerado pelo produtor padro, que
no caso o construtor default da prpria classe (new User()).
Listagem 12. Exemplo de uso de um Qualifier CDI
@Inject
@LoggedInUser
private User loggedInUser;

import javax.inject.Qualifier;
@Qualifier
@Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface LoggedInUser {

}

A anotao @LoggedInUser um qualifier CDI (@Qualifier)


usado para produzir/injetar objetos do tipo User que representam
um usurio logado. Um qualifier nada mais do que um meio de
se fornecer vrias implementaes para o mesmo tipo de bean.
Por padro, toda classe produzida pelo CDI atravs de seu
construtor sem argumentos, no entanto, possvel instru-lo a criar
o bean de outras maneiras. No caso de User, criamos o mtodo
ResourceProducer.getLoggedInUser(), que ir produzir beans
que representam um usurio logado, e para denotar isso, este
mtodo foi anotado com @LoggedInUser. Dessa forma, quando
uma classe for injetar um objeto User, o CDI ter agora duas opes
para gerar o objeto: a opo default (construtor sem argumento) e
o mtodo ResourceProducer.getLoggedInUser().

@Inject
private User dummyUser;

Protegendo pginas privadas


Agora que j temos como obter e saber se existe um usurio
logado na aplicao, vamos criar um mecanismo de segurana
para bloquear o acesso a pginas privadas de usurios no autenticados. Para isso, iremos utilizar um evento do CDI atravs da
classe SecurityObserver, apresentada na Listagem 13.
Nesta classe, o mtodo checkAfterRestoreView() ser notificado
pelo CDI todas as vezes que ocorrer o evento After Restore View
do JSF. Isso acontece porque este mtodo possui um parmetro
anotado com @Observes que utilizado para indicar ao CDI que o
mtodo precisa ser avisado quando o evento observado ocorrer.
O evento definido com a prxima anotao; nesse caso,
@AfterPhase(JsfPhaseId.RESTORE_VIEW). Essa anotao
fornecida pelo mdulo JSF da biblioteca DeltaSpike e possibilita
aplicao monitorar o ciclo de vida JSF como eventos do CDI,
dispensando assim o uso de PhaseListeners. Para conseguir
capturar os eventos, essa anotao deve ser utilizada em objetos
do tipo PhaseEvent.
Alm de notificar o evento, o CDI ainda injetar os trs parmetros
do mtodo: PhaseEvent, HttpServletRequest e tambm o usurio
logado (@LoggedInUser). Note que o PhaseEvent nem utilizado,
mas necessrio para que se possa usar @AfterPhase.
Baseado nessas informaes recebidas o mtodo checkAfterRestoreView() no permitir que usurios logados acessem a
tela de login e os redirecionar para a pgina home da aplicao
(redirectLoggedinUserToHome()). Da mesma forma, tambm no
permitir que usurios no logados acessem pginas privadas,
redirecionando-os para a tela de login (redirectAnonymousToLogin()).

Cadastro de Livro
O cadastro de livro permitir ao usurio do WebShelf cadastrar
novos livros que, por ventura, ele no tenha conseguido encontrar

26

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

na pesquisa. Essa funcionalidade seguir o mesmo molde do cadastro de usurio, que composto de uma tela JSF, um controller
e um bean de negcio.

Nota
Atualmente a API de object-mapping bastante limitada, e por conta disso seu uso ser restrito a
cenrios mais simples, normalmente CRUDs sem qualquer feature mais complexa do Cassandra.

Listagem 13. Classe de segurana para pginas privadas (webshelf-web).


Listagem 14. Cdigo da classe Book (webshelf-business).
package br.com.devmedia.webshelf.security;
import javax.enterprise.event.Observes;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.servlet.http.HttpServletRequest;
import org.apache.deltaspike.jsf.api.listener.phase.AfterPhase;
import org.apache.deltaspike.jsf.api.listener.phase.JsfPhaseId;

package br.com.devmedia.webshelf.model;
import java.nio.ByteBuffer;
import javax.validation.constraints.NotNull;
import org.hibernate.validator.constraints.NotBlank;
public class Book {

import br.com.devmedia.webshelf.model.User;
import br.com.devmedia.webshelf.util.LoggedInUser;

@NotNull(message = ISBN: no pode estar em branco.)


private Long isbn;

public class SecurityObserver {


private static final String URL_PATTERN_PRIVATE_PAGES = /private/;
private static final String LOGIN_PAGE = /public/login.xhtml;
private static final String HOME_PAGE = /private/home.xhtml;
protected void checkAfterRestoreView(@Observes @AfterPhase
(JsfPhaseId.RESTORE_VIEW) PhaseEvent event,
HttpServletRequest request, @LoggedInUser User user) {
this.redirectLoggedinUserToHome(user, request);
this.redirectAnonymousToLogin(user, request);
}

@NotBlank(message = Ttulo: no pode estar em branco.)


private String title;
@NotBlank(message = Autor: no pode estar em branco.)
private String author;
private String country;
private String publisher;

@NotNull(message = Imagem: no pode estar vazio.)
private byte[] image;

private void redirectLoggedinUserToHome(User user, HttpServletRequest request) {


if (request.getRequestURI().contains(LOGIN_PAGE)) {
if (user.getLogin()!=null) {
handleNavigation(HOME_PAGE);
}
}
}

//getters and setters


public ByteBuffer getImageBuffer() {
return ByteBuffer.wrap(image);
}

private void redirectAnonymousToLogin(User user, HttpServletRequest request) {


if (request.getRequestURI().contains(URL_PATTERN_PRIVATE_PAGES)) {
if (user.getLogin()==null) {
handleNavigation(LOGIN_PAGE);
}
}
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((isbn == null) ? 0 : isbn.hashCode());
return result;
}

private void handleNavigation(String page) {


FacesContext context = FacesContext.getCurrentInstance();
context.getApplication().getNavigationHandler().handleNavigation
(context, null, page + ?faces-redirect=true);
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Book other = (Book) obj;
if (isbn == null) {
if (other.isbn != null)
return false;
} else if (!isbn.equals(other.isbn))
return false;
return true;
}

Criando a classe modelo de Livro


A primeira etapa a seguir criar a classe de domnio Book, que
ser composta dos seguintes campos: ISBN(nico), ttulo, autor,
pas, editora e uma imagem. A implementao pode ser vista na
Listagem 14. Diferentemente de User, esta classe no utilizar a
API de object-mapping, pois trata-se de um cenrio mais complexo,
onde ser necessrio criar mais de uma tabela na base de dados
para representar a mesma entidade. Assim, para no termos que
gerar vrias classes de livro (uma para cada tabela), preferiu-se
no usar essa feature do driver Cassandra.

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

27

27

Como usar o Apache Cassandra em aplicaes Java EE - Parte 2

Por se tratar de um POJO, Book uma classe simples que contm


basicamente seus campos e os respectivos mtodos de acesso.
A mesma tambm faz uso de Bean Validation para assegurar as
validaes bsicas sob os seus atributos.
Um importante aspecto nessa classe a implementao da
dupla equals() e hashCode(). Assim, a classe poder ser utilizada em conjunto com a API Collections de forma mais segura e
consistente. Como pode ser visto, um Book considerado igual
a outro se os seus ISBNs forem iguais. Isso importante porque,
por conta da desnormalizao do Cassandra, essa unicidade do
ISBN no poder ser garantida via banco de dados, como veremos
logo mais.
Outra parte a se prestar ateno o mtodo getImageBuffer().
O intuito desse mtodo converter o campo byte[] que representa
uma imagem em um objeto do tipo ByteBuffer. A razo disso
que o driver do Cassandra utiliza esta ltima classe para mapear
atributos Java com suas colunas do tipo blob, justamente o tipo
da coluna imagem nas tabelas de livro.

Implementando a pgina responsiva de Cadastro de Livro com PrimeFaces


A Listagem 15 apresenta o cdigo da tela de Cadastro de Livro.
De forma simples, essa pgina contm os inputs necessrios
para preencher todos os campos da classe Book, e assim como
as demais, tambm foi implementada de maneira responsiva,
utilizando os mesmos recursos do PrimeFaces. Portanto, no ter
seu cdigo detalhado.

Modelando o cadastro de livros no Cassandra


Antes de pensarmos na modelagem do cadastro de livros, importante contextualizarmos outra funcionalidade que ir impactar
diretamente na modelagem: a pesquisa de livros. Essa pesquisa
ser um dos principais recursos do WebShelf e atravs dele que
ser possvel consultar livros por ISBN, Ttulo e Autor.
A utilizao desses trs campos do cadastro de livros para
realizar buscas demonstra claramente trs padres de consulta.
Como explicado na seo sobre modelagem de dados no Cassandra, na primeira parte do artigo, uma das melhores prticas
modelar suas tabelas com base nas consultas que precisaro ser
realizadas.

Listagem 15. Tela de Cadastro de Livro (private/insertBook.xhtml).


<?xml version=1.0 encoding=UTF-8 ?>
<!DOCTYPE html PUBLIC -//W3C//DTD XHTML 1.0 Transitional//EN
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd>
<ui:composition xmlns=http://www.w3.org/1999/xhtml
xmlns:ui=http://java.sun.com/jsf/facelets
xmlns:h=http://java.sun.com/jsf/html
xmlns:p=http://primefaces.org/ui
template=/WEB-INF/templates/default.xhtml>
<ui:param name=mainContentTitle value=Cadastro de Livro />
<ui:param name=renderedMenuBar value=true />
<ui:define name=mainContent>
<h:form prependId=false enctype=multipart/form-data>
<div class=ui-fluid>
<p:panelGrid columns=3 layout=grid
columnClasses=ui-grid-col-1,ui-grid-col-4,ui-grid-col-7
styleClass=ui-panelgrid-blank>
<p:outputLabel for=bookTitle value=Ttulo: />
<p:inputText id=bookTitle value=#{bookController.book.title} />
<p:message for=bookTitle />
<p:outputLabel for=bookAuthor value=Autor: />
<p:inputText id=bookAuthor value=#{bookController.book.author} />
<p:message for=bookAuthor />
<p:outputLabel for=bookPublisher value=Editora: />
<p:inputText id=bookPublisher
value=#{bookController.book.publisher} />
<p:message for=bookPublisher />
<p:outputLabel for=bookCountry value=Pas: />
<p:inputText id=bookCountry
value=#{bookController.book.country} />
<p:message for=bookCountry />
<p:outputLabel for=bookIsbn value=ISBN: />
<p:inputText id=bookIsbn value=#{bookController.book.isbn} />
<p:message for=bookIsbn />
</p:panelGrid>
<br />
<p:panelGrid columns=1 layout=grid
columnClasses=ui-grid-col-1
styleClass=ui-panelgrid-blank>
<p:fileUpload id=bookImage label=Imagem auto=true
allowTypes=/(\.|\/)(jpg|jpeg|png)$/ sizeLimit=20480
value=#{bookController.image}
invalidFileMessage=Tipo de arquivo invlido.
invalidSizeMessage=Tamanho de arquivo no permitido.
fileUploadListener=#{bookController.uploadImage} process=@this
update=@this bookImageName>
</p:fileUpload>
<h:outputText id=bookImageName
value=#{bookController.image == null ? : bookController.image.
fileName} />
<h:outputText />
<p:commandButton value=Salvar action=#{bookController.insert}
ajax=false validateClient=true />
</p:panelGrid>
</div>
</h:form>
</ui:define>
</ui:composition>

28

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Essa abordagem normalmente resulta na criao de uma tabela


para cada padro de consulta. Isso significa que para atender
os requisitos da pesquisa de livros vamos criar trs tabelas:
book_by_isbn, book_by_title e book_by_author, todas elas com os
mesmos dados. A diferena ser a constituio de sua primary
key, mais especificamente, a partition key, como pode ser notado
na Listagem 16.
Essa modelagem o principal motivo para no usarmos objectmapping nas funcionalidades relacionadas ao cadastro de livros, j
que a definio da tabela atravs de @Table esttica e teramos
que criar trs classes para mapear cada uma das tabelas.
Listagem 16. Criao das tabelas de Livro.
CREATE TABLE IF NOT EXISTS webshelf.book_by_isbn (
isbn bigint,
title text,
author text,
country text,
publisher text,
image blob,
PRIMARY KEY (isbn)
);
CREATE TABLE IF NOT EXISTS webshelf.book_by_title (
isbn bigint,
title text,
author text,
country text,
publisher text,
image blob,
PRIMARY KEY (title, isbn)
);
CREATE TABLE IF NOT EXISTS webshelf.book_by_author (
isbn bigint,
title text,
author text,
country text,
publisher text,
image blob,
PRIMARY KEY (author, isbn)
);

E porque precisamos de trs tabelas ao invs de uma, se todas


tm as mesmas informaes?
A questo que no Cassandra qualquer query precisa filtrar a
tabela no mnimo pelas colunas que compem sua partition key, ou
seja, se voc quiser filtrar a consulta de livros apenas pelo Ttulo,
ter que ter uma tabela onde o campo title, sozinho, seja a partition
key. Dito isso, podemos observar que a nica diferena entre cada
tabela apresentada na Listagem 15 justamente esse elemento.
Assim, a tabela book_by_isbn, por exemplo, que ser usada para
buscar livros de acordo com o seu ISBN, tem sua primary key composta apenas por esse campo, e consequentemente, sua partition
key tambm formada somente por esse campo.
J a tabela book_by_title, que ser usada para buscar livros de acordo com o ttulo, tem sua primary key composta pelas colunas title
e isbn. O campo title vem na primeira posio para assegurar que
o mesmo ser a partition key da tabela. Deste modo, podero ser

executadas consultas utilizando apenas esse o campo como filtro.


Todavia, como a coluna title no garante a unicidade dessa tabela,
foi includo o campo isbn na primary key.
A tabela book_by_author, por sua vez, tem intenso semelhante
a book_by_title, sendo a nica diferena que a mesma ser usada
para pesquisar livros de acordo com o autor.
Nota
Vale lembrar que a partition key o primeiro campo ou o primeiro conjunto de campos da primary
key. Os exemplos a seguir deixam mais clara essa definio:
PRIMARY KEY (user_login, status, book_isbn): user_login a partition key;
PRIMARY KEY ( (user_login, status), book_isbn): user_login e status, colocados entre parnteses,
formam a partition key.

A lgica por trs da partition key, como explicado na primeira


parte do artigo, que atravs dessa chave o Cassandra determina em qual n ficar armazenado o dado. Da a necessidade
de sempre ter esse filtro nas consultas, pois sem essa restrio,
como o Cassandra poderia saber em qual n do cluster est a
informao?
Tambm pelo mesmo motivo, as condies suportadas para
as colunas que compem a partition key so apenas igual (=) e
in, ou seja, o Cassandra precisa saber o valor exato da partition
key para determinar em qual mquina buscar a informao. Se
fosse possvel fazer algo como uma operao like, o Cassandra
teria de varrer todas as mquinas do cluster para achar o dado
procurado, o que resultaria numa grande perda de performance.
Um bom exemplo desse mecanismo descrito na primeira parte
do artigo na seo Distribuio A chave para a escalabilidade
horizontal.
Vale ressaltar que mesmo sendo permitido o uso do in, essa
clausula vista como uma m prtica por vrios desenvolvedores, pois pode obrigar o Cassandra a obter dados de vrios ns
diferentes numa nica operao. Por exemplo, digamos que no
in voc informe trs valores, e suponha que o particionador do
Cassandra distribuiu os dados dessas trs chaves em mquinas
diferentes. Nesse cenrio, uma nica consulta far com que o
Cassandra colete dados de trs ns distintos para ento retornar
a resposta para o cliente. Esse tipo de operao envolvendo vrias
mquinas acarreta uma perda de performance considervel e por
isso deve ser evitada.

Desenvolvendo a lgica de negcio de Livro


Como j modelamos as tabelas do cadastro de livros, agora
possvel pensar na lgica de negcio que ser inserida na classe
BookBean, implementada na Listagem 17.
Assim como UserBean, esta classe tambm um EJB Stateless
sem suporte a transaes que injeta, atravs de CDI, os objetos
Validator e CassandraCluster. No entanto, o mtodo insertBook()
traz novos conceitos. Como os livros precisam ser cadastrados em
trs tabelas, a operao de insert composta de trs statements,
cada um inserindo os dados numa tabela distinta.

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

29

29

Como usar o Apache Cassandra em aplicaes Java EE - Parte 2

Listagem 17. Cdigo da classe BookBean (webshelf-business)


package br.com.devmedia.webshelf.service;
//imports omitidos...
@Stateless
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class BookBean {
@Inject
private CassandraCluster cassandra;
@Inject
private Validator validator;
public void insertBook(Book book) {
executeBeanValidation(book);

BatchStatement batch = new BatchStatement();
batch.add(cassandra.boundInsertBookByISBN().bind(book.getIsbn(),
book.getTitle(), book.getAuthor(), book.getCountry(), book.getPublisher(),
book.getImageBuffer()));
batch.add(cassandra.boundInsertBookByTitle().bind(book.getIsbn(),
book.getTitle(), book.getAuthor(), book.getCountry(), book.getPublisher(),
book.getImageBuffer()));
batch.add(cassandra.boundInsertBookByAuthor().bind(book.getIsbn(),
book.getTitle(), book.getAuthor(), book.getCountry(), book.getPublisher(),
book.getImageBuffer()));

cassandra.execute(batch);
}
private void executeBeanValidation(Book book) {
Set<ConstraintViolation<Book>> constraintViolations = validator.validate(book);
if (!constraintViolations.isEmpty()) {
throw new ConstraintViolationException(constraintViolations);
}
}
}

Essa operao de gravar o livro nas trs tabelas modeladas


(book_by_isbn, book_by_title e book_by_author), de uma maneira
geral, poderia ser feita de duas formas: executar cada insert numa
operao independente ou executar os trs inserts numa nica
operao (batch).
A primeira abordagem, no entanto, poderia resultar numa base
de dados inconsistente, visto que o Cassandra no tem o conceito de transaes ACID. Dessa maneira, uma operao poderia
completar com sucesso e outra no. Por exemplo, suponha que
o primeiro comando seja o insert na tabela book_by_isbn e que o
mesmo completa com sucesso. Em seguida, durante os inserts
nas tabelas book_by_title e book_by_author, imagine que acontea
algum erro e tais comandos no sejam gravados na base. Nessa
situao, o livro que deveria estar presente nas trs tabelas s
existir em uma, pois mesmo que os ltimos dois comandos tenham sofrido algum erro, a primeira execuo j foi completada
com sucesso, gravada na base e j se encontra disponvel para os
demais clientes consultarem.

30 Java Magazine Edio 150

30

Usando BatchStatements para garantir a atomicidade


Para contornar o problema descrito utilizamos a segunda abordagem o uso de batches como recomenda a quarta regra de
utilizao do driver DataStax. Por isso declaramos BatchStatement no mtodo insertBook().
Nesse mtodo, o batch composto pelos trs inserts do cadastro
de livro, como pode ser notado atravs das chamadas ao mtodo
add() da classe BatchStatement. Este mtodo recebe como parmetro um Statement que enfileirado para posterior execuo
na ordem em que foi adicionado. No cdigo da Listagem 17
os statements so criados pelos mtodos CassandraCluster
.boundInsert*(), que sero melhor explanados ao analisarmos
a Listagem 18.
Observe que o mtodo add() no executa o comando ainda. Isso
s acontece na chamada ao mtodo CassandraCluster.execute(),
onde o BatchStatement passado como parmetro (cassandra.
execute(batch)). Nesse momento, a execuo de cada um dos
statements adicionados previamente acontece numa operao
atmica, e com isso, ou os trs inserts funcionaro ou nenhum
ser de fato persistido.
importante ressaltar que o principal intuito para usar batches
no Cassandra o que acabamos de citar: atomicidade. Portanto,
no espere melhorias de performance por conta disso. Na verdade,
essa atomicidade geralmente implica numa piora da performance,
j que o Cassandra ter que se preocupar com esse quesito (atomicidade da operao), o que envolver vrias checagens extras
para garantir a consistncia e que no ocorrem numa operao
isolada.

Preparando instrues CQL para insero de livros


Para fechar o cadastro de livros falta apenas criar os mtodos na
classe CassandraCluster que iro gerar os PreparedStatements
com os comandos necessrios para a incluso. Esses mtodos
podem ser visualizados na Listagem 18.
Note que cada um dos mtodos boundInsertBook*() prepara uma
instruo CQL para executar um insert em uma das tabelas de livro.
Como essas tabelas so estruturalmente iguais, mudando apenas a
primary key e a partition key, os trs comandos so semelhantes,
diferenciando-se unicamente pelo nome da tabela alvo.
Listagem 18. Comandos CQL para insero de livros (CassandraCluster),
public BoundStatement boundInsertBookByAuthor(){
return prepare(insert into webshelf.book_by_author (isbn,title,author,country,
publisher,image) values(?,?,?,?,?,?););
}
public BoundStatement boundInsertBookByTitle(){
return prepare(insert into webshelf.book_by_title (isbn,title,author,country,
publisher,image) values(?,?,?,?,?,?););
}
public BoundStatement boundInsertBookByISBN(){
return prepare(insert into webshelf.book_by_isbn (isbn,title,author,country,
publisher,image) values(?,?,?,?,?,?););
}

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Note ainda que as implementaes desses mtodos tambm so


bem parecidas, consistindo apenas em declarar o mtodo prepare() para que seja criado um PreparedStatement para cada um dos
inserts e que estes sejam armazenados num cache para utilizao
em operaes subsequentes. Em seguida, o mtodo prepare()
retorna um BoundStatement que, por sua vez, retornado para
os clientes poderem setar os parmetros dos CQLs (caractere ?)
e executar o comando.
De acordo com o que apresentamos neste artigo, ficou evidente
que o Apache Cassandra uma opo NoSQL bastante vivel para
utilizao com Java EE e as diversas tecnologias que cercam essa
plataforma. Isso pde ser constatado quando abordamos o uso do
Cassandra numa aplicao com tecnologias como EJB, CDI, JSF,
PrimeFaces e DeltaSpike.
Neste segundo artigo o foco foi completamente voltado para a
parte prtica, e mesmo para quem no conhecia muito a respeito
do Cassandra, podemos dizer que o leitor agora tem uma boa viso
das possibilidades, vantagens, melhores prticas e restries que
essa tecnologia impe.
Embora vrios dos principais tpicos j tenham sido discutidos,
obviamente isso apenas o incio e ainda ser preciso aprender
muitas outras tcnicas, features e conceitos para se tornar um
profissional com domnio mais concreto sobre a tecnologia.
Para tanto, recomendamos que busque conhecer novas tcnicas
de modelagem baseadas em cenrios mais complexos. Isso far
com que voc abra a cabea para pensar de uma forma mais
Cassandra e menos relacional.

Duas timas fontes so as diversas apresentaes de cases reais


disponveis no SlideShare e no YouTube.
Alm disso, sugerimos que o leitor se empenhe em ler a documentao sobre CQL e Driver DataStax (veja na seo Links) para
que consiga atingir um nvel profissional no Cassandra.
Para encerrar, vale ressaltar que se voc no leu a primeira
parte ter bastante dificuldade em compreender aspectos mais
avanados e abstratos da arquitetura do Cassandra.
Por isso, aconselhamos que d um passo atrs e leia o artigo
introdutrio, onde foi apresentado um embasamento terico
essencial para evoluir no aprendizado dessa tecnologia.
Links:
Apache Cassandra Product Guide.
http://docs.datastax.com/en/cassandra/2.2/index.html
DataStax - CQL Product Guide.
http://docs.datastax.com/en/cql/3.3/index.html
DataStax - Java Driver Product Guide.
http://docs.datastax.com/en/developer/java-driver/2.1/index.html
DataStax Academy.
https://academy.datastax.com
PrimeFaces Web Site.
http://www.primefaces.org
Documentao DeltaSpike.
https://deltaspike.apache.org/documentation

Autor
Marlon Patrick
marlon.patrick@mpwtecnologia.com - marlonpatrick.info
bacharel em Cincia da Computao e atua como Consultor
Java no Grupo Mquina de Vendas com aplicaes de misso
crtica, tem oito anos de experincia com tecnologia Java e certificado
SCJP 5.

Voc gostou deste artigo?


D seu voto em www.devmedia.com.br/javamagazine/feedback
Ajude-nos a manter a qualidade da revista!

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

31

31

Introduo ao Java 9:
Conhea os novos
recursos
Aprenda neste artigo as principais novidades do
JDK, que trar para o Java a modularizao, jShell,
HTTP 2.0, JMH, entre outras melhorias

plataforma Java uma das opes mais populares quando pensamos em desenvolvimento de
aplicaes, e isso se torna ainda mais evidente
quando o escopo so as solues voltadas para a web.
Atualmente, estima-se que cerca de nove milhes de
desenvolvedores adotam o Java. Completando 20 anos
em breve, a plataforma classificada por muitos como
antiga, nesse universo em que novas solues surgem
e desaparecem num piscar de olhos.
Todo esse tempo, obviamente, possibilitou mais robustez e confiabilidade ao Java, no entanto, por ser projetada
com alguns conceitos hoje tidos como obsoletos, a exemplo da arquitetura no-modular, possui uma estrutura
monoltica, ou seja, no dividida em mdulos. Essa
ausncia, presente em algumas das linguagens mais
modernas, torna mais difcil o reuso e a manuteno do
cdigo, restringindo a sua utilizao, principalmente, em
dispositivos de baixa capacidade de processamento. Por
causa disso, h muito tempo a comunidade Java solicita
uma grande reforma na estrutura da plataforma, com o
objetivo de torn-la modular.
Vale lembrar, ainda, que ao longo de sua histria a
plataforma Java cresceu de um pequeno sistema criado
para dispositivos embarcados para uma rica coleo de
bibliotecas, que atende s mais diversas necessidades e
que precisam rodar em ambientes com sistemas operacionais e recursos de hardware distintos. Hoje sabemos
que possuir um canivete suo com tantos recursos
essencial ao desenvolvedor, contudo, essa abundncia
tambm traz alguns problemas:

32

Fique por dentro


Este artigo apresenta as principais funcionalidades previstas para a
mais nova verso da linguagem Java: a to esperada modularizao,
a API de suporte ao protocolo HTTP 2.0, bem como outras menores,
como o jShell, a biblioteca JMH, entre outras. Essas novidades do
Java 9 possibilitaro uma grande melhoria de desempenho s aplicaes, principalmente s que executam em dispositivos com baixo
poder de processamento. Sendo assim, de suma importncia que
desenvolvedores e arquitetos tenham domnio sobre o potencial dos
novos recursos, de forma a tirar o melhor proveito dos mesmos em
suas prximas solues.

Tamanho: O JDK sempre foi disponibilizado como um grande e


indivisvel artefato de software, com vrias bibliotecas e recursos
para o desenvolvedor. A insero de novidades na plataforma ao
longo dos anos culminou no aumento constante deste, tornando-o
cada vez mais pesado;
Complexidade: O JDK profundamente interconectado, ou seja,
as bibliotecas so muito dependentes umas das outras, compondo
assim uma estrutura monoltica. Alm disso, com o tempo essa
estrutura resultou em conexes inesperadas entre APIs e suas
respectivas implementaes, levando a um tempo de inicializao
e consumo de memria maiores, degradando o desempenho de
aplicaes que necessitam da plataforma para funcionar. Para se
ter uma ideia, um programa que escreve um simples Al, mundo! no console, ao carregar e inicializar mais de 300 classes, leva,
em mdia, 100ms em uma mquina desktop.

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Sabendo que quanto maior a aplicao maior ser o tempo


necessrio para inicializ-la, fundamental evoluir esse cenrio para aprimorar o tempo de inicializao e o consumo
de memria. Com esse objetivo, os desenvolvedores do Java
optaram por dividir o JDK, especificando um conjunto de
mdulos separados e independentes.
Praticamente concludo, o processo de reestruturao do
Java em mdulos consiste na identificao de interconexes
entre bibliotecas e na eliminao dessas dependncias quando possvel. Isso reduz o nmero de classes carregadas em
tempo de execuo e, consequentemente, melhora tanto o
tempo de inicializao quanto o consumo de memria, pois
ao reduzir o acoplamento a relao de dependncia entre
certas classes diminuir. Deste modo, com o JDK modular,
apenas os mdulos necessrios para inicializar a aplicao
sero carregados. Outra consequncia que a utilizao de
mdulos poder ser empregada no apenas pelo JDK, mas
tambm por bibliotecas e aplicaes, de tal forma a melhorar
cada vez todo o universo Java.
Em conjunto com a comunidade, a modularizao do JDK
est sendo desenvolvida pela Oracle sob a liderana de
Mark Reinhold em um projeto de codinome Jigsaw (quebracabea). Parte do JDK 9, est previsto para ser entregue no
primeiro semestre de 2017 e, uma vez que a maior, principal
e mais aguardada mudana em relao verso 8, o assunto
do primeiro tpico apresentado neste artigo.
O segundo tpico abordado ser o jShell, ferramenta de
linha de comando que possui suporte a REPL (Read Eval
Print Loop). Assim, a partir de agora ser possvel escrever
e testar expresses em Java na linha de comando, sem a necessidade de escrever classes e mtodos. Essa ferramenta j
est disponvel para o pblico atravs das verses de acesso
antecipado (Early Access) do JDK 9 com o intudo de obter retorno da comunidade sobre o seu funcionamento e sugestes
para futuras melhorias.
O terceiro assunto que analisaremos neste artigo ser o
JMH, ou Java Microbenchmarking Harness, o qual fornece uma
estrutura para construir, rodar e analisar testes de desempenho em vrios nveis de granularidade. A importncia dessa
funcionalidade vem da necessidade de se obter avaliaes de
desempenho precisas, uma vez que o tempo de aquecimento
(warmup time) para inicializar os recursos para rodar o teste
de desempenho, bem como as otimizaes automticas realizadas pelo compilador Java no cdigo a ser testado, causam
grande impacto no resultado dessas avaliaes. Essa constatao ainda mais evidente quando se trata de operaes que
duram apenas micro ou nanossegundos.
O quarto tpico diz respeito alterao do coletor de lixo
(garbage collector) padro do Java, que passa a ser o G1, o qual
funciona melhor em JVMs (Java Virtual Machine) cuja memria de alocao (heap space) superior a 4GB, caracterstica
muito comum nos dias atuais. Dessa forma, esperado que
o gerenciamento de memria da JVM seja mais eficiente,

melhorando o desempenho do funcionamento da mquina


virtual como um todo.
O quinto tpico destaca a implementao de uma API com
suporte ao protocolo HTTP 2.0 e websockets. Essa API muito
importante porque a especificao da verso 2.0 do HTTP foi
disponibilizada j h algum tempo, baseada na implementao
do protocolo SPDY do Google, o qual recebeu crticas muito
positivas, pois de fato possibilita a acelerao da navegao na
web. Assim, o objetivo fazer com que o Java possa se antecipar
adoo massiva do novo HTTP e fornea uma opo para a
utilizao do mesmo nas aplicaes desenvolvidas com essa
plataforma.
Por fim, ser analisada a melhoria da API de processos do Java,
pois at o momento o JDK possui uma grande limitao para
controlar e gerenciar processos do sistema operacional. Hoje,
para obter um simples nmero de identificao de um processo
no sistema operacional, preciso utilizar cdigo nativo do SO
(em geral em C), o que dificulta a implementao e leva a um
alto acoplamento.

Projeto Jigsaw Modularizao do JDK


O projeto Jigsaw um esforo da Oracle e da comunidade Java
para a implementao da modularizao da plataforma, tendo
em mente os seguintes objetivos, de acordo com a pgina oficial
do projeto:
Tornar a plataforma Java SE e o JDK mais facilmente escalveis para
dispositivos pequenos e/ou de baixo poder de processamento;
Melhorar a segurana e manutenibilidade da plataforma Java
SE e do JDK;
Permitir um melhor desempenho das aplicaes Java;
Tornar mais fcil aos desenvolvedores a construo e manuteno de bibliotecas e grandes aplicaes nas plataformas Java.
Para alcanar esses objetivos, foi proposto projetar e implementar
um sistema de mdulos padro para a plataforma Java SE e JDK.

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

33

33

Introduo ao Java 9: Conhea os novos recursos

A partir disso, os requisitos para a criao do sistema de mdulos


foram especificados e, em seguida, foi feito um rascunho (draft)
com essa especificao.
De acordo com a JSR 376 (documento de especificao dos
requisitos), o sistema de mdulos possui duas caractersticas
fundamentais:
1. Configurao confivel (Reliable configuration): Substituio
do mecanismo de classpath padro por uma forma dos componentes de software declararem dependncias explcitas, a fim de
evitar conflitos; e
2. Forte encapsulamento (Strong encapsulation): Permisso para
um componente de um mdulo declarar quais dos seus tipos de
dados declarados como pblicos so acessveis a componentes de
outros mdulos e quais no so.
Nota
Nas verses anteriores do Java, o tipo pblico especifica que qualquer classe no classpath,
independentemente do pacote em que est, pode acessar aquele tipo diretamente. Como os
mdulos so uma nova entidade que abrange pacotes/componentes, preciso informar quais
tipos que foram declarados como pblicos so acessveis a outros componentes que fazem parte
de outros mdulos.

Essas caractersticas auxiliam no desenvolvimento de aplicaes, bibliotecas e da prpria plataforma Java, pois garante
maior escalabilidade com aplicaes/plataformas mais enxutas,
e apenas com as dependncias necessrias; maior integridade,
ao evitar a utilizao de dependncias cujas verses no so as
mais adequadas; assim como grandes melhorias em termos de
desempenho, com a resoluo de dependncias e coleta de lixo
mais eficientes, bem como um tamanho menor dos arquivos de
execuo.

Mdulos
Os mdulos so um novo tipo de componente do Java, possuindo um nome e um cdigo descritivo, isto , um arquivo que
define os detalhes e propriedades dos mesmos, alm de outras
informaes adicionais. Essas informaes adicionais descrevem,
basicamente, configuraes de servios ou recursos que o mdulo
pode utilizar.
No cdigo descritivo, por sua vez, o mdulo deve informar se
faz uso de tipos (classes, interfaces ou pacotes) de outros mdulos,
de forma que a aplicao possa compilar e executar sem erros.
Para isso, deve-se utilizar a clusula requires em conjunto com
o nome do recurso. Alm disso, pode ser necessrio informar ao
compilador quais tipos desse mdulo podem ser acessados por
outros, ou seja, declarar quais recursos ele exporta, o que feito
atravs da clusula exports.
A partir disso, o sistema de mdulos localiza os mdulos necessrios e, ao contrrio do sistema de classpath, garante que o
cdigo de um mdulo acesse apenas os tipos dos mdulos dos
quais ele depende. Como complemento, o sistema de controle de
acesso da linguagem Java e da mquina virtual tambm previnem

34 Java Magazine Edio 150

34

que cdigos acessem tipos oriundos de pacotes que no so exportados pelos mdulos que os definem.
Com o intuito de evitar a forte dependncia entre mdulos, um
mdulo pode declarar que utiliza (uses) uma interface (tipo genrico) em vez de uma classe (tipo especfico) de um determinado
servio cuja implementao fornecida (provided) em tempo
de execuo por outro modulo. Essa estratgia permite que os
desenvolvedores possam, entre outras coisas, estender a API do
Java, codificando classes que implementam uma interface que
utilizada pelo mdulo em desenvolvimento, interface essa declarada atravs da sintaxe uses nomedainterface. Isso ocorre porque
a dependncia do mdulo que declara a interface com a classe
criada pelo desenvolvedor resolvida em tempo de compilao
e de execuo, sem necessidade de quaisquer intervenes. Juntamente a essa capacidade de resoluo de componentes genricos,
o sistema de mdulos mantm a hierarquia atual de classloaders,
facilitando a execuo ou migrao de aplicaes legadas para
verso 9 do Java.
Tendo em vista que os mdulos so descritos atravs de um cdigo, a maneira mais simples de declar-los especificar apenas
os seus respectivos nomes em um arquivo, conforme o cdigo a
seguir:
module br.com.devmedia {}

Como j mencionado, no entanto, existem mais opes de configurao, como informar que um mdulo depende de outro,
utilizando a clusula requires. Desse modo, supondo que o mdulo criado anteriormente (br.com.devmedia) dependa de outro
(br.com.devmediaOutro), a estrutura utilizada para descrever
essa relao ser semelhante exposta na Listagem 1.
possvel, ainda, declarar os pacotes do mdulo cujos tipos
pblicos podem ser acessados por outros, atravs da clusula
exports. Na Listagem 2, br.com.devmedia define que os pacotes
br.com.devmedia.pacote1 e br.com.devmedia.pacote2 podem
ser utilizados por outros mdulos. Portanto, se na declarao de
um mdulo no existe a clusula exports, o mdulo no ir, de
forma alguma, exportar pacotes para outros.
Listagem 1. Declarao do mdulo br.com.devmedia com dependncia ao mdulo br.com.devmediaOutro.
module br.com.devmedia {
requires br.com.devmediaOutro;
}
Listagem 2. Configurao do mdulo br.com.devmedia com dependncia e
exports declarados.
module br.com.devmedia {
requires br.com.devmediaOutro;
exports br.com.devmedia.pacote1;
exports br.com.devmedia.pacote2;
}

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

E a declarao do mdulo, como feita? Por conveno, deve ser


criado um arquivo descritor, de nome module-info.java, no diretrio
raiz do mdulo, por exemplo:
module-info.java
br/com/devmedia/pacote1/FabricaPacoteUm.java
br/com/devmedia/pacote2/ClassePacoteDois.java

ambiente de execuo do Java, para dispositivos cada vez menores,


uma vez que os executveis tambm sero menores.
O nico mdulo que deve estar presente em todas as implementaes o mdulo base, o qual possui o nome java.base.
Este define e exporta todos os pacotes que fazem parte do ncleo
da plataforma, incluindo o prprio sistema de mdulos (vide
Listagem 4).

... (outros pacotes/classes/interfaces)


Listagem 4. Declarao do mdulo java.base do JDK9.

Feito isso, o descritor poder ser compilado, assim como feito


com as classes/interfaces, e resultar em um artefato de nome
module-info.class, colocado no mesmo diretrio dos demais arquivos compilados.
Por fim, saiba que, do mesmo modo que os nomes dos pacotes, os
nomes dos mdulos no podem estar em conflito. Assim, a forma
recomendada de se nomear mdulos, a fim de evitar problemas,
utilizar o padro de nome de domnio ao contrrio, como j
demonstrado nos exemplos anteriores.

module java.base {
exports java.io;
exports java.lang;
exports java.lang.annotation;
exports java.lang.invoke;
exports java.lang.module;
exports java.lang.ref;
exports java.lang.reflect;
exports java.math;
exports java.net;
...
}

Artefatos de mdulos
Como sabemos, as ferramentas de linha de comando da plataforma Java so capazes de criar, manipular e consumir arquivos JAR.
Diante disso, de forma a facilitar a adoo e migrao para a nova
verso do JDK, os desenvolvedores criaram tambm o arquivo JAR
modular. Como se pode imaginar, um arquivo JAR modular um
arquivo JAR comum que possui a definio do mdulo compilada
(o arquivo module-info.class) no seu diretrio raiz (vide BOX 1). Por
exemplo, um JAR para o mdulo especificado anteriormente teria
a estrutura apresentada na Listagem 3.

Qualquer outro mdulo do Java sempre ir depender do mdulo


base, o qual no depende de ningum. Como consequncia, todos os outros mdulos compartilharo o prefixo java. em seu
nome, como o java.sql (para conectividade com o banco de dados),
java.xml (para processamento de XML) e java.logging (para registro de operaes/eventos). Entretanto, existem excees, como
os mdulos que fazem parte apenas do JDK. Por conveno, esses
utilizam o prefixo jdk..

Resoluo e dependncia
BOX 1. Arquivo JAR modular
Pode ser utilizado como um mdulo (a partir do Java 9) ou como um arquivo JAR comum (em
todas as verses). Assim, para fazer uso de um arquivo JAR modular como se fosse um JAR comum,
basta informar o mesmo no classpath da aplicao. Dessa forma o arquivo module-info.class ser
ignorado. Essa estratgia permite que aplicaes legadas, que executam em verses anteriores do
Java, possam se beneficiar de bibliotecas desenvolvidas de forma modular, mantendo com isso a
retrocompatibilidade.

Para melhor compreender como os mdulos so encontrados


ou resolvidos em tempo de compilao ou de execuo, preciso
entender como eles esto relacionados entre si. Supondo que uma
aplicao faa uso do mdulo br.com.devmedia, mencionado
anteriormente, como tambm do mdulo java.sql, da plataforma

Listagem 3. Estrutura do JAR para o mdulo exemplo.


META-INF/
META-INF/MANIFEST.MF
module-info.class
br/com/devmedia/pacote1/FabricaPacoteUm.class
br/com/devmedia/pacote2/ClassePacoteDois.class
...(outras classes e pacotes)

Mdulos da plataforma Java SE 9


A especificao da plataforma Java SE 9 a divide em um conjunto
de mdulos. Entretanto, uma implementao da mesma pode conter todos ou apenas alguns mdulos. Essa opo importante porque alguns projetos no precisam fazer uso de todos os mdulos,
tornando assim mais fcil escalar as aplicaes, como tambm o

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

35

35

Introduo ao Java 9: Conhea os novos recursos

Java 9, o mdulo que conteria o ncleo da aplicao seria descrito


conforme a Listagem 5.

Em virtude de a declarao do mdulo br.com.devmedia j ter


sido demonstrada em uma listagem anterior, na Listagem 6
possvel ver a declarao da outra dependncia, o java.sql.

Listagem 5. Declarao do mdulo br.com.devmedia-app.


Listagem 6. Declarao do mdulo java.sql.

module br.com.devmedia-app {
requires br.com.devmedia;
requires java.sql;
}

module java.sql {
requires java.logging;
requires java.xml;
exports java.sql;

Note que o mdulo ncleo da aplicao, no caso br.com.devmedia-app, seria o ponto de partida para que o sistema encontre
as dependncias necessrias para a mesma, tendo como base os
nomes dos mdulos indicados pela clusula requires do descritor
do mdulo inicial, como demonstrado no exemplo. Saiba, ainda,
que cada mdulo do qual o mdulo inicial depende pode depender de outros. Nestes casos, o sistema de mdulos procurar
recursivamente por todas as dependncias de todos os outros, at
que no reste nenhuma a ser acrescentada. Logo, se essa relao
de dependncia entre mdulos fosse desenhada em um grfico,
o desenho teria a forma de um grafo, no qual cada relao entre
duas dependncias seria expressa por uma aresta e cada dependncia seria um vrtice.
Com o intuito de construir o grafo com as dependncias do mdulo br.com.devmedia-app, o compilador precisa ler as descries
dos mdulos que ele depende (java.sql e br.com.devmedia). Logo,
para melhor compreender a construo desse grafo, preciso
dar uma olhada na declarao dos mdulos que se depende.

exports javax.sql;
exports javax.transaction.xa;
}

Nota
No foi criada uma nova listagem com a descrio do mdulo br.com.devmedia porque j foi
demonstrado na Listagem 2. Alm disso, para ser mais breve, tambm foram omitidas as descries
dos mdulos java.logging e java.xml, dos quais o mdulo java.sql depende.

Baseado nas declaraes dos mdulos apresentados, o grafo


resultante para a resoluo de dependncias do mdulo br.com.
devmedia-app contm os vrtices e arestas descritos na Figura 1.
Nessa figura, as linhas de cor azul representam as dependncias
explcitas, ou seja, que esto descritas no module-info.java, nas clusulas requires, enquanto as linhas de cor cinza representam as
dependncias implcitas ou indiretas de cada mdulo em relao
ao mdulo base.

Figura 1. Grafo de mdulos da aplicao

36 Java Magazine Edio 150

36

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Figura 2. Grafo de Mdulos com o mdulo org.postgresql.jdbc adicionado

Servios
Como sabemos, o baixo acoplamento de componentes
de software alcanado atravs de interfaces de servios e
provedores de servios uma poderosa ferramenta para a
construo de grandes sistemas. Por esse motivo, o Java suporta h um bom tempo essa tcnica atravs da classe java
.util.ServiceLoader, a qual utilizada pelo JDK e tambm por
bibliotecas e aplicaes.
Essa classe localiza provedores de servios em tempo de execuo ao procurar por arquivos de configurao dos servios
na pasta META-INF/services. Entretanto, se um servio fornecido por um mdulo, esses arquivos no estaro no classpath
da aplicao. Sendo assim, foi preciso implementar uma nova
estratgia para localizar os provedores de servios e carreg-los
corretamente, como demonstraremos a seguir.
Vejamos um exemplo: supon ha que o mdulo br.com
.devmedia-app usa um banco de dados PostgreSQL e que o
driver JDBC (recurso) para o mesmo seja fornecido em um
mdulo declarado de acordo com a Listagem 7.
Conforme pode ser observado nessa listagem, a declarao de
exportao de org.postgresql.jdbc refere-se ao pacote do mdulo
de mesmo nome, que possui o driver JDBC do banco de dados
Post-greSQL. Esse driver uma classe Java que implementa a
interface de servio java.sql.Driver, contida no mdulo java
.sql (veja a nota a seguir). Alm disso, para que o mdulo org
.postgresql.jdbc possa fazer uso dos mdulos dos quais
depende(java.sql e org.slf4j), preciso que os mesmos sejam adicionados ao grafo de mdulos em tempo de execuo, conforme
a Figura 2, tornando possvel que a classe de carregamento de

servios ServiceLoader instancie a classe do driver, procurando,


atravs de reflection, por classes no pacote org.postgresql.jdbc
que implementem a interface java.sql.Driver.

Listagem 7. Declarao do mdulo org.postgresql.jdbc.


module org.postgresql.jdbc {
requires java.sql;
requires org.slf4j;
exports org.postgresql.jdbc;
}

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

37

37

Introduo ao Java 9: Conhea os novos recursos

Para realizar essas adies ao grafo, o compilador deve ser


capaz de localizar os provedores de servios nos mdulos a ele
acessveis. Uma forma de fazer isso seria realizar uma busca por
dependncias na pasta META-INF/services, da mesma forma que
a classe ServiceLoader faz em verses anteriores do Java, s que
agora com mdulos. Todavia, uma alternativa melhor, por viabilizar uma melhoria de performance, possibilitar que os criadores
de mdulos de acesso a servios externos (como o acesso a banco
de dados atravs da API JDBC) forneam uma implementao
especfica do servio, atravs da clusula provides, como pode
ser observado na Listagem 8.
O ganho de performance se deve ao fato de que em vez de realizar uma busca por uma implementao da interface java.sql
.Driver na pasta META-INF/services a qual pode ter centenas
de servios um mdulo que define exatamente a classe que implementa essa interface evita a necessidade de realizar qualquer
busca extensiva, ganhando assim tempo na compilao.
Nota
Quando um mdulo depende diretamente de outro, ento o cdigo do primeiro ser capaz de
referenciar tipos do segundo. Para esse tipo de situao, diz-se que o primeiro mdulo l o segundo
ou, da forma inversa, que o segundo mdulo legvel ao primeiro. No grfico da Figura 1, o mdulo
br.com.devmedia-app l os mdulos br.com.devmedia e o java.sql, mas no o java.xml, por exemplo.
Essa caracterstica de legibilidade a base da configurao confivel, pois permite que o sistema de
mdulos garanta que:
Cada dependncia satisfeita por um nico mdulo;
Dois mdulos no podem referenciar um ao outro de forma a criar uma dependncia cclica,
mesmo que ambos declarem os respectivos requires e exports. O compilador identificar isso como
um erro;
Cada mdulo s pode fazer referncia uma nica vez a um pacote especfico (com mesmo nome
e contedo), mesmo que indiretamente. Exemplo: se um mdulo M1 faz referncia a dois mdulos
(M2 e M3) que dependem de um pacote Z, o compilador perceber que Z referenciado duas vezes.
Nesse contexto, para evitar desperdcio de memria o compilador s carregar esse pacote uma vez,
o que faz com que as aplicaes tambm se tornem mais eficientes;
Se dois (ou mais) mdulos definem pacotes com o mesmo nome, mas contedos diferentes,
ambos podem ser carregados na memria sem que haja comprometimento no funcionamento
da aplicao. A princpio, essa garantia pode parecer conflitante com a anterior, mas no o por
envolver dois mdulos distintos;
A configurao confivel no somente mais confivel, mas tambm mais rpida, pois
diferentemente do que feito com classpathes na verso anterior do Java, quando um cdigo em
um mdulo referencia um tipo contido em um pacote, h uma garantia de que aquele pacote est
definido em algum mdulo do qual ele depende (explicitamente ou implicitamente). Dessa forma,
quando for necessrio buscar pela definio de algum tipo especfico, no h a necessidade de
procurar por ele em vrios mdulos ou por todo o classpath, como era feito em verses anteriores,
tornando as execues das operaes mais rpidas.
Nota
Na Listagem 7 pode parecer ambguo o fato de um mdulo chamado org.postgresql.jdbc ter um
pacote exportado de mesmo nome. O fato que as implementaes de drivers jdbc para bancos de
dados geralmente so anteriores ao sistema de mdulos do Java e devido a isso esto encapsuladas
dentro de pacotes. Logo, de forma a facilitar a identificao desse pacote legado, definiu-se a
conveno de criar um mdulo com o mesmo nome do pacote que possui a implementao do driver.

38 Java Magazine Edio 150

38

Ao declarar provides java.sql.Driver with org.postgresql


.Driver, o criador do mdulo est informando que uma instncia
da classe org.postgresql.Driver deve ser criada para que seja possvel utilizar o servio java.sql.Driver. Por sua vez, igualmente
importante que o mdulo que faz uso de um determinado servio
declare esse uso em sua prpria descrio, atravs da clusula
uses (vide Listagem 9).
Listagem 8. Declarao do mdulo org.postgresql.jdbc.
module org.postgresql.jdbc {
requires java.sql;
requires org.slf4j;
exports org.postgresql.jdbc;
provides java.sql.Driver with org.postgresql.Driver;
}
Listagem 9. Declarao do mdulo java.sql.
module java.sql {
requires public java.logging;
requires public java.xml;
exports java.sql;
exports javax.sql;
exports javax.transaction.xa;
uses java.sql.Driver;
}

Analisando as descries dos mdulos anteriores muito fcil


ver e entender que um deles prov um servio que pode ser
utilizado pelo outro. Essas declaraes explcitas permitem que
o sistema de mdulos garanta, em tempo de compilao, que a
interface do servio a ser utilizado (no caso, o java.sql.Driver) est
acessvel tanto para o provedor quanto para o usurio do mesmo.
Mais ainda, garante que o provedor do servio (org.postgresql
.Driver) implementa, de fato, os servios por ele declarados.
Dessa forma, antes da aplicao ser executada possvel verificar
uma srie de erros que poderiam ocorrer devido ausncia de
alguma dependncia, o que em verses anteriores do Java s era
possvel em tempo de execuo. Essa caracterstica agrega mais
confiana de que o software funcionar corretamente, e tambm
viabiliza mais agilidade ao desenvolvimento de aplicaes, pois
diminui a necessidade de executar a aplicao com a finalidade
de identificar erros.

Class loaders (carregadores de classes)


Outra mudana importante no Java que agora cada mdulo
estar associado a um class loader (BOX 2) em tempo de execuo,
entretanto, cada carregador de classe pode estar associado a vrios
mdulos. Sendo assim, um class loader pode instanciar tipos de
um ou mais mdulos, desde que esses mdulos no interfiram
entre si (exportem o mesmo pacote) e que esses tipos s sejam
instanciados por um nico class loader.
Essa definio essencial para possibilitar a retrocompatibilidade com verses anteriores da linguagem, uma vez que mantm a
hierarquia de class loaders da plataforma. Desse modo, os class
loaders bootstrap e extension continuam a existir na nova verso

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

e sero utilizados para carregar as classes pertencentes a mdulos


da prpria plataforma. J o class loader application/system tambm foi mantido, mas com a funo de carregar tipos oriundos
de mdulos de dependncias da aplicao. Essa flexibilidade
facilita a modularizao de aplicaes legadas, uma vez que os
class loaders das verses anteriores podem ser estendidos com
o intuito de carregar classes contidas em mdulos com poucas
alteraes.
BOX 2. Class loaders
Os class loaders fazem parte do ambiente de execuo do Java (JRE) e so responsveis por carregar
classes dinamicamente na mquina virtual (JVM). Logo, devem localizar as bibliotecas, ler o
contedo delas e carregar suas respectivas classes.
Quando a JVM iniciada, trs class loaders so utilizados:
Bootstrap ClassLoader Responsvel por carregar as bibliotecas da plataforma Java, esse
class loader foi implementado em cdigo nativo (no uma classe Java) e carrega classes Java
importantes, como a java.lang.Object, a qual o pai de todos os objetos Java, bem como outros
cdigos na memria. At a verso 8 do Java, as classes que esse class loader instancia ficavam
empacotadas no rt.jar, presente no diretrio jre/lib do JRE. No entanto, como na verso 9 no
teremos mais arquivos JAR e sim mdulos (ainda que se possa utilizar um mdulo como se fosse um
JAR para retrocompatibilidade), essas classes foram movidas para arquivos JMOD. A partir disso, o
arquivo JMOD que contm a classe Object, por exemplo, o java.base.jmod, localizado no diretrio /
jmods/java.base.jmod/classes/java/lang/ do novo JDK;
Extension ClassLoader Nas verses anteriores do Java, carregava as classes oriundas de bibliotecas
presentes do diretrio extension do JRE. Entretanto, com o advento da modularizao, as classes do
diretrio extension foram migradas para mdulos. Logo, esse class loader continua a carregar as
mesmas classes, s que agora a partir de mdulos. A classe que implementa esse class loader a
ExtClassLoader, a qual uma inner class na classe sun.misc.Launcher;
Application/System ClassLoaders Responsvel por carregar classes que esto no classpath da
aplicao (varivel de ambiente CLASSPATH). implementada pela classe AppClassLoader, a qual
tambm uma inner-class na classe sun.misc.Launcher. Saiba, ainda, que todas as classes criadas
pelos desenvolvedores (por exemplo, as classes main) so carregadas por esse class loader.

jShell = Java + REPL


Como j mencionado, o jShell uma ferramenta REPL de linha de
comando para a linguagem Java, possibilitando assim a execuo
de declaraes e expresses Java de forma interativa. Essa funcionalidade importante porque permite a iniciantes na linguagem
executar, de forma rpida, expresses quaisquer, acelerando o
aprendizado ao fornecer uma resposta imediata na tela, sem a
necessidade de realizar compilao e execuo de cdigo em uma
ferramenta de desenvolvimento.
Alm de ser uma tima opo para iniciantes, o jShell viabiliza
aos desenvolvedores experimentar algoritmos, criar prottipos
ou at mesmo tentar utilizar uma nova API. Essa ferramenta
pode ser testada por qualquer pessoa que baixar as verses early
access do Java 9 e o JAR do projeto Kulla, projeto cujo objetivo
implementar o jShell.

destino de sua preferncia e configurar a varivel de ambiente


para apontar para esta verso do JDK. Logo aps, pressione as
teclas Windows+R para visualizar uma janela de execuo que
solicitar ao usurio o programa que ele deseja abrir. Neste momento, digite cmd e clique em OK.
Nota
Nesse artigo, a pasta na qual o arquivo foi extrado foi a F:\java9\jdk-9. Deste modo, sempre que
houver esse caminho no texto, troque pelo caminho da pasta para a qual voc extraiu o JDK em
sua mquina.

Com o terminal de linha de comando aberto, digite set %JAVA_


HOME%=F:\java9\jdk-9 para criar uma varivel de ambiente
chamada JAVA_HOME cujo contedo o caminho de instalao
do JDK. Em seguida, digite echo %JAVA_HOME% para confirmar
que a varivel foi criada corretamente. Assim, s falta adicionar
esta varivel varivel de ambiente Path utilizada pelo sistema operacional com a execuo do seguinte comando: set
Path=%JAVA_HOME%\bin;%Path%.
Com o JDK 9 baixado e instalado, o segundo passo instalar
o jShell. Para isso, basta baixar o arquivo JAR (veja o endereo
indicado na seo Links) e renome-lo para kulla.jar.

Utilizando o jShell
Esta seo tem o intuito de demonstrar, brevemente, as principais funcionalidades do jShell, de modo que o leitor tenha uma
melhor compreenso do funcionamento da ferramenta. Portanto,
detalhes de implementao sero omitidos, focando apenas nas
formas bsicas de uso.
O primeiro passo executar o terminal de linha de comando, tal
qual informado anteriormente: teclas Windows + R e digitar cmd.
Com o terminal aberto, mude o diretrio do mesmo para o
diretrio no qual est o arquivo kulla.jar, digitando cd /d f:\java9\
kulla\. Uma vez alterado o diretrio, o jShell pode ser executado
com o comando java -jar kulla.jar.

Passos para a instalao e configurao


O primeiro passo para utilizar o jShell baixar a verso early
access do JDK 9. Feito isso, basta descompactar o arquivo para o

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

39

39

Introduo ao Java 9: Conhea os novos recursos

Ao utilizar o jShell no terminal possvel inserir trechos de cdigo


como declaraes, variveis, mtodos, definies de classes, importaes e expresses que a ferramenta automaticamente tomar as
providncias necessrias para execut-los e mostrar o resultado.
Deste modo, pode-se, por exemplo, digitar System.out
.println(Oi!); sem a necessidade de criar uma classe e/ou um mtodo para imprimir um texto na tela, conforme expe a Figura 3.

possvel tambm executar expresses e clculos em geral.


Nesses casos, toda vez que o usurio fornecer uma expresso
como 2+2 uma varivel temporria ser criada com o resultado da
operao, de tal forma que o usurio possa fazer referncia mesma posteriormente. Isso pode ser verificado na Figura 5, na qual
uma varivel temporria, chamada $3, criada com o resultado
da expresso 2+2 e em seguida o valor dessa varivel somado ao
valor da varivel x (valor = 45), resultando no nmero 49.
E quando necessrio informar cdigos com mais de uma
linha, como na definio de mtodos e classes, o usurio no
precisa se preocupar com detalhes de implementao, pois o
jShell consegue detectar que os dados fornecidos pelo usurio
esto incompletos. Esse comportamento pode ser observado na
Figura 6, na qual foi definido um mtodo que multiplica um
nmero inteiro por 2.

Figura 3. Imprimindo um texto com o jShell


Por padro, o jShell mostra informaes descritivas sobre os
comandos que foram executados pelo usurio como, por exemplo,
na definio de uma varivel (vide Figura 4). Essa funcionalidade
importante porque permite ao usurio compreender exatamente
o que o jShell fez, principalmente quando se est aprendendo a
utilizar a linguagem.

Figura 5. Operaes matemticas e expresses no jShell

Figura 4. Inicializando uma varivel no jShell


Nota
A utilizao do ponto-e-vrgula na definio de variveis opcional no jShell.

Figura 6. Criando e executando um mtodo Java no jShell

JMH Java Microbenchmarking Harness


Outra interessante funcionalidade do novo JDK o JMH (Java
Microbenchmarking Harness). Esse recurso nada mais do que uma
ferramenta/biblioteca para construir, rodar e analisar nano, micro,
mili e macro benchmarks (vide BOX 3) que foi desenvolvida pelo
russo Aleksey Shipilev (vide BOX 4) e posteriormente integrada
ao Java 9 como soluo oficial para testes de desempenho na
plataforma Java.
Essa ferramenta foi muito requisitada porque o Java no possua
uma opo para realizar esses tipos de medio, dependendo de
solues de terceiros e dificultando, dessa forma, a realizao de
testes de desempenho confiveis.
Visto que vrios fatores podem distorcer os resultados, como
otimizaes realizadas pelo prprio compilador ou pelo hardware, a realizao desse tipo de teste uma tarefa difcil. Saiba
que quando se trata de testes de desempenho em baixssima
escala, como o caso de microbenchmarks, cada operao que

40 Java Magazine Edio 150

40

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

est sendo medida leva to pouco tempo que qualquer tarefa


que for executada antes ou depois da mesma e estiver dentro do
escopo de medio levar mais tempo que a operao em anlise,
distorcendo assim o resultado.
BOX 3. Micro/nano/mili/macro benchmarking
a medio de performance de pequenos trechos de cdigo que levam micro/nano/milissegundos
para serem executados. Esse tipo de medio ideal para avaliar a performance de um componente
pequeno ou a chamada a um mtodo/funo de um programa, por exemplo.

na verdade dispe de quatro: o serial, o paralelo, o cms e o G1.


Relacionado a isso, recentemente a Oracle percebeu que boa parte
dos usurios tem uma melhor experincia com as aplicaes quando elas rodam sobre um ambiente que faz uso do coletor de lixo
G1, em vez do paralelo, que era utilizado como padro. Assim, a
empresa fez uma proposta de melhoria comunidade, atravs da
JEP 248, para tornar o G1 o coletor padro a partir do Java 9.
Nota
Com o objetivo de garantir a preciso do tempo medido, o JMH faz uma srie de operaes que
permitem que a contagem do tempo s comece aps as otimizaes do compilador.

BOX 4. Aleksey Shipilev


Engenheiro de performance em ambientes Java formado em Cincia da Computao pela
Universidade de ITMO, em So Petersburgo, que trabalha atualmente na Oracle. Desenvolveu o JMH
como um projeto de cdigo aberto com outros colegas, e devido qualidade deste, a Oracle optou
por incorpor-lo como um framework para testes de desempenho no JDK 9, a fim de auxiliar no
desenvolvimento de sutes de testes de desempenho mais precisas, bem como facilitar a execuo
desses testes em pequenos trechos de cdigo. Alm desse, Shipilev contribui ativamente com vrios
projetos de cdigo aberto.

Essa observao importante porque possvel que o desenvolvedor tenha escrito um trecho de cdigo que o compilador
entenda, por exemplo, que no realiza nenhuma tarefa realmente
necessria, sendo, portanto, removido pelo mesmo em tempo de
compilao, de forma a otimizar a execuo da aplicao. Com
a otimizao do cdigo, no entanto, a aplicao executar mais
lentamente do que sem quaisquer intervenes do compilador.
Isso porque as melhorias so realizadas apenas quando a aplicao inicializada, fazendo com que o tempo total para a mesma
terminar todas as tarefas de uma determinada execuo seja
maior, j que contabiliza o tempo levado pelo compilador para
otimizar o cdigo, mais o tempo que o cdigo otimizado precisa
para finalizar a execuo.
Deste modo, os resultados dos testes de desempenho desse trecho de cdigo estaro distorcidos. Com isso em mente, saiba que
uma das premissas para bons testes de desempenho
fazer com que a contagem do tempo de execuo do
teste s inicie aps as otimizaes no cdigo da aplicao, viabilizando testes que possibilitam obter, com
preciso, o tempo levado pela mesma para realizar as
tarefas. Esse o propsito do JMH.
Enfim, com o JMH ser possvel escrever testes de
desempenho mais confiveis, uma vez que o mesmo
fortemente integrado plataforma Java, o que facilita
o monitoramento do gerenciamento de memria da
JVM, das otimizaes do compilador, entre outros fatores que podem distorcer os resultados dos testes.

Nota
No faz parte do escopo deste artigo entrar em detalhes sobre o funcionamento do JMH e como
escrever testes de desempenho com essa ferramenta.

O coletor de lixo paralelo (Parallel GC) tem a vantagem de


utilizar vrios processos Java para fazer a limpeza da memria e compact-la, liberando o espao mais rapidamente para
a alocao de mais recursos. Por outro lado, a desvantagem
que para conseguir realizar a limpeza a JVM precisa pausar
os processos do software que est rodando, escanear toda a
memria do mesmo, com o intuito de identificar quais objetos
no podem ser acessados, e descartar esses objetos, para ento
voltar a executar os processos. Essa paralisao crtica para
grandes aplicativos que precisam ser altamente responsivos,
pois quanto mais memria o aplicativo usa, maior o tempo de
paralisao e resposta. Um dos motivos para isso acontecer
que assim como os coletores de lixo serial e CMS, o paralelo
estrutura a memria utilizvel pela JVM em trs sees: young
generation, old generation e permanent generation, cada uma com
um tamanho fixo (veja a Figura 7).
Por sua vez, o coletor de lixo G1 mais moderno, tendo sido
includo na atualizao 4 do JDK 7, e foi desenvolvido visando

Novo garbage collector padro (G1)


Um dos equvocos que grande parte dos desenvolvedores Java comete pensar que a JVM possui
apenas um coletor de lixo (garbage collector), quando

Figura 7. Estrutura de memria da JVM com coletor de lixo diferente do G1

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

41

41

Introduo ao Java 9: Conhea os novos recursos

a necessidade de suportar melhor uma quantidade de memria


disponvel para a JVM superior a 4GB, o que passou a ser bastante
comum em mquinas mais modernas e, principalmente, em servidores. Para isso, o G1 possui outra estratgia de gerenciamento:
dividir a rea de memria em blocos de mesmo tamanho, cada
um como um pedao contguo da memria virtual.
Assim, cada bloco pode assumir o estado de eden, survivor
ou old para designar que pertencem s reas de mesmo nome,
tal qual ocorre nos outros coletores, com a diferena de que
no existe um nmero fixo para a quantidade de blocos para
cada rea, conforme demonstra a Figura 8. Portanto, o tamanho dessas reas pode variar de acordo com a necessidade do
coletor de lixo, o que garante uma maior flexibilidade no uso
da memria.

No momento de executar a coleta de lixo na memria, o G1 percorre todas as trs regies (eden, survivor e old generation) e faz a
transio de objetos alocados de uma regio para a outra, conforme
o tempo de vida de cada um. Em seguida, inicia a desalocao dos
objetos pela regio mais vazia at a mais cheia, potencializando a
liberao de memria. Esse mtodo assume que as regies mais
vazias tm mais probabilidade de possuir objetos que podem ser
descartados.
Nota
importante mencionar que o G1 no executa a coleta de lixo a todo momento. Em vez disso,
os desenvolvedores especificam um tempo de pausa aceitvel para a aplicao e, baseado nisso,
o coletor de lixo adota um modelo de predio para alcanar esse tempo desejado. Alm disso,
considerando dados de outras coletas, estima quantas regies podem ser coletadas durante o
perodo especificado.

HTTP 2.0

Figura 8. Estrutura de memria da JVM com o coletor de lixo G1

42 Java Magazine Edio 150

42

A verso 2.0 do protocolo HTTP foi definida como padro em


fevereiro de 2015 pelo IESG (Internet Engineering Steering Group) e
at o fim desse mesmo ano os navegadores web mais utilizados
adicionaram o suporte mesma. De forma simples, podemos
descrever o HTTP 2.0 como uma evoluo da verso 1.1, tendo
sido baseada no protocolo aberto SPDY, cujo desenvolvimento
foi iniciado pelo Google.
Como o HTTP 1.1 data de 1999 e no recebeu melhorias desde
ento, essa verso possui uma srie de limitaes/problemas,
principalmente devido evoluo da web, a qual passou de uma
coleo de hyperlinks e textos para a necessidade de viabilizar
a transmisso de contedos mais pesados, como udios, vdeos,
interfaces mais complexas, animaes, entre outras coisas. Essa
evoluo evidenciou as limitaes do protocolo e serviu de motivao para a criao de uma verso mais nova, a 2.0.
Para compreender melhor esse cenrio, considere a seguinte
a analogia: imagine que todas as ruas do mundo tivessem sido
construdas na poca das carruagens e fossem estreitas, esburacadas e com baixo limite de velocidade. Provavelmente levaria
um bom tempo para chegar de um ponto a outro, principalmente
por causa da velocidade dos cavalos. Agora, imagine que nada
mudou nas estradas, mas ao invs de cavalos so utilizados carros.
Nesse cenrio, a velocidade do cavalo deixa de ser um problema,
mas congestionamentos comeam a surgir porque a quantidade
de carros aumentou (a populao cresceu) e eles ocupam mais
espao.
O que ocorre com o trfego de dados na web no muito diferente dessa analogia. O protocolo HTTP foi criado h 25 anos e,
como j citado, a verso mais atualizada foi padronizada apenas
em 1999. Todo esse tempo uma eternidade quando lidamos
com tecnologia da informao, pois em nossa rea as evolues
so muito frequentes. Portanto, da mesma forma que as estradas
estreitas e esburacadas de outrora, a web, quando da padronizao
do protocolo HTTP, era bem diferente da atual: as pginas web
eram pequenas, as conexes de internet, mais lentas, e os recursos

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

de hardware, limitados e caros. De certa forma, as mquinas e


a ausncia de banda larga eram o gargalo para viagens mais
rpidas.
Quando um navegador carrega uma pgina web utilizando o
HTTP 1.1, ele s pode solicitar um recurso (uma imagem, um
arquivo JavaScript, etc.) por vez a cada conexo com o servidor,
conforme a Figura 9. Deste modo, note que o navegador passa um
bom tempo apenas aguardando a resposta de cada solicitao.

Figura 10. Navegadores tentam paralelizar criando mais conexes


Essa ineficincia para carregar recursos o motivo da adoo
por muitos desenvolvedores web da compactao de arquivos
JavaScript e CSS, pois uma vez o recurso possuindo um tamanho
menor, o site carregar mais rpido. Entretanto, medidas como
essas so apenas formas de contornar o problema e no de resolvlo. Embora seja recomendado continuar com a otimizao das
pginas web para baixar cada vez menos recursos e em tamanhos
menores, o problema no ser resolvido at que haja uma alterao fundamental na forma de comunicao do protocolo HTTP,
maximizando a utilizao das conexes e minimizando o tempo
de espera/resposta. Eis que surge o HTTP 2.0.
Figura 9. Modelo de comunicao do HTTP 1.1
J que o HTTP 1.1 no permite que sejam feitas mltiplas solicitaes em paralelo numa mesma conexo, pois analogamente ao
caso das estradas, s h uma faixa para ir e voltar, e os navegadores
tentam contornar essa limitao criando mais uma conexo com
o servidor (criando uma nova faixa na estrada), como pode ser
observado na Figura 10.
Acontece que utilizar duas conexes melhora um pouco a velocidade, mas no resolve o problema, pois como pode ser observado
na imagem, o navegador ainda passa um bom tempo esperando
a resposta da solicitao e s conseguir baixar dois recursos por
vez. Embora seja possvel criar at seis conexes com o servidor
de destino, essa ainda no uma boa opo, porque cada conexo
ser usada de maneira ineficiente, desperdiando tempo.
Neste cenrio, se levarmos em conta que uma pgina comum
em um site moderno possui, por exemplo, 100 recursos, o tempo
para baixar cada recurso culmina em uma pgina que ser carregada lentamente.

A soluo
O HTTP/2 busca fazer melhor uso das conexes com os servidores web ao modificar como as solicitaes e respostas trafegam
pela rede, o que era uma grande limitao da verso anterior do
protocolo. Com a nova verso, o navegador realiza uma nica
conexo com o servidor web e ento pode, de acordo com a necessidade, efetuar vrias solicitaes e receber vrias respostas
ao mesmo tempo, como sinaliza a Figura 11.
De acordo com essa figura, o navegador utiliza uma nica conexo e enquanto est enviando mais uma solicitao ao servidor
(#6), tambm est recebendo mltiplas respostas para solicitaes
feitas anteriormente como, por exemplo, o cabealho para o arquivo #3, o corpo do arquivo solicitado pela requisio #1, o corpo do
arquivo solicitado pela requisio #3, cujo cabealho j foi recebido, e o cabealho do arquivo solicitado pela requisio #2.
Note que com apenas uma conexo o navegador consegue
enviar vrias requisies e receber vrias respostas, em ordem
diversa, otimizando assim o trfego de dados. Note, tambm,
que as respostas para uma nica solicitao podem ser fragmentadas em vrios pedaos menores, como aconteceu com os dados

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

43

43

Introduo ao Java 9: Conhea os novos recursos

Figura 11. Modelo de comunicao do HTTP 2.0


solicitados pela requisio #3. Isso importante porque permite
que o servidor responda mais rpido, ainda que com partes menores. Alm disso, evita que o servidor s processe a requisio
seguinte aps terminar a atual o que causa gargalo no trfego
das informaes visto que agora tem a capacidade de enviar
vrias respostas de uma nica vez. Logo, ao dividir o trfego em
pequenos blocos, tanto o cliente (que faz a solicitao) quanto o
servidor (que devolve o que o cliente pediu) passam a ser capazes de lidar com mltiplas solicitaes e respostas, aumentando,
portanto, a velocidade do dilogo entre as partes.
Analogamente, essa situao seria como ir a um supermercado
e pedir a algum para pegar uma lista completa de itens de uma
nica vez, por exemplo: Pegue o leite, os ovos e a manteiga.
Logo, a pessoa retornar com todas as coisas da lista solicitada.
No HTTP 1.1, seria necessrio pedir um item por vez, por exemplo:
Pegue o leite -> Aqui est o leite, Agora pegue os ovos ->
Aqui esto os ovos e assim sucessivamente.
fcil observar que muito mais eficiente pedir todos os itens
de uma s vez, economizando tempo e, assim, agilizando todo o
processo. Em outras palavras, toda a comunicao muito mais

44 Java Magazine Edio 150

44

eficiente fazendo uso de uma nica conexo com multiplexao,


como ocorre no HTTP 2.0. Essa abordagem, apesar de um pouco
mais complexa de entender, fundamental para uma internet
cada vez mais rpida.
Em primeiro lugar, as conexes no precisaro esperar que um
recurso termine de ser transferido para atender a outras solicitaes. Por exemplo, suponha que existe uma pgina web com vrias
imagens (algo como o Google Imagens) e o navegador precisa fazer
uma requisio para receber cada uma. Com a multiplexao, em
vez de esperar que o servidor termine de enviar todas as partes
de uma imagem para que possa requisitar a prxima, ele pode
solicitar um conjunto de imagens ou todas elas de uma vez e
comear a receb-las em partes. Nesta proposta, caso seja menor
e possua menos partes a serem baixadas, a segunda imagem, por
exemplo, pode ser renderizada antes mesmo da primeira. Essa
caracterstica previne um problema bastante conhecido na verso
anterior, chamada de head-of-line blocking (bloqueio do primeiro
da fila, em traduo livre), que ocorre quando um recurso de
tamanho grande (por exemplo, uma imagem de fundo de 1MB)
bloqueia todos os outros recursos de serem baixados at que ele
seja baixado completamente.
Outra vantagem do HTTP 2 o conceito de Server Push. Este
representa a capacidade de um servidor de enviar um recurso
ou contedo para um cliente/navegador web sem que o mesmo
tenha solicitado. Por exemplo, quando um navegador visita um
site, o servidor envia o logotipo ao mesmo sem que seja necessrio haver uma solicitao. Essa atuao proativa do servidor
permite que os recursos sejam carregados muito mais rpido do
que anteriormente.
Vale ressaltar, ainda, que o HTTP 2 funciona melhor utilizando
a proteo dos dados trafegados via SSL. A conjuno entre o protocolo HTTP e a criptografia SSL chamada de protocolo HTTPS.
Saiba que, embora seja possvel fazer a transferncia de dados sem
essa proteo na verso 2, o protocolo SPDY exigia a utilizao
do HTTPS. Deste modo, por questes de compatibilidade com o
SPDY, boa parte dos servidores web requer a segurana via SSL,
o que acaba sendo positivo, pois fora a proteo dos dados dos
usurios na comunicao entre computadores.

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

O que isso significa para o Java?


O Java, desde a verso 1.0, prov a utilizao do protocolo HTTP.
Entretanto, grande parte do cdigo que garante esse suporte de
um perodo anterior ao surgimento desse protocolo e por conta
disso foi baseado em bibliotecas de comunicao independentes
de redes (classe URL, por exemplo). Nesse ponto interessante
lembrar que quando o protocolo foi inserido na API do Java no era
esperado que a web pudesse se tornar to dominante e acessvel
como hoje. Alm disso, nesse mesmo perodo, como o HTTPS
ainda no tinha sido criado, por motivos bvios no foi includo
na API, o que ocorreu posteriormente.
Lembre-se, ainda, que o protocolo HTTP e a web possuem
dcadas de existncia e somando-se o fato de no terem acompanhado a demanda (uso de streaming, interfaces responsivas e
mais pesadas, entre outros), apresentam hoje uma baixa eficincia
para trafegar dados. Como se no pudesse piorar, atualmente a
preocupao com a segurana dos dados to grande que mesmo
os sites mais simples esto adotando o HTTPS. No entanto, esse
protocolo ainda mais lento que o HTTP, pois necessita criptografar e descriptografar tudo o que transporta. Como resultado
de tudo isso, a baixa capacidade de lidar com um mundo cada vez
mais gil e conectado e volumes de dados cada vez maiores atravs
da rede culmina em crticas por parte dos usurios, que tm a sua
experincia de navegao e utilizao comprometidas.
Para dificultar ainda mais, vlido ressaltar que o recurso fornecido pelo JDK ao HTTP tambm possui grandes limitaes, no
permitindo, por exemplo, fazer uso do mesmo na ntegra, ou seja,
os desenvolvedores, alm de terem que lidar com as deficincias
do protocolo, no conseguem explorar todos os seus recursos. Essa
deficincia do JDK faz com que muitos desenvolvedores adotem
bibliotecas de terceiros, como a Apache HttpComponents, o que
torna a criao de uma API que fornea suporte completo ao
HTTP/2 algo primordial para o Java, visto que esse ser um dos
recursos mais importantes durante a prxima dcada.
Em vista disso, a nova API do HTTP contida no Java 9 servir
de base para codificar com a verso 2 do protocolo. Para isso, a
API Java que era compatvel com a verso 1 do HTTP teve seu
cdigo reescrito, valendo-se da necessidade de ser independente
da verso do HTTP (retrocompatvel) e ofertar formas de utilizar as funcionalidades do HTTP/2. Ademais, para utilizar esse
protocolo em aplicaes Java ser bastante simples, bastando
implant-las em servidores web que funcionem com a nova verso desse protocolo. Essa facilidade um incentivo ao desuso de
solues incomuns (hacks ou gambiarras) que buscavam contornar
as limitaes do HTTP 1.1 nas aplicaes legadas.

Melhorias na API de processos


Atualmente o Java fornece um recurso limitado para o controle de
processos (threads ou tarefas) do sistema operacional, pois prov
apenas uma API bsica para configurar o ambiente e inicializar
tarefas, dificultando a manipulao das mesmas. Para conseguir o
nmero de identificao de um processo (PID - Process ID), por exemplo, preciso usar cdigo nativo do SO. Assim, caso a aplicao seja

executada em diferentes sistemas operacionais, necessrio ter uma


verso implementada com o cdigo especfico para cada um.
A Listagem 10 expe o cdigo para recuperar o PID de um
processo no Linux utilizando a verso atual do Java. Note que
h, na segunda linha, um cdigo que faz uso de comandos nativos (por exemplo, /bin/sh) para realizar uma tarefa simples. Com
isso, a implementao se torna mais difcil, o mesmo vale para a
manuteno, e o cdigo ainda estar mais sujeito a erros. Enfim,
fundamental oferecer uma soluo que evite o uso de cdigo
nativo para controlar processos.
Listagem 10. Impresso do nmero de identificao de um processo utilizando a
verso atual da API.
public static void main(String[] args) throws Exception{

Process proc = Runtime.getRuntime().exec(new String[]{ /bin/sh, -c,
echo $PPID });
if (proc.waitFor() == 0)
{
InputStream in = proc.getInputStream();
int available = in.available();
byte[] outputBytes = new byte[available];
in.read(outputBytes);
String pid = new String(outputBytes);
System.out.println(ID do processo: + pid);
}
}

Com base nisso, o Java 9, atravs da JEP 102, aprimora a API


de processos oferecendo uma soluo simples, mas com vrias
funcionalidades, que permite um maior controle sob os processos
do sistema operacional. Dentre as funcionalidades, destaca-se a
capacidade de obter o PID ou outros detalhes do processo da JVM
sobre a qual a aplicao est rodando, bem como informaes de
processos criados atravs da API, como pode ser observado nas
Listagens 11e 12.
Na primeira listagem temos a implementao de uma classe que
tem a finalidade de obter as informaes do processo da JVM que
est executando a aplicao e imprimi-las no terminal do sistema
operacional. Para isso, a varivel processoAtual, da classe ProcessHandle, obtm o controle do processo que est em execuo
atravs do comando ProcessHandle.current(). Em seguida, h a
execuo do System.out.println(), que imprime um cabealho,
e uma chamada ao mtodo imprimeDetalhesProcesso(), que
recebe como parmetro processoAtual. Esse mtodo obter as
informaes do processo atravs do comando processoAtual
.info() e ir armazen-las na varivel processoAtualInfo, da classe ProcessHandle.Info. Feito isso, com as informaes do processo
em mos, basta imprimi-las no terminal atravs do println(), que
pode ser observado no restante do cdigo.
J na Listagem 12 temos a implementao de uma classe que
cria um processo que abre o terminal do Windows e lista todas
as informaes desse processo. Essa classe inicia sua execuo
atravs da chamada ao mtodo exec() Runtime.getRuntime()
.exec(cmd /k dir) o qual retorna um objeto Process que

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

45

45

Introduo ao Java 9: Conhea os novos recursos

representa o processo recm-criado para a execuo do comando cmd /k dir, passado como parmetro. Em seguida, o
mtodo imprimeDetalhesProcesso(), cujo cdigo pode ser
visto na Listagem 11, invocado, recebendo como parmetro
um controlador do processo (ProcessHandle) obtido atravs de
processo.toHandle().
Listagem 11. Impresso na tela das informaes do processo atual com a nova API.
public class ExemploProcessoAtual{
public static void main(String[] args){
//Pega o controle (handle) do processo em andamento
ProcessHandle processoAtual = ProcessHandle.current();
System.out.println(**** Informacao do processo atual ****);
imprimeDetalhesProcesso(processoAtual);
}

public static void imprimeDetalhesProcesso(ProcessHandle processoAtual){
//Retorna uma instncia do objeto que referencia as informaes do processo
ProcessHandle.Info processoAtualInfo = processoAtual.info();
if (processoAtualInfo.command().orElse().equals()){
return;
}
//Imprime o id do processo na tela
System.out.println(PID: + processoAtual.getPid());
//Imprime o comando de incio do processo na tela. Se no tiver um comando,
//retornar vazio - orElse()
System.out.println(Comando: + processoAtualInfo.command().orElse())
//Imprime os parmetros do comando de incio do processo na tela
String[] parametros = processoAtualInfo.parametros().orElse(new String[]{});
if ( parametros.length != 0){
System.out.print(parametros: );
for(String parametro : parametros){
System.out.print(parametro + );
}
System.out.println();
}
//Imprime o horrio de incio do processo na tela
System.out.println(Inicio: + processoAtualInfo.startInstant().
orElse(Instant.now()).toString());
//Imprime o tempo que o processo est rodando
System.out.println(Rodando:+ processoAtualInfo.totalCpuDuration()
.orElse(Duration.ofMillis(0)).toMillis() + ms);
//Imprime o nome do usurio ao qual o processo est vinculado
System.out.println(Usuario: + processoAtualInfo.user().orElse());
}
}
Listagem 12. Cdigo que cria um processo e imprime suas informaes.
import java.io.IOException;
import java.io.*;
import java.util.*;

46

Nota
Quando se fala em subprocessos no ter comandos para inicializa-los, no quer dizer que eles no
realizam tarefas, mas sim que os processos que os gerenciam que so responsveis por inicializ-los,
manipul-los e encerr-los.

Como precisamos acessar cada processo para obter suas informaes, utilizamos o mtodo ProcessHandle.allProcesses(). Esse
mtodo retorna uma stream e a partir dela realizamos as filtragens
com o comando .filter(processHandle -> processHandle.info()
.command().isPresent(), que usa o controlador para acessar as
informaes dos processos que tm um comando associado (isPresent()). Em seguida, pode-se identificar que a lista ser restrita
a trs processos com a chamada a .limit(3). Por ltimo, tem-se a
etapa da execuo do cdigo que envolve imprimir as informaes
de cada processo da lista final. Para isso, chamado o mtodo
forEach(), que percorre a lista de processos e imprime no terminal
de linha de comando as informaes dos mesmos valendo-se do
mtodo imprimeDetalhesProcesso().
Listagem 13. Imprime as informaes de trs processos que esto rodando no
sistema operacional
public class ExemploListagemTodosProcessosSO{
public static void main(String[] args){
//lista os processos que esto rodando no sistema operacional
ProcessHandle.allProcesses()
.filter(processHandle -> processHandle.info().command().isPresent())
.limit(3)
.forEach((processo) ->{
imprimeDetalhesProcesso(processo);
});
}
public static void imprimeDetalhesProcesso(ProcessHandle processoAtual){

public class Exemplo2{


public static void main(String[] args) throws IOException{
//Cria um processo
Process processo = Runtime.getRuntime().exec(cmd /k dir);
System.out.println(**** Detalhes do processo criado ****);
//Imprime detalhes do processo criado
imprimeDetalhesProcesso(processo.toHandle());
}

public static void imprimeDetalhesProcesso(ProcessHandle processoAtual){
//contedo igual ao mesmo mtodo na listagem 11
}
}

46 Java Magazine Edio 150

A Listagem 13, por sua vez, apresenta um cdigo que demonstra


a capacidade de listar processos que esto rodando no sistema
operacional, bem como suas propriedades (PID, nome, estado,
etc.). Contudo, como a lista de processos muito grande, para
demonstrar mais um recurso da nova API codificaremos alguns
filtros para limitar o resultado. Assim, filtraremos os processos
que possuam um comando de inicializao associado, de forma a
no obter subprocessos uma vez que eles no possuem comandos
para inicializ-los , e limitaremos a seleo a trs processos.

//contedo igual ao mesmo mtodo na listagem 11


}
}

As demonstraes realizadas nas listagens anteriores, principalmente na Listagem 10, reforam a necessidade de implementao
de uma soluo para tal problema. Felizmente, a nova verso da
API de processos resolve essa antiga pendncia, permitindo controlar centenas de processos com uma nica thread, diminuindo
a complexidade da codificao e maximizando o desempenho
das solues que fazem uso desse recurso.

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Portanto, com a nova API os desenvolvedores tero muito mais controle sobre os processos em um sistema operacional, alm do bvio
ganho em produtividade e segurana. Apesar disso, recomendado
muito cuidado ao utilizar todo esse controle, pois pode interferir na
estabilidade do ambiente que roda a aplicao e por isso imprescindvel ter um conhecimento aprimorado desses recursos.
Como verificado, o Java 9 trar melhorias importantes e h muito
esperadas pela comunidade, empresas e desenvolvedores que fazem uso da plataforma. Essas novidades, em sua maioria, tm como
meta obter ganhos em termos de desempenho e, principalmente,
escalabilidade, a fim de atender tambm dispositivos com pouco
poder de processamento. Sendo assim, podemos afirmar que a
novidade de maior destaque mesmo o sistema de mdulos.
Essa nova estrutura do Java possui muitas facetas e por isso alguns
detalhes no foram descritos neste documento, devido ao espao.
No entanto, grande dos desenvolvedores precisa conhecer apenas
alguns conceitos, como os apresentados, pois so os que realmente
sero utilizados no dia a dia, a exemplo da declarao de mdulos
e da criao de arquivos JAR modulares.
Em relao ao JShell, espera-se que seja uma ferramenta notvel
para diminuir, principalmente, a curva de aprendizado da linguagem. Logo, a expectativa que os aprendizes faam bastante uso da
mesma, como tambm os usurios mais avanados, a fim de testar
alguns comandos e expresses antes de efetivamente inseri-los nas
aplicaes. Ademais, essa funcionalidade aproxima a linguagem
Java das linguagens de script, as quais j possuem solues como
essa por padro.
O JMH, por sua vez, um recurso que dever ser explorado por
desenvolvedores mais avanados, que possuem conhecimento em
testes de desempenho, visto que difcil obter resultados precisos
com esse tipo de teste. Entretanto, uma API de extrema importncia, uma vez que o Java no possua uma forma de implementar
tais testes sem recorrer a bibliotecas de terceiros, o que dificultava
bastante a obteno de resultados satisfatrios.
J a definio do G1 como o coletor de lixo padro uma alterao bem-vinda e bastante esperada pela comunidade, aps longos
debates acerca do assunto. Essa alterao permitir menos pausas
nas aplicaes e garantir uma melhor experincia ao usurio.
Nesse momento algumas pessoas podem argumentar que a opo de escolher o G1 como coletor de lixo j existia nas verses
anteriores, porm isso no deixa essa mudana menos importante,
pois muitos desenvolvedores e administradores no sabiam da
existncia dessa opo.
Com relao ao HTTP 2.0, um protocolo que promete ser uma
revoluo na Internet, ao oferecer maior capacidade de transferncia
de contedo atravs da multiplexao e compresso de dados. Essa
uma evoluo indispensvel e que vem para suprir as deficincias
da verso atual do HTTP. Apesar de ainda pouco adotado, por ser
bastante recente, a tendncia que seu uso cresa exponencialmente
ao longo dos anos. Portanto, fundamental que o Java se antecipe
e disponibilize uma API que possibilite o uso do novo protocolo
nas aplicaes, o que se materializar com o advento da verso 9
da plataforma.

Por ltimo, temos as melhorias na API de processos, que embora


paream de menor relevncia, sero de grande ajuda para assegurar a manutenibilidade do cdigo de aplicaes, frameworks e
servidores de aplicao (WildFly, GlassFish, WebLogic, Jetty, etc.)
que necessitam fazer uso de paralelismo ou acessar processos do
sistema operacional. Essas mudanas, assim como a modularizao, exigiram bastante trabalho por parte da comunidade, pois
foi necessrio modificar a API original de cada plataforma para
a qual a JVM est disponvel.
Como verificado, com o Java 9 grandes avanos sero possveis,
o que torna imprescindvel aos desenvolvedores dominar as novidades a fim de tirar o melhor proveito de todos os recursos. Com
esse intuito, neste artigo comeamos a dar os primeiros passos.

Autor
Jos Guilherme Macedo Vieira
jguilhermemv@gmail.com
Arquiteto de Software e desenvolvedor Java EE de uma das empresas pblicas de tecnologia da informao mais respeitadas
do pas, a DATAPREV. Possui mais de 10 anos de experincia na plataforma
Java. Projeta solues de alta escalabilidade, fazendo uso, em especial,
da plataforma Java EE. Tem ministrado diversos treinamentos in-company, bem como
participado de projetos inovadores na rea de Big Data. Pesquisador e entusiasta do
Apache Cassandra, com o qual trabalhou em projetos em reas como anlise de riscos e
deteco de fraudes. Colaborador da revista Java Magazine.
Links:
Modularidade do Java 9.
http://paulbakker.io/java/java-9-modularity/
Especificao do sistema de mdulos.
http://openjdk.java.net/projects/jigsaw/spec/sotms/
Especificao para tornar o G1 o coletor de lixo padro.
http://openjdk.java.net/jeps/248
Especificao do JMH.
http://openjdk.java.net/jeps/230
Especificao das melhorias da API de processos.
http://openjdk.java.net/jeps/102
Especificao da API do HTTP/2.
http://openjdk.java.net/jeps/110
Projeto Kulla
https://adopt-openjdk.ci.cloudbees.com/view/OpenJDK/job/langtools-1.9-linux-x86_64kulla-dev/lastSuccessfulBuild/artifact/

Voc gostou deste artigo?


D seu voto em www.devmedia.com.br/javamagazine/feedback
Ajude-nos a manter a qualidade da revista!

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

47

47

Simplificando o
desenvolvimento de
microsservios com o
WildFly Swarm
Conhea neste artigo uma nova abordagem da
Red Hat para criar microsservios utilizando Java

expanso da internet no Brasil e no mundo


traz consigo novas necessidades para quem
trabalha com toda a espcie de ferramenta ou
equipamento que faz esse grande universo de contedo
virtual girar. Esse vido anseio por um mundo mais
conectado gera novos desafios para vrios campos da
computao. Desafios esses que vo desde a necessidade
de hardwares cada vez mais potentes at a preocupao
cada vez maior com questes relacionadas segurana
e performance. Isso acaba por surtir um impacto direto
no desenvolvimento de novas ferramentas para esse
mundo mais conectado.
Diante disso, atualmente, uma aplicao serve como
uma ferramenta estratgica da empresa, funcionando
de maneira similar a um hub de informaes e fluxos
dentro dela, ou mesmo como um produto final entregue
aos seus clientes. Nessa nova realidade, arquitetos e
desenvolvedores presenciam uma verdadeira revoluo
que vem acontecendo em nossa rea, impulsionada
pelas questes supracitadas que se tornam a cada dia
mais crticas.
Escalabilidade e alta disponibilidade, por exemplo,
nunca foram itens to essenciais, exigindo que as equipes de desenvolvimento estejam em constante evoluo.
Para arquitetos de software e gerentes, no entanto, ainda
pesa outro elemento no momento de planejar a soluo,
o custo. Apesar de ser um fator que deve ser considerado

48

Fique por dentro


Este artigo apresenta o Swarm, nova abordagem oferecida pela Red
Hat para implementar microsservios em Java. Assim, desenvolvedores
que desejam aprender sobre o assunto podem utilizar esse contedo
como ponto de partida para entender como migrar suas aplicaes ou
cri-las do zero adotando essa ferramenta. Alm disso, vamos analisar
o atual estado do projeto, funcionalidades que j so suportadas e a
expectativa para as prximas verses.

em qualquer empresa em nvel global, no Brasil ainda temos a


questo da alta do dlar, que atua como um agravante.
Quando falamos em aplicaes que possuem um grande
fluxo de usurios, como grandes portais, existe a tendncia de
disponibilizar seus servios atravs de servidores localizados
em nuvem, como a Amazon ou o Microsoft Azure. Contudo,
apesar de grandes empresas fornecedoras de servios em nuvem j disponibilizarem servidores no Brasil ou pelo menos
na Amrica Latina, a contratao desses servios acaba sendo
afetada diretamente pela variao cambial, por ser taxada em
dlar. Ainda assim, mesmo sofrendo com aspectos sobre os
quais no temos controle, podemos melhorar o custo otimizando cdigo, alm de um setup mais refinado da estrutura
que funciona como base.
Para tanto, lembre-se que grandes aplicaes exigem tambm uma equipe alinhada. Assim, ferramentas que facilitem a

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

comunicao e a gesto das pessoas ganham importncia por


organizarem e disseminarem informao de maneira rpida e
gil. O aplicativo Slack uma soluo que aposta fortemente na
comunicao, no somente entre pessoas, mas tambm entre equipes, departamentos, filais e ferramentas, oferecendo integrao
a uma vasta gama de opes. Nesse contexto, aplicaes como
o Octopus, Deploybot e Jenkins podem, atravs dela, manter
os interessados informados de um modo mais eficiente e sem a
necessidade de criar hooks dentro das mesmas para estabelecer a
relao com o Slack, possibilitando uma melhor comunicao sem
grande esforo. Desse modo, todos os envolvidos nas diferentes
fases do projeto conseguem ser notificados de uma maneira mais
rpida e simples.
Nesse cenrio, o desenvolvimento tambm atingido por tais
mudanas, entrando em cena novas ferramentas que oferecem a
oportunidade de fazer mais com menos e de forma mais rpida.
Vrias delas que h poucos anos nem existiam e hoje so referncias de mercado, como o caso do MongoDB; ou voc, h seis anos,
pensaria em usar uma ferramenta para armazenar seus dados que
no adota pelo menos as trs formas normais?
No que tange a arquitetura de software, um modelo que vem
sendo adotado por grandes portais, inclusive no Brasil, o modelo
de microsservios. Impulsionada por especialistas como Martin
Fowler, a ideia ganha apoiadores a cada dia. Em um modelo monoltico, onde a aplicao pensada e construda como um bloco
nico, qualquer necessidade de aumentar a performance precisa
ser pensada e realizada de um modo geral, afetando o todo. Por
outro lado, quando a aplicao dividida em microsservios, s
existe a necessidade de escalarmos aqueles que necessitam da
performance extra, o que reflete diretamente no custo de alavancar
somente uma pequena parte da aplicao.
A ideia de dividir para melhor gerenciar recursos no algo novo
no universo Java. Este j apresenta diferentes solues preparadas
para essa nova maneira de se construir servios, a exemplo do
Spring Boot, uma das mais conhecidas. Tendo em seu core implementaes dos containers Tomcat, Jetty e Undertow, oferece uma
configurao rpida e simples, bastando poucas linhas para voc
ter um container web funcional com sua aplicao j embarcada
e sem a necessidade de um deploy para tal.
Observando esse novo cenrio, a Red Hat tambm vem buscando inovar. Na verso 9 do WildFly, por exemplo, houve muitas
mudanas, como a implementao do Undertow no lugar do
Tomcat, buscando assim tornar seu servidor web um container
mais flexvel, sem deixar de fornecer tudo o que um container full
Java EE pode oferecer. Contudo, para abraar de vez a realidade
das aplicaes voltadas para microsservios, foi necessrio mais
uma vez reinventar o container. A modularizao apresentada na
verso 7 do JBoss AS foi reestruturada e a maioria dos elementos
que eram carregados por padro na inicializao do container
foram removidos. Agora, qualquer elemento que seja necessrio
para a aplicao precisa ser adicionado manualmente.
O fruto de toda essa reformulao gerou um novo produto, o
WildFly Swarm, que promete no ser um novo container, como

consta na prpria pgina da ferramenta, e sim uma nova maneira


de empacotar e rodar aplicaes Java EE, fazendo isso de maneira
flexvel, por carregar somente o que a aplicao precisa; madura,
por respeitar todos os padres do escopo enterprise do Java; e
direta, por carregar seu projeto e o container em um nico JAR.

Por que Java e microsservios so uma boa ideia?


Caso voc j tenha trabalhado com outras linguagens ou mesmo
acompanha outras tecnologias, sabe que muitas vezes o Java tido
por outras comunidades como uma linguagem na qual no fcil
produzir algo de modo rpido. E a maneira como a linguagem
trata seus novos adeptos tambm no das mais sutis, exigindo
muitas horas de estudo e prtica at que voc realmente consiga
dar frutos de qualidade. Outro aspecto que, por muitas vezes,
um projeto relativamente pequeno pode chegar em poucos ciclos
de desenvolvimento casa de dezenas de dependncias, demandando mais do container.
Note que ao falar dessa maneira at podemos ter a impresso
de que falta inovao nesse universo. No entanto, observando
os ltimos anos podemos constatar que o Java tem buscado
adotar vrios aspectos de outras linguagens, como as expresses
lambda, para atualizar seu repertrio nativo e das bibliotecas
que lhe do suporte. Dessa maneira, ela consegue melhorar seu
relacionamento com os desenvolvedores, pois o desejo da comunidade impulsiona a linguagem, justamente por ser um grupo que
vocaliza muito bem seus anseios. Com isso, novas metodologias
como microsservios logo ganham implementaes a exemplo
do Spring Boot e do WildFly Swarm que permitem transformar
containers robustos e us-los como pequenos servios.
A construo de aplicaes utilizando microsservios, faz voc
pensar de maneira diferente em como dividir a aplicao, e a
preparar para crescer ao longo do tempo.

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

49

49

Simplificando o desenvolvimento de microsservios com o WildFly Swarm

Figura 1. Aplicao carregada em um modelo padro

Desse modo ganhamos mais possibilidades, podendo focar em


pequenas partes sem a necessidade de escalar o todo. Alm disso, a
lgica de negcio pode ser desenvolvida por equipes e tecnologias
diferentes, pois a aplicao deixa de ser um bloco monoltico para
se tornar um conjunto formado de partes menores, que unidas
formam o todo.
Diante disso, a aplicao se torna orgnica, mutvel, pois a no
obrigatoriedade de seguir uma linguagem ou framework torna
mais flexvel a migrao para algo mais performtico sem grandes
impactos. Por termos pequenas aplicaes agrupadas e se por
qualquer que seja o motivo exista a necessidade de migrar toda
a parte de persistncia de um Hibernate para um EclipseLink,
podemos realizar tal tarefa de modo gradativo, atualizando um
servio por vez, como tambm temos a possibilidade de manter as
duas implementaes, pois uma pode tratar determinado servio
de maneira mais eficiente que outra.
Saiba, ainda, que no gerenciamento de dependncias tambm
existem ganhos. Em um projeto monoltico, muitas vezes temos
bibliotecas que atendem apenas a uma pequena parte da aplicao.
Quando dividimos o projeto em vrios servios, podemos carregar
menos bibliotecas em cada um, o que torna o gerenciamento das
classes pela JVM uma tarefa mais amena, pois a mquina virtual

precisar manter uma quantidade menor de classes carregadas


em cada uma das suas instncias.
Assim, com o intuito de abranger essa nova proposta para a
construo de aplicaes, acrescentando tudo que j foi consolidado no WildFly, foi criado o Swarm, soluo que se beneficia de
caractersticas como modularizao para possibilitar extrair mais
performance do container e utilizar menos recursos de software
e hardware.
Enfim, o projeto Swarm vem para reforar esse princpio, mas
com a ideia de ir alm da modularizao do container, se oferecendo como uma ferramenta que busca auxiliar o desenvolvedor
a construir aplicaes com partes menores que funcionam de
maneira independente, mas mantendo a comunicao entre si.

O antes e o depois
Ao utilizar um servidor de aplicaes como o WildFly para
desenvolver solues que adotam uma arquitetura voltada para
microsservios, realizamos o setup inicial do container e, ento,
iniciamos o desenvolvimento da soluo em si. O problema
de adotar servidores de aplicaes padro para arquiteturas
desse tipo que provavelmente voc acabar carregando mais
funcionalidades do que sua soluo necessita, o que pode levar
ao consumo excessivo de recursos. A Figura 1 demonstra essa
afirmao, mostrando que o servio MyApp precisa somente da
API JAX-RS, mas o WildFly carrega vrias outras APIs.
Para lidar com esse problema o Swarm faz uso de um recurso
bem conhecido no Java, o Uber JAR (ou Fat JAR). Diferente do modelo convencional, esse tipo de JAR contm no somente o projeto
final compilado, mas pode tambm conter todas as dependncias
embutidas (a maneira como ele empacotado lembra muito a dos
arquivos WAR). A diferena do arquivo gerado que ele contm
tambm uma instncia do Swarm. Assim, o que temos ao final
um arquivo auto executvel que contm a aplicao e o prprio
container com os mdulos selecionados pelo desenvolvedor.
A Figura 2 mostra a estrutura do servio MyApp nesse modelo.
O resultado dessa mudana uma menor utilizao de recursos,
gerando um nico artefato de tamanho reduzido.

Um pouco de histria
Para compreender o Swarm, no podemos ignorar a histria
que veio antes dele. E caso nunca tenha utilizado o JBoss AS, a

50 Java Magazine Edio 150

50

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

contextualizao vlida para que voc no se perca naquilo que


pode tornar uma verdadeira sopa de letrinhas.

como parada e inicializao do container, deploy de aplicaes,


configuraes de portas e pools, etc.
No entanto, em 2013 o projeto foi renomeado para WildFly, e um
dos motivos para tal mudana foi o fato de termos duas verses
distintas do JBoss: a verso AS, para a comunidade; e o JBoss EAP,
para as aplicaes enterprise, oferecendo, tambm, como grande
diferencial, suporte direto da Red Hat e seus parceiros.
Por fim, em 2015 foi criado o Swarm, que no vem como um
substituto para o JBoss AS ou para o WildFly, mas sim como uma
opo encontrada pela Red Hat para descontruir e reconstruir
o container de um modo que atenda as aplicaes focadas em
microsservios; algo que s foi possvel graas reformulao
do JBoss AS em 2011. Agora, com o Swarm, s precisamos ser
especficos em quais so as nossas necessidades e tudo que no
for explicitamente declarado no estar presente na hora que o
projeto for iniciado.
Nota

Figura 2. Modelo de um servio criado com o Swarm


Em meados de 1999, nasceu a primeira verso do JBoss, chamado de EJBoss (Enterprise Java Beans in Open Source System). Um
ano aps esse lanamento, saiu a verso 1.0 e o nome do projeto
passou a se chamar JBoss AS, ou JBoss Application Server. Nessa
poca o projeto era liderado por Marc Fleury, um dos responsveis
por alavancar a ferramenta. Com passar do tempo, o container
ganhou fora e seguiu evoluindo com o Java ao longo dos anos,
impulsionado por uma forte comunidade; a mesma comunidade
que viu o JBoss ganhar novas funcionalidades e muitas vezes, por
consequncia, se tornar mais pesado. Ento, j em 2011, o container
foi totalmente remodelado. Nascia, assim, a verso 7, que trouxe
uma estrutura modularizada com a adoo do OSGI.
A partir de ento, passamos a obter um maior controle das dependncias, alm do ciclo de vida dos mdulos. A configurao
do servidor tambm se tornou mais simples e de muitos arquivos
antes necessrios, passamos a utilizar apenas dois: o standalone
.xml, para quando o ambiente no for clusterizado; e o domain
.xml, para quando o ambiente for clusterizado. Desse momento
em diante todas as configuraes do container, desde o pool de
conexes, threads, entre outras, passaram a ser referidas como
subsistemas, o que fez com que os desenvolvedores tivessem que
reaprender a lidar com os recursos conhecidos de uma maneira
diferente.
Nota
O OSGI pode ser visto como um conjunto de especificaes responsvel por definir um ambiente de
computao padronizado e orientado a componentes.

Outra novidade muito bem-vinda foi a implementao do CLI,


que se tornou uma poderosa ferramenta para configurarmos o
ambiente, alm de facilitar a automatizao de vrias operaes,

Caso queira saber mais sobre a histria do JBoss AS, em edies anteriores da Java Magazine existem
vrios artigos que explicam detalhadamente as diferenas entre todas as verses.

Qual o impacto ao migrar uma aplicao para o Swarm?


Possibilitar que o usurio migre seu projeto que funciona no
JBoss AS ou no WildFly sem grandes esforos uma das grandes
preocupaes demonstradas pela equipe do projeto. Contudo,
levar seu projeto para o Swarm significa dividi-lo em partes
mentores e para isso existe a possibilidade de alguns elementos
precisarem ser alterados. Se isso ocorrer, provavelmente ser
porque sua aplicao deixar de ser uma soluo monoltica para
ser dividida em pequenos servios independentes.
Neste momento, a camada de persistncia ainda um ponto que
gera muita discusso, por no haver um consenso de que cada
microsservio deve ter ou no sua prpria base de dados. Note que
tal deciso pode impactar diretamente na quantidade de esforo
necessrio na hora de migrar sua aplicao, pois dividir algo que
foi pensado para trabalhar como um bloco nico, se mal planejado,
pode gerar cdigos repetidos em servios diferentes.
Outro possvel problema que ao criar microsservios teremos
mais pools de conexo ativos com o banco de dados e isso pode
vir a ser um gargalo. Assim, importante analisar como cada
parte da aplicao lida com a informao. Ao realizar essa tarefa
conseguimos identificar que nem tudo que est ligado diretamente
ao banco precisa realmente estar, o que nos permite considerar a
adoo de ferramentas que vo auxiliar a dispersar as solicitaes
realizadas diretamente s bases de dados. Neste momento uma
boa opo avaliar o que Martin Fowler chamou de persistncia
poliglota, o qual possibilita que cada servio seja atendido pelo
tipo de banco de dados que mais acolhe suas necessidades. Dessa forma, um servio de busca, por exemplo, pode se beneficiar
de solues como o ElasticSearch, dados que no possuem uma
estrutura relacional podem estar em um MongoDB, assim como
outros que possuem pouca ou nenhuma taxa de alterao podem

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

51

51

Simplificando o desenvolvimento de microsservios com o WildFly Swarm

ser acessados via Redis. No entanto, tenha em mente tambm que


ao agregar tecnologias a mais, alm do ganho em flexibilidade e,
provavelmente, desempenho, teremos um custo e complexidade
maiores, afinal, todo o planejamento relacionado disponibilidade
e escalabilidade dever ser aplicado s novas tecnologias, assim
como teremos que contar com uma equipe mais qualificada e/
ou maior.

Nosso primeiro projeto com Swarm


Para a nossa primeira experincia com o Swarm, vamos criar um
pequeno projeto com um servlet simples que responder a uma
requisio e mostrar o famoso Hello World. Deste modo, crie
um novo projeto utilizando o archetype Quickstart do Maven,
ou seja, abra o prompt de comando no Windows ou o terminal
no Linux/Mac e digite o comando:

Certifique-se que o nmero das verses esteja igual ou superior


ao da Listagem 1, pois so as verses mnimas requeridas para se
trabalhar com o Swarm. Portanto, se sua aplicao possui alguma
dependncia ou recurso restrito do Java 7, preciso primeiro
atualizar a aplicao e suas dependncias. Por ltimo, na linha 7
definimos a verso do Swarm que ser adotada na declarao das
dependncias. Assim, garantimos que todos os recursos dessa
soluo que utilizaremos no projeto sejam compatveis.
Conforme j mencionado, sabemos que o Swarm modularizado. Dessa forma, atravs das dependncias que informamos
no POM que controlamos os mdulos que sero utilizados em
nosso projeto.
Listagem 1. Variveis de configurao para o projeto.
01. <properties>

mvn archetype:generate -DgroupId=br.com.devmedia -DartifactId=SwarmStart

02.

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

-DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

03.

<project.reporting.outputEncoding>UTF-8</project.reporting

Como podemos notar, o projeto criado extremamente simples


e traz somente uma nica dependncia, o JUnit.
Neste momento, abra a pasta do projeto e, em sua raiz, acesse
o arquivo pom.xml para adicionar as propriedades conforme a
Listagem 1. Vale destacar que o Swarm tambm oferece integrao
com o Gradle, mas at a verso na qual essa matria se baseou
ainda no existia documentao sobre isso.
Alm das duas propriedades iniciais, que indicam que nosso
cdigo est em UTF-8 e que deve ser compilado da mesma forma,
na linha 4 definimos a verso mnima do Maven. J nas linhas
5 e 6 definimos a verso 8 do Java para o cdigo fonte e tambm
para os arquivos compilados.

04.

<maven.min.version>3.2.1</maven.min.version>

05.

<maven.compiler.target>1.8</maven.compiler.target>

06.

<maven.compiler.source>1.8</maven.compiler.source>

07.

<version.wildfly-swarm>1.0.0.Alpha6</version.wildfly-swarm>

.outputEncoding>

08. </properties>

A Listagem 2 traz a declarao das duas dependncias do Swarm


que faremos uso em nosso primeiro exemplo.
A primeira o Undertow, novo servidor web do WildFly que vem
para substituir o antigo JBoss Web Server, um fork do Tomcat. Essa
dependncia agrega muitas funcionalidades ao Swarm, dentre elas
a possibilidade de utilizarmos JavaScript em nosso back-end. J
conhecido no WildFly por ser flexvel e performtico, o Undertow
trabalha com APIs bloqueantes e no bloqueantes baseadas em
NIO e oferece suporte a Servlets 3.1.
Listagem 2. Declarao do Undertow e do Weld.
01. <dependency>
02.

<groupId>org.wildfly.swarm</groupId>

03.

<artifactId>undertow</artifactId>

04.

<version>${version.wildfly-swarm}</version>

05. </dependency>
06. <dependency>
07.

<groupId>org.wildfly.swarm</groupId>

08.

<artifactId>weld</artifactId>

09.

<version>${version.wildfly-swarm}</version>

10. </dependency>

A segunda dependncia o Weld, implementao do CDI da


Red Hat que possibilita a criao de contextos e injeo de dependncias para a plataforma Java EE. Por ser um padro JCP, est
presente em vrios servidores alm do prprio Swarm, como o
WebSphere, GlassFish, Oracle WebLogic, dentre outros.

52 Java Magazine Edio 150

52

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Ademais, pode ser encontrado em implementaes para ambientes servlet-only como o Tomcat e o Jetty.
Agora necessitamos declarar os plug-ins que sero utilizados
na fase de build do nosso projeto. A Listagem 3 mostra cada um
deles.
Listagem 3. Plugins utilizados no projeto para configurao e gerao do WAR,
controle do Swarm.
01. <build>
02.

<finalName>${project.artifactId}</finalName>

03.

<plugins>

04.

<plugin>

05.

<groupId>org.apache.maven.plugins</groupId>

06.

<artifactId>maven-war-plugin</artifactId>

07.

<version>2.6</version>

08.

<configuration>

09.
10.

<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>

11.

</plugin>

12.

<plugin>
<groupId>org.wildfly.swarm</groupId>

14.

<artifactId>wildfly-swarm-plugin</artifactId>

15.

<version>${version.wildfly-swarm}</version>

16.

<executions>

17.

<execution>

18.

<goals>
<goal>package</goal>

20.

</goals>

21.

</execution>

22.

</executions>

23.

<configuration>

24.
25.

<bundleDependencies>true</bundleDependencies>
</configuration>

26.

</plugin>

27.

<plugin>

28.

<groupId>org.apache.maven.plugins</groupId>

29.

<artifactId>maven-compiler-plugin</artifactId>

30.

<version>3.3</version>

31.

<configuration>

32.
33.
34.
35.
36.

Listagem 4. Implementao da classe HelloSwarm.


01. package br.com.devmedia;

13.

19.

instalado na mquina tem a verso 3.3, conforme a linha 30.


J nas linhas 32 e 33 garantimos que a verso do Java seja a
oito, para atender a necessidade do Swarm, que necessita dessa
verso ou superior.
A novidade na Listagem 3 fica por conta do plugin do Swarm.
Atravs dele podemos configurar vrios aspectos de como queremos que nossa aplicao seja tratada. Nesse caso, informamos
que queremos que as dependncias da aplicao estejam dentro
do JAR que ser gerado, como declarado na linha 24.
Com isso, terminamos a configurao do pom.xml e podemos
partir para a implementao da nica classe que vamos utilizar
no exemplo. Basicamente, nossa classe estender HttpServlet para
sobrescrever o mtodo doGet() e devolver para a tela o famoso
Hello World no estilo Swarm. Na Listagem 4 podemos verificar
o cdigo da classe HelloSwarm.

02.
03. import java.io.IOException;
04. import javax.servlet.ServletException;
05. import javax.servlet.annotation.WebServlet;
06. import javax.servlet.http.HttpServlet;
07. import javax.servlet.http.HttpServletRequest;
08. import javax.servlet.http.HttpServletResponse;
09.
10. @WebServlet(name = HelloSwarmServlet, urlPatterns = /HelloSwarm)
11. public class HelloSwarm extends HttpServlet{
12.
13.

private static final long serialVersionUID = 1L;

14.
15.

@Override

16.

protected void doGet(HttpServletRequest req, HttpServletResponse rep)


throws ServletException, IOException {

17.
18.

rep.getWriter().write(Hello Swarm);
}

19. }

<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
</plugins>

37. </build>

Como podemos notar, foram declarados trs plug-ins. Caso j


tenha algum contato com o Maven, dois deles provavelmente
voc j deve ter utilizado. O primeiro o maven-war-plugin.
Utilizamos essa opo para gerarmos o WAR e tambm informar
ao Maven que nosso projeto no possuir um web.xml. Assim, por
mais que nosso projeto seja do tipo web, quando o compilarmos
atravs do Maven ele ser executado com sucesso. O segundo
plugin mais conhecido o maven-compiler-plugin, e no momento que o projeto for compilado ele verificar se o Maven

Com a classe pronta possvel notar que no existe nenhum tipo


de import relacionado ao Swarm, ou seja, toda a implementao
do cdigo baseada em um servlet simples. Alm disso, como
no temos o arquivo web.xml, utilizamos a anotao @WebServlet
para indicar o nome do servlet, atravs da propriedade name, e
o caminho que o mesmo deve mapear, atravs da propriedade
urlPatterns, conforme mostrado na linha 10.
Na linha 16 sobrescrevemos o mtodo doGet() para que, quando o path especificado for acessado, possamos devolver nossa
mensagem para a tela. J na linha 17, atravs do objeto rep, que
uma instncia de HttpServletResponse, conseguimos pegar o
objeto PrintWriter e, atravs dele, enviar a mensagem de volta
para a tela.
Feito isso, para ver o projeto funcionado, vamos utilizar o plugin do
Maven para compila-lo, gerar o WAR e coloc-lo em execuo.

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

53

53

Simplificando o desenvolvimento de microsservios com o WildFly Swarm

Assim, acesse o prompt de comando no Windows ou o terminal


no Mac ou Linux, navegue at a pasta raiz do projeto e digite o
comando:
mvn wildfly-swarm:run

Em sua primeira execuo o Swarm pode levar um bom tempo


para finalizar. Isso porque o Maven precisar realizar o download
de vrias dependncias. Portanto, at tudo estar pronto, pode
demorar um certo tempo. Ao final do processo voc ver no
console mensagens informando que o Undertow est no ar e que
sua aplicao j est disponvel. Ento, basta acess-la atravs do
navegador ao informar a URL http://localhost:8080/HelloSwarm.
Com a pgina carregada, a mensagem que definimos em nossa
classe exibida. Nesse momento possvel afirmar que o fato de
utilizarmos o Swarm no afetou em nada o cdigo necessrio para
criar um servlet. Contudo, vejamos a pasta target para identificar o artefato gerado. Dentre vrios arquivos e pastas presentes
nesse local, existe um JAR com o nome do projeto mais o sufixo
swarm.jar. Trata-se do arquivo do projeto que acabamos de
criar pronto para ser executado, ou seja, j compilado com o Swarm
e as dependncias que definimos.
Como importante curiosidade, o artefato gerado tem tamanho
bem reduzido: em torno de 436KB. Alm disso, lembre-se que no
preciso fazer o deploy desse arquivo no WildFly, por exemplo, que
em sua recm-lanada verso 10, ocupa 132 MB com a instalao
padro. Ainda assim, Ken Finnigan, um dos principais envolvidos

no Swarm, j informou, durante o Java One 2015, que a equipe


trabalha para reduzir ainda mais o arquivo gerado.
Obviamente, o tamanho do arquivo tambm afetado pelo nmero de dependncias, ou mesmo pelo modo como configuramos
o Swarm. Repare na linha 2 da Listagem 5. Ela mostra a parte da
configurao de parmetros do Swarm apresentada na Listagem 3
referente ao plugin do Swarm, mas aqui alteramos o valor de
bundleDependencies para false. Assim, as dependncias no
sero includas no arquivo gerado e sero buscadas diretamente
no repositrio Maven durante a execuo do projeto. Por causa
desse ajuste que conseguimos um tamanho to reduzido. Alm
disso, adicionamos as linhas 3, 4 e 5 para mudarmos alguns parmetros do projeto.
Na linha 3, temos o parmetro httpPort, que especifica em qual
porta o Swarm ir rodar. No caso, trocamos o valor para 9080. Dessa forma, ao executar novamente o projeto, para acessar a aplicao
devemos utilizar o endereo http://localhost:9080/HelloSwarm.
Ainda existem itens relacionados a configuraes do Swarm que
sero disponibilizadas ao longo das releases, lanadas quase que
semanalmente. No entanto, j temos opes como, por exemplo,
definir uma porta especfica para debug (veja as linhas 4 e 5 da
Listagem 5), o que nos permite utilizar ferramentas como as do
Eclipse para conectar nossa aplicao e examin-la.
Listagem 5. Alterao da porta padro do Swarm, ativao do modo debug e
definio de porta para isso.
01. <configuration>
02. <bundleDependencies>false</bundleDependencies>
03. <httpPort>9080</httpPort>
04. <debug>true</debug>
05. <debugPort>9081</debugPort>
06. </configuration>

Trabalhando com o mnimo


Ao final do primeiro exemplo podemos notar que a quantidade
de configuraes para o projeto poder executar baixa, e desejamos que essa caracterstica persista, pois um dos nossos objetivos
que os servios sejam pequenos, seja pelo seu tamanho ou pela
quantidade de configuraes necessrias. Note que se um servio
possui um setup com muitos parmetros, existe a possibilidade
de ele acumular muitas atribuies e, por isso, talvez seja uma
boa ideia quebr-lo em servios menores. Lembre-se que cada um
deve ser planejado para ser gil e evitar que um mesmo trabalho
seja realizado em locais diferentes.

54 Java Magazine Edio 150

54

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

No planejamento, importante definir tambm quais partes


do Swarm vamos utilizar, inclusive os subsistemas herdados do
WildFly. Como vimos, cada uma delas deve ser informada no
POM como uma dependncia simples. A documentao oficial
as coloca como uma coleo bem definida de recursos a serem
adicionados em sua aplicao, e cada uma dessas pequenas partes
recebe o nome de Fraction.
Assim, quando adicionamos dependncias como o Weld e o
Undertow, estamos adicionando pequenas partes ao nosso Swarm,
que passa a carregar subsistemas que, por padro, no estariam
presentes.

Criando classes main customizadas


Em projetos Java SE comum criarmos uma classe para ser a
principal. Como diferencial, essa classe deve implementar o mtodo main(), a ser chamado quando o JAR for executado. No Swarm,
curiosamente, tambm podemos utilizar esse recurso. Para isso,
no entanto, precisamos, primeiro, nos certificar que o tipo de
empacotamento no POM esteja definido como JAR. Em seguida,
devemos criar uma classe simples, App, que servir como nossa
classe principal. Por fim, basta implementar o mtodo main().
A Listagem 7 mostra o cdigo dessa classe.
Listagem 7. Cdigo da classe App.

Nota

01. package br.com.devmedia;

O Swarm ainda no possui todos os mdulos do WildFly no formato de fractions, contudo, Ken
Finnigan afirmou que a comunidade tem sido muito receptiva e vem contribuindo consideravelmente
para o amadurecimento da ferramenta, o que deve acelerar o ritmo de lanamento das prximas
releases e implementao de novas fractions.

02.
03. import org.jboss.shrinkwrap.api.ShrinkWrap;
04. import org.wildfly.swarm.container.Container;
05. import org.wildfly.swarm.undertow.WARArchive;
06.
07. public class App

A incluso de fractions em um projeto deve ser feita atravs


do POM, do mesmo modo que voc incluiria uma dependncia
Maven. Como exemplo, a Listagem 6 mostra o que precisamos
adicionar ao POM para poder utilizar a fraction de log.
Listagem 6. Declarao da dependncia do fraction de logging.
<dependency>

08. {
09.

public static void main( String[] args ){

10.

try {

11.

Container container = new Container();

12.

container.start();

13.

System.out.println( Swarm no ar );

14.

WARArchive deployment = ShrinkWrap.create(WARArchive.class);

15.

deployment.addClass(HelloSwarm.class);

16.

<groupId>org.wildfly.swarm</groupId>

17.

<artifactId>wildfly-swarm-logging</artifactId>

18.

<version>${version.wildfly-swarm}</version>

19.

</dependency>

20.

container.deploy(deployment);
} catch (Exception e) {
e.printStackTrace();
}
}

21. }

Pronto! Temos uma API de log em nossa aplicao. Agora, para


acionar o log, preciso informar o nvel que desejamos. Supondo
que seja em modo debug, basta adicionar o seguinte parmetro
no comando mvn wildfly-swarm:run:
-Dswarm.logging=DEBUG

Dessa forma, o comando para executar nossa aplicao seria o


seguinte:
mvn wildfly-swarm:run -Dswarm.logging=DEBUG

Isso suficiente para iniciar o servio em modo debug.


Apesar dessa opo ser eficiente para setar opes que podem
mudar atravs de script a cada novo start, uma maneira de
configurao do Swarm que vem sendo bem recebida pelos desenvolvedores so as customizaes atravs das classes main de
cada servio. O nico problema dessa abordagem, por enquanto,
que s pode ser utilizado quando voc empacota seu projeto
como um JAR, ou seja, fica indisponvel caso seu projeto esteja
definido para ser empacotado como um WAR.

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

55

55

Simplificando o desenvolvimento de microsservios com o WildFly Swarm

Tipo

Descrio

JARArchive

Uma verso melhorada do JAR que fornece mtodos fceis para adicionar dependncias.

WARArchive

Uma verso melhorada do WAR que fornece mtodos fceis para adicionar contedo esttico como pginas HTML, CSS e JavaScript, assim como
arquivos de configurao, como o web.xml.

JAXRSArchive

Web archive que permite criar uma aplicao REST a partir de uma classe sem a necessidade de declararmos a anotao @ApplicationPath. Basta adicionar essa classe ao JAXRSArchive atravs do mtodo addResource() e depois definir o nome do servio com o mtodo
setApplicationName(nome-servico). Assim, a URL REST estar disponvel em http://localhost:8080 /nome-servico.

Secured

Tipo de arquivo que injeta o arquivo keycloak.json e configura restries de segurana. O Keycloak uma soluo de SSO para aplicaes web que
disponibilizam servios atravs de chamadas RESTful.

RibbonArchive

Arquivo que pode registrar servios baseados em Ribbon-based, soluo que age como um load balancer de chamadas desenvolvido pela Netflix.

Tabela 1. Tipos de arquivos oferecidos na verso atual do Swarm

Quando procedemos dessa forma, para iniciar o projeto devemos realizar todo o trabalho que anteriormente era efetuado
automaticamente pelo Swarm. Assim, agora precisamos subir
o container, preparar os arquivos que estaro no projeto e, por
ltimo, realizar o deploy.
Na linha 11 temos o cdigo que cria um container, uma instncia
do Undertow, e como voc pode imaginar, toda a inicializao do
mesmo feita na linha 12. Com isso, temos o Swarm j funcionando, mas ainda sem o nosso projeto.
Na linha 14 criamos um WARArchive, que age como um arquivo
WAR carregado em memria. Aqui vale uma ressalva: note que
ainda estamos utilizando o empacotamento do nosso projeto como
um JAR. Usamos o recurso de alocao em memria como um
WAR apenas para realizar o deploy de nosso servio.
Nesse cdigo, fizemos uso tambm da classe ShrinkWrap,
que facilita criar, importar, exportar e manipular arquivos em
memria. Na linha 14 utilizamos seu mtodo esttico create(),
que aceita como parmetro uma classe genrica para definir o
tipo de arquivo a ser criado. Como queremos alocar um WAR em
memria para realizar o deploy em nosso container, declaramos
a classe WARArchive. A partir da podemos adicionar os nossos
recursos, como classes, pginas HTML e arquivos JavaScript,
que, diferentemente do que ocorre em um projeto web padro no
Swarm, precisaro ser explicitamente inseridos no WAR virtual
que estamos manipulando.
A Tabela 1, retirada da prpria documentao do Swarm, mostra
os tipos de arquivos virtuais que o mesmo disponibiliza. Saiba
que esses tipos de arquivos esto diretamente relacionados ao
tipo de aplicao que voc vai querer realizar o deploy. Como
estamos fazendo o deploy de um projeto web padro, utilizamos
o WARArchive.
Voltando explicao do cdigo da Listagem 7, o prximo passo adicionar nossa classe ao deploy que iremos realizar, como
demonstra a linha 15. Logo aps, na linha 16, realizamos de fato
o deploy.
Agora, precisamos inserir no POM o apontamento para a classe
main customizada (App). Sendo assim, depois do elemento bundleDependencies, adicione a seguinte linha:
<mainClass>br.com.devmedia.App</mainClass>

56 Java Magazine Edio 150

56

Caso tenha criado um projeto com um pacote ou classe com nomes diferentes, no esquea de atualizar o seu POM. Em seguida,
inicie o Swarm novamente e observe que obtivemos o mesmo
resultado de quando no utilizamos a classe customizada.
Vale ressaltar que o uso de uma classe principal customizada
nem sempre opcional. Em alguns fractions, como o do JSF,
existe a necessidade de criar a classe main customizada porque
precisaremos incluir arquivos HTML, CSS, JavaScript, dentre
outros ao nosso deploy.

Adicionando outros fractions ao nosso projeto


Para entender como podemos acrescentar outros tipos de arquivo
ao nosso projeto e como gerenci-los atravs da classe main, vamos
adicionar o fraction do JSF. De preferncia, use o mesmo projeto
dos exemplos anteriores, pois queremos, ao final, ter a funcionalidade que criamos anteriormente e a que vamos criar agora.
Dito isso, dentro da pasta main de seu projeto, crie uma pasta
chamada resources. Em seguida, dentro de resources, crie a pasta
WEB-INF e dois arquivos, com os nomes de index.html e index
.xhtml. Nas Listagens 8 e 9 temos o cdigo desses arquivos.
Note, na Listagem 8, que foi declarado um simples redirect para
o verdadeiro index, na linha 4, forando o redirecionamento.
Na Listagem 9, observe que usamos alguns componentes JSF.
Na linha 7, por exemplo, declaramos um ui:composition para
importar um arquivo de template que criaremos na sequncia.
Na linha 9, declaramos um elemento outputText para receber o
retorno do mtodo welcomeFromJSF(), de uma classe que ainda
vamos criar.
Agora, dentro da pasta WEB-INF, crie os arquivos web.xml e
template.xhtml. Como voc j deve ter notado, montamos manualmente a estrutura de pastas de um projeto web simples.
A Listagem 10 apresenta o nosso web.xml. Ao analis-lo, veremos que nas linhas 5 e 6 definimos que o projeto se encontra em
desenvolvimento. Esse parmetro no tem impacto direto em
nossa aplicao e serve mais como uma alerta para o JSF, que
pode, atravs dele, saber como atuar da melhor maneira conforme
o ambiente. Por exemplo, se o stage estiver como Development,
ele vai proporcionar mais detalhes no caso de erros e alertas.
Da linha 8 linha 12, registramos o Faces Servlet e informamos
que o mesmo deve ser carregado no mesmo momento que a

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

aplicao. Por ltimo, entre as linhas 13 e 16, informamos que o


Faces Servlet deve monitorar todas as URLs que terminem com
a extenso .xhtml.
Listagem 8. Contedo do arquivo index.html.
01. <!DOCTYPE html>
02. <html>
03. <head>
04.
<meta http-equiv=Refresh content=0; URL=index.xhtml>
05. </head>
06. </html>
Listagem 9. Contedo do arquivo index.xhtml.
01. <?xml version=1.0 encoding=UTF-8 ?>
02. <!DOCTYPE html PUBLIC -//W3C//DTD XHTML 1.0 Transitional//EN
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd>
03. <html xmlns=http://www.w3.org/1999/xhtml
04. xmlns:ui=http://xmlns.jcp.org/jsf/facelets
05. xmlns:h=http://xmlns.jcp.org/jsf/html>
06. <body>
07.
<ui:composition template=/WEB-INF/template.xhtml>
08.
<ui:define name=content>
09.
<h:outputText value= #{welcome.welcomeFromJSF()}/>
10.
</ui:define>
11.
</ui:composition>
12. </body>
13. </html>
Listagem 10. Contedo do arquivo web.xml.
01. <?xml version=1.0 encoding=UTF-8?>
02.

<web-app version=3.1 xmlns=http://xmlns.jcp.org/xml/ns/javaee


xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance

03.

xsi:schemaLocation=http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd>

04.

<context-param>

05.

<param-name>javax.faces.PROJECT_STAGE</param-name>

06.

<param-value>Development</param-value>

07.

</context-param>

08.

<servlet>

09.

<servlet-name>Faces Servlet</servlet-name>

10.

<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>

11.

<load-on-startup>1</load-on-startup>

12.

</servlet>

13.

<servlet-mapping>

14.
15.
16.
17.

J a Listagem 11 apresenta o cdigo de template.xhtml. Como


podemos notar, um XHTML simples com algumas tags do JSF
apenas para criar alguns elementos que sero utilizados na tela. Na
linha 15, por exemplo, temos a tag <ui:insert>, que possibilita inserir cdigo HTML gerado no back-end, e na linha 17, criamos outra
<div>, onde inserimos uma imagem com o logo do Swarm.
Logo aps, dentro do pacote br.com.devmedia, crie a classe
Welcome (veja a Listagem 12), a qual ser utilizada para gerar a
mensagem que vamos inserir em nosso HTML atravs do mtodo
welcomeFromJSF().
Listagem 11. Contedo do arquivo template.xhtml.
01. <?xml version=1.0 encoding=UTF-8 ?>
02. <!DOCTYPE html PUBLIC -//W3C//DTD XHTML 1.0 Transitional//EN
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd>
03. <html xmlns=http://www.w3.org/1999/xhtml
xmlns:ui=http://xmlns.jcp.org/jsf/facelets
xmlns:h=http://xmlns.jcp.org/jsf/html>
04.
05.
06.

<h:head>
<meta http-equiv=Content-Type content=text/html; charset=UTF-8 />
<title>Exemplo de Swarm + JSF</title>

07.

</h:head>

08.

<h:body>

09.
10.
11.
12.

<div id=top>
<ui:insert name=top>
<h4>Seu primeiro Facelet via Swarm.</h4>
</ui:insert>

13.

</div>

14.

<div id=content>

15.

<ui:insert name=content>Content</ui:insert>

16.

</div>

17.

<div id=bottom>

18.

<ui:insert name=bottom>Powered by
<img src=http://wildfly-swarm.io/images/swarm_logo.png
style=width:200px; height:auto; /></ui:insert>

19.

</div>

20.

</h:body>

21. </html>

<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
</web-app>

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

57

57

Simplificando o desenvolvimento de microsservios com o WildFly Swarm

Listagem 12. Cdigo da classe Welcome.


01.
02.
03.
04.
05.
06.
07.
08.
09.

package br.com.devmedia;
import javax.enterprise.inject.Model;
@Model
public class Welcome {
public String welcomeFromJSF() {
return Bem vindo ao JSF!! Ou voc prefere hello world?;
}
}

vamente no deploy; na linha 26 a vez de HelloSwarm, fazendo


com que a classe que implementamos anteriormente tambm
esteja presente; e na linha 27, realizamos o deploy.
Neste momento, para visualizar as atualizaes, inicie novamente o Swarm executando:
mvn wildfly-swarm:run

Listagem 13. Dependncia do fraction do JSF a ser adicionado ao pom.xml.


01. <dependency>

Note que essa classe servir como um Model do JSF, o que


especificado atravs da anotao @Model, na linha 4. Com isso,
os mtodos dela ficaro expostos de uma maneira que conseguiremos acion-los do nosso HTML atravs da tag insert do JSF.
Assim finalizamos a parte web de nosso projeto e podemos
adicionar o fraction do JSF ao POM, o que feito conforme a
Listagem 13
Para concluir, altere nossa classe App para adicionar os
arquivos recm-criados ao arquivo WAR, conforme mostra a
Listagem 14.
Na linha 16 adicionamos nossa nova classe ao deploy. Da linha
17 at a linha 20, utilizamos um mtodo diferente para inserir os
arquivos web, addAsWebResource(), que nos auxilia informando que o artefato includo deve ser tratado como um elemento
esttico. J nas linhas 22 e 24, adicionamos os arquivos web.xml
e template.xhtml atravs do mtodo addAsWebInfResource().
Assim, eles sero tratados como arquivos de pr-configurao do
projeto. Na linha 25, todas as dependncias so inseridas efeti-

02.

<groupId>org.wildfly.swarm</groupId>

03.

<artifactId>jsf</artifactId>

04.

<version>${version.wildfly-swarm}</version>

05. </dependency>
Listagem 14. Cdigo da classe App atualizado.
01. package br.com.devmedia;
02.
03. import org.jboss.shrinkwrap.api.ShrinkWrap;
04. import org.jboss.shrinkwrap.api.asset.ClassLoaderAsset;
05. import org.wildfly.swarm.container.Container;
06. import org.wildfly.swarm.undertow.WARArchive;
07.
08. public class App
09. {
10.

public static void main( String[] args ) throws Exception

11.

12.

Container container = new Container();

13.

container.start();

14.

System.out.println( Swarm no ar );

15.

WARArchive deployment = ShrinkWrap.create(WARArchive.class);

16.

deployment.addClass(Welcome.class);

17.

deployment.addAsWebResource(

18.

new ClassLoaderAsset(index.html, App.class.getClassLoader()),


index.html);

19.

deployment.addAsWebResource(

20.

new ClassLoaderAsset(index.xhtml, App.class.getClassLoader()),


index.xhtml);

21.

deployment.addAsWebInfResource(

22.

new ClassLoaderAsset(WEB-INF/web.xml, App.class.getClassLoader()),


web.xml);

23.

deployment.addAsWebInfResource(

24.

new ClassLoaderAsset(WEB-INF/template.xhtml,
App.class.getClassLoader()), template.xhtml);

25.

deployment.addAllDependencies();

26.

deployment.addClass(HelloSwarm.class);

27.
28.

container.deploy(deployment);
}

29. }

58 Java Magazine Edio 150

58

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Ao acessar a URL http://localhost:8080/HelloSwarm, voc notar


que nossa pgina recm-criada ser exibida. Isso porque nosso
projeto web, por padro, procura pelo arquivo index.html e como
nosso index redireciona o fluxo para o arquivo index.xhtml, todo
o contedo deste mostrado, inclusive nosso cumprimento customizado pela classe Welcome. A Figura 3 mostra nossa pequena
tela no browser.

Groups e chats no IRC. Os responsveis pelo projeto acompanham


de perto a comunidade e realizam vrias alteraes ao longo de
cada release lanada. Portanto, participe, report bugs e traga suas
ideias e descobertas, auxiliando assim o projeto a crescer.

Autor
Joel Backschat
joel@cafecomjava.com.br
Bacharel em Sistemas da Informao pela Universidade da
Regio de Joinville, possui certificao SCJP e Adobe FLEX. J
trabalhou em empresas como TOTVS e Supero. Desde 2014 arquiteto
de software da Fcamara Formao e Consultoria nas reas de inovao
e logstica porturia. Entusiasta de novas tecnologias, mantm o site cafecomjava.com.
br para compartilhar suas experincias que vo do hardware ao software.
Links:

Figura 3. Sada da tela montada com JSF, utilizando o Swarm


Com a classe principal customizada notvel que precisamos
de um pouco mais de trabalho para rodar o projeto. Entretanto, o
uso dessa classe possibilita um setup mais minucioso do projeto,
viabilizando executar o deploy de classes ou arquivos especficos
conforme a situao.
Como outro diferencial, apesar de recm-lanado, o Swarm j
oferece suporte ferramenta de monitoramento Hawkular tambm da Red Hat para voc ter a capacidade de monitorar seus
servios e tomar aes de acordo com a necessidade.
Por fim, saiba que o Swarm vem conquistando adeptos rapidamente. Seu uso em produo, no entanto, ainda no
aconselhvel, conforme relatado pelo prprio time da Red Hat.
Na documentao ainda faltam itens como a integrao com o
Gradle, que j foi demonstrada no Java One 2015, mas ainda no
consta no site oficial. Alm disso, ferramentas de monitoramento
mais populares, como New Relic e Ruxit, esto em fase de adequao para poder oferecer suporte ao Swarm, assim como ainda
no est claro como ir funcionar a conexo com servidores HTTP
como o Apache, para prover o balanceamento de carga entre instncias de um mesmo servio. De qualquer modo seu estudo
estimulado juntamente com a participao em fruns, no Google

Pgina do Swarm.
http://wildfly-swarm.io/
Documentao oficial do WildFly Swarm.
https://wildfly-swarm.gitbooks.io/
Pgina do projeto Undertow.
http://undertow.io/
Palestra de Ken Finnigan sobre Swarm.
https://developers.redhat.com/video/youtube/i1aiUaa8RZ8/
Grupo no Google sobre o Swarm.
https://groups.google.com/forum/#!forum/wildfly-swarm
Canal no IRC sobre o Swarm.
http://webchat.freenode.net/?channels=wildfly-swarm
Matria sobre persistncia poliglota.
http://martinfowler.com/bliki/PolyglotPersistence.html

Voc gostou deste artigo?


D seu voto em www.devmedia.com.br/javamagazine/feedback
Ajude-nos a manter a qualidade da revista!

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

59

59

DevOps: Como adequar


seu processo de CI a
essa nova cultura
Veja neste artigo como aplicar os conceitos de
DevOps em um contexto de integrao contnua
Este artigo do tipo mentoring
saiba mais: www.devmedia.com.br/mentoring-saibamais

Cenrio
DevOps um dos termos mais populares do momento no mercado
da Tecnologia da Informao. Muito se fala a respeito, e a expectativa
quanto aos benefcios de sua adoo alta. Com base nisso, este artigo
visa contribuir com a discusso e disseminao de alguns dos elemen-

tos bsicos que o constituem, a partir de um misto de impresses,

m artigo publicado na edio 149 da Java Magazine, aprendemos a construir um ambiente de


integrao contnua baseado em Maven e Jenkins,
suportado por medies estticas de qualidade a partir
de uma plataforma chamada Sonar Qube. O objetivo
principal de todo o artigo foi, a partir de um tutorial
prtico, introduzir algumas das ferramentas mais populares do mercado contemporneo para o desenho de
processos dinmicos de desenvolvimento de software,
com foco na prtica DevOps.
O resultado desse trabalho, que servir de ponto de
partida para este artigo, foi resumido na Figura 1. Nela,
vemos a mquina de desenvolvimento, configurada
com uma IDE (neste caso, o Eclipse), Git e Maven. Uma
vez que parte do cdigo do software tenha sido escrito,
ele passa por uma validao local, realizada a partir da
execuo de testes unitrios. Ento, esse mesmo cdigo
submetido a um controle local e, a seguir, remoto, de
verso.
Quando esse repositrio remoto, hospedado em uma
conta GitHub, recebe esse contedo, dispara uma requisio de execuo do primeiro de uma cadeia de jobs
em um servidor de integrao contnua (Jenkins), hospedado em um gear OpenShift. A partir desse primeiro
job, todos os demais so executados sequencialmente,
e o resultado final um build completo, associado a

60

constataes de mbito cultural e, tambm, tutoriais que, em uma


abordagem essencialmente prtica, introduziro algumas das tecnologias e plataformas mais populares empregadas nesse contexto.

uma anlise esttica da qualidade do projeto. Essa anlise feita


por uma instncia do Sonar Qube, tambm hospedada em um
gear OpenShift.
No artigo de hoje, retomaremos muitos dos pontos que acabamos de citar, estendendo-os e refinando-os para oferecer,
ao final, um processo alinhado aos principais fundamentos e
prticas do que hoje se rotula como DevOps no mercado de TI.
Obviamente, por ser esse um tema ainda em franca evoluo,
a viso levantada ao longo deste material ser, naturalmente,
fonte para uma srie de discusses complementares. Saiba que
esse no um material definitivo sobre DevOps, mas uma introduo que visa trazer ao leitor informaes que o ajudem a
adot-lo em seu cotidiano.

Entendendo o nosso ponto de partida


O projeto-guia de todo este artigo, bem como daquele publicado
na edio 149, consiste em uma aplicao Java, stand-alone, para o
clculo de resistncias eltricas a partir de um padro de faixas
e cores. Esse projeto apresenta, alm do cdigo principal, testes
unitrios que validam todos os requisitos levantados. Todo esse

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

contedo est devidamente salvo em um repositrio pblico


a fase test seja processada, o Maven executar, tambm, todas as
no GitHub, podendo ser visto e baixado pelo leitor a partir da
fases configuradas como anteriores a essa, e o resultado dessa
referncia que se encontra na seo Links.
operao ser a validao, a compilao e, por fim, o processaA primeira grande deciso que tivemos de tomar, antes mesmo
mento de todos os testes unitrios do projeto.
de iniciar a escrita do software em si, relacionou-se plataforCaso todos os testes executem com sucesso, o Jenkins passar
ma de gerenciamento do ciclo de vida desse projeto. Esse um
a executar o job seguinte, de nome devmediadevops_package_meaponto bastante importante, e qualquer escolha que se tome
sure_job. O objetivo dessa tarefa realizar o empacotamento de
nesse instante refletir, posteriormente, em todo o andamento
todos os mdulos do projeto, gerar o pacote da aplicao em si e,
do trabalho.
em seguida, medir a qualidade de todo o material por meio de
Por ciclo de vida de projeto, devemos entender atividades
uma anlise do Sonar Qube.
que vo desde o gerenciamento de dependncias do produto
O empacotamento realizado, novamente, a partir do Maven, por
meio do comando mvn package -DskipTests=true -f $OPENSHIFT_
at operaes bsicas como compilao, teste, empacotamento,
implantao e, inclusive, execuo.
DATA_DIR/workspace/sonar/devops/pom.xml. Note que, nesse
momento, os testes so ignorados, uma vez que j os executamos
Nesse contexto, o Maven ainda uma ferramenta muito pono job anterior. Portanto, na tarefa de nome devmediadevops_packapular. fato que, h alguns anos, vem disputando espao com
ge_measure_job, estamos apenas compilando o projeto novamente,
outras plataformas bem interessantes, como o Gradle, mas seu
empacotando-o em seguida. A ao seguinte, configurada como
ndice de utilizao ainda muito expressivo, sobretudo, no
um passo de ps-build nesse job, consiste na comunicao remota
mundo corporativo. Ao final do artigo, na seo Links, recocom o servidor em que o Sonar Qube est instalado (via SSH) para,
mendamos uma leitura complementar sobre esse tema.
ento, disparar a execuo da anlise do projeto. O resultado
Foi essa grande popularidade, aliada maior familiaridade de
publicado em uma pgina gerada e alimentada pelo Sonar, e que
toda a comunidade com o seu modelo de configurao, que nos
tem a aparncia e estrutura exibidas na Figura 3.
levou a adotar o Maven em nosso projeto.
Outro aspecto que j encontraremos prconfigurado antes do incio deste texto um
processo de build em um servidor Jenkins,
rodando sobre a plataforma OpenShift. O
cenrio de partida algo como o ilustrado
na Figura 2. Perceba que, atualmente, temos
apenas um processo de build, composto por
quatro jobs. O primeiro passo dessa cadeia
envolve uma limpeza de toda a rvore
de diretrios do projeto, eliminando dali
quaisquer resqucios de material gerado
em builds anteriores. Em seguida, por meio
do job intitulado devmediadevops_test_job,
executa-se a fase de testes do ciclo de vida
padro do Maven. Nesse momento, so
colocados para rodar todos os testes unitrios do projeto, que validaro toda a lgica
principal do produto. Ao ordenarmos que Figura 1. Estrutura de Integrao Contnua e Anlise Esttica do projeto-guia

Figura 2. Configurao dos jobs no processo de integrao contnua

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

61

61

DevOps: Como adequar seu processo de CI a essa nova cultura

Figura 3. Dashboard do Sonar Qube aps a execuo de uma anlise

Refinando o processo de integrao contnua


Como acabamos de ver na seo anterior, o projeto original conta com apenas um processo de build. Um detalhe
que no foi citado at aqui, mas que precisamos introduzir
neste momento, que essa cadeia de jobs disparada automaticamente, sempre que um novo commit realizado no
repositrio remoto.
Este processo automtico viabilizado a partir da utilizao
de um recurso chamado web hook, criado e administrado por
meio da interface web do GitHub. Essa a forma oferecida
para contornar uma limitao imposta pela empresa, de no
permitir conexes remotas via SSH com seus servidores. Os
detalhes sobre o funcionamento desse mecanismo podem ser
encontrados em uma referncia informada na seo Links.
Temos, portanto, um processo relativamente pesado em
nossas mos. Imagine que, a cada submisso de cdigo nesse
repositrio, todo esse conjunto de atividades algumas bem
demoradas, como a anlise esttica feita pelo Sonar executado. Isso pode tornar o cotidiano de um projeto bastante
lento, e prejudicar muito mais do que ajudar o andamento
do trabalho. E por que?
O fato a ser observado que a nossa realidade vem sendo
fortemente transformada pelo advento das metodologias
geis, h uns bons anos. Uma das lies que essa nova filosofia de trabalho nos ensina, dentre tantas, que integrao
frequente de cdigo uma das chaves para o sucesso. Ao
avaliarmos, continuamente, o impacto da juno do contedo
desenvolvido por todo um time, tornamo-nos muito mais

62 Java Magazine Edio 150

62

capazes de enxergar a verdadeira realidade de um projeto.


Integrar continuamente nos permite antecipar problemas,
aumentando em muito o nosso poder de reao e controle
frente a qualquer desvio identificado.
Em decorrncia disso, uma das prticas hoje bem populares
no cotidiano de times de desenvolvimento de software a
submisso, ao menos uma vez por dia, de todo o cdigo-fonte
produzido por cada desenvolvedor. Ainda nesta linha, da
busca de transparncia e controle pleno, h uma preferncia
notvel pelo uso de branch nico (normalmente denominado
trunk ou master, em sistemas como Subversion e Git), evitandose ao mximo o uso de branches paralelos (muito comum em
tempos no to remotos assim).
Branches podem ser muito teis em desenvolvimento paralelo, de refatorao significativa, ou ainda em provas de
conceito. Entretanto, em times geis, no h muito sentido
que desenvolvedores se isolem em ramos paralelos para,
somente l na frente, integrar todo o material que produziram. O ideal que possamos observar a sade do software
continuamente. Nesta seo, apresentamos uma sugesto
que pode ajudar o leitor a entender como todo esse ferramental pode ajud-lo a estabelecer um controle de qualidade
sobre o seu projeto.
Voltando um pouco ao problema da lentido e ineficincia
introduzido no incio dessa seo, imagine o quo complicado seria rodar uma anlise do Sonar Qube a cada submisso
de cdigo. Esse processo normalmente lento e, com certeza,
sobrecarregaria desnecessariamente o processo de build.

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Tipo de projeto

Freestyle project

Git URL

https://github.com/pedrobrigatto/devmedia_devops_series.git

Branches to build

*/master

Triggers

Trigger builds remotely


Authentication Token > devmedia_devops_token

Build

Invoke top-level Maven Targets:


clean verify -f $OPENSHIFT_DATA_DIR/workspace/sonar/devops/pom.xml

Tabela 1. Configurao do novo job para execuo por commit


A mesma linha de raciocnio vlida para a implantao de
verses em servidores de QA ou produo. Isso, embora tenha
uma natureza aparente de Dev, tem uma ligao importante com
Ops tambm, dado que a implantao e a instalao de produtos/
servios so tarefas historicamente associadas a operadores.
Uma abordagem um pouco mais inteligente e mais aderente
cultura de DevOps seria um planejamento de processos de build
que atenda melhor a rotina de trabalho neste projeto. Nesse novo
plano, teramos dois processos distintos de build, ambos definidos
no mesmo servidor Jenkins, de forma que o primeiro, rotineiro
e usado durante o horrio de expediente regular, envolveria um
fluxo bem mais simples de verificao do projeto; o outro, mais
completo, seria agendado para executar apenas uma vez ao dia,
envolvendo no apenas a verificao do software, mas a anlise
de qualidade e a gesto dos artefatos gerados. Veremos como
colocar esse plano em ao a partir de agora.

Redesenhando o processo de integrao contnua


Nesta seo, criaremos um novo job em nosso servidor Jenkins.
Seu nome ser devmediadevops_daily_job, e os detalhes de sua
configurao esto contidos na Tabela 1. Esse o job a que nos
referimos na seo anterior, quando descrevemos um fluxo mais
leve, rpido, que apenas verifica a consistncia do projeto. Esse
ser o procedimento a ser executado a cada commit de desenvol-

vedor e deve, portanto, ser iniciado a partir do repositrio remoto


hospedado no GitHub. Para isso, precisamos apenas nos certificar
que o Web Hook configurado em nossa conta aponte para o job
correto (https://devmediajenkins-pedrobrigatto.rhcloud.com/job/devmedia_daily_job/build?token=devmedia_devops_token), como podemos
ver na Figura 4.
Isto j ser suficiente para que o GitHub dispare a execuo do
job no Jenkins. O resultado prtico disso que, cada vez que um
desenvolvedor submeter cdigo ao repositrio remoto, ter, dentro de poucos instantes, condies de saber se o que fez refletiu
positiva ou negativamente no repositrio em que todo o cdigo,
de todo o time, encontra-se reunido, integrado. Esse um medidor fundamental da sade de um projeto, no apenas para quem
produz cdigo, mas para todos os demais membros da equipe,
que podem interferir e auxiliar na resoluo de problemas to
logo eles sejam verificados.
O prximo passo alterarmos, tambm, o primeiro job do outro
fluxo de build (devmediadevops_cleanup_job). Recuperando as informaes do incio do artigo, vemos que ele define exatamente
o token de autorizao que utilizamos no job dirio (devmediadevops_daily_job) e que, por sua vez, empregado na configurao do
web hook do GitHub, como j ilustrado na Figura 4. Precisamos
alterar este trigger, abandonando o uso de um token e adotando
o modelo de agendamento de builds.

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

63

63

DevOps: Como adequar seu processo de CI a essa nova cultura

A Figura 5 ilustra esse procedimento. Perceba que a opo Trigger


builds remotely no est mais selecionada, tendo sido substituda
pela opo Build periodically. O padro de periodicidade estabelecido a partir de cinco valores numricos separados por um
espao em branco, na seguinte ordem:
Minutos, compreendidos entre 0 e 59;
Horas, compreendidas entre 0 e 23;
Dias do ms, compreendidos entre 1 e 31;
Ms, compreendido entre 1 e 12;
Dia da semana, compreendido entre 0 e 7 (sendo 0 e 7 correspondentes a domingo).

Pela definio, conclumos que a estratgia adotada para nosso


build noturno a de execuo diria, com incio programado
para a meia-noite.
Os benefcios diretos de uma organizao como a que acabamos
de propor so, principalmente, maior agilidade na verificao
do status do projeto por cada operao de commit (por meio
de um processo de integrao mais leve e objetivo, envolvendo
apenas a compilao e verificao do cdigo-fonte) e a garantia
de, diariamente, termos um relatrio dirio da sade do projeto,
cujas informaes so essenciais para a orientao de todo o trabalho a ser realizado. Atravs de relatrios como os gerados pelo

Figura 4. Configurao do Web Hook no repositrio remoto do projeto

Figura 5. Configurao da periodicidade de execuo do job

64 Java Magazine Edio 150

64

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Sonar Qube, por exemplo, conseguimos extrair, facilmente, dados


muito teis, tais como dbito tcnico, porcentagem de cobertura
de cdigo via testes unitrios, complexidade ciclomtica, dentre
outros.
Poderamos, ainda, trabalhar com uma terceira ou quarta estratgia, caso a implantao do produto em servidores de QA e/ou
Produo tivesse que seguir uma periodicidade particular, acordada entre todas as partes envolvidas. Tudo depende, portanto,
dos acordos firmados, e um bom plano ser sempre fundamental
para que as expectativas traadas sejam devidamente atendidas,
de todas as partes.

volume de dados, nmero de servidores e poder computacional


dos mesmos, dentre outros. Portanto, conhecer nossa real demanda fundamental para contratarmos corretamente.
Vejamos, agora, um aspecto completamente novo em relao
ao que iniciamos analisando neste artigo. Uma vez que j estabelecemos dois processos de build separados para o dia-a-dia do
time de projeto, precisamos garantir que todos os artefatos sejam
devidamente versionados e controlados. Para isso, utilizamos uma
soluo extremamente popular de gerenciamento de artefatos,
chamada Nexus.

Mas o que isso tem a ver com Ops?

O conceito de entrega contnua implica na submisso contnua


de verses de um produto ou servio de software apreciao
de equipes responsveis pelo processo de verificao. O termo
frequentemente usado para os locais em que esse material publicado o de ambiente de qualidade, pois so os profissionais
de qualidade que normalmente fazem uso desses entregveis,
nessas condies.
Esta , alis, a grande diferena da Entrega Contnua para a Implantao Contnua (Continuous Deployment). Enquanto no primeiro caso estamos falando de um ambiente de qualidade, o segundo
envolve exatamente o mesmo procedimento, mas em ambiente de
produo (ou seja, acessado diretamente pelo cliente).
Como a OpenShift nos limita a uma quantidade mxima
de trs gears em sua oferta gratuita e gostaramos que o leitor
acompanhasse todos os passos do desenvolvimento deste artigo,
optamos por investir nossa ltima ficha na configurao de
um servidor de gerenciamento de artefatos. O principal motivo
a importncia que um ambiente como esse tem em qualquer
empresa sria de desenvolvimento de software, garantindo um
controle muito apurado sobre as diversas verses de um produto
conforme ele evolui.
Outro motivo que, como trabalharemos com a transferncia de
uma aplicao stand-alone, a implantao em si nada mais que a
transferncia do arquivo em si. Para execut-la, a nica exigncia

perspectiva do DevOps, em que o principal objetivo eliminar


a lacuna entre desenvolvimento e operaes, um planejamento de
builds com qualidade essencial. Embora, aparentemente, possamos no enxergar um relacionamento explcito com Ops neste
instante, um bom planejamento e acompanhamento constante da
sade do projeto so fundamentais para uma alta qualidade do
que entregue. Isso tem efeito direto em eventos subsequentes,
como a implantao de sistemas, uma menor incidncia de defeitos
verificados a cada nova verso, menor esforo com atividades de
suporte, dentre outros.
Alm disso, o planejamento consistente de builds importante
para que o time de operaes planeje adequadamente o provisionamento de recursos (principalmente em modelos de infraestrutura in-house, que normalmente requerem maior esforo
por parte da equipe local, da prpria empresa). Ainda que esteja
tudo hospedado na nuvem, em plataformas como OpenStack,
OpenShift, CloudBees ou Amazon, a previsibilidade do uso de
recursos computacionais garante um desenho e consequente
contratao mais preciso dos respectivos servios.
Em todos os artigos mais recentes publicados pela Java Magazine
acerca do tema DevOps, utilizamos um modelo de PaaS oferecido
atravs da OpenShift, da Red Hat. A contratao de recursos por
meio dessa plataforma depende de inmeros aspectos, tais como

Entrega contnua: o deploy e o repositrio de artefatos

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

65

65

DevOps: Como adequar seu processo de CI a essa nova cultura

que a mquina hospedeira tenha o JRE 7 instalado. Um contexto


totalmente diferente seria se, por exemplo, estivssemos lidando com uma aplicao web, que implicaria no uso de recursos
adicionais como um container web e justificaria, assim, uma
demonstrao a parte.

Preparando uma nova gear OpenShift e hospedando o Nexus


Para criar esse novo servidor, recorreremos ferramenta rhc.
Exatamente como fizemos com a gear devmediasonar, herdada do
artigo da Edio 149 e j introduzida em sees anteriores, adotamos o cartridge dyi-1.0 e usamos o esqueleto gerado por ele para
instalar uma aplicao pr-configurada do Nexus, disponvel em
um repositrio Git na web.
Comearemos pela anlise da Listagem 1. O primeiro comando
que executamos o rhc app create, passando apenas o cartridge
dyi-1.0 como parmetro. O resultado dessa operao o provisionamento de uma gear camada devmediasonar, cujas credenciais so
informadas imediatamente aps o comando ter sido concludo.
Em seguida, acessamos o diretrio do projeto, localmente, para
iniciar o download e a preparao de uma verso pr-configurada
do Nexus para, enfim, hosped-la na gear recm-criada.
Listagem 1. Terminal Preparao de uma aplicao DIY para configurao do
Nexus na plataforma OpenShift.
Pedro-Brigattos-MacBook:DevMediaOpenshiftApps pedrobrigatto$ rhc app create
devmedianexus diy-0.1
RSA 1024 bit CA certificates are loaded due to old openssl compatibility
Application Options
---------------------------------Domain: pedrobrigatto
Cartridges: diy-0.1
Gear Size: default
Scaling: no
Creating application devmedianexus ... done
Disclaimer: This is an experimental cartridge that provides a way to try unsupported languages, frameworks, and middleware on OpenShift.
Waiting for your DNS name to be available ... done
Cloning into devmedianexus...
Warning: Permanently added the RSA host key for IP address 52.3.119.139 to the
list of known hosts.
Your application devmedianexus is now available.
URL:
http://devmedianexus-pedrobrigatto.rhcloud.com/
SSH to: 568653ab2d5271af6400003d@devmedianexus-pedrobrigatto.rhcloud.com
Git remote: ssh://568653ab2d5271af6400003d@devmedianexus-pedrobrigatto
.rhcloud.com/~/git/devmedianexus.git/
Cloned to: /Users/pedrobrigatto/Personal/Projects/DevMediaOpenshiftApps/
devmedianexus
Run rhc show-app devmedianexus for more details about your app.
Pedro-Brigattos-MacBook:DevMediaOpenshiftApps pedrobrigatto$

66 Java Magazine Edio 150

66

Essa verso do Nexus, que acabamos de mencionar, encontrase disponvel em um repositrio do GitHub cuja URL a git://
github.com/shekhargulati/nexus.git. Os comandos executados para
prepar-la e implant-la foram agrupados na Listagem 2. O primeiro passo que demos foi, de dentro do diretrio da gear criada
(devmediasonar), adicionar ao Git uma referncia para o branch do
projeto que baixaremos, e cuja URL acabamos de citar. Em seguida, realizamos um merge do contedo desse branch com aquele
encontrado no diretrio devmediasonar, dando preferncia ao material do branch caso algum conflito seja encontrado. Finalmente,
submetemos todo o material para o nosso servidor. O processo de
configurao e inicializao normalmente bem rpido e, assim
que concludo, j permite que acessemos a aplicao a partir do
painel de administrao de nossa conta OpenShift.

Acessando o Nexus a partir do Jenkins: a preparao do ambiente


Assim que a nossa verso do Nexus j estiver disponvel,
necessrio que configuremos o ambiente da gear devmediajenkins
para tornar possvel o seu acesso a diretrios desse gerenciador
de artefatos. Para isso, conectamo-nos a ela via SSH, usando o
comando ssh apresentado na Listagem 3. Esse servidor Jenkins,
importante lembrar, faz parte do trabalho que herdamos de um
artigo anterior, publicado na edio 149 da Java Magazine. Ao estabelecermos comunicao com a gear devmediajenkins, navegamos
at seu diretrio $OPENSHIFT_DATA_DIR/.m2 para editarmos o
arquivo settings.xml l encontrado.
Ao abr-lo para edio, inserimos o contedo exibido na
Listagem 4. As modificaes realizadas, na prtica, estabelecem
o seguinte:
Um diretrio a ser utilizado como repositrio local de dependncias, definido a partir do n localRepository;
A configurao dos servidores de releases e snapshots do Nexus, com suas respectivas credenciais de acesso (a partir de ns
<server>);
O segundo servidor configurado na Listagem 4, que acabamos
de mostrar, usado pelo Jenkins para acessar os diretrios do
Nexus, a fim de transferir os arquivos resultantes do processo de
build. O usurio definido neste n, identificado com as credenciais
deployment / deployment123, padro do Nexus, mas o recomendado que o leitor crie seus prprios usurios e os configure de
acordo com as polticas de acesso que desejar, para garantir um
controle maior sobre o servidor.
Por fim, salvamos todo o trabalho descrito at aqui. Isso tudo
o que precisamos fazer em termos de preparao de ambiente,
do lado da gear devmediajenkins. Na prxima seo, veremos o
que precisamos fazer para que o projeto passe a ter seus artefatos
implantados no Nexus.

Preparando o projeto para trabalhar com o Nexus


Um dos principais critrios que usamos para adotar o Maven
como plataforma de gerenciamento do projeto a sua grande
flexibilidade.

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Listagem 2. Terminal Configurao e ativao do Nexus na plataforma OpenShift.


Pedro-Brigattos-MacBook:DevMediaOpenshiftApps pedrobrigatto$ cd devmedianexus/
Pedro-Brigattos-MacBook:devmedianexus pedrobrigatto$ git remote add nexus git://
github.com/shekhargulati/nexus.git
Pedro-Brigattos-MacBook:devmedianexus pedrobrigatto$ git pull -s recursive -X theirs
nexus master
...
Pedro-Brigattos-MacBook:devmedianexus pedrobrigatto$ git push
warning: push.default is unset; its implicit value has changed in
Git 2.0 from matching to simple. To squelch this message
and maintain the traditional behavior, use:
git config --global push.default matching
To squelch this message and adopt the new behavior now, use:
git config --global push.default simple
When push.default is set to matching, git will push local branches
to the remote branches that already exist with the same name.
Since Git 2.0, Git defaults to the more conservative simple
behavior, which only pushes the current branch to the corresponding
remote branch that git pull uses to update the current branch.
See git help config and search for push.default for further information.
(the simple mode was introduced in Git 1.7.11. Use the similar mode
current instead of simple if you sometimes use older versions of Git)
Counting objects: 771, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (472/472), done.
Writing objects: 100% (771/771), 111.50 MiB | 126.00 KiB/s, done.
Total 771 (delta 284), reused 764 (delta 280)
remote: Stopping DIY cartridge

remote: Building git ref master, commit d7b9251


remote: Preparing build for deployment
remote: Deployment id is ea9ba91b
remote: Activating deployment
remote: + [ -d /var/lib/openshift/568653ab2d5271af6400003d/app-root/
data//tomcat ]
remote: + mkdir /var/lib/openshift/568653ab2d5271af6400003d/app-root/data//prefs
remote: + cp -rf /var/lib/openshift/568653ab2d5271af6400003d/app-root/runtime/
repo//diy/tomcat /var/lib/openshift/568653ab2d5271af6400003d/app-root/data/
remote: + cd /var/lib/openshift/568653ab2d5271af6400003d/app-root/data//tomcat
remote: + rm -rf logs
remote: + ln -s /var/lib/openshift/568653ab2d5271af6400003d/app-root/logs/ logs
remote: + sed -ig s/OPENSHIFT_APP_DNS/devmedianexus-pedrobrigatto.rhcloud.
com/ conf/server.xml
remote: Starting DIY cartridge
remote: + export PLEXUS_NEXUS_WORK=/var/lib/openshift/568653ab2d5271af6400
003d/app-root/data/
remote: + PLEXUS_NEXUS_WORK=/var/lib/openshift/568653ab2d5271af6400003d/
app-root/data/
remote: + export CATALINA_OPTS= -Djava.util.prefs.userRoot=/var/lib/openshift/5686
53ab2d5271af6400003d/app-root/data//prefs
remote: + CATALINA_OPTS= -Djava.util.prefs.userRoot=/var/lib/openshift/568653ab2
d5271af6400003d/app-root/data//prefs
remote: + cd /var/lib/openshift/568653ab2d5271af6400003d/app-root/data//tomcat
remote: + sed -ig s/OPENSHIFT_INTERNAL_IP/127.5.223.129/g conf/server.xml
remote: + bin/startup.sh
remote: ------------------------remote: Git Post-Receive Result: success
remote: Activation status: success
remote: Deployment completed with status: success
To ssh://568653ab2d5271af6400003d@devmedianexus-pedrobrigatto.rhcloud.
com/~/git/devmedianexus.git/
52b117a..d7b9251 master -> master
Pedro-Brigattos-MacBook:devmedianexus pedrobrigatto$

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

67

67

DevOps: Como adequar seu processo de CI a essa nova cultura

Por meio de plug-ins, podemos expandir os recursos de nosso


projeto e implantar nele inmeras das tcnicas e dos procedimentos que, juntos, caracterizam um contexto tpico de DevOps.
No tocante a implantao de artefatos em servidores como
o Nexus, o plug-in que adotamos neste artigo o org.sonatype
.plugins:nexus-staging-maven-plugin, cujos detalhes veremos
adiante.
Listagem 3. Terminal Dados de acesso ao gear em que o Jenkins est instalado.
Pedro-Brigattos-MacBook:DevMediaOpenshiftApps pedrobrigatto$ rhc createapp devmediajenkins jenkins-1 https://cartreflect-claytondev.rhcloud.com/
reflect?github=majecek/openshift-community-git-ssh
...
Creating application devmediajenkins ... done
Jenkins created successfully. Please make note of these credentials:
User: admin
Password: **********
Note: You can change your password at: https://devmediajenkins-pedrobrigatto.
rhcloud.com/me/configure
Waiting for your DNS name to be available ... done
Cloning into devmediajenkins...
Warning: Permanently added the RSA host key for IP address 54.84.13.138 to the
list of known hosts.
Your application devmediajenkins is now available.
URL:
http://devmediajenkins-pedrobrigatto.rhcloud.com/
SSH to: 5**83***000**@devmediajenkins-pedrobrigatto.rhcloud.com
Git remote: ssh://***20**@devmediajenkins-pedrobrigatto.rhcloud.com/~/git/
devmediajenkins.git/
Cloned to: /Users/pedrobrigatto/Personal/Projects/DevMediaOpenshiftApps/
devmediajenkins
Run rhc show-app devmediajenkins for more details about your app.
Listagem 4. settings.xml Configurao do Maven no servidor do Jenkins.
<settings>
<localRepository>/var/lib/openshift/5683ab440c1e66ea82000098/app-root/
data/.m2</localRepository>
<servers>
<server>
<id>releases</id>
<username>deployment</username>
<password>deployment123</password>
</server>
<server>
<id>snapshots</id>
<username>deployment</username>
<password>deployment123</password>
</server>
</servers>
</settings>

68 Java Magazine Edio 150

68

Para entender o que precisamos fazer, comearemos pelo estudo


da Listagem 5. As caractersticas que abordaremos a partir de
agora esto todas destacadas em negrito. A primeira delas a alterao de uma propriedade do plug-in padro utilizado pelo Maven
para o processo de deploy (a saber, org.apache.maven.plugins:mavendeploy-plugin). Basicamente, precisamos solicitar ao Maven que
ignore a execuo desse plug-in. Fizemos isso para garantir que,
para a fase de deploy de nosso projeto, seja sempre utilizado outro
plug-in, o j citado org.sonatype.plugins:nexus-staging-maven-plugin.
Essa garantia estabelecida quando associamos, a esse plug-in
que acabamos de citar, o identificador default-deploy, para a fase
e o goal deploy.
Por fim, ainda no arquivo pom.xml, podemos ver a declarao do
repositrio de snapshots do Nexus. Isso importante para que,
quando o job de deployment do Jenkins for executado, o caminho
do repositrio seja encontrado. O identificador desse servidor (id)
bastante importante, pois o seu valor deve corresponder a algum
dos identificadores de servidores declarados no arquivo settings
.xml do servidor Jenkins, cujo contedo j tivemos a oportunidade
de avaliar pela Listagem 4. Quando todas as informaes convergem, o job saber tudo o que necessrio para que a implantao
ocorra: credenciais de acesso, URL e caminho do repositrio.
Assim que terminamos a edio do pom.xml, precisamos submet-lo ao controle de verso do repositrio GitHub. Para isso,
executamos os passos listados a seguir:
Certificamo-nos que estvamos dentro do diretrio raiz do
projeto (devops);
Executamos o comando git add .;
Com o comando git commit m comentrio de identificao, submetemos o contedo ao controle de verso local, descentralizado;
A partir da execuo da instruo git push u origin master,
enviamos as modificaes realizadas localmente para o controle
de verso remoto, sob o branch master (nosso nico branch desse
repositrio no GitHub).
A prxima fase desse tutorial, agora que Nexus e Jenkins j esto
configurados para se comunicar, criar o quarto e ltimo job da
srie para, finalmente, automatizar a transferncia de mdulos de
nosso projeto para o nosso repositrio controlado de artefatos.

O quarto job: transferindo artefatos para o Nexus


A criao desse job foi realizada a partir da funo Jenkins > New
Item no portal de administrao do Jenkins. Trata-se de um item
chamado devmediadevops_deploy, do tipo Freestyle project, e cujos
nicos pontos de ateno encontram-se na seo Build.
Nela, clicamos no boto Add build step e selecionamos a alternativa Invoke top-level Maven goals, preenchendo-a com o contedo da
Listagem 6. Mais uma vez, declaramos a referncia completa ao
descritor do projeto, por meio do parmetro -f, garantindo que o
projeto sempre ser encontrado. A novidade aqui, entretanto, o
uso de outro parmetro, denominado DaltDeploymentRepository.
Ele usado para passar ao Maven uma referncia explcita para o
repositrio no qual desejamos que os artefatos sejam implantados

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Listagem 5. pom.xml Configurao das dependncias e do plug-in de deploy do Maven.


<project xmlns=http://maven.apache.org/POM/4.0.0
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xsi:schemaLocation=http://maven.apache.org/POM/4.0.0
http://maven.apache.org/ xsd/maven-4.0.0.xsd>
<modelVersion>4.0.0</modelVersion>
<groupId>br.com.devmedia.articles</groupId>
<artifactId>devops</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>DevOps</name>
<properties>
<spring.version>4.2.2.RELEASE</spring.version>
<junit.version>4.12</junit.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<sonar.language>java</sonar.language>
<sonar.plugin.version>2.1</sonar.plugin.version>
</properties>
<modules>
<module>model</module>
<module>standalone-cli</module>
</modules>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>

<artifactId>sonar-maven-plugin</artifactId>
<version>${sonar.plugin.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<executions>
<execution>
<id>default-deploy</id>
<phase>deploy</phase>
<goals>
<goal>deploy</goal>
</goals>
</execution>
</executions>
<configuration>
<serverId>releases</serverId>
<nexusUrl>http://devmedianexus-pedrobrigatto.rhcloud.com/nexus/
</nexusUrl>
</plugins>
</pluginManagement>
</build>
<distributionManagement>
<repository>
<id>snapshots</id>
<name>Snapshots</name>
<url>http://devmedianexus-pedrobrigatto.rhcloud.com/nexus/content/
repositories/snapshots/
</url>
</repository>
</distributionManagement>
</project>

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

69

69

DevOps: Como adequar seu processo de CI a essa nova cultura

(neste caso, snapshots). Isso tudo o que precisamos configurar no


Jenkins para que o job consiga cumprir o seu papel. Para finalizar,
clicamos em Save e registramos todo o trabalho.
Chegamos, enfim, ao final da configurao de toda a cadeia de
jobs necessria para demonstrar, na prtica, um fluxo simplificado
de entrega contnua. As prximas sees, por sua vez, introduziro
alguns recursos, prticas e ferramentas adicionais, com o objetivo
de complementar e enriquecer toda a bagagem que adquirimos,
at aqui, sobre a cultura de DevOps.

Visualizao e gerenciamento de jobs com o Build Pipeline


Em sees anteriores, aprendemos como configurar duas cadeias
de jobs. A primeira delas, devmediadevops_daily_job, envolve apenas
a verificao do cdigo-fonte (compilao e testes) e ser utilizada
para executar builds automticas a partir de commits de desenvolvedores. J a segunda, iniciada a partir do job devmediadevops_cleanup_job, executada diariamente, meia-noite do horrio do
servidor em que o Jenkins est implantado, e envolve um conjunto
maior de atividades, incluindo uma anlise esttica da qualidade
do projeto e a gerao de um relatrio a ela relacionado.
Listagem 6. Job devmediadevops_deploy Build step para implantao do
artefato no repositrio do Nexus.
deploy -f $OPENSHIFT_DATA_DIR/workspace/sonar/devops/pom.xml
-U -DaltDeploymentRepository=snapshots::default::http://devmedianexus-pedrobrigatto.rhcloud.com/nexus/content/repositories/snapshots

Nesta seo, veremos uma forma grfica, amigvel, de trabalhar


com essas cadeias de job. Trata-se de um plug-in do Jenkins chamado Build Pipeline, cuja configurao ser discutida ao longo
dos prximos pargrafos.
Para orientar o nosso estudo, observemos o contedo da Figura 6.
Esta a pgina que vemos quando, a partir da tela inicial da ferramenta de administrao do Jenkins (cuja URL est informada
na Listagem 3), navegamos at o item Jenkins > Manage Jenkins
> Manage Plugins > Available. No campo de pesquisa informado
no topo dessa pgina, devemos procurar pelo plug-in intitulado
Build Pipeline. Quando fazemos isso, conforme a Figura 6 tambm
ilustra, temos como primeira opo o plug-in que desejamos. Basta,
ento, selecion-lo e instal-lo, reiniciando o servidor para que a
configurao surta efeito.
Assim que o servidor for reiniciado e acessamos, mais uma
vez, o painel de administrao do Jenkins, j podemos comear
a configurar a visualizao da cadeia de jobs em um formato de
pipeline. Vejamos a ilustrao contida na Figura 7. Nela, observamos a existncia de um boto com o sinal de +, indicado pela
seta mais ao topo. Ao clicarmos nele e selecionarmos a opo Build
Pipeline View do formulrio que se segue, somos apresentados a
uma tela em que essa View ser, enfim, configurada. Na seo
Label da pgina em questo, observe que existe um campo com os
dizeres Select initial job, seguido de uma caixa de seleo que lista
todos os jobs configurados. Basta que informemos o job desejado
(devmediadevops_cleanup_job para o caso da cadeia executada periodicamente, uma vez ao dia, ou devmediadevops_daily_job, para

Figura 6. Instalao do plug-in Build Pipeline do Jenkins

70 Java Magazine Edio 150

70

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Figura 7. Criao de uma visualizao de Pipeline no Jenkins

Figura 8. Visualizao do pipeline no Jenkins


o job executado a cada submisso de cdigo) e a pipeline ser
devidamente montada e exibida.
Assim que salvarmos essa configurao, o resultado ser algo
parecido com o que est ilustrado na Figura 8.
A partir dessa View, podemos testar, manualmente, a cadeia
de jobs, acompanhando visualmente o andamento do processo.
Da mesma forma, podemos repetir a execuo de qualquer job,
individualmente, se assim desejarmos. Essa , portanto, apenas
uma maneira amigvel de administrar e/ou validar, visualmente,
um fluxo de jobs.

Algumas palavras sobre provisionamento


Todo o contedo visto at esta seo tem um vis mais voltado para atividades ligadas ao desenvolvimento de software

do que quelas tipicamente associadas ao time de operaes.


Entretanto, em DevOps, a alta qualidade no resultado final
passa, necessariamente, por uma boa execuo de todas as
tarefas envolvidas no processo e, como procuramos, tambm,
evidenciar, mesmo os movimentos de um nico desenvolvedor
podem gerar efeitos em todo o restante da cadeia, afetando
inclusive a rotina Ops.
Em DevOps, um dos temas categorizados como de operaes
e que, at aqui, abordamos apenas implicitamente, por meio
das plataformas que utilizamos o de provisionamento de
recursos. Esse assunto, tal como visto at esse momento, foi
sempre algo que usamos de forma transparente, ao consumirmos servidores ou, melhor dizendo, gears criados e
gerenciados a partir de uma conta OpenShift.

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

71

71

DevOps: Como adequar seu processo de CI a essa nova cultura

No entanto, nem sempre teremos nossa disposio os servidores da empresa ou bancados por ela para executar nossos
testes locais. Nesses casos, podemos adotar as mesmas tecnologias que essas solues de PaaS usam e, assim, montar nossos
prprios ambientes para, seno replicar, simular o mais prximo
da realidade os ambientes-alvo de nossas aplicaes (mesmo
sistema operacional, mesmos caminhos de diretrio, mesmos
scripts, etc.).
Uma das tecnologias muito populares no quesito de provisionamento de recursos, e que estudaremos de forma introdutria no
texto que se segue, o Vagrant. Essa ferramenta desenvolvida e
mantida por uma empresa chamada HashiCorp e apresenta uma
forma muito simples para criar, configurar e gerenciar mquinas
virtuais, atuando logo acima de ferramentas de virtualizao
como VMware e VirtualBox.
Essa facilidade de uso deve-se principalmente forma como o
Vagrant se apresenta para ns. Toda a definio de uma infraestrutura escrita a partir de um arquivo chamado Vagrantfile,
que se usa de uma linguagem pr-definida e muito bem documentada em seu site oficial (disponvel na seo Links) para,
passo a passo, criar todos os ns de um ambiente desejado,
consideradas todas as caractersticas de poder de processamento,
quantidade de espao em disco, memria, configuraes de rede,
dentre outros. Veremos, logo mais, um pouco sobre a estrutura
desse arquivo.
Assim que o Vagrant instalado, podemos passar a utiliz-lo via
linha de comando. Trata-se de uma ferramenta cujas instrues
so bastante simples e com as quais rapidamente nos familiarizamos medida do uso. Ao abrir um terminal e digitar, por exemplo,

vagrant v, sua verso ser impressa. O prximo passo para iniciar


os trabalhos com essa ferramenta escolher um diretrio em
que nosso projeto ser criado. Feito isso, basta que, via linha de
comando, executemos vagrant init que, automaticamente, gerar
o j citado arquivo descritor do projeto (Vagrantfile).
A partir de agora, veremos um exemplo prtico de como
construdo um arquivo desses, tomando como base o contedo
da Listagem 7.
Essa listagem apresenta a configurao de um cluster composto
por trs ns (mquinas virtuais) comunicando-se a partir de uma
rede privada. A primeira linha dentro do bloco de configurao
de nosso projeto consiste na definio da imagem a ser usada que,
no vocabulrio do Vagrant, conhecida como box.
Nesse exemplo, usamos uma imagem do sistema operacional
CentOS (chef/centos-6.5). Essa imagem ser, em um primeiro
momento, baixada para nossa mquina de trabalho e, posteriormente, utilizada no provisionamento. A imagem do CentOS,
bem como uma srie de outras (de outros sistemas operacionais,
verses, etc.) encontram-se disponveis a partir de um catlogo
padro mantido pela prpria HashiCorp, conhecido como Atlas.
O endereo para acessar esse repositrio de imagens pode ser
verificado na seo Links.
Embora estejamos utilizando uma imagem pblica de uma
mquina CentOS, nada impede, caso precisemos ou queiramos,
definir as nossas prprias boxes, publicando-as em um repositrio pblico ou privado, dependendo dos critrios adotados pela
empresa ou pelo projeto.
O segundo ponto destacado em negrito na Listagem 7 corresponde definio de uma rede privada sobre o protocolo DHCP.

Listagem 7. Vagrantfile Configurao de um cluster a partir do Vagrant.


# -*- mode: ruby -*-

vb.name = clusternode1

# vi: set ft=ruby :

end
clusternode1.vm.provision :shell, path: vm_bootstrap.sh

Vagrant.configure(2) do |config|

end

config.vm.box = chef/centos-6.5

config.vm.define clusternode2 do |clusternode2|


clusternode2.vm.network forwarded_port, guest: 8082, host: 8083

if Vagrant.has_plugin?(vagrant-cachier)

clusternode2.vm.provider virtualbox do |vb|

config.cache.scope = :box

vb.memory = 1024
vb.name = clusternode2

config.cache.enable :generic, {

end

wget => { cache_dir: /var/cache/wget },

clusternode2.vm.provision :shell, path: vm_bootstrap.sh

curl => { cache_dir: /var/cache/curl },

end

}
end

config.vm.define clusternode3 do |othernode1|


clusternode3.vm.provider virtualbox do |vb|

config.vm.network private_network, type: dhcp

vb.memory = 1024
vb.name = clusternode3

config.vm.define clusternode1 do |clusternode1|

end

clusternode1.vm.network forwarded_port, guest: 8082, host: 8082


clusternode1.vm.provider virtualbox do |vb|
vb.memory = 1024

72 Java Magazine Edio 150

72

clusternode3.vm.provision :shell, path: vm_bootstrap_with_loadBalancer.sh


end
end

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Perceba o quanto simples uma configurao que, normalmente,


tomaria algum tempo caso tivesse que ser feita manualmente em
ferramentas como o VirtualBox, por exemplo.
Em seguida, vemos o primeiro bloco correspondente a um n do
cluster em configurao. Note que usamos, aqui, uma propriedade
denominada config.vm.define para definir uma nova VM e, como
estamos criando mais de uma mquina nesse mesmo arquivo,
rotulamos esse n em particular como clusternode1. Os recursos
e caractersticas que definimos para essa mquina so:
Ferramenta de provisionamento: Virtual Box;
Total de memria RAM disponvel: 1 GB (dado que a unidade
padro MB);
Nome do n: clusternode1;
Redirecionamento da porta 8082 do guest (VM) para a porta
8082 do host.
Outro ponto muito importante e interessante da configurao
que, uma vez que a mquina tenha sido criada e esteja ativa,
podemos informar ao Vagrant que o restante do procedimento
de preparao ser realizado a partir de um script desenvolvido
por ns (e, portanto, altamente customizvel). Isso feito quando
declaramos a instruo clusternode1.vm.provision, usando :shell
como opo e, em seguida, informando o caminho (relativo
raiz do projeto) para o script que deve ser executado.
Da mesma forma, outros dois ns foram configurados, sendo
que o ltimo, somente com finalidade didtica, de ilustrao,
utiliza um script diferente dos dois primeiros, sugerindo que
atua como um n de balanceamento de carga entre os dois ns
iniciais do cluster (observe que, nesse caso, declaramos o uso de
um script denominado vm_bootstrap_with_loadBalancer.sh).
Embora, a princpio, a sintaxe possa confundir um pouco, o
importante a ser extrado desse material que ocorre, a partir do
contedo nele declarado, um procedimento relativamente complexo de preparao de ambiente, convertido em poucas linhas

de um arquivo de declarao. Alm disso, vlido notar que


estamos falando de um arquivo de configurao que pode,
portanto, ser versionado como qualquer outro. Uma vez
submetido ao controle de verso, pode ser baixado, utilizado
e at editado por qualquer membro do projeto, estabelecendose um ambiente de testes que todos os desenvolvedores do
projeto consideraro em seu dia a dia de trabalho (seja localmente, caso suas mquinas suportem, ou mesmo em algum
laboratrio disponibilizado pela empresa).
Assim que o projeto estiver configurado e esse arquivo for
salvo, podemos subir os ns deste cluster usando, via terminal, o comando vagrant up <nome do n>. Caso no forneamos
nenhum nome de n, todos eles sero iniciados. Por sua vez,
quando informamos um nome especfico, apenas aquele n
ser criado e colocado em execuo.
Assim que a(s) mquina(s) estiver(em) disponvel(eis), podemos acess-la(s) usando o comando vagrant ssh <nome do
n>, o que abre uma sesso de comunicao remota com a(s)
mquina(s) em questo.
Por fim, quando tivermos encerrado nossas atividades e
no mais desejarmos utilizar essa mquina, podemos tanto
destru-la, por meio do comando vagrant destroy <nome do n>
(liberando todos os recursos, inclusive espao em disco, do
hospedeiro), quanto somente deslig-la temporariamente,
por meio do comando vagrant halt <nome do n>.
Alm do Vagrant, h outras solues to ou mais atraentes disponveis no mercado (muitas delas gratuitamente).
O OpenStack, por exemplo, uma excelente plataforma cujo
estudo recomendamos ao leitor que se interessa pelo tema. H,
ainda, aquelas que, por si s, so extremamente poderosas,
mas que, ainda por cima, oferecem excelente integrao com
o Vagrant. Um exemplo fabuloso o Docker, que tem uma
filosofia diferente por trabalhar com containers ao invs de

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

73

DevOps: Como adequar seu processo de CI a essa nova cultura

mquinas virtuais, mas que se integra perfeitamente com o


Vagrant, unindo o melhor dos dois mundos em uma soluo
combinada e poderosssima de provisionamento de ambientes.
Solues mais recentes, como o Buddy (veja a seo Links), surgiram trazendo uma oferta de todo esse ferramental j devidamente
integrado e pronto para ser utilizado, seguindo um modelo de
oferta parecido com o da OpenShift. A referncia para essa opo
pode ser encontrada na seo Links.
DevOps um conceito ainda muito recente e que, definitivamente, no pode ser implantado em empresa alguma sem passar por
um intenso planejamento, muita discusso e uma fina sintonia
entre todos os departamentos e pessoas envolvidas.
Trata-se de algo que envolve no apenas ferramentas, tecnologias,
mas, principalmente, pessoas e uma mentalidade totalmente diferente do que se vinha aplicando no mundo corporativo at pouco
tempo atrs. Ainda que empresas se adequem, existe um desafio
ainda maior a ser vencido, que incorporar o cliente nessa nova
filosofia. O sucesso de uma prtica DevOps s chegar nos casos
em que todos, compreenderem o que est sendo feito, os objetivos
a serem atingidos e as novas responsabilidades que surgem com
esse novo formato.
A rea de operaes, historicamente, envolve uma srie de
processos que no sero automatizados to cedo. Empresas
tradicionais, com negcios tradicionais, com mentalidade mais
conservadora, ainda compem uma parcela significativa do mercado. Nesses casos, o encanto de uma abordagem promissora como
DevOps esbarra, naturalmente, em preconceito, receio, incertezas
e, principalmente, uma necessidade forte de provas, de fatos, de
qualquer coisa que sustente, antecipadamente, todas as promessas
que os ares do DevOps carregam consigo.
Desenvolvimento, por sua vez, a rea que menor presso sofre,
pois, ainda que no se consiga implantar rapidamente uma cultura DevOps em uma empresa, inmeras das tcnicas, prticas,
tecnologias e plataformas apresentadas ao longo do texto podem
ser, ainda que a conta-gotas, inseridas no cotidiano dos projetos
e provar-se teis e vantajosas. Esse modelo de insero gradual,
quase que em um carter de prova de conceito, muito mais
bem aceito em um contexto de desenvolvimento do que naquele
de operaes.
Reduzir essa lacuna entre Dev e Ops , principalmente, trabalhar
as pessoas e suas particularidades. buscar, todos juntos, listar
medidas que possam ajudar a tornar esse elo mais forte, menos
turbulento.

74 Java Magazine Edio 150

74

O ponto fundamental desenvolver uma cultura corporativa em


que as pessoas compreendam o valor real de uma mudana como
a que DevOps prope em suas linhas gerais para, s ento, dar a
guinada a partir de prticas e tecnologias disponveis no mercado.
O aparato tcnico imenso e muito bom, mas s funcionar em
um modelo genuinamente DevOps se as mentes pensantes por
trs de tudo isso absorverem, de fato, seus princpios.

Autor
Pedro E. Cunha Brigatto
pedrobrigatto.devmedia@gmail.com
Engenheiro da Computao graduado pela Universidade Federal de So Carlos, desenvolvedor certificado SAP Netweaver
(Java Stack) e programador certificado SCJP. Especialista em Engenharia
de Software graduado pela Unimep e ps-graduado em Administrao
pela Fundao BI-FGV, atua com desenvolvimento de software desde 2005. Atualmente
atua como consultor tcnico no desenvolvimento de solues de alta disponibilidade
na Avaya.

Links:
Cdigo do projeto tema no GitHub.
https://github.com/pedrobrigatto/devmedia_devops_series
Texto refletindo sobre a opo entre Gradle e Maven.
http://devops.com/2015/03/27/puzzle-gradle-maven/
Trabalhando com hooks no Git.
https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks
Atlas catlogo de imagens da HashiCorp.
https://atlas.hashicorp.com/boxes/search
Pgina oficial do Buddy.
https://buddy.works/

Voc gostou deste artigo?


D seu voto em www.devmedia.com.br/javamagazine/feedback
Ajude-nos a manter a qualidade da revista!

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Guia HTML 5

Um verdadeiro manual de referncia


com tudo que voc precisa sobre HTML!

DEVMEDIA
http://www.devmedia.com.br/guias/guia-html/3

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Edio 150 Java Magazine

75

75

DevOps: Como adequar seu processo de CI a essa nova cultura

76 Java Magazine Edio 150

76

Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

Você também pode gostar