java-magazine 150 uqadszlo

Upload: anderson-kerlly

Post on 03-Mar-2018

227 views

Category:

Documents


2 download

TRANSCRIPT

  • 7/26/2019 Java-magazine 150 Uqadszlo

    1/76

    9 7 7 1 6 7 6 8 3 6 0 0 2 05100

    ISSN 1676836-1

  • 7/26/2019 Java-magazine 150 Uqadszlo

    2/76

  • 7/26/2019 Java-magazine 150 Uqadszlo

    3/76

    Edio 150 2016 ISSN 1676-8361

    Editor

    Eduardo Spnola ([email protected])

    Consultor Tcnico Diogo Souza ([email protected])

    ProduoJornalista Responsvel Kaline Dolabella - JP24185

    Capa e Diagramao Romulo Araujo

    DistribuioFC Comercial e Distribuidora S.A

    Rua Teodoro da Silva, 907, Graja - RJ

    CEP 20563-900, (21) 3879-7766 - (21) 2577-6362

    Fale com o Editor!

    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 qualartigo voc menos gostou.Fique a vontade para entrar em contato com oseditores e dar a sua sugesto!Se voc estiver interessado em publicar um artigo na revista ou no site JavaMagazine,entre em contato com o editor,informando o ttulo e mini-resumodo tema que voc gostaria de publicar:

    EDUARDO OLIVEIRA SPNOLA

    eduspinola.wordpress.com

    @eduspinola / @Java_Magazine

    Assine agora e tenha acesso atodo o contedo da DevMedia:www.devmedia.com.br/mvp

    Atendimento ao leitorA DevMedia possui uma Central de Atendimento on-line, onde vocpode tirar suas dvidas sobre servios, enviar crticas e sugestes efalar com um de nossos atendentes. Atravs da nossa central tambm possvel alterar dados cadastrais, consultar o status de assinaturase conferir a data de envio de suas revistas. Acesse www.devmedia.com.br/central, ou se preferir entre em contato conosco atravs dotelefone 21 3382-5038.

    [email protected] 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, comdetalhes sobre preos e formatos de anncios.

    Java, o logo tipo da xcara de caf Java e todas as marcas e log otipos

    baseados em/ ou referentes a Java so marcas comerciais ou

    marcas registradas da Sun Microsystems, Inc. nos Estados Unidos e

    em outros pases.

    EXPEDIENTE

  • 7/26/2019 Java-magazine 150 Uqadszlo

    4/76

    Sumrio

    A Java Magazine tem que ser feita ao seu gosto.

    Para isso, precisamos saber o que voc, leitor, achada revista!

    D seu voto sobre esta edio, artigo por ar tigo, atra-vs do link:

    www.devmedia.com.br/javamagazine/feedback

    D

    seu

    Feedback

    sobre

    es

    taedio

    D seu feedback sobre esta edio!

    60 DevOps: Como adequar seu processo de CI a essa nova cultura[Pedro E. Cunha Brigatto ]

    Artigo no tipo Mentoring Destaque - Reflexo

    18 Como usar o Apache Cassandra em aplicaes Java EE Parte 2[Marlon Patrick ]

    Artigo no estilo Curso

    06 Por dentro do banco de dados NoSQL Couchbase Parte 2[Fernando Henrique Fernandes de Camargo ]

    Artigo no estilo Curso

    48 Simplificando o desenvolvimento de microsservios com o WildFly Swarm[Joel Backschat ]

    Contedo sobre Novidades

    Contedo sobre Novidades Destaque - Vanguarda

    32 Introduo ao Java 9: Conhea os novos recursos[ Jos Guilherme Macedo Vieira ]

  • 7/26/2019 Java-magazine 150 Uqadszlo

    5/76

  • 7/26/2019 Java-magazine 150 Uqadszlo

    6/76

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

    ESTEARTIGOFAZPARTEDEUMCURSO

    M

    uito tem se falado de NoSQL nos ltimos anos.De forma simples, podemos descrev-lo comoo movimento que norteou a criao de ban-

    cos de dados bem especializados em certos problemase alguns de uso mais geral. O que todos eles tm emcomum a capacidade de serem utilizados em grandesaplicaes e conseguirem escalonar o suficiente para queas mesmas continuem com boa performance mesmo comgrande fluxo de dados e usurios simultneos.

    Dentre as opes disponveis, destacam-se aquelascuja estrutura de dados baseia-se em documentos. Issoporque eles podem servir tanto para aplicaes de usogeral, 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 apre-sentando timos resultados em benchmarks que provamsua superioridade em performance.

    O Couchbase, banco de dados apresentado na primeiraparte deste artigo, se mostra muito til a seus usurios.No s pela sua grande performance e tima escalabili-dade, mas tambm pela sua capacidade de sincronizaocom dispositivos mveis, uma nova funcionalidade queainda no existe em seu concorrente. Com esse recurso,viabiliza-se a facilidade da sincronizao de dados entredispositivos mveis, que podero operar sem acesso

    internet, e o servidor, que ser o centro de dados.

    Com suas vantagens analisadas, foi vista a teoria sobre seufuncionamento, desde o gerenciamento do cluster e a conexoentre seus elementos, at o funcionamento interno de cada n,o que faz com que toda essa performance proposta seja garan-tida. 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, todosos ns se conhecem e a aplicao cliente poder se conectar comqualquer um deles, sendo informada da existncia de todos osoutros automaticamente.

    Esses ns, individualmente, garantem tempos de respostas naordem de milissegundos atravs de um cache em memria, quearmazena os ltimos documentos acessados. Alm disso, as ope-raes de insero e alterao so realizadas primeiro na memria,o que possibilita ainda mais agilidade.

    Para implementar o escalonamento horizontal, os dados so dis-tribudos entre esses ns com o recurso de auto-sharding. E para seprevenir de desastres, uma rplica de cada documento guardada

    em outro n. Assim, caso um dos ns venha a ficar fora do ar, um

    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.

    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 dadosbem flexvel.

    Fique por dentro

    Veja neste artigo como desenvolver aplicaes

    escalveis com Couchbase

    Por dentro do banco

    de dados NoSQLCouchbase Parte 2

  • 7/26/2019 Java-magazine 150 Uqadszlo

    7/76

    Edio 150 Java Magazine 7

    7Copyright - 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 acontecea 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 dearmazenar outros valores que no sejam documentos.

    Por fim, uma tcnica foi apresentada para que seja feito o ver-sionamento de esquemas dos documentos de forma que sejamatualizados, quando cada documento for acessado naturalmentepela aplicao. Utilizando essa tcnica, veremosa seguir o de-senvolvimento de uma aplicao Java que se comunicar com um

    banco de dados Couchbase implementando as operaes bsicase o mapeamento entre objetos e documentos.

    Desenvolvimento com o Java SDKAntes de iniciar a implementao com o cliente Java do Cou-

    chbase precisamos instal-lo e configur-lo. Para isso, est dis-ponvel na seo Linksa URL com as instrues de download einstalaoda verso gratuita, alm de como executar o serviodo Couchbase. Esses detalhes variam de acordo com o sistemaoperacional do usurio.

    Concluda essa etapa, acesse a URL http://localhost:8091. Nessemomento uma tela ser apresentada para que seja feita a confi-gurao inicial do banco de dados. Nessa tela pode-se registrar o administrador como desejar e, por ser apenas um banco usadopara desenvolvimento, no necessrio mais de 1GB de memriaem sua configurao. Alm disso, tambm ser questionada aquantidade de memria para o bucketpadro, que o primeirobucketcriado no Couchbase. Como um outro bucketser criadopara o exemplo, o padro no tem importncia e pode at mesmoser deletado aps essa configurao inicial. Ento, recomenda-secolocar a quantidade mnima de memria para ele. Para esclarecer,

    bucket, para o Couchbase, o equivalente a um schemado MySQL.Normalmente, ser criado um para cada aplicao.

    Aps essas definies, o painel de controle do Couchbase serexibido, como mostra a Figura 1. Nesse painel, na abaData Buckets,crie um novo bucket com o nomeExemploe tipoCouchbase, vistoque ser utilizado para o armazenamento de documentos (o tipomembase serve para armazenar apenas chave-valor). Feito isso,as opes desse bucket ficaro visveis ao clicar na seta exibidaao lado de seu nome. Clique, ento, nessa seta, depois em Editedigite exemplo no campo depassword.

  • 7/26/2019 Java-magazine 150 Uqadszlo

    8/76

    Por dentro do banco de dados NoSQL Couchbase Parte 2

    8 Java MagazineEdio 1508 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

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

    Em um projeto Java, seja ele Maven ou Gradle, deve-se adicionaro seguinte artefato referente ao SDK do Couchbase: com.couchbase.

    client:java-client:2.1.4. Caso seja criado um projeto Java comumJava, deve-se baixar o arquivo JAR desse cliente, cujo endereotambm encontra-se na seo Links. Outra biblioteca utilizadaaqui ser a Joda Time, que muito conhecida como substitutada API nativa de gerenciamento de data e tempo do Java. Nesteexemplo, ela ser utilizada para salvar informaes importantesno documento, como o momento em que foi criado e o momento desua ltima modificao. Para o Maven e o Gradle, basta adicionaro seguinte artefato:joda-time:joda-time:2.8.1.

    Com o projeto criado e as dependncias satisfeitas, o prximopasso estabelecer uma conexo com o banco de dados, o quepode ser feito em uma classe contendo um mtodomain()como

    o cdigo daListagem 1.Nesse cdigo demonstramos a simples tarefa de abrir e fecharuma conexo com o banco de dados. Note que comeamoscriando uma conexo com um cluster passando uma lista deIPs conhecidos. Nesse caso, por ser um nico servidor local,sem qualquer cluster, podemos chamar o mtodo create()semqualquer argumento.

    Porm, se tivssemos um cluster com vrios servidores co-nectados, deveramos passar uma lista com os IPs de algunsdeles na chamada desse mtodo.

    Com uma conexo aberta para o cluster, deve-se conectar

    ao bucket criado para nossa aplicao. Para isso, chama-se omtodo openBucket()do cluster passando como argumentoo nome e a senha do bucket. Por fim, aps seu uso, fechamosa conexo com o cluster, o que possibilita a liberao dosrecursos utilizados e desencadeia no fechamento da conexodo bucket.

    A partir do momento em que uma conexo aberta, o objetoque ser utilizado por todo o sistema para acessar o bancoserobucket. Deste modo, esse pode ser configurado comosingleton no sistema, possibilitando que seja injetado peloSpring ou outro framework de injeo de dependncias. Poroutro lado, o cluster usadoapenas para abrir conexes com

    buckets e desconectar de todos eles de uma s vez atravs dodisconnect(), caso o sistema utilize mais de um. Portanto, oobjeto cluster ter esse mtodo invocado no shutdown dosistema para que os recursos sejam liberados.

    A seguir, os principais mtodos da classe Bucketso expli-cados: AsyncBucket async(): retorna uma verso assncrona doBucket. Com essa verso, todos os mtodos comuns do Bucketestaro disponveis, porm, eles retornam um ObservabledoRxJava, 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 cujoidentificador seja igual ao passado como parmetro. Caso taldocumento no exista, nullser retornado;JsonDocument getAndLock(String id, int lockTime): fun-ciona da mesma maneira que o mtodo anterior, contudo, essemtodo utilizado para a concorrncia pessimista, explicadaanteriormente. Assim, caso o documento seja encontrado, eleser retornado e bloqueado. Caso esse mtodo seja invocadonovamente enquanto o documento estiver bloqueado, uma

    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();

    }

    }

  • 7/26/2019 Java-magazine 150 Uqadszlo

    9/76

    Edio 150 Java Magazine 9

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

    exceo ser lanada. Apesar de permitir ajustar a quantidade desegundos que o documento ficar bloqueado, caso esse no sejadesbloqueado manualmente, o valor mximo que o documentopermanecer bloqueado de 30 segundos, mesmo que seja pas-

    sado um valor maior; > D insert(D document): insere

    um documento no banco de dados caso ainda no exista. Casocontrrio, um erro ser lanado. Em caso de sucesso, o mesmodocumento ser retornado com o valor de CAS atualizado. Assim,novas atualizaes podem ser feitas no mesmo com concorrnciaotimista; > D replace(D document): mais umavariante dos dois mtodos anteriores. Diferentemente desses, esseter sucesso apenas se o documento j existir. Caso contrrio, umerro indicar que o mesmo no existe;

  • 7/26/2019 Java-magazine 150 Uqadszlo

    10/76

    Por dentro do banco de dados NoSQL Couchbase Parte 2

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

    Listagem 2. Interface que especifica os mtodos de um mapeador, bem comoespecifica nomes de campos para os metadados.

    public interface DocumentMapper {

    String TYPE = type;

    String TYPE_VERSION = typeVersion;

    String CREATED_AT = createdAt;

    String LAST_MODIFIED = lastModified;

    E getEntity(JsonDocument document);

    JsonDocument getDocument(E entity);

    }

    Listagem 3. Classe abstrata criada com o propsito de ser superclasse de todos osmapeadores.

    public abstract class AbstractDocumentMapper implementsDocumentMapper {

    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))); }

    }

    Como mencionado na discusso sobre modelagem de documen-tos, ser utilizado um esquema de mapeamento com controle deverso. Vejamos a explicao e o cdigo de exemplo desse mtodono tpico a seguir.

    Mapeadores de documentos

    O mapeamento de objetos se dar atravs de classes simples,expostas adiante. Antes de mostrar esse cdigo, no entanto, se-ro apresentadas as entidades de exemplo. Vale notar que todaselas implementaro uma classe abstrata, chamadaEntity, com ointuito de tornar disponvel em todas as entidades as seguintespropriedades: String id, DateTimecreatedAt, DateTimelastMo-difiede Longrevision. Com elas, armazenamos o identificador dodocumento, o momento em que o mesmo foi inserido, o momentoem que foi modificado pela ltima vez e o valor do CAS.

    Para exemplificar, sero criadas apenas duas entidades: Taske

    Category. A entidade Taskrepresentar uma tarefa a ser realiza-da e Categorya encaixar em determinada categoria de tarefas.Tal aplicao ser uma simples TODO List. Para torn-la o maissimples possvel, ambas as entidadestero 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 queconfigura uma relao de um para muitos.

    Com as entidades prontas, deve-se criar os mapeadores dedocumentos para as mesmas. A Listagem 2mostra a interfaceimplementada por todos os mapeadores. Essa utiliza generics paraespecificar qual tipo de entidade tal mapeador tratar, especifican-do seus dois mtodos de converso. Alm disso, quatro campos

    que estaro presentes em todos os documentos so especificadosatravs de constantes. E para facilitar o trabalho das implemen-taes dessa interface, a classe AbstractDocumentMapper,mostrada na Listagem 3, implementa os mtodos do mapeador deforma a tratar os metadados do documento, como o identificador,momento de criao e ltima modif icao e o valor do CAS.

    O primeiro ponto a se notar no cdigo de AbstractDocument-Mapper o uso da classe ISODateTimeFormat, da biblioteca JodaTime. O objetivo com isso que todos os timestamps sejam salvosno formato padro definido pela ISSO 8601. Tal formato tem a van-tagem de ter um ordenamento natural das datas, de forma que umaordenao simples de Strings colocaria os timestamps em ordem.

  • 7/26/2019 Java-magazine 150 Uqadszlo

    11/76

    Edio 150 Java Magazine 11

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

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

    public class TaskDocumentMapperV1 extends AbstractDocumentMapper {

    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 {

    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;

    }}

    Tambm vale notar que foi util izada a verso do mtodo create()da classeJsonDocumentque espera um valor de CAS. Quandotal valor no est disponvel, normalmente utilizaramos outraverso desse mtodo. Porm, para facilitar, simplesmente passa-

    mos 0 quando o CAS for nulo, visto que esse o valor padro doCouchbase, equivalente a nulo. O restante da implementao dessaclasse clara e no necessita de maiores explicaes.

    No exemplo abordado, sero duas classes que herdam deAbstractDocumentMapper : TaskDocumentMapperV1e Cate-goryDocumentMapperV1. O sufixo V1 indica que essas classesso responsveis por mapear a primeira verso de seus respec-tivos documentos. A lgica de versionamento ser mostradamais adiante. As Listagens 4 e 5mostram essas duas classesmapeadoras.

    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 diretamenteo contedo desses, que armazenado em uma instncia deJson-Object. E assim como feito em um JSON comum, as propriedadesso armazenadas nos pares chave-valor, tendo seus nomes comochaves. Dessa forma, utilizamos o mtodo put()para preenchercada propriedade.

    Deve-se notar o ajuste das propriedades TYPEe TYPE_VER-SION, as quais so usadas para armazenar o tipo de documentoe a verso do esquema. Na Listagem 5, o tipo ajustado com ovalor category, defin ido como constante na classe CategoryDo-cumentMapper, que ainda ser mostrada. E por se tratar da pri-meira verso, ajustamos o campo de verso com o nmero inteiro

    1. Tambm pode-se notar na Listagem 4que o identificador dacategoria de determinada tarefa armazenado em um campodo documento, caso a categoria exista. Isso cria o mapeamentode um para muitos, de maneira parecida com o mtodo adotadoem bancos de dados relacionais.

    Para finalizar a lgica dos mapeadores, as Listagens 6 e 7mostram as implementaes das classes TaskDocumentMa-pper e CategoryDocumentMapper . Ambas foram utilizadasanteriormente nas Listagens 4 e 5, onde suas constantesDOC_TYPE foram usadas para armazenar o tipo correto deseus documentos.

    A lgica de versionamento implementada por essas classes

    simples. Elas possuem um mapa cuja chave especif ica uma ver-so do documento e o valor um mapeador para determinadaverso. Ao mapear um documento para uma entidade, a versodo documento verificada para se obter o mapeador correto.O caminho contrrio, em que a entidade ser convertida emdocumento, sempre feito com a verso atual. Quando umanova verso de um documento criada, a constante que espe-cifica a verso atual ser atualizada, e um mapeador, para talverso, ser criado para a mesma e adicionado ao mapa. Paramanter a compatibilidade, verses anteriores do mapeador seroatualizadas. Isso porque esses devero ler um documento emverso antiga e mape-lo para a verso mais nova da entidade,

    que poder ter seus campos modificados.

  • 7/26/2019 Java-magazine 150 Uqadszlo

    12/76

    Por dentro do banco de dados NoSQL Couchbase Parte 2

    12 Java MagazineEdio 15012 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

    Listagem 6. Implementao da lgica de versionamento dos mapeadores dosdocumentos de tarefa.

    public class TaskDocumentMapper implements DocumentMapper {

    public static final String DOC_TYPE = task;

    public static final Integer CURRENT_VERSION = 1;

    private Map mappers;

    public TaskDocumentMapper(TaskDocumentMapperV1

    taskDocumentMapperV1) {

    this.mappers = new HashMap();

    this.mappers.put(CURRENT_VERSION, taskDocumentMapperV1);

    }

    @Override

    public Task 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);

    }

    }

    Listagem 7. Implementao da lgica de versionamento dos mapeadores dosdocumentos de categoria.

    public class CategoryDocumentMapper implements

    DocumentMapper {

    public static final String DOC_TYPE = category;

    public static final Integer CURRENT_VERSION = 1;

    private Map mappers;

    public CategoryDocumentMapper(CategoryDocumentMapperV1

    categoryDocumentMapperV1) {

    this.mappers = new HashMap();

    this.mappers.put(CURRENT_VERSION, categoryDocumentMapperV1);

    }

    @Override

    public Category getEntity(JsonDocument document) {

    return mappers.get(((Number)document.content().get(TYPE_VERSION)).

    intValue()).getEntity(document); }

    @Override

    public JsonDocument getDocument(Category category) {

    return mappers.get(CURRENT_VERSION).getDocument(category);

    }

    }

    Tambm vale observar que esses mapeadores, responsveispelo versionamento de mapeadores, no implementam a classe

    AbstractDocumentMapper, mas sim a interface Document-Mapper. O objetivo deixar transparente ao resto do sistemao mecanismo de mapeamento de documentos. Normalmente,essas instncias sero criadas atravs de injeo de dependnciase os utilizadores das mesmas no precisaro se preocupar comdetalhes do versionamento.

    Antes de partirmos para a demonstrao do uso destas classes, preciso entender que elas devem ser combinadas para se obterobjetos completos. Ao observarmos aListagem 4, por exemplo,

    notamos que o objeto criado a partir de um documento de tarefapossui uma instncia vazia de Category, com apenas o identifi-

    cador preenchido. Para preencher esse objeto com uma categoriacompleta, deve-se pesquisar por seu documento, utilizar omapeador de categoria e ento ajust-la com um objeto completo.A responsabilidade de fazer essa combinao geralmente ficapor conta de um DAO, que ir interagir com documentos e usaros mapeadores para transform-los em objetos.

    Finalizando essa seo sobre mapeamento de documentos, aListagem 8 mostra o mtodo de insero e atualizao de enti-dades. Esse mtodo utilizado na Listagem 9, a qual demonstra

  • 7/26/2019 Java-magazine 150 Uqadszlo

    13/76

    Edio 150 Java Magazine 13

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

    todo o processo de insero, modificao, atualizao, obtenocompleta e, por fim, remoo das entidades de categoria e tarefa.

    O cdigo da Listagem 8 bem simples. Ele verifica se a enti-dade j possui uma data de criao e, caso no possua, cria uma

    com a data atual. Por outro lado, a data de modificao sempreatualizada. Depois disso, o mapeador utilizado para convertero objeto em documento e utiliza-se o mtodo upset(), que insereou atualiza um documento. Assim, o documento retornado poresse mtodo tem sua propriedade de CAS extrada e ajustada naentidade. Isso permite que futuras atualizaes da entidade sejampossveis evitando um CASMismatchException .

    Da mesma forma, o cdigo da Listagem 9no possui complica-es. Deve-se notar a combinao dos mapeadores para se obterum objeto completo. Primeiramente, o documento de tarefa obtido e convertido em objeto. Ento, busca-se sua categoriautilizando o identificador do objeto incompleto de categoria. Por

    fim, o documento de categoria convertido em sua entidade epassado para o objeto de tarefa. Isso algo que normalmente es-taria dentro de um DAO, mas para fins de facilitar a visualizao,foi colocado aqui. O restante do cdigo autoexplicativo. Basica-mente, altera-se as entidades, as armazena no banco utilizandoo mtodo apresentado na Listagem 8e depois as remove atravsdo mtodo remove(), que recebe o identificador do documentocomo parmetro.

    Com o cdigo demonstrado possvel fazer todas as operaesbsicas com documentos, desde que se tenha seu identificador.Mas normalmente essa pr-condio no satisfeita. Ao invsdisso, um sistema normalmente lista entidades de acordo com

    determinadas condies para que o usurio escolha o que fazercom alguma dessas entidades. Em um banco de dados relacional,isso feito com queries SQL. A seguir, veremos como buscarregistros no Couchbase.

    Utilizando Views

    A forma de busca por dados utilizada no Couchbase algo novoe 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 MapReducepara indexar os dados em Views, as quais so uma forma deorganizao 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 filtraentradas e pode extrair informao. Seu resultado final uma listaordenada de chave-valor, chamada de index, que armazenadaem disco e atualizada de forma incremental medida que osdocumentos so atualizados. Opcionalmente, pode-se prover umasegunda funo, chamada reduce, que tem o objetivo de somar,agregar ou realizar outros clculos sobre as informaes.

    Quando nos referimos a funes, estamos mencionando rotinasprogramadas em JavaScript, as quais sero executadas em cadaum dos documentos. Essas rotinas de map e reduce das Views

    so armazenadas como strings em documentos especiais JSON,

    Listagem 8. Cdigo de insero/atualizao de entidades. Normalmente estariaem um DAO.

    private static void upsert(DocumentMapper

    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();

    }

  • 7/26/2019 Java-magazine 150 Uqadszlo

    14/76

    Por dentro do banco de dados NoSQL Couchbase Parte 2

    14 Java MagazineEdio 15014 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

    conhecidos como design documents, e ento esses so associadosao bucket. Vale ressaltar que cada design document pode arma-zenar 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 deforma 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 podemser emitidas com qualquer campo de um documento. Ento, sefor desejada uma busca por contatos cujo nmero de telefone seinicie com o DDD 62, uma View ser criada filtrando apenas do-cumentos de contatos e usandoo nmero de telefone como chave.Por fim, uma busca nessaView com chaves que iniciem com 62ser 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 Couchbasenovamente, abra a aba de Views e selecione o bucket Exemplo,criado anteriormente. Nessa tela, nota-seque existem duas abas:Development Viewse Production Views. Essa diferena existe paraque, ao criar Views no banco de dados, esse no tenha quedas deperformance enquanto o processo de index feito. Portanto, umaDevelopment View trabalhar apenas com um subconjunto dosdados, a no ser que seja requisitado o contrrio.

    Como estamos no momento de criao da View, devemos utilizara aba Development Views e clicar em Create Development View. Emseguida, preencha tanto o campoDesign Document Namequanto ocampo View Namecom all_taskse clique em Save. Note que o De-

    sign Document Name prefixado com _design/dev_, sem opo demudana, visto que um padro fixado pelo Couchbase. Com oscampos preenchidos como especificado, o design document sercriado com a linguagem JavaScript e uma View chamada all_tasksser criada junto a ele. Clicando emEditna View recm-criada, umatela apresentar o cdigo da View em duas partes: Map e Reduce.

    Em Map est presente um cdigo simples, sendo essa umafuno JavaScript que recebe como parmetro o documento e os

    metadados do mesmo. Vemos tambm uma chamada para omtodo emit(), que basicamente adiciona ao index uma chavee valor. Nesse cdigo de exemplo, apenas o identificador dodocumento emitido com um valor nulo. Isso porque o valor

    aqui utilizado apenas pela funo Reduce, que se encontravazia, visto que opcional.Caso essa View seja mantida dessa forma, ela simplesmente

    buscar por todos os documentos e os ordenar pelo identifica-dor. Alm de no ser o que desejamos, tambm no faz sentidoessa ordenao, visto que geramos os identificadores com UUIDaleatrio 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-laspor data de criao, utilizaremos o cdigo mostrado naListagem 10como funo Map.

    O funcionamento dessa funo simples. Primeiro, certif ica-se que o documento atual do tipo JSON, lembrando queele poderia ser binrio, armazenar Strings, ou outros tiposmencionados anteriormente. Ento, verificamos se o tipodo 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 valora data de criao em formato ISO. Como no ser utilizadauma funo de Reduce, o valor passado para a funoemit() nulo. Em JavaScript, um argumento nulo pode simplesmenteser ignorado.

    Para testar essa View pode-se executar parte do cdigo mostra-do na Listagem 9, retirando o trecho que remove os documentos

    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);

    }

    }

  • 7/26/2019 Java-magazine 150 Uqadszlo

    15/76

    Edio 150 Java Magazine 15

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

    inseridos. Assim, ao clicar em Show Results, ser apresentada umatabela com aKey igual data de criao e oValueigual anull. Cadalinha 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_taskse clicarem Publish. Feito isso, esse passar para a aba de produo como nome _design/all_tasks. Observe que o prefixo _design/dev_foialterado automaticamente.

    Para demonstrar a utilizao dessa View pelo sistema, foi cria-da a Listagem 11. Nela, implementamos uma ViewQueryqueordenar as chaves de forma decrescente, pular os primeiros 0registros e limitar em 10 os registros retornados, demonstrandoassim o uso de paginao com os mtodos skip()e limit(). Tra-duzindo essa query, obteremos as dez ltimas tarefas criadas nosistema, sendo elas ordenadas em ordem decrescente de criao,ou seja, as mais novas so apresentadas primeiro.

    Ainda observando esse cdigo, percebemos que a classeViewResult retornada pelo mtodo query() do bucket umiterator que contm as linhas obtidas como resultado. Cadauma dessas linhas tem o identificador do documento, a chave evalor emitidos, alm do documento representado. E assim comofeito antes, o documento convertido em objeto utilizando ummapeador.

    Dessa forma, para obter a categoria de cada tarefa deve-se fazeruma busca pelo identificador de cada uma. Para que isso se tornedesnecessrio, ser criada outra View que far algo parecido como conhecido JOIN do SQL.

    O processo de criao dessa View ser o mesmo, mas a chama-

    remos de tasks_with_categories. O cdigo da funo Map dela apresentado na Listagem 12.

    A parte a se notar dessa tcnica a seguinte: chaves tambm po-dem ser emitidas como arrays, como pode ser visto nas invocaesde emit(). Quando isso feito, as chaves continuaro sendo orde-nadas naturalmente, mas utilizando todos os elementos do array

    de forma que o elemento seguinte considerado apenas em casode empate na ordenao baseada no elemento anterior. Voltando View criada, essa emitir todos os documentos de categoria etarefa. Os documentos de categoria tero como chave um array deelemento nico com o identificador de cada documento, enquantoos documentos de tarefa tero um array com o identificador dacategoria e a data de criao da tarefa como chave.

    Quando essas chaves so ordenadas, as categorias e suas tarefasficam juntas, visto que suas chaves comeam com o identificadorda categoria. Com essas chaves emitidas, o registro da categoriaaparece antes das tarefas, j que sua chave possui apenas umelemento, 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 queutilizar essa View ser de diferenciar os documentos de categoriados de tarefa. Assim, ao iterar pelas linhas obtidas como resultado,sempre que uma categoria for encontrada ela ser guardada emuma varivel temporria e todas as tarefas subsequentes seroadmitidas como da categoria corrente e tero seu valor de categoriaajustado com ela. O cdigo com essa lgica implementada podeser visualizado na Listagem 13.

    Outro uso para essa View seria a busca por todas as tarefas deuma categoria j conhecida. Isso poderia ser feito com os mto-dos startKey() eendKey() de ViewQuery, como mostrado na

    Listagem 14. Esses mtodos so usados para especificar um in-tervalo 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 de-terminada categoria funcione, devemos saber que um elementovazio de um array toma precedncia sobre qualquer valor,quando tratamos de ordenao natural. Ento, utiliza-se como cha-ve inicial um array com um nico elemento, a chave da categoriaprocurada, e a chave final ser um array cujo primeiro elemento a chave da categoria e o segundo a Stringespecial \uefff.Essa Stringrepresenta um nico caractere UTF-8, que grande o

    suficiente para garantir que nenhum valor do segundo elementodas chaves seja maior em uma ordenao alfabtica. Traduzindotudo isso, buscamos por chaves que comecem com o identificadorda categoria e aceitamos qualquer valor como segundo elementoda chave, desde vazio at um valor qualquer.

    Essas tcnicas podem ser combinadas com outras para se buscarregistros atravs de Views. Para isso, no entanto, deve ser feitoum bom planejamento com o intuito de reutilizar essas Views na medida do possvel, pois uma criao excessiva delas podegerar um impacto bastante negativo na performance do bancode dados.

    A outra funo de uma View fazer resumo de dados. Para isso,

    recomenda-se o uso da funo Reduce, que no ser apresentada

    Listagem 11. Cdigo que demonstra o uso da View all_tasks para listar as ltimasdez 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]);

    }

    }

    }

  • 7/26/2019 Java-magazine 150 Uqadszlo

    16/76

    Por dentro do banco de dados NoSQL Couchbase Parte 2

    16 Java MagazineEdio 15016 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

    Fernando Henrique Fernandes de [email protected]

    desenvolvedor Java EE, Android e Grails. Atualmente mes-trando em Engenharia de Computao na UFG. Desenvolve emJava 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.

    Autor

    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

    D seu voto em www.devmedia.com.br/javamagazine/feedback

    Ajude-nos a manter a qualidade da revista!

    Voc gostou deste ar tigo?

    neste artigo, mas que simples de ser implementada. Na seoLinksesto disponveis guias para um estudo mais aprofundadosobre Views e isso inclui sua funo Reduce.

    Uma boa notcia para aqueles que ficaram receosos com essaforma 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 idnticaao SQL. Ela no foi abordada neste artigo porque relativamentenova e vem evoluindonas ltimas verses do Couchbase.

    Para suprir as necessidades de grandes aplicaes como oFacebook e as solues do Google, diversas opes de bancos dedados foram surgindo, resultando na criao do acrnimo NoSQLpara defini-las. Dentre elas, bancos comoo Apache Cassandra eBigTable foram criados e por fim abertos ao pblico.

    Nessa linha de novos bancos, o primeiro a se destacar com es-trutura de dadosbaseada em documentos foi o MongoDB, o qualpermanece sendo o mais utilizado nos dias de hoje. Porm, com afuso do CouchDB com o Membase surgiu um forte concorrente,

    quefoi apresentado neste artigo: o Couchbase.Alm de demonstrar melhor performance que a concorrncia

    em testes de benchmark, o Couchbase facilita a criao de novostipos de aplicao: aquelas que requerem funcionamento offl i-ne. Um grande exemplo disso so aplicaes mobile que tm ocontedo fornecido por um servidor, mas que devero trabalharfrequentemente sem acesso internet. Para essas existe a combi-nao do Couchbase Server com o Couchbase Lite interligadospelo Sync Gateway. Pelo lado do servidor, deve-se simplesmenteconfigurar o Sync Gateway para criar um meio de acesso aoCouchbase Server. J pelo lado da aplicao mobile, utiliza-seo Couchbase Lite para armazenar, recuperar e sincronizar os

    dados com o servidor. Sua programao muito semelhante

    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 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));

    encontrada neste artigo e os dados so sincronizados sem grandeesforo do desenvolvedor.

    Mesmo quando aplicaes mobile no esto em discusso, oCouchbase prova ser um timo banco de dados para diversas

    situaes. Um exemplo muito interessante para ele seriam aslojas virtuais, afinal, em pocas de grandes promoes, como afamosa Black Friday, podemos notar que grande parte dessas lojasacaba no aguentando o fluxo de usurios, chegando a cair ouapresentar problemas. Com o uso de Couchbase torna-se possvelum escalonamento horizontal temporrio na camada de banco dedados. Assim, a loja virtual poderia operar com um nico n emdatas comuns e fazer a adio de ns quando um grande fluxode acesso for previsto.

    Enfim, no s o Couchbase, mas as solues NoSQL em geralampliam o horizonte de possibilidades. E algumas delas foramapresentadas neste artigo, o qual tambm teve como objetivo ensi-

    nar o leitor a utilizar esse banco de dados de grande potencial.

  • 7/26/2019 Java-magazine 150 Uqadszlo

    17/76

    Edio 150 Java Magazine 17

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

    Programador Java:

    Por onde comear?

    Programador Java:

    Por onde comear?

    DEVMEDIAhttp://www.devmedia.com.br/programador-java-por-onde-comecar/33638

    Descubra nesse vdeo como entrarna carreira Java com o p direito!

  • 7/26/2019 Java-magazine 150 Uqadszlo

    18/76

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

    ESTEARTIGOFAZPARTEDEUMCURSO

    Com a exploso da Internet que vivenciamos atu-almente, muitos novos desafios esto surgindopara a indstria de software, desde a preocu-pao com a escalabilidade das aplicaes, que agorapossuem milhes de usurios, at mesmo a melhoriacontnua da usabilidade desses sistemas, dado que os

    usurios se tornam cada vez mais exigentes e desejammais facilidades.

    Um desses desafios trata-se do armazenamento eprocessamento da imensa massa de dados gerada pelosdiversos servios disponveis. Por conta dela, novastecnologias de banco de dados emergiram nos ltimosanos para atender uma srie de requisitos que o mode-lo mais tradicional (Relacional) no conseguiu suprir.A esse novo movimento de tecnologias de bancos dedados deu-se o nome de NoSQL.

    Diante da relevncia do tema, apresentamos na pri-meira parte desse artigo um dos bancos NoSQL mais

    renomados desse ecossistema, o Apache Cassandra. Para

    isso, foram abordados em detalhes vrios aspectos da arquiteturado Cassandra, fornecendo ao leitor um embasamento terico fun-damental para o aprendizado prtico desse banco de dados. Almdisso, 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 daintegrao entre Cassandra e Java EE.

    Nesta segunda etapa do artigo, iremos aprofundar a parte prticae evoluir a aplicao de modo que ela se torne funcional j com

    quase todas as funcionalidades propostas. Ao final o leitor ser

    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

    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 comu-

    nicar com o Cassandra, WildFly 9, PrimeFaces 5.3, Cassandra 2.2, alm

    de outras tecnologias.

    Fique por dentro

    Aprenda neste artigo como implementar eexecutar comandos no Cassandra seguindo asmelhores prticas atravs de uma aplicao Java EE

    Como usar o Apache

    Cassandra emaplicaes Java EE -

    Parte 2

  • 7/26/2019 Java-magazine 150 Uqadszlo

    19/76

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

    capaz de executar comandos no Cassandra via Java, tanto usandoCQL diretamente quanto usando a API object-mapping. O leitortambm ir adquirir um entendimento bsico sobre modelagemde dados no Cassandra, que como dito na primeira parte do artigo, bem diferente da modelagem relacional.

    Para tanto, evoluiremos nossa aplicao adicionando funcio-nalidades para executar comandos CQL de forma a seguir as

    recomendaes do driver Cassandra, bem como demonstraremosa maneira adequada de utilizar tais funcionalidades. Alm disso,sero abordados os novos recursos do PrimeFaces para criar p-ginas 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 opessero utilizadas de forma a facilitar a implementao da aplicaode exemplo, bem como demonstrar a integrao de todas essastecnologias.

    Evoluindo o WebShelfNa primeira parte do artigo iniciamos o desenvolvimento do

    WebShelf: uma aplicao que possibilita aos seus usurios man-ter uma prateleira de livros online ao estilo do Amazon Shelfari.At o momento, as principais configuraes do projeto j foramdemonstradas e explicadas, o que nos possibilita agora focar maisnas 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, podemosagora criar a tela de Cadastro de Usurio. A Listagem 1traz essaimplementao.

    De modo simples, essa pgina define os dois parmetros que o

    template precisa atravs da tag ui:param. Em seguida, adicionao seu contedo no template atravs da tag ui:define.

    Para deixar a tela mais responsiva envolvemos todo o contedoda pgina numa divque tem a classe ui-fluiddo PrimeFaces.Atravs dessa classe diversos componentes deste framework sorenderizados de forma responsiva.

    Ainda pensando na responsividade, utilizamos o componentep:panelGrid(no confundir com o componente padro do JSFh:panelGrid) em conjunto com o Grid CSS, ambos do PrimeFaces.O Grid CSS um layout leve que produz uma interface responsivapara celulares, tablets e desktops e est presente no PrimeFacesdesde 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 suascolunas. Isso configurado atravs do atributo columnClasses,que define uma classe CSS indicando quantas colunas do GridCSS elas devem ocupar (ui-grid-col-1, ui-grid-col-4, ui-grid-col-7) totalizando as 12 colunas que preenchem a tela. No nossoexemplo, a primeira coluna do p:panelGrid ir ocupar 1 colunado Grid CSS (ui-grid-col-1), a segunda coluna ir ocupar 4 co-lunas do Grid CSS (ui-grid-col-4) e a terceira 7 colunas do GridCSS. Caso voc precise fazer um arranjo diferente, interessantesaber 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).

  • 7/26/2019 Java-magazine 150 Uqadszlo

    20/76

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

    20 Java MagazineEdio 15020 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

    Para encerrar, temos um p:commandButtonpara invocar omtodo que ir gravar o usurio no banco de dados. Esse botoir 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 seralertados antes de qualquer requisio ser enviada para o servidor,evitando roundtripsdesnecessrios e melhorando a performanceda aplicao. Para que essa feature seja usada, necessrio ativ-laatravs do parmetro primefaces.CLIENT_SIDE_VALIDATIONno web.xml, como pode ser visto na Listagem 5da parte 1, publi-cada na edio anterior.

    Desenvolvendo o controller de Usurio

    Implementada a pgina web do Cadastro de Usurio, naListagem 2criaremos o controllerque ir intermediar a camadade negcio com a UI.

    Como podemos notar, UserController um bean CDI comescopo View. Esse escopo obtido atravs da classe javax.faces.view.ViewScoped, presente a partir do JSF 2.2, e no deve serconfundida com a antiga classe javax.faces.bean.ViewScope,que tem vrios problemas, como pode ser visto em The benefitsand pitfalls of ViewScoped(veja a seo Links).

    A anotao @Namedpermite que o bean possa ser acessadopor um nome especfico, por exemplo, em telas JSF atravs deExpression Language. Como no foi especificado nenhum nomeno atributo value, ento o nome do bean passa a ser o nome daclasse com a primeira letra minscula: userController.J o atributo messagesusa a classeJsfMessage do DeltaSpike,

    uma biblioteca que fornece diversas extenses para se trabalharcom Java EE e que nasceu da juno de outros players (JBossSeam e Apache CODI). A classeJsfMessagefornece uma maneiraelegante e simples de adicionar mensagens JSF no contexto atual.Para isso, basta criar uma interface como na Listagem 3.

    Preparando CassandraCluster para suportar object-mapping

    Antes de implementar a lgica de negcio, ser necessrioadicionar alguns mtodos na classe CassandraClusterpara quepossamos, de fato, executar comandos no Cassandra, visto queanteriormente havamos definido apenas alguns atributos. O pri-meiro deles o mtodo mapper(), apresentado a seguir. Sua funo

    ser criar uma instncia da classe Mapperque ir possibilitar ouso da API object-mappingdo driver DataStax.

    public Mapper mapper(Class entityClazz){

    return mappingManager.mapper(entityClazz);

    }

    Este mtodo simplesmente delega sua execuo para o mtodoMappingManager.mapper() e como parmetro recebe qualquerclasse anotada com @Table, como o caso de User.

    O seu retorno uma instncia da classe Mapperparametrizadacom o mesmo tipo da classe do parmetro entityClazz, e dessa

    forma, ir prover operaes como busca, insero e deleo de

    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;

    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;

    }

    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;

    @MessageBundle

    public interface Messages {

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

    prateleira de livros online!)

    String insertUserSuccess();

    @MessageTemplate(Login/Senha invlido.)

    String invalidCredentials();

    }

  • 7/26/2019 Java-magazine 150 Uqadszlo

    21/76

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

    registros na tabela correspondente da entidade sem que sejanecessrio 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 Prepa-redStatement. Alm disso, as instncias dessa classe precisam sermantidas num cache para evitar que o mesmo CQL seja preparadomais de uma vez, o que pode gerar problemas de performance.

    Para implementar tais recomendaes utilizamos no nossoexemplo um Mapde PreparedStatements, onde o Mapser ins-tanciado uma nica vez dentro da classe CassandraClustere comisso se encarregar de fazer o cache desses objetos (vide BOX 1).A ideia tornar a prpria Stringque representa o comando CQL(passada como parmetro) a chave do Map. Dessa forma, para cada

    instruo CQL passada nesse mtodo haver apenas um Prepa-redStatement. Essa estratgia pode ser vista atravs do mtodoCassandraCluster.prepare(), apresentado na Listagem 4.

    O ganho com PreparedStatements verificado quando uma instru-o executada repetidas vezes, pois com esse tipo de statemento parse acontece uma nica vez em cada n que for execut-lo.Nas execues subsequentes, apenas o ID do statement e os va-lores dos parmetros so enviados pela rede. Considerando issoem um ambiente distribudo com vrios ns, uma melhora deperformance significativa pode ser obtida.

    Por fim, saiba que o mtodoprepare()sempre retornar uma novainstncia de BoundStatement, a qual ser utilizada pelos clientespara fazer o bind dos parmetros contidos na instruo CQL.

    Adicionando mtodo para execuo de comandos no CassandraClusterPara que a classe CassandraClusteresteja com todas as prin-

    cipais funcionalidades disponveis, agora falta incluir o mtodoexecute(), apresentado na Listagem 5. Como o prprio nome jdiz, esse mtodo ser o responsvel por executar instrues CQLno Cassandra.

    Apesar desse cdigo ser pequeno interessante prestar bemateno para entender o que acontece. Basicamente, ele recebe umStatemente 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 ResultSetquepode ser utilizado para obter os dados de uma consulta (no caso

    do comando ser uma consulta).

    A parte interessante nesse mtodo a presena da anotao@Lock. Essa anotao faz parte da especificao EJB e deve serusada em conjunto com EJBs singleton, como o caso de Cas-sandraCluster. Um EJB singleton, por padro, no permite quemais de uma thread invoque um mtodo de negcio. Nesse caso,entenda-se mtodo de negcio como qualquer mtodo de classepblico, como o caso de execute().

    Assim, um cliente ter que esperar o outro terminar para queento seu pedido seja atendido. Por exemplo, suponha que athread A chamouexecute() passando um statement que vaidemorar cinco minutos para finalizar sua execuo. Quandoa chamada de Aestava com 1 minuto de execuo, a thread B

    tambm invocou execute()com outro statement, que nesse casoir levar apenas 1 segundo para executar. No entanto, como Ainiciou a execuo primeiro, a thread Bs ser atendida quandoAterminar, ou seja, o comando de Bque deveria levar apenas 1segundo ir levar 4 minutos (tempo restante para completar aexecuo de A) e 1 segundo.

    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();

    }

    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.

    BOX 1. Caches

    Listagem 5. Mtodo para execuo de CQL em CassandraCluster.

    @Lock(LockType.READ)

    public ResultSet execute(Statement stmt){

    return session.execute(stmt);

    }

  • 7/26/2019 Java-magazine 150 Uqadszlo

    22/76

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

    22 Java MagazineEdio 15022 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

    Para que isso no acontea, utilizamos @Lock(LockType.READ) para sinalizar ao container EJB que esse mtodo, especificamen-te, 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 mto-do simultaneamente? No h perigo de uma chamada atrapalhara outra?. A resposta que no h problema algum. Como ditona primeira parte deste artigo, a classe Session thread-safe e,portanto, pode ser utilizada num ambiente multithreading semqualquer perigo.

    Outro mtodo que poderia sofrer esse mesmo problema seria oprepare(). Isso porque ele tambm pode ter execues que vo levarum tempo considervel e, por isso, ficar aguardando sua finalizaogeraria um grande gargalo. No nosso cenrio isso no ir acontecer

    porque esse mtodo privado e, sendo assim, no sofre o lock docontainer, pois no considerado um mtodo de negcio. No en-tanto, se sua aplicao precisar expor esse mtodo, teria tambmque remover o lock do container EJB. Caso voc necessite usar ocontrole de lock constantemente, isso pode indicar que precisorepensar a implementao, como explicado no BOX 2.

    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().

    BOX 2. Exposio de Session

    Criando o CQL para inserir Usurio

    Agora que o mtodo prepare()est implementado, pode-secriar algumas instrues, como a de insero de um usurio,demonstrada na Listagem 6. O intuito desse mtodo disponi-

    bilizar para o cliente uma instncia de BoundStatementcom o

    comando necessrio para inserir um usurio no Cassandra. Deposse do statement o desenvolvedor poder setar os valores dosparmetros (identificados pelo caractere ?) e ento fazer a execu-o do comando.

    Note que a sintaxe do CQL nesse exemplo bem semelhante doSQL. A principal diferena est no uso da instruo IF NOT EXISTS,que ser melhor explicada na seo Usando Lightweight Transac-tions (LWT) para garantir a unicidade, logo mais frente.

    Enfim, o bean de Usurio

    Feitas as melhorias em CasandraCluster, agora possvelcriar o bean da entidade usurio, UserBean. Trata-se de um EJB

    Stateless que usa CDI para injetar algumas dependncias, como

    verificado na Listagem 7. O sufixo Bean refere-se convenode 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 mapper = cassandra.mapper(User.class); return mapper.get(login); }

    public void deleteUser(User user) { Mapper mapper = cassandra.mapper(User.class); mapper.delete(user); }

    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 constraintViolations = validator.validate(user);

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

    }

  • 7/26/2019 Java-magazine 150 Uqadszlo

    23/76

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

    Como pode ser visto no mtodo executeBeanVali-dation(), nesta classe injetamos um Validator parainvocar as validaes da API Bean Validation. Almdisso, injetado um CassandraClusterpara realizar

    a comunicao com o Cassandra.Ainda analisando este cdigo, o mtodo findU-serByLogin() obtm um Mapper e entofaz a consulta pelo login, que nesse caso tambma primary key da tabela. Essa consulta realizadaatravs do mtodoMapper.get(), que aceita umalista de parmetros correspondente primary key databela na ordem declarada na sua criao. O retorno um objeto com os campos devidamente preenchidosgraas ao mapeamento feito na classe User. Operaosemelhante acontece no mtodo deleteUser(), que tambm faz usode Mapperpara remover o registro.

    Usando Lightweight Transactions (LWT) para garantir a unicidade

    Por fim, tem-se o mtodo insertUser(), o qual cadastra o usuriono Cassandra. Apesar da classe Mapperpossuir um mtodo save()que poderia ser usado aqui, foi necessrio criar um statementmanualmente atravs do mtodo CassandraCluster.boundIn-sertUser()para que fosse possvel usar lightweight transactions, jque o Mapperno oferece (ainda) essa possibilidade.

    Como j informado, no WebShelf o login do usurio nico. Paragarantir essa regra em bancos de dados relacionais, normalmenteusamos uma transao, na qual executada uma consulta paraverificar a existncia do login. Caso ele no exista, executado o

    comando de insert e finalmente feito o commit da operao.No Cassandra, por sua vez, no possvel proceder dessa forma

    devido ausncia de transaes ACID. Desse modo, se voc tentarfazer isso, poder cair numa race condition, como exemplificadona Figura 1. Para contornar esse problema foi criado o conceito deLWT, que no caso da insero do usurio se caracteriza pelo usoda seguinte condio ao fim do insert: IF NOT EXISTS. Ou seja,ao invs de fazer uma consulta para verificar se o login existe ouno, no ato do insert j ser feito essa checagem sem que outrasrequisies interfiram na operao.

    A execuo dessa instruo retorna um ResultSeta partir doqual possvel checar se o insert foi aplicado ou no atravs do

    mtodo wasApplied(). Caso este retorne false, significa que ologin j existe.

    Contudo, como dito na primeira parte deste tutorial, essa fe-ature deve ser usada com moderao, pois impacta fortementena performance. Nesse exemplo, optou-se por fazer uso de LWTporque a regra de unicidade de usurio considerada crtica paraa aplicao.

    Implementao do LoginComo em quase todas as aplicaes web, tambm iremos de-

    senvolver um mecanismo de login. O funcionamento deste sersimples: o usurio ter que informar o seu login e sua senha e,

    caso tudo esteja correto, dever ser redirecionado para a pgina

    Figura 1. Exemplo de race condition Fonte: FinishJUG

    inicial da aplicao, caso contrrio, uma mensagem de erro deverser mostrada. Para quem ainda no tem cadastro, ser disponi-

    bilizado um boto que levar o usurio para a tela de Cadastrode Usurio.

    Elaborando pgina responsiva de login com PrimeFaces

    A tela de login tem quatro componentes principais, conformepode ser visto na Listagem 8: um input de login, outro da senha,um boto para efetuar o login e outro para se cadastrar.

    Observe que o parmetro renderedMenuBar, logo no incio docdigo, foi setado para false, j que no deve ser exibido nenhummenu enquanto o usurio no se logar.

    Com o intuito de deixar a tela responsiva, utilizamos os mesmosrecursos apresentados na Listagem 1. Assim, todos os compo-

    nentes visuais da pgina foram englobados por uma div quetem a classe ui-fluide os inputs foram organizados dentro decomponentes p:panelGrid configurados para usar o Grid CSS(layout=grid). Alm disso, os p:panelGrid definiram o tamanhode suas colunas em funo das 12 colunas que o Grid CSS usa paradividir a tela responsivamente (ui-grid-col-*).

  • 7/26/2019 Java-magazine 150 Uqadszlo

    24/76

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

    24 Java MagazineEdio 15024 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia

    Uma coisa a observar que nessa pgina temos dois p:panelGrid.Isso foi necessrio porque, para gerar uma melhor visualizao datela, as configuraes dos tamanhos das colunas so diferentespara a rea dos campos texto (Logine Senha) e a rea dos botes

    (Entrare 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 utilizadopara fazer o login e o segundo (Cadastrar-se) servir para redire-cionar o usurio para a tela de cadastro de usurio, caso o mesmoainda no tenha se registrado no site.

    As Figuras 2e 3demonstram o comportamento responsivo datela de login.

    Desenvolvendo controller de login

    Aps criarmos o XHTML do login, podemos desenvolver a clas-se que efetuar as principais aes dessa tela: LoginController.Basicamente, essa classe ter a responsabilidade de executar asfuncionalidades de login e logout da aplicao e pode ser visua-lizada na Listagem 9.

    Listagem 8. Cdigo da tela de login (public/login.xhtml).

    Figura 3. Tela de login visualizada em um celular

    Figura 2. Tela de login visualizada em um computador

    Visto que no precisar guardar nenhum estado entre re-quests, esta classe um bean CDI com escopo de requisio(@RequestScoped). E como ela ser acessada nos XHTMLs(login.xhtmledefault.xhtml) atravs de Expression Language, tam-

    bm foi anotada com @Named.Dentre os atributos da classe, os campos de login e senha (uti-

    lizados na pgina de login) so devidamente validados com oauxlio da API Bean Validation atravs da anotao @NotBlank.Dessa forma, ambos so obrigatrios. Alm desses dois, existemmais 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 ologin do usurio, que realizado atravs do mtodo doLogin().Este faz a pesquisa do usurio pelo seu login e caso o mesmoexista e sua senha seja igual senha informada, ento conside-rado um login vlido e assim o objeto usurio armazenado nasesso. Caso no exista o usurio ou sua senha seja diferente doinputado, uma mensagem de erro retornada.

    Por fim, o mtodo logout()simplesmente remove o usurio dasesso e invalida-a, bem como redireciona o usurio para a tela

    de login.

  • 7/26/2019 Java-magazine 150 Uqadszlo

    25/76

    Edio 150 Java Magazine 25

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

    oportuno salientar que a maneira utilizada aqui para fazer ocontrole de login do sistema muito simples e, portanto, deve serevitada em produo, conforme comentado no BOX 3.

    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.

    BOX 3. Cassandra vs JAAS

    Disponibilizando o usurio logado como bean CDI

    Obter o usurio logado uma das tarefas mais comuns a qualqueraplicao web. Para isso, iremos criar a classe ResourceProducer,

    que far uso das facilidades do CDI a fim de tornar essa tarefamais simples dentro do projeto WebShelf. Essa implementao mostrada na Listagem 10.

    Na classe ResourceProducer temos apenas um mtodo: getLo-ggedInUser(). Este produz beans CDI (@Produces) com escopode sesso (@SessionScoped)e que so acessveis via ExpressionLanguage (@Named) atravs do nome loggedInUser. A outraanotao (@LoggedInUser) um qualif ier CDI para denotar queo bean aqui produzido equivale ao usurio logado. Sua implemen-tao e explicao sero apresentadas com a Listagem 11.

    Listagem 9. Controller do login (webshelf-web)

    package br.com.devmedia.webshelf.controller;

    // 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;

    @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;

    }

    public void setPassword(String senha) {

    this.password = senha; }

    public void setLogin(String usuario) {

    this.login = usuario;

    }

    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;

    }

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

    request.getSession(Boolean.FALSE).invalidate();

    }

    request.getSession().setAttribute(loggedInUser, user);

    return /private/home.xhtml?faces-redirect=true;

    }

    public String logout() throws ServletException {

    request.getSession().removeAttribute(loggedInUser);

    request.getSession().invalidate();

    return /public/login.xhtml?faces-redirect=true;

    }

    }

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

    package br.com.devmedia.webshelf.util;

    import javax.enterprise.context.SessionScoped;

    import javax.enterprise.inject.Produces;

    import javax.inject.Inject;

    import javax.inject.Named;

    import javax.servlet.http.HttpSession;

    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 (loggedInUser == null) {

    loggedInUser = new User();

    }

    return loggedInUser;

    }

    }

  • 7/26/2019 Java-magazine 150 Uqadszlo

    26/76

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

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

    Como podemos observar, a implementao do mtodo getLo-ggedInUser() bem simples. Ele retorna o usurio logado queest na sesso caso exista um. Vale lembrar que o usurio logado colocado na sesso atravs do mtodo LoginController.doLo-

    gin(). Se no houver nenhum usurio logado, ento ele devolveum objeto Uservazio. Isso porque no permitido retornarnulo em mtodos produtores no CDI.

    A anotao @LoggedInUser um qualifier CDI (@Qualifier)usado para produzir/injetar objetos do tipo Userque representamum usurio logado. Um qualifier nada mais do que um meio dese 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 criaro bean de outras maneiras. No caso de User, criamos o mtodoResourceProducer.getLoggedInUser(), que ir produzir beansque representam um usurio logado, e para denotar isso, estemtodo foi anotado com @LoggedInUser. Dessa forma, quandouma classe for injetar um objeto User, o CDI ter agora duas opespara gerar o objeto: a opo default (construtor sem argumento) eo mtodo ResourceProducer.getLoggedInUser().

    A questo que fica : como informar ao CDI que desejamosobter o objeto gerado pela classe ResourceProducer, ao invs doobjeto gerado pelo construtor default? nesse contexto que entrao qualifier. atravs dele que o CDI saber qual dos produtores

    usar. Veja o exemplo na Listagem 12.Nesse caso, como o atributo loggedInUserest anotado com@LoggedInUser, ele ser produzido pelo mtodo ResourcePro-ducer.getLoggedInUser(), visto que somente este mtodo produzusurios com tal qualificador. J o atributo dummyUser, por noespecificar nenhum qualifier, ser gerado pelo produtor padro, queno caso o construtor default da prpria classe (new User()).

    Protegendo pginas privadas

    Agora que j temos como obter e saber se existe um usuriologado na aplicao, vamos criar um mecanismo de seguranapara bloquear o acesso a pginas privadas de usurios no auten-ticados. Para isso, iremos utilizar um evento do CDI atravs daclasse SecurityObserver, apresentada na Listagem 13.

    Nesta classe, o mtodo checkAfterRestoreView()ser notificadopelo CDI todas as vezes que ocorrer o evento After Restore View

    do JSF. Isso acontece porque este mtodo possui um parmetroanotado com @Observes que utilizado para indicar ao CDI que omtodo 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 conseguircapturar os eventos, essa anotao deve ser utilizada em objetosdo tipo PhaseEvent.

    Alm de notificar o evento, o CDI ainda injetar os trs parmetrosdo mtodo: PhaseEvent, HttpServletRequeste tambm o usurio

    logado (@LoggedInUser). Note que o PhaseEventnem utilizado,mas necessrio para que se possa usar @AfterPhase.

    Baseado nessas informaes recebidas o mtodo checkAfter-RestoreView() no permitir que usurios logados acessem atela de login e os redirecionar para a pgina home da aplicao(redirectLoggedinUserToHome()). Da mesma forma, tambm nopermitir que usurios no logados acessem pginas privadas,redirecionando-os para a tela de login (redirectAnonymousTo-Login()).

    Cadastro de LivroO cadastro de livro permitir ao usurio do WebShelf cadastrar

    novos livros que, por ventura, ele no tenha conseguido encontrar

    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;

    import javax.inject.Qualifier;

    @Qualifier

    @Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD })

    @Retention(RetentionPolicy.RUNTIME)

    public @interface LoggedInUser {

    }

    Listagem 12. Exemplo de uso de um Qualifier CDI

    @Inject

    @LoggedInUser

    private User loggedInUser;

    @Inject

    private User dummyUser;

  • 7/26/2019 Java-magazine 150 Uqadszlo

    27/76

    Edio 150 Java Magazine 27

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

    na pesquisa. Essa funcionalidade seguir o mesmo molde do ca-dastro de usurio, que composto de uma tela JSF, um controllere um bean de negcio.

    Criando a classe modelo de Livro

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

    no usar essa feature do driver Cassandra.

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

    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;

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

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

    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);

    }

    private void redirectLoggedinUserToHome(User user, HttpServletRequest request) {

    if (request.getRequestURI().contains(LOGIN_PAGE)) {

    if (user.getLogin()!=null) {

    handleNavigation(HOME_PAGE);

    }

    } }

    private void redirectAnonymousToLogin(User user, HttpServletRequest request) {

    if (request.getRequestURI().contains(URL_PATTERN_PRIVATE_PAGES)) {

    if (user.getLogin()==null) {

    handleNavigation(LOGIN_PAGE);

    }

    }

    }

    private void handleNavigation(String page) {

    FacesContext context = FacesContext.getCurrentInstance();

    context.getApplication().getNavigationHandler().handleNavigation

    (context, null, page + ?faces-redirect=true);

    }

    }

    Listagem 14. Cdigo da classe Book (webshelf-business).

    package br.com.devmedia.webshelf.model;

    import java.nio.ByteBuffer;

    import javax.validation.constraints.NotNull;

    import org.hibernate.validator.constraints.NotBlank;

    public class Book {

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

    private Long isbn;

    @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;

    //getters and setters

    public ByteBuffer getImageBuffer() {

    return ByteBuffer.wrap(image);

    }

    @Override

    public int hashCode() {

    final int prime = 31;

    int result = 1;

    result = prime * result + ((isbn == null) ? 0 : isbn.hashCode());

    return result;

    }

    @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;

    }

    }

    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.

    Nota

  • 7/26/2019 Java-magazine 150 Uqadszlo

    28/76

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

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

    Por se tratar de um POJO,Book uma classe simples que contmbasicamente seus campos e os respectivos mtodos de acesso.A mesma tambm faz uso de Bean Validation para assegurar asvalidaes bsicas sob os seus atributos.

    Um importante aspecto nessa classe a implementao dadupla equals()e hashCode(). Assim, a classe poder ser utiliza-da em conjunto com a API Collections de forma mais segura econsistente. Como pode ser visto, um Book considerado iguala outro se os seus ISBNs forem iguais. Isso importante porque,por conta da desnormalizao do Cassandra, essa unicidade doISBN no poder ser garantida via banco de dados, como veremoslogo mais.

    Outra parte a se prestar ateno o mtodo getImageBuffer().O intuito desse mtodo converter o campo byte[]que representauma 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 tipoda coluna imagem nas tabelas de livro.

    Implementando a pgina responsiva de Cadastro de Livro com PrimeFaces

    A Listagem 15apresenta o