Couchbase e Java EE

O Couchbase é um banco de dados NoSQL que é baseado em duas tecnologias já existentes, o memcache e o CouchDB. Ou seja, ele é um banco de dados não relacional que possui suporte a duas implementações: chave-valor e documento. O objetivo desse post é falar um pouco das vantagens desse banco, instalação com o docker além do seu uso com Java EE.

 

Qual o diferencial do Couchbase para outros bancos dados?

Os pontos fortes do Couchbase:

  • Ele é bastante visual: Isso mesmo, boa parte das configurações que são feitas por ele é a partir uma interface gráfica bem intuitiva a partir do browser. Com essa interface é possível, criar índices, definir tamanho de memória, juntar clusters, criar buckets, número de memória principal que um bucket utilizará dentre outras coisas.

  • Dois bancos em um: Com o Couchbase é possível trabalhar com documento ou chave ou os dois ao mesmo tempo com um único banco.
  • Uma ferramenta de monitoração excelente: Como parte da sua riquíssima inteface gráfica, com ele é possível saber a saúde dos servidores, quantos servidores estão conectados, processamento, memória, logs e muito mais.
  • N1QL: O N1QL, se pronuncia níquel, é uma query language declarativa que estende do SQL, ou seja, com ele é possível realizar consultas ou alterações no banco de dados de uma maneira bem semelhante ao que se faz com o banco relacional, tecnologia bem popular para a maioria dos desenvolvedores Java. Tendo como diferencial, o N1QL é uma query focado em consultas para JSON.

SELECT * FROM databases WHERE category='NoSQL'

 

  • Um motor de busca integrado: Ainda em fase de desenvolvimento. O motor de busca é um recurso muito comum quando se fala em banco de dados distribuídos, principalmente, para evitar as queries com like e coringa, o famoso like ‘%as%’. Com ele é possível criar índice com Character Filter, Tokenizer, Token Filters, Analizer, Mappings, além de atualizar índice, tudo isso, a partir de uma interface gráfica muito intuitiva.
  • Estruturas especiais para a implementação de chave valor: Assim como o redis, o couchbase possui estruturas especiais, assim com o couchbase é possível utilizar Set, List, Queue e também Map.

Mas porque não utilizar simplesmente o cache?

A grande vantagem está nas suas chamadas específicas, imagine que um sistema armazena a lista dos 1000 produtos mais comprados e é necessário adicionar mais um. Com um simples cache, será necessário serializar os mil elementos, adicionar um novo elemento e apenas em seguida retornar para o banco de dados a chave com mil e um elementos. Com a estrutura especial como uma lista, é necessário apenas enviar o elemento que se deseja adicionar, assim, economiza rede e também processamento. O outro ponto é que os bancos de dados chave-valor foram criados para que os dados tenho um tempo de vida maior que um cache.

 

Instalação

A instalação é bastante prática, existe a possibilidade de fazer a instalação manualmente ou uma imagem docker.

Para mais informações:

https://hub.docker.com/r/couchbase/server/

Durante a instalação:

  • Nas configurações habilite a opção Full-Text Resource
  • Quando estiver configurado e com usuário e senha definida crie um bucket, nesse artigo utilizaremos o bucket “heroes”.
  • Siga as instruções no site do couchbase e crie um índice para o bucket anterior, sugestão de nome, heroes-index.

Para o artigo não ficar muito longo a instalação e a configuração do docker e/ou couchbase não fará parte desse artigo, para informações:

Começando com o modelo

Como nosso objetivo é criar um exemplo simples com Java EE e Couchbase, utilizaremos o CDI 2.0 com Java SE e faremos um simples exemplo com heróis. E com vilões:

 

@Entity
public class Hero implements Serializable {

    @Id
    private String id;

    @Column
    private String name;

    @Column
    private String realName;

    @Column
    private Integer age;

    @Column
    private Set<String> powers;

}

@Entity
public class Villain implements Serializable {

    @Id
    private String id;

    @Column
    private String name;

}

 

Uma coisa importante, é que mesmo não sendo JPA, as anotações são fortemente inspiradas nele. O objetivo do JNoSQL foi tentar usar o máximo a nomenclatura do JPA, porém, levando em consideração que o banco em questão é NoSQL.

 

Configuração

Com o servidor funcionando e rodando o próximo passo será a definir a configuração do banco de dados. Para isso será necessário criar um arquivo dentro do META-INF, no caso o jnosql.json. Como a ideia é exibir as duas APIs, será necessário definir a configuração para as duas APIs, para chave-valor e documentos, porém, poderá ser feito num único documento como mostra o código abaixo:

[
   {
      "description":"The couchbase document configuration",
      "name":"document",
      "provider":"org.jnosql.diana.couchbase.document.CouchbaseDocumentConfiguration",
      "settings":{
         "couchbase-host-1":"localhost",
         "couchbase-user":"root",
         "couchbase-password":"123456"
      }
   },
   {
      "description":"The couchbase key-value configuration",
      "name":"key-value",
      "provider":"org.jnosql.diana.couchbase.key.CouchbaseKeyValueConfiguration",
      "settings":{
         "couchbase-host-1":"localhost",
         "couchbase-user":"root",
         "couchbase-password":"123456"
      }
   }
]

 

O arquivo tem configuração com os seguintes parâmetros:

  • name: o nome da unidade de configuração
  • description: uma descrição, essa informação não é utilizada pelo JNoSQL
  • provider: O nome da classe que será invocada para chamar interpretar as configurações e rodasr as configurações do banco.
  • Settings: São informações específicas de cada banco de dados, no caso do couchbase configuramos os hosts clientes, o usuário e a senha para se conectar no banco de dados.

O arquivo configurado o próximo passo é injetar o as configurações dentro do código.

 

@ApplicationScoped
public class CouchbaseProducer {

    private static final String HEROES = "heroes";

    @Inject
    @ConfigurationUnit(name = "document")
    private DocumentCollectionManagerFactory<CouchbaseDocumentCollectionManager> entityManager;

    @Inject
    @ConfigurationUnit(name = "key-value")
    private BucketManagerFactory<BucketManager> bucketManager;


    @Produces
    public DocumentCollectionManager getManager() {
        return entityManager.get(HEROES);
    }

    @Produces
    public CouchbaseDocumentCollectionManager getCouchbaseDocumentCollectionManager() {
        return entityManager.get(HEROES);
    }

    @Produces
    public List<String> getHeroList() {
        return bucketManager.getList(HEROES, String.class);
    }


    @Produces
    public Set<String> getHeroSet() {
        return bucketManager.getSet(HEROES, String.class);
    }

    @Produces
    public BucketManager getBucketManager() {
        return bucketManager.getBucketManager(HEROES);
    }

}

 

A partir da anotação ConfigurationUnit ele retornará uma fábrica responsável pela criação de gerentes de entidades. Seja para chave valor, BucketManagerFactory, seja para documentos, DocumentCollectionManagerFactory. A partir da produção das classes que serão gerentes de entidades o JNoSQL se encarregará de gerenciar todo ciclo de vidas das outras classes que veremos a seguir.

Documentos

Uma vez configurado e os modelos definidos o próximo passo é realizar as operações básicas no banco de dados. Para isso será utilizado a classe DocumentTemplate, as classes to tipo template realizam operações CRUD de maneira simples e intuitiva, é ela que se encarregará de converter as entidades para a camada de comunicação, aka Diana, que por sua vez enviará para o banco de dados.

public class App {


    public static void main(String[] args) {

        try (SeContainer container = SeContainerInitializer.newInstance().initialize()) {

            Hero ironMan = Hero.builder().withRealName("Tony Stark").withName("iron_man")
                    .withAge(34).withPowers(Collections.singleton("rich")).build();
            DocumentTemplate template = container.select(DocumentTemplate.class).get();

            template.insert(ironMan);

            DocumentQuery query = select().from("Hero").where(eq(Document.of("_id", "iron_man"))).build();
            List<Hero> heroes = template.select(query);
            System.out.println(heroes);

        }
    }

    private App() {
    }
}

 

Couchbase é um banco que possui recursos particulares muito interessante, por exemplo, é possível utilizar o motor de busca dentro do couchbase além do uso do N1QL. Como olhar recursos específicos é uma das premissas do JNoSQL. O Couchbase tem uma extensão do DocumentTemplate, o CouchbaseTemplate, essa classe é uma especialização do DocumentTemplate que executa recursos específicos do Couchbase, já mencionado anteriormente.

public class App1 {


    public static void main(String[] args) {

        try (SeContainer container = SeContainerInitializer.newInstance().initialize()) {

            Hero ironMan = Hero.builder().withRealName("Tony Stark").withName("iron_man")
                    .withAge(34).withPowers(Collections.singleton("rich")).build();

            CouchbaseTemplate couchbaseTemplate = container.select(CouchbaseTemplate.class).get();
            couchbaseTemplate.insert(ironMan);

            DocumentQuery query = select().from("Hero").where(eq(Document.of("_id", "iron_man"))).build();
            List<Hero> heroes = couchbaseTemplate.select(query);
            System.out.println(heroes);

            MatchQuery match = SearchQuery.match("rich").field("powers");
            SearchQuery search = new SearchQuery("heroes-index", match);
            List<Hero> searchResult = couchbaseTemplate.search(search);
            System.out.println(searchResult);


        }
    }
    private App1() {
    }
}

Além do template é possível utilizar as classes do tipo Repositório, que por sua vez, são interfaces no qual possui métodos comuns além de ser possível adicionar os seus métodos e o JNoSQL se encarregará de implementá-los.

public interface HeroRepository extends Repository<Hero, String> {

    Optional<Hero> findByName(String name);

    Stream<Hero> findByAgeGreaterThan(Integer age);

    Stream<Hero> findByAgeLessThan(Integer age);

    void deleteByName(String name);

}

Mais uma vez, levando em consideração a diversidade, existe a especialização do repositório para o Couchbase, a interface CouchbaseRepository. Essa interface tem como principal recurso a execução do N1QL.

 

public interface HeroRepository extends CouchbaseRepository<Hero, String> {

    Optional<Hero> findByName(String name);

    Stream<Hero> findByAgeGreaterThan(Integer age);

    Stream<Hero> findByAgeLessThan(Integer age);

    void deleteByName(String name);

    @N1QL("select * from heroes where realName= $realName")
    Optional<Hero> find(@Param("realName") String realName);

}

public class App2 {



    public static void main(String[] args) {

        try (SeContainer container = SeContainerInitializer.newInstance().initialize()) {
            Hero ironMan = Hero.builder().withRealName("Tony Stark").withName("iron_man")
                    .withAge(34).withPowers(Collections.singleton("rich")).build();

            HeroRepository repository = container.select(HeroRepository.class).get();
            repository.save(ironMan);

            System.out.println(repository.findByName("iron_man"));
            System.out.println(repository.find("Tony Stark"));

        }
    }

    private App2() {
    }
}```
<h3>Chave valor</h3>
Utilizando a API de chave-valor é possível utilizar as estruturas especiais, a partir da chave. Nesse caso, será utilizando uma List e um Set de String e assim como no documento, existe a classe template para chave valor.


```java
@ApplicationScoped
public class VillainService {

    @Inject
    private List<String> names;

    @Inject
    private Set<String> powers;

    @Inject
    private KeyValueTemplate template;


    public void addName(String hero) {
        names.add(hero);
    }

    public void addPower(String ids) {
        this.powers.add(ids);
    }

    public void put(Villain villain) {
        template.put(villain);
    }

    public Optional<Villain> get(String name) {
        return template.get(name, Villain.class);
    }

    public List<String> getNames() {
        return names;
    }

    public Set<String> getPowers() {
        return powers;
    }
}


public class App3 {


    public static void main(String[] args) {

        try (SeContainer container = SeContainerInitializer.newInstance().initialize()) {
            Villain lock = new Villain();
            lock.setId("lock");
            lock.setName("Lock");

            VillainService service = container.select(VillainService.class).get();


            service.addName("Doctor Doom");
            service.addName("Magneto");
            service.addName("Red Skull");

            service.addPower("Strong");
            service.addPower("Strong");
            service.addPower("fly");

            service.put(lock);

            System.out.println(service.get("lock"));
            System.out.println("The villain powers");
            service.getPowers().forEach(System.out::println);
            System.out.println("The villain names");
            service.getNames().forEach(System.out::println);

        }
    }

    private App3() {
    }
}

A opção de repositório para chave valor também está disponível, porém, como os recursos de busca num banco chave valor são limitados, o recurso de query method em chave valor não é suportado.

public interface VillainRepository extends Repository<Villain, String> {
}

 

Porém, uma vez com a classe repositória definida é possível utilizá-la como documento ou como chave-valor.

 

@Inject
@Database(DatabaseType.COLUMN)
private VillainRepository villainRepository;

@Inject
@Database(DatabaseType.KEY_VALUE)
private VillainRepository villainRepository;

public class App4 {


    public static void main(String[] args) {

        try (SeContainer container = SeContainerInitializer.newInstance().initialize()) {
            Villain lock = new Villain();
            lock.setId("lock");
            lock.setName("Lock");

            Villain doom = new Villain();
            doom.setId("doom");
            doom.setName("Dc Doom");

            VillainRepository repository = container.select(VillainRepository.class, DatabaseQualifier.ofKeyValue()).get();

            repository.save(lock);
            repository.save(doom);
            System.out.println(repository.findById("lock"));
            System.out.println(repository.findById("doom"));

        }
    }

    private App4() {
    }
}


 

 

Com isso foram apresentados os recurso do Couchbase, um banco de dados NoSQL que suporta dois tipos de bancos NoSQL, chave valor e documentos, além de ter uma incrível e rica interface visual na qual é possível fazer diversos tipos de configurações. Também foi possível demonstrar o uso desses recursos em parceria com o JNoSQL.

 

Demo: https://github.com/JNOSQL/artemis-demo/tree/master/artemis-demo-java-se/couchbase
Couchbase: https://www.couchbase.com/

Anúncios

MicroProfile 1.0

micro-profile-logo

O termo microsserviço, no mundo do desenvolvimento de software, se tornou um termo muito popular. Assim, diversos artigos e diversas ferramentas vem surgindo relacionado a esse tópico. No mundo Java, o Spring vem discutindo sobre esse tópico principalmente após a versão 4.0 e o Spring Boot. Porém, essa é a única solução existente? A resposta é não, recentemente foi lançado uma nova ferramenta no mundo Java, o MicroProfile, e esse foi um dos tópicos mais comentados no JavaOne 2016.

Existem diversos termos e pontos de vista em relação ao o que significa microsserviço. Segundo o Henrique Lobo o microsserviço é: mais uma solução para o problema do alto acoplamento em aplicações corporativas. A diferença é que desta vez estamos atacando aplicações monolíticas.

Ou seja, de uma maneira geral o microsserviço é o oposto de uma arquitetura monolítica, que visualiza a aplicação como um único componente. Uma vez que o componente é a menor unidade dentro do software que pode ser substituída de forma independente. A arquitetura monolítica trabalha numa aplicação com um único componente, ou seja, em termo de escalabilidade caso seja necessário crescer a aplicação, é necessário criar uma instância de todo o componente. Porém, sua arquitetura é mais simples, um grande desafio do microsserviço é separar a aplicação em uma menor unidade, uma separação errônea significa um grande desastre na vida de uma aplicação.

Uma vez conseguindo separar um software em menor componentes ou serviços (o serviço seria um tipo de componente especial que realiza a comunicação utilizando mecanismo remoto como webservice). O microsserviço tem algumas vantagens:

  • Consegue entregar novos recursos mais rapidamente, uma vez que apenas o componente que foi alterado precisará ser atualizado.
  • Com componentes menores, os times também podem ser, seguindo o estilo e a metodologia ágil.
  • A escalabilidade se torna mais simples, no caso, caso o serviço A precise escalar, é necessário apensar que tenha mais uma instância desse recurso A.

Uma vez definido, rapidamente, os conceitos ao redor de aplicações de microsserviços, aplicações monolíticas, componentes, etc. O próximo passo desse post é falar um pouco sobre a iniciativa do MicroProfile.


Q&A MicroProfile

O que é Microprofile?

O Microprofile é uma iniciativa formada por alguns fornecedores Java EE (Red Hat, Payara, Tomitribe, IBM) e Java User Groups (SouJava e London Java Community) cujo o principal objetivo é trazer inovações do microsserviços ao redor ou próximo do mundo Java EE. Atualmente, os servidores, com o comportamento lazy, e as tecnologias elas estão prontas para aplicações de microsserviços, porém, a ideia é tornar a experiência ainda melhor.

microprofile-logos

O seu propósito é bastante simples:

  • Ser um ambiente simples para iterar, testar, inovar e principalmente falhar rapidamente.
  • Após testes, experiência chegar em um comum acordo entre todos.
  • Uma vez, nesse comum acordo e um código estável o próximo passo, e o último, é o processo de padronização.

micro-profile-process

E o Java EE?

O Java EE continua sendo uma série de especificações ou guarda-chuva de especificações bem definidas cujo o foco principal são aplicações focadas ao mundo Web. O Java EE em sua sexta versão trouxe um grande número de novidades, principalmente com o CDI, e sua versão atual, a sétima versão, trouxe ainda mais novidades e melhorias nos recursos existentes.

A próxima versão está sendo trabalhada sob a JSR 366 no qual o líder da especificação é a Oracle.

microprofile-java-ee-road-map

MicroProfile é Java EE?

Não, apesar do envolvimento de diversos fornecedores do mundo Java EE o MicroProfile não é e não tem nada relacionado com o mundo Java EE. Como foi dito previamente, a Oracle é a líder da especificação do Java EE e detentora da sua propriedade intelectual, assim apenas ela pode definir o que é Java EE ou não. Da mesma forma que o CDI a Red Hat é líder da especificação. O fato do MicroProfile utilizar algumas especificações do mundo Java EE não o remete a um proflie do Java EE, do contrário, uma vez que o Spring suporte algumas especificações ele também o seria.

JCP e MicroProfile?

Apesar da exaustiva tentativa de comprar o JCP com todos e qualquer movimento Java, são movimentos totalmente diferentes. O JCP é focado em padrão e o MicroProfile a ideia é que seja um local para inovação. Inovação é o primeiro passo para um padrão, é necessário testar, experimentar e caso seja um sucesso padronizar. Afinal, como padronizar algo que não existe ainda? Faria sentido padronizar a internet na idade média?

Essa resposta também é válida para os framworks do Spring, o JCP não tem nenhuma posição contra o Spring, afinal, eles são muito importantes dando esse primeiro passo, a inovação. Inclusive vários membros do mundo Spring participaram e participam das JSRs, especificação Java, é o caso da JSR 330 que teve a participação do Rod Johnson, conhecido mundialmente como o pai do Spring.

Uma informação importante é que boa parte das instituições que começaram o MicroProfile faz parte do Comitê executivo do JCP.

A ideia do MicroProfile é ser um subset do Java EE?

Não. Como foi dito previamente, o Java EE é uma umbrela de especificação, ou seja, algo estável e fora do mundo de inovação. Além do problema de apenas a Oracle como Spec Leader definir o que é Java EE ou não. Por exemplo, umas das ideias é que nas próximas versões do MicroProfile o Jcache, JSR 107, fazer parte do MicroProfile e essa especificação não está presente no full profile do Java EE 7 e nem se tem previsão de se adicionar essa especificação na próxima versão do Java EE.

JCP + Microprofile

Uma vez bem detalhado os papéis de cada organização/iniciativa, uma para padrão e outro para inovação. Ambos tem algo em comum: O feedback contínuo da comunidade.

O último passo do processo do Microprofile é definir padrões e nesse passo será um trabalho em conjunto com o JCP. Vale lembrar que o padrão traz algumas vantagens:

  • A escolha do fornecedor e/ou escolha da implementação
  • Portabilidade da aplicação
  • Retrocompatibilidade
  • Uma vez estável, reduz o risco do projeto

MicroProfile 1.0

Lançado na proximidade do JavaOne 2016 a versão 1.0 do microprofile vem com três recursos:

  • JAX-RS: Java API for RESTful Web Services (JAX-RS) uma API especificada que traz suporte para a criação de serviços REST.
  • CDI: Contexts and Dependency Injection ferramente especificada para injeção de dependência com contexto.
  • JSON-P: Uma API especificada para realizar parse, gerar, transformar e buscar JSON.

Próximos passos

roadmap-microprofile

Uma vez lançada a sua primeira versão os próximos passos estão em mover para uma fundação e o trabalho para a versão 1.1 do MicroProfile. A ideia original é que seja lançado uma versão a cada seis meses. O backlog para as próximas versões está vasto.

  • Container API
  • Microservic-y annotations (Ex: @CircuitBreaker)
  • Testing
  • Distributed Logging
  • Distributed Tracing
  • OAuth2/OpenID Connect
  • Microservice Security
  • Service Discovery
  • Health Check
  • Configuration
  • Metrics/Monitoring
  • WebSockets
  • JSON-B
  • Bean Validation
  • JPA
  • JTA
  • Concurrency Utilities for Java EE
  • Messaging / Eventing
  • Asynchronous/Reactive Support / Patterns
  • Integration with Reactive / Eventing systems
  • Big Data/NoSQL strong & weak consistency support
  • NetFlix OSS Integration
  • JCache
  • JDK 8 Lambda+Streams
  • EJB Lite
  • Servlets
  • HTTP/2
  • Startup Time
  • Disk Space
  • Memory
  • Uber-jar
  • Java 9 Modularity

JCP é lento em relação ao MicroProfile

Tal comparação não faz sentido algum! Como foi explicado, previamente, são organizações de propósito totalmente diferente. Comparar o JCP só faz sentido se for para outro órgão de padronização como o W3C com o HTML 5, começou o seu trabalho em 2004 e até o presente momento nenhum navegador dá suporte total ao Html 5.

Show me the Code

Dentro do organization do github do microprofile existe alguns exemplos de código o mais recente foi o exemplo utilizado no JavaOne, o microprofile-conference. Porém, também será criado uma simples aplicação demo utilizando microprofile, cujo a implementação será o tomee e maven.

Essa aplicação será realmente simples:

Será um serviço de votos, basicamente, dado uma pergunta a API permitirá uma resposta com sim ou não, sim será algo bem simples já que o principal objetivo é criar um simples serviço.

O primeiro passo é a criação do contador de voto, basicamente ele terá um contador para sim e outro para não, esse recurso terá único para toda aplicação, ou seja, um escopo de aplicação.

@ApplicationScoped
public class VoteCounter {

private final AtomicInteger yes = new AtomicInteger();

private final AtomicInteger no = new AtomicInteger();

public void voteYes() {
yes.incrementAndGet();
}

public void voteNo() {
no.incrementAndGet();
}

public Map<String, Integer> getResult() {
Map<String, Integer> votes = new HashMap<>();
votes.put("yes", yes.get());
votes.put("no", no.get());
return votes;
}
}

Após criar o contador o próximo passo é criar a API para exibir esse recurso.

@ApplicationScoped
@Path("votes")
@Produces(MediaType.APPLICATION_JSON)
public class VoteResource {

@Inject
private VoteCounter voteCounter;

@GET
@Path("question")
public String getQuestion() {
return "Did you like the post?";
}

@POST
@Path("/yes")
public void voteYes() {
voteCounter.voteYes();
}

@POST
@Path("/no")
public void voteNo() {
voteCounter.voteNo();
}

@GET
public Map<String, Integer> getResult() {
return voteCounter.getResult();
}
}

Após feito o código, o próximo passo são os códigos de configuração. Com o JAXRS, definir o caminho que serve como base de todos os URI.

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("/resource")
public class ApplicationConfiguration extends Application {
}

Também tem as configurações do web descriptor e do beans.xml para o CDI. Até o momento nada diferente tanto em código quanto em configuração que o desenvolvedor Java EE conhece.

Olhando para o arquivo de configuração do maven, o pom.xml, além de definir a API do Java EE 7 como provida é adicionado um plugin para o tomee:

  • O parâmetro tomeeClassifier define o profile do tomee que será selecionado. Os tipos disponíveis são: webprofile, plus, plusme
  • O parâmetro context define o nome do contexto.

 

<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>org.soujava</groupId>
    <artifactId>microprofile-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>microprofile-demo</name>

    <url>https://soujava.org.br/</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.ee.version>7.0</java.ee.version>
        <java.se.version>1.8</java.se.version>
        <version.tomee>7.0.1</version.tomee>
        <version.jackson>2.8.2</version.jackson>
    </properties>

    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>${java.ee.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${version.jackson}</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.jaxrs</groupId>
            <artifactId>jackson-jaxrs-json-provider</artifactId>
            <version>${version.jackson}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>${java.se.version}</source>
                    <target>${java.se.version}</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.0.0</version>
            </plugin>
            <plugin>
                <groupId>org.apache.tomee.maven</groupId>
                <artifactId>tomee-maven-plugin</artifactId>
                <version>${version.tomee}</version>
                <configuration>
                    <tomeeClassifier>webprofile</tomeeClassifier>
                    <context>${project.artifactId}</context>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

 

Feito isso basta empacotar e em seguida executar o exemplo com os seguintes comandos:

  • mvn clean package tomee:exec -DskipTests
  • java -jar target/microprofile-demo-1.0-SNAPSHOT-exec.jar

Com isso temos os seguintes recursos:

 

Assim, concluímos o material sobre essa novidade para o mundo dos frameworks para microsserviços.

Referências:

O que é arquitetura baseada em micro serviços?

http://www.itexto.net/devkico/?p=1755

Microservices for Java Developers: A Hands-On Introduction to Frameworks and Containers.: http://developers.redhat.com/promotions/microservices-for-java-developers/
Arquitetura de microserviços ou monolítica?

http://blog.caelum.com.br/arquitetura-de-microservicos-ou-monolitica/

Microservices

http://martinfowler.com/articles/microservices.html

Java EE 7 spec:

https://java.net/downloads/javaee-spec/WebProfile.pdf

MicroProfile Site:

http://microprofile.io/

Código exemplo:

https://github.com/soujava/microprofile-demo

Trabalhando com dinheiro no Mundo Java EE [MONEY-API]

jobtrends

As aplicações Java EE estão ficando cada vez mais comuns no mundo do desenvolvimento do software para web utilizando Java. Esse crescimento se deu principalmente em 2009 com o lançamento do Java EE 6 e o nascimento do Contexto de injeção de independência, o CDI. Umas das grandes vantagens do uso do Java EE, é que ela é uma especificação Java, assim não existe o vendor lock-in, em outras palavras, não é necessário ficar preso em um fabricante. Outro ponto interessante é que caso não seja necessário usar todo o container do Java EE, é possível utilizar alguns recursos em um servidor simples, por exemplo, no caso é possível rodar o CDI, JSF, Bean validator, etc. em um container como o Tomcat. Outro ponto importante que vale ressaltar é que cada componente, citado anteriormente, é especificado separadamente, uma JSR, essas JSRs são regidas pelo JCP e todos podem participar, dar feedback, reportar bugs, etc. na concepção até o final dessa especificação Java.

O Java EE é certamente a plataforma mais popular para softwares no mundo corporativo, esses softwares têm diversos objetivos. O que eles normalmente tem em comum é o fato de lidar com dinheiro. O valor monetário é muito comum em diversos softwares, por exemplo, sistemas financeiros, locadoras, livraria, sistemas de recursos humanos, contabilidade etc. Porém não existia um padrão para trabalhar com dinheiro no mundo Java, por anos vários desenvolvedores usavam os tipos primitivos ou tipos oriundos do Java para resolver este problema, por exemplo, Long, Double, Integer, String, etc. Utilizar tipos primitivos traz diversos problemas para um software, dentre eles, falta de encapsulamento, problemas no design da API numa aplicação, quebra do SOLID, dentre outros motivos (Como falar das desvantagens do tipo primitivos para representar dinheiro não é o foco desse artigo, ficaremos apenas com esses exemplos, para mais informações existe o money-api book o link estará abaixo desse artigo). Com o intuito de solucionar esse problema existe a JSR 354, o money-api, essa especificação foi desenvolvida por diversas empresas que lidam diretamente com dinheiro, dentre elas, o Credit Suisse, uns dos maiores e mais famosos banco do mundo, e o Paypal, um dos maiores providers de pagamento do mundo.

Para tornar o artigo mais didático, será imaginado um simples exemplo de uma aplicação Java EE, um sistema de compra de livros. Nesse aplicativo, se falará de cada componente do Java EE separadamente. Se utilizará a versão atual do Java EE, JavaEE 7. O foco desse artigo não será falar sobre a API de dinheiro, apenas sua integração com o Java EE.

JPA

Para o exemplo ficar simples, o modelo livro basicamente terá três atributos:

  • Id, que será um número sequencial
  • Nome, o nome do livro
  • Valor, o valor do livro
@Entity 
public class Book implements Serializable { 

@Id 
@GeneratedValue(strategy = GenerationType.IDENTITY) 
private Long id; 

@Column 
private String name; 

@Column 
private MonetaryAmount value; 

//getter and setter 
    
}

Um problema comum em lidar com tipos únicos, como o dinheiro, é que normalmente os bancos não tem suporte nesse tipo de informação. Uma solução para esse problema nasceu no JPA 2.1 com os converters. A criação de um converter é bastante simples, basta implementar a interface AttributeConverter.

public class MonetaryAmountConveter implements  AttributeConverter&amp;amp;lt;MonetaryAmount, BigDecimal&amp;amp;gt;{ 

    private static final CurrencyUnit CURRENCY = Monetary.getCurrency(&amp;quot;BRL&amp;quot;); 
    
    private static final MonetaryQuery&amp;amp;lt;BigDecimal&amp;amp;gt; EXTRACT_BIG_DECIMAL = (m) -&amp;amp;gt; m.getNumber() 
            .numberValue(BigDecimal.class); 
            
    @Override 
    public BigDecimal convertToDatabaseColumn(MonetaryAmount attribute) { 
       return Optional.ofNullable(attribute).orElse(FastMoney 
               .zero(CURRENCY)) 
               .query(EXTRACT_BIG_DECIMAL); 
    } 

    @Override 
    public MonetaryAmount convertToEntityAttribute(BigDecimal dbData) { 
        return Money.of(dbData, CURRENCY); 
    } 
} 

Converter do JPA que persiste apenas o valor numérico.

E definir no campo como ele será convertido, para isso existe a anotação Convert.

@Column
@Convert(converter = MonetaryAmountConveter.class)
private MonetaryAmount value;

No JPA 2.1 os converters trabalham com um campo com um campo único, assim se pode trabalhar com algumas opções:

  • Persistir apenas o número: em sistemas que trabalham apenas com uma única moeda, é possível, por exemplo, definir a moeda em uma constante, que sempre será a mesma, e assim persistir o valor numérico. Assim é possível realizar operações no próprio banco de dados, como soma, porém caso o projeto cresça e precise internacionalizar haverá um grande problema. Uma solução para contornar esse problema seria ter um tenancy para cada país/moeda.

  • Persistir o número e a moeda como String: Utilizando essa estratégia seria o suficiente para manter o sistema internacionalizável. Se teria as informações suficientes para poder recuperar tanto a moeda quanto o valor numérico, porém, seria mais difícil, por exemplo, usar recursos do banco de dados, por exemplo, realizar somatório, assim um somatório teria que ser serializado para o modelo e assim a operação ser realizada.

  • Persistir o número e a moeda em campos diferentes: Essa opção, infelizmente, não está disponível no JPA, conforme foi discutido anteriormente no JPA 2.1 o converter apenas aceita um único campo como converção. Porém, é possível utilizar o hibernate diretamente para realizar essa operação, algo bem semelhante ao JPA 2.1, porém implementando a interface CompositeUserType. Com isso é possível, realizar operações de soma dentro do banco de dados, porém, existe a possibilidade de somar sem levar em consideração da moeda, ou não levar em consideração o modo de arredondamento desejado, que as implementações do MonetaryAmount podem garantir.

O Jadira é um framework que possui diversas implementações do UserType, interface que define o tipo que será persistido no banco de dados semelhante ao AttributeConverter. O Jadira já possui suporte tanto para as JSR 310, time-api, quanto o JSR 354, money-api.

CDI

Não é interessante que a entidade Livro seja responsável por definir a moeda que o objeto vai utilizar, afinal, não é sua responsabilidade. Outro ponto é que existem diversas estratégias para a criação de uma moeda: Definir a moeda a partir do usuário ou definir a moeda a partir do tenancy, por exemplo, caso o usuário utilize as máquinas do “.com.br” a moeda será real, “.com.us” dollar etc. Em nenhum desses casos deixar essa lógica espalhada no código não é uma boa estratégia, ou deixar o livro responsável por isso, nesse caso haverá quebra do SOLID. Para isso, poderíamos utilizar o CDI. O contexto de injeção de dependência, CDI, funciona basicamente como cola no mundo Java EE.

public class MoneyProducer { 

    @Inject 
    private HttpServletRequest request; 

    @Produces 
    @RequestScoped 
    public CurrencyUnit getCurrency() { 
        return Monetary.getCurrency(request.getLocale()); 
    } 
} 

Criar uma moeda a partir do request

public class MoneyProducer { 

    @Inject 
    @Named(&amp;quot;money_from_configurations&amp;quot;) 
    private String money; 

    @Produces 
    @RequestScoped 
    public CurrencyUnit getCurrency() { 
        return Monetary.getCurrency(money); 
    } 
}

Pegando a informação da moeda a partir de uma configuração.

Com um produtor de moeda definida pelo CDI, se pode injetar a moeda e “magicamente” ela será instanciada. Para não ter diversos pontos injetando moeda e criando dinheiro com ‘n’ implementações, uma recomendação é ter uma classe especializada na criação do dinheiro, caso seja necessário, criar um qualificadores para definir cada tipo de implementação.

public class MonetaryAmountFactory { 

    @Inject 
    private CurrencyUnit currency; 
    
    
    public MonetaryAmount create(Number number) { 
        return Money.of(number, currency); 
    } 

} 

 

JAX-RS

É muito comum, realizar integração com outros sistemas e para realizá-lo, o modo mais comum, é via uma API rest, dessa forma a integração será transparente pela tecnologia, linguagem, etc. que o outro sistema utilize. No mundo Java EE existe a especificação que lida com isso, o JAX-RS. Assim como na implementação de JPA, não existe um converter nativo para a moeda, a interface CurrencyUnit, e para o dinheiro, a interface MonetaryAmount. Na nova versão do JAX-RS é possível criar os próprios converters, para isso é necessário:

Implementar a interface ParamConverterProvider e anota-la com a anotação @Provider. Essa interface tem apenas um método e como saida espera como saida a interface ParamConverter.

@Provider 
public class MonetaryAmountConverterProvider implements ParamConverterProvider { 


    @Override 
    public &amp;amp;lt;T&amp;amp;gt; ParamConverter&amp;amp;lt;T&amp;amp;gt; getConverter(Class&amp;amp;lt;T&amp;amp;gt; rawType, Type genericType, Annotation[] annotations) { 
        if (MonetaryAmount.class.isInstance(rawType)) { 
            return new ParamConverter&amp;amp;lt;T&amp;amp;gt;() { 
                @Override 
                public T fromString(String value) { 
                    if(value == null || value.isEmpty()) { 
                        return null; 
                    } 
                    return rawType.cast(Money.parse(value)); 
                } 

                @Override 
                public String toString(T value) { 
                    if(value == null) { 
                        return null; 
                    } 
                    return value.toString(); 
                } 
            }; 
        } 
        return null; 
    } 
} 


@Provider 
public class CurrencyUnitConverterProvider implements ParamConverterProvider { 

    private CurrencyUnitParamConverter converter = new CurrencyUnitParamConverter(); 

    @Override 
    public &amp;amp;lt;T&amp;amp;gt; ParamConverter&amp;amp;lt;T&amp;amp;gt; getConverter(Class&amp;amp;lt;T&amp;amp;gt; rawType, Type genericType, Annotation[] annotations) { 
        if (CurrencyUnit.class.isInstance(rawType)) { 
            return new ParamConverter&amp;amp;lt;T&amp;amp;gt;() { 
                @Override 
                public T fromString(String value) { 
                    if(value == null || value.isEmpty()) { 
                        return null; 
                    } 
                    return rawType.cast(Monetary.getCurrency(value)); 
                } 

                @Override 
                public String toString(T value) { 
                    if(value == null) { 
                        return null; 
                    } 
                    return value.toString(); 
                } 
            }; 
        } 
            return null; 
    } 
}

JSF

A interação com o usuário é um ponto muito importante nas aplicações, assim é necessário representar o dinheiro e permitir que o usuário informe a moeda ou dinheiro. Assim como as outras especificações do mundo Java EE, não existe suporte para a money-api, porém existe converters dos componentes do JSF. Para isso basta implementar a interface Converter.

@FacesConverter(&amp;quot;money.midas.CurrencyConverter&amp;quot;) 
public class CurrencyConverter implements Converter { 

	@Override 
	public Object getAsObject(FacesContext context, UIComponent component, 
			String value) { 
		 
		if (Objects.isNull(value)) { 
			return null; 
		} 
		return Monetary.getCurrency(value); 
	} 

	@Override 
	public String getAsString(FacesContext context, UIComponent component, 
			Object value) { 
		if (Objects.isNull(value)) { 
			return null; 
		} 
		return value.toString(); 
	} 

} 

@FacesConverter(&amp;quot;money.midas.MoneyConverter&amp;quot;) 
public class MoneyConverter implements Converter { 

	@Override 
	public Object getAsObject(FacesContext context, UIComponent component, 
			String value) { 
		 
		if (Objects.isNull(value)) { 
			return null; 
		} 
		return Money.parse(value); 
	} 

	@Override 
&amp;amp;lt;strong&amp;amp;gt;	public String getAsString(FacesContext context, UIComponent component, 
			Object value) { 
		if (Objects.isNull(value)) { 
			return null;
		} 
		return value.toString(); 
	} 
}

O Projeto Midas

Com o intuito de realizar a integração entre as especificaçõeshttp://jadira.sourceforge.net/ e outros frameworks e o money-api existe o projeto Midas, dentre os projetos que estão no midas dar suporte estão:

  • JSF
  • Bean Validation
  • JPA
  • JAX-RS
  • CDI
  • Google Guice
  • Spring

No midas, todos os converters do JSF, JPA, JAX-RS que foram citatos nesse artigo estão nesse projeto. Além dessas especificações, o midas tem suporte para o bean validation.

  • CurrencyAccepted: Define as moedas que serão aceitas dentro da moeda, CurrencyUnit, e do dinheiro, MonetaryAmount. É possível informar as moedas permitidas ou usando o código da moeda ou de * o Locale de onda a moeda veio.
  • CurrencyRejected: Define as moedas que não serão aceitas, rejeitadas, dentro da moeda, CurrencyUnit, e do dinheiro, MonetaryAmount. É possível informar as moedas permitidas ou usando o * código da moeda ou de o Locale de onda a moeda veio.
  • MonetaryMax: Define o valor máximo para o dinheiro.
  • MonetaryMin: Define um valor mínimo para o dinheiro.
@CurrencyAccepted(currencies = &amp;quot;BRL&amp;quot;) 
private CurrencyUnit currencyUnit; 

@CurrencyAccepted(currencies = &amp;quot;BRL&amp;quot;) 
@MonetaryMax(&amp;quot;10.12&amp;quot;) 
private MonetaryAmount money;

Com isso foi apresentado a integração entre o money-api e o Java EE. Ainda existe muito mais integrações para serem desenvolvidas com essa espificação que trabalha com dinheiro e , certamente, toda ajuda será bem-vinda por todos os membros da comunidade.

Referências