[JNoQL] Começando com Diana API de Chave-valor

Umas das API que existem no Diana, camada de comunicação do JNoSQL, é a camada de chave valor. O objetivo desse artigo é falar rapidamente sobre essa API para esse tipo de banco de dados.

No nosso caso será utilizado o Cassandra, assim:

<dependency>
  <groupId>org.jnosql.diana</groupId>
  <artifactId>hazelcast-driver</artifactId>
  <version>0.0.1</version>
</dependency>

p.s: Atualmente é necessário adicionar o repositório do JNoSQL para ter acesso as suas dependências:

<repositories>
  <repository>
    <id>jnsoql-repo</id>
    <name>JNoSQL Maven Repository</name>
    <url>https://dl.bintray.com/jnosql/maven/</url>
    <layout>default</layout>
  </repository>
</repositories>

As classes da API de documento seguem a seguinte estrutura:

KeyValueConfiguration: Classe responsável pela configuração e conexão com o banco de dados, uma vez esse setup varia fortemente em cada banco de dados. Ela costuma ter formato variável
BucketManagerFactory: classe responsável pela criação de uma interface Manager
BucketManager: assim como no JPA, essa classe é a responsável pelo gerenciamento da entidade.
KeyValueEntity: uma entidade chave valor, composto pela chave e seu respectivo valor.

Com o driver do Hazelcast é possível realizar o CRUD de maneira simples:

public class App {
    public static void main(String[] args) {

        KeyValueConfiguration configuration = new HazelCastKeyValueConfiguration();
        try (BucketManagerFactory managerFactory = configuration.get()) {
            BucketManager bucket = managerFactory.getBucketManager("bucket");
            bucket.
            List<String> list = managerFactory.getList("bucketList", String.class);
            Set<String> set = managerFactory.getSet("bucketSet", String.class);
            Map<String, Integer> map = managerFactory.getMap("bucketList", String.class, Integer.class);
            Queue<String> queue = managerFactory.getQueue("queueList", String.class);
            bucket.put("key", "value");
            Optional<Value>
            value = bucket.get("key");
        }
    }
}

Uma boa discussão é a relação entre o Jcache e uma api para chave-valor, de uma maneira bem simples os bancos de chave-valor além de possuirem propósitos diferentes eles, geralmente, possuem também estruturas especiais, por exemplo, List, Queue, Set, Map ou até mesmo uma específica como o Racking que o Redis possui. Assim, além do suporte ao Bucket e as estruturas especiais pré-definidas (List, Queue, Set e Map) a api do JNoSQL será extensível o suficiente para ter estruturas especiais. Com isso concluímos o artigo falando da camada de comunicação do projeto JNoSQL para colunas.
Referências:

https://wiki.eclipse.org/JNoSQL
https://www.gitbook.com/book/jnosql/jnosql-book/details
https://github.com/JNOSQL/diana-demos

[JNoSQL] Começando com Diana API de Colunas

Umas das API que existem no Diana, camada de comunicação do JNoSQL, é a camada de família de colunas. O objetivo desse artigo é falar rapidamente sobre essa API para esse tipo de banco de dados.

No nosso caso será utilizado o Cassandra, assim:

<dependency>
  <groupId>org.jnosql.diana</groupId>
  <artifactId>cassandra-driver</artifactId>
  <version>0.0.1</version>
</dependency>

p.s: Atualmente é necessário adicionar o repositório do JNoSQL para ter acesso as suas dependências:

<repositories>
  <repository>
    <id>jnsoql-repo</id>
    <name>JNoSQL Maven Repository</name>
    <url>https://dl.bintray.com/jnosql/maven/</url>
    <layout>default</layout>
  </repository>
</repositories>


As classes da API de documento seguem a seguinte estrutura:

  • ColumnConfiguration: Classe responsável pela configuração e conexão com o banco de dados, uma vez esse setup varia fortemente em cada banco de dados. Ela costuma ter formato variável
  • ColumnManagerFactory: classe responsável pela criação de uma interface Manager
  • ColumnManager: assim como no JPA, essa classe é a responsável pelo gerenciamento da entidade.
  • ColumnEntity: uma entidade de uma família de colunas
  • Column: Um ColumnEntity é composto por diversas colunas, ele é composto por uma tupla em que a chave é o nome e o valor é a informação propriamente dita. Essa informação poderá ser qualquer valor, variando de cada driver, inclusive uma coluna ou subcoluna.

 

Com o driver do Cassandra é possível realizar o CRUD de maneira simples:

public class App {

    public static final String KEY_SPACE = "newKeySpace";
    public static final String COLUMN_FAMILY = "newColumnFamily";

    public static void main(String[] args) {

        ColumnConfiguration condition = new CassandraConfiguration();

        try(ColumnFamilyManagerFactory managerFactory = condition.get()) {
            ColumnFamilyManager columnEntityManager = managerFactory.get(KEY_SPACE);
            ColumnEntity entity = ColumnEntity.of(COLUMN_FAMILY);
            Column id = Column.of("id", 10L);
            entity.add(id);
            entity.add(Column.of("version", 0.001));
            entity.add(Column.of("name", "Diana"));
            entity.add(Column.of("options", Arrays.asList(1, 2, 3)));

            columnEntityManager.save(entity);

            ColumnQuery query = ColumnQuery.of(COLUMN_FAMILY);
            query.and(ColumnCondition.eq(id));
            Optional<ColumnEntity> result = columnEntityManager.singleResult(query);
            System.out.println(result);

        }


    }
}

Vale salientar que é necessário existir um banco Cassandra instalado e rodando. Com o Cassandra é necessário também informar o IP para que o Diana consiga se conectar. Para isso, é necessário o arquivo diana-cassandra.properties no classpath, além do IP é possível configurar a estrutura inicial utilizando o CQL, Cassandra query language. Assim, ele terá as informações dos bancos que serão conectados. No nosso, exemplo:

cassandra-hoster-1=172.17.0.2
cassandra-threads-number=4
cassandra-initial-query-1=CREATE KEYSPACE IF NOT EXISTS newKeySpace WITH replication = {'class': 'SimpleStrategy', 'replication_factor' : 3};
cassandra-initial-query-2=DROP TABLE IF EXISTS newKeySpace.newColumnFamily;
cassandra-initial-query-3=CREATE COLUMNFAMILY IF NOT

Tip: uma maneira simples de instalar o Cassandra é utilizando o docker:
https://hub.docker.com/_/cassandra/

Especializações

O Cassandra possui recurso que irão além da simples API, recursos como nível de consistência e também o CQL.

public class CassandraApp {


    public static final String KEY_SPACE = "newKeySpace";
    public static final String COLUMN_FAMILY = "newColumnFamily";

    public static void main(String[] args) {

        CassandraConfiguration condition = new CassandraConfiguration();

        try(CassandraDocumentEntityManagerFactory managerFactory = condition.get()) {
            CassandraColumnFamilyManager columnEntityManager = managerFactory.get(KEY_SPACE);
            ColumnEntity entity = ColumnEntity.of(COLUMN_FAMILY);
            Column id = Column.of("id", 10L);
            entity.add(id);
            entity.add(Column.of("version", 0.001));
            entity.add(Column.of("name", "Diana"));
            entity.add(Column.of("options", Arrays.asList(1, 2, 3)));

            columnEntityManager.save(entity);

            //common implementation
            ColumnQuery query = ColumnQuery.of(COLUMN_FAMILY);
            query.and(ColumnCondition.eq(id));
            List&ltColumnEntity&gt columnEntities = columnEntityManager.find(query, ConsistencyLevel.LOCAL_QUORUM);

            //cassandra implementation
            columnEntityManager.save(entity, ConsistencyLevel.THREE);
            List&ltColumnEntity&gt entities = columnEntityManager.cql("select * from newKeySpace.newColumnFamily");
            CassandraPrepareStatment cassandraPrepareStatment = columnEntityManager.nativeQueryPrepare("select * from newKeySpace.newColumnFamily where id ?");
            List&ltColumnEntity&gt entityList = cassandraPrepareStatment.bind(10L).executeQuery();
            System.out.println(entities);

        }


    }

}

Com isso concluímos o artigo falando da camada de comunicação do projeto JNoSQL para colunas.

Referências:

[JNoSQL] Começando com Diana API de Documentos

Os bancos documentos, é um dos tipos de bancos não relacionais que armazena e recupera a informação numa estrutura de documentos, semelhante a um documento XML, esse tipo de banco é uma especialização de um banco chave valor.  Umas das API que existem no Diana, camada de comunicação do JNoSQL, é a camada de Documentos. O objetivo desse artigo é falar rapidamente sobre essa API para esse tipo de banco de dados.

 

No nosso caso será utilizado o MongoDB, assim:

<dependency>
    <groupId>org.jnosql.diana</groupId>
    <artifactId>mongodb-driver</artifactId>
    <version>0.0.1</version>
</dependency>

p.s: Atualmente é necessário adicionar o repositório do JNoSQL para ter acesso as suas dependências:

<repositories>
    <repository>
        <id>jnsoql-repo</id>
        <name>JNoSQL Maven Repository</name>
        <url>https://dl.bintray.com/jnosql/maven/</url> 
        <layout>default</layout>
    </repository>
</repositories>

As classes da API de documento seguem a seguinte estrutura:

  • DocumentConfiguration: Classe responsável pela configuração e conexão com o banco de dados, uma vez esse setup varia fortemente em cada banco de dados.
  • DocumentManagerFactory: classe responsável pela criação de uma interface Manager
  • DocumentManager: assim como no JPA, essa classe é a responsável pelo gerenciamento da entidade.
  • DocumentEntity: uma entidade de uma coleção de documentos
  • Document: Um DocumentEntity é composto por diversos documentos, ele é composto por uma tupla em que a chave é o nome e o valor é a informação propriamente dita. Essa informação poderá ser qualquer valor, variando de cada driver, inclusive um outro documento ou subdocumento.

Com o driver do MongoDB é necessário é possível realizar o CRUD de maneira simples:

public class MongoDBApp {

    public static final String DATABASE = "default";
    public static final String DOCUMENT_COLLECTION = "person";

    public static void main(String[] args) {
        String idValue = UUID.randomUUID().toString();
        DocumentConfiguration configuration = new MongoDBDocumentConfiguration();
        try (DocumentCollectionManagerFactory collectionFactory = configuration.get();) {
            DocumentCollectionManager collectionManager = collectionFactory.get(DATABASE);

            DocumentEntity entity = DocumentEntity.of(DOCUMENT_COLLECTION);
            entity.add(Document.of("name", "Daniel Soro"));
            entity.add(Document.of("age", 26));
            entity.add(Document.of("_id", idValue));

            DocumentEntity entitySaved = collectionManager.save(entity);
            Optional<Document> id = entitySaved.find("_id");

            DocumentQuery query = DocumentQuery.of(DOCUMENT_COLLECTION);
            query.and(DocumentCondition.eq(id.get()));
            List<DocumentEntity> documentsFound = collectionManager.find(query);


        }
    }
}

Vale salientar que é necessário existir um banco MongoDB instalado e rodando. Com o MongoDB é necessário também informar o IP para que o Diana consiga se conectar. Para isso, é necessário o arquivo diana-mongodb.properties no classpath. Assim, ele terá as informações dos bancos que serão conectados. No nosso, exemplo:

mongodb-server-host-1=localhost:27017

Tip: uma maneira simples de instalar o mongodb é utilizando o docker: https://hub.docker.com/_/mongo/

Para demonstrar a facilidade de utilizar a API, utilizaremos também um segundo banco de dados, dessa vez, o Couchbase.

Ao utilizar a sua dependência:

<dependency>
    <groupId>org.jnosql.diana</groupId>
    <artifactId>couchbase-driver</artifactId>
    <version>0.0.1</version>
</dependency>

E no código:

public class CouchbaseApp {

    public static final String DATABASE = "default";
    public static final String DOCUMENT_COLLECTION = "person";

    public static void main(String[] args) {
        String idValue = UUID.randomUUID().toString();
        DocumentConfiguration configuration = new CouchbaseDocumentConfiguration();
        try (DocumentCollectionManagerFactory collectionFactory = configuration.get();) {
            DocumentCollectionManager collectionManager = collectionFactory.get(DATABASE);

            DocumentEntity entity = DocumentEntity.of(DOCUMENT_COLLECTION);
            entity.add(Document.of("name", "Daniel Soro"));
            entity.add(Document.of("age", 26));
            entity.add(Document.of("_id", idValue));
            DocumentEntity entitySaved = collectionManager.save(entity);
            Optional<Document> id = entitySaved.find("_id");

            DocumentQuery query = DocumentQuery.of(DOCUMENT_COLLECTION);
            query.and(DocumentCondition.eq(id.get()));
            List<DocumentEntity> documentsFound = collectionManager.find(query);


        }
    }
}

Assim, como no caso do MongoDB, é necessário que o Couchbase seja instalado e configurado é necessário configurar o arquivo diana-couchbase.properties com os Ips dentre outras informações:

couchbase-host-1=localhost
couchbase-user=root
couchbase-password=123456

Tip: Seguindo a mesma dica do MongoDB, uma boa estratégia é utilizar docker: https://hub.docker.com/_/couchbase/

Especializações

Como mencionado, a extensibilidade dos bancos não relacionais é muito importante uma vez que os bancos têm comportamentos específicos. Por exemplo, o Couchbase tem o N1SQL que é uma query específica realizada em cima do JSON que atualmente, apenas o mesmo possui.

public class CouchbaseApp2 {

    public static final String DATABASE = "default";
    public static final String DOCUMENT_COLLECTION = "person";

    public static void main(String[] args) {
        String idValue = UUID.randomUUID().toString();
        CouchbaseDocumentConfiguration configuration = new CouchbaseDocumentConfiguration();
        try (CouhbaseDocumentCollectionManagerFactory collectionFactory = configuration.get();) {
            CouchbaseDocumentCollectionManager collectionManager = collectionFactory.get(DATABASE);

            DocumentEntity entity = DocumentEntity.of(DOCUMENT_COLLECTION);
            entity.add(Document.of("name", "Daniel Soro"));
            entity.add(Document.of("age", 26));
            entity.add(Document.of("_id", idValue));
            DocumentEntity entitySaved = collectionManager.save(entity);
            Optional<Document> id = entitySaved.find("_id");

            JsonObject params = JsonObject.create();
            params.put("id", id.get().get());
            List<DocumentEntity> entities = collectionManager.n1qlQuery("select * from " + DATABASE + " where _id = $id", params);
            System.out.println(entities);
        }
    }
}

Com isso concluímos o artigo falando da camada de comunicação do projeto JNoSQL para documentos, mesmo com a API comum as implementações serão diferentes, por exemplo, a forma de armazenar o valor, json ou bjson, ou a criação automática do campo “_id” ou não, caso ele não tenha sido informado

Referências:

JNoSQL – Diana Release 0.0.1

O JNoSQL é uma tecnologia Java cujo o foco é criar um API comum para comunicação para os bancos não relacionais, não esquecendo também da diversidade que eles possuem. Com o intuito de atender esse objetivo ele é subdividido em duas camadas: A camada de comunicação, aka Diana, responsável pela comunicação Java com o banco de dados, fazendo uma relação com o mundo relacional, ele teria um papel semelhante ao JDBC. A camada de abstração, aka Artemis, responsável pela abstração é responsável por converter uma entidade ou objeto da aplicação para um modelo de baixo nível. Realizando a analogia com o mundo relacional, seria o que o JPA ou um ORM para o NoSQL. O objetivo desse artigo será falar sobre a camada de comunicação, aka Diana.

Dividir e conquistar é uma estratégia comum no mundo Java. Cada uma dessas camadas tem quatro APIs independentes, uma para cada tipo de banco de dados.

AFINAL, VALE A PENA CRIAR UMA API COMUM?

Se olharmos, por exemplo, para as APIs de alguns bancos de dados de Documentos teremos as seguintes classes:

  • BaseDocument para o ArangoDB
  • JsonDocument para o couchbase
  • Json serializador para o Elasticsearch
  • org.bson.Document para o MongoDB
  • ODocument para o OrientDB

Repararemos que eles não tem muita coisa em comum com relação a nomenclatura e compatibilidade de classe ou nome e parâmetros de métodos, etc.. Porém, ao verificar profundamente sua estrutura:

//orientDB
ODocument document = new ODocument(“collection”);
document.field(name, value);

//mongodb
Document document = new Document();
document.append(name, value);

//couchbase
JsonObject jsonObject = JsonObject.create();
jsonObject.put(name, value);

//arangodb
BaseDocument baseDocument = new BaseDocument();
baseDocument.addAttribute(name, value);

É possível perceber eles possuem uma inspiração semelhante, afinal, uma coleção de documento possui um ou mais documentos no qual é uma tupla composta pelo valor e o seu respectivo nome.

Esse tipo de problema acontece também com outras definições, por exemplo, com o TTL, além das operações de CRUD.

Os bancos NoSQL, assim como os bancos de dados, possuem operações básicas que fazem parte do CRUD, ou seja, criar, recuperar atualizar e deletar informações. O problema nesse caso além da estrutura a ser recuperada está na nomenclatura utilizada, por exemplo:

  • insert ou save
  • delete ou remove
  • find ou search
  • callback para realizar operações assíncronas

Para o time to live, de uma informação:

  • long para segundos
  • int para segundos
  • long para milissegundos
  • int para milissegundos

Com essa API, é possível converter para a implementação desejada sem que haja impacto algum nas operações de banco de dados, além de não ser necessário aprender uma nova nomenclatura.

Document document = Document.of(name, value);
DocumentEntity entity = DocumentEntity.of(“collection”);
entity.add(document);

A diversidade nos bancos não relacionais

Certamente a diversidade, seja do tipo e o comportamento único que um banco de dados pode oferecer não pode ficar de fora em uma API desse tipo. Assim, o Diana permite realizar operações de um banco específico como live query do OrientDB, N1ql do couchbase, CQL do Cassandra, recurso de search engine do Elasticsearch dentre outros. Com a vantagem de que a estrutura de dados retornada será uma padronizada.

Drivers disponíveis

Nessa primeira versão o diana terá suporte nos seguintes drivers:

  • ArangoDB
  • Cassandra
  • Coucbase
  • Elasticsearch
  • Hazelcast
  • Hbase
  • MongoDB
  • OrientDB
  • Redis
  • Riak
  • ScyllaDB

Referências:

Vamos falar de Padrões NoSQL [Q&A]

duke-diana-min

Com a evolução da tecnologia da persistência, termo como “persistência poliglota” vem se tornando cada vez mais comum para o vocábulo do desenvolvedor. Atualmente, não basta utilizar a linguagem de programação mais apropriada para o problema, mas também utilizar o melhor banco e a melhor forma de persistir tais informações. Uma boa escolha trará certamente uma facilidade na leitura, elasticidade além de performance tanto na leitura quanto na escrita, isso variará de acordo com o objetivo e interesse de cada programa. Com isso, nem todas as aplicações utilizam ou utilizarão as soluções clássicas para persistência, os bancos relacionais. Os bancos NoSQL se tornaram uma solução muito comum para isso. O objetivo desse artigo é esclarecer algumas sobre o mais recente projeto criado, o JNoSQL, dúvidas com relação aos próximos passos dos bancos NoSQL focando na linguagem mais utilizada no mundo, Java, no formato de perguntas e respostas.

Para os detalhes iniciais como definição, já realizei um post sobre isso:

https://otaviojava.wordpress.com/2016/09/10/nosql-java/

O que é o JNoSQL?

O projeto JNoSQL é um projeto que visa a criação de ferramentas para o desenvolvimento de bancos não relacionais, os NoSQL, seu foco será padronizar os bancos NoSQL olhando para sua diversidade, ou seja, criando mecanismo para que o mesmo seja extensivo. Para facilitar o desenvolvimento dessa ferramenta foram criadas duas camadas abaixo do JNoSQL:

A camada de comunicação (aka Diana): Essa camada é responsável por realizar a comunicação entre a aplicação Java e o banco de dados. Essa API será dividida em quatro partes, uma para cada tipo de banco NoSQL. Ele será semelhante ao JDBC do mundo relacional.

A camada de abstração (aka Artemis): semelhante ao ORM ele é responsável por facilitar a abstração para o desenvolvedor Java, ela será orientada a anotações e realizará integrações com outras tecnologias como, por exemplo, o bean validation. O seu coração será o CDI.

Essa divisão tem algumas vantagens:

  • Divisão de problemas (Assim, os bancos de dados darão atenção apenas a camada de comunicação enquanto desenvolvedores de framework darão atenção numa camada superior.
  • Facilidade na implementação, uma vez um novo banco de dados interessado em implementar a API do JNoSQL será necessário apenas implementar a API de comunicação não se preocupando com as outras camadas.
  • Facilidade em componentização, com essa estratégia será possível trocar um dos dois componentes sem que necessariamente exista impacto no outro lado.

Vamos padronizar?

Sim, a ideia é que o resultado do trabalho dentro do JNoSQL seja o foco de padronização. Inicialmente, apenas a camada de comunicação será levada como uma JSR. Esse é um dos motivos que esse projeto tem como parcerias diversos provedores de banco de dados, por exemplo, Cassandra, Hazelcast, OrientDB, couchbase, ArangoDB, MongoDB, HBase além da participação da comunidade (SouJava, LondoJC, ThinkerPOP, dentre outros).

É possível padronizar 100%?

Mesmo os bancos relacionais que já estão a décadas no mercado, não é possível padronizar todo o processo. O mesmo acontece com os bancos NoSQL, inicialmente serão padronizadas as entidades, as hierarquias e algumas ações. Existirão quatro tipos de API, detalhado anteriormente. Mesmo com bancos de dados do mesmo tipo, por exemplo família de coluna com Cassandra e o hbase, existem comportamentos diferentes, por exemplo o nível de consistência e o Cassandra Query Language que existem apenas no Cassandra, para isso a API deverá fornecer a extensibilidade para que seja possível realizar tal operação, no entanto, mesmo com esses comportamentos diferentes o Cassandra continuará lidando as mesmas entidades que o Hbase (Família de Colunas, Colunas, chave da família de coluna, etc.). Por exemplo:

//both implementations (Hbase and Cassandra)
ColumnFamilyEntity entity = …/
entityManager.save(entity);
//just on Cassandra
cassandraEntityManager.save(entity, ConsistencyLevel.ALL);
List entities = entityManager.cql(¨select * from entity¨);

Existirão casos também na qual a API retornará uma exceção de operação não suportada, caso o banco de dados não suporte algumas operações.

Quais métodos serão padronizados?

Nessa primeira versão o foco será padronizar apenas os modelos além de métodos de operação básicas, são eles:

  • Inserir de maneira síncrona, assíncrona, assíncrona com calback além do uso de TTL
  • Atualizar de maneira síncrona, assíncrona, assíncrona com calback além do uso de TTL
  • Deletar de maneira síncrona, assíncrona, assíncrona com calback além do uso de TTL
  • Realizar queries de maneira síncrona e assíncrona

Ou seja, inicialmente as APIs conseguirão realizar as operações de CRUD.

A única exceção será a API de grafos, na verdade, o modelo de grafos já existe uma padronização já conhecida: o ThinkerPOP. A ideia é que a API contenha as partes mais estáveis desse projeto e sua implementação de referência seja uma simples adapter entre os projetos, em outras palavras, todos os bancos que suportam o ThinkerPOP, automaticamente suportarão o Diana.

Mesmo sem padronizar tudo ainda vale a pena?

Nós acreditamos, que sim! Será necessário realizar o primeiro passo e o JNoSQL realizará esse passo. Utilizaremos a estratégia incremental, ou seja, serão dados pequenos passos e pequenos releases para que recebemos rápidos feedbacks sobre a API.

O que acontece com um banco de dados que suporta dois tipos de bancos de dados?

Ele terá que implementar uma API para cada banco de dados que ele suporte. A ideia também é que existe um TCK, que terá como objetivo verificar se a banco de dados suporta ou não corretamente essa API.

Qual é o seu atual status?

O projeto foi recentemente aceito para Eclipse Foundation e estamos recebendo alguns feedbacks sobre a API. Também foi realizada uma apresentação do projeto para o JCP no F2F em Londres: https://goo.gl/2pKwrd

Como faço para ajudar?

Atualmente existem diversas maneiras de ajudar o projeto:

  • Documentação
  • Revisar a documentação já existente
  • Feedback na API
  • Encontrar bugs na implementação
  • Implementar novos drivers
  • Criar exemplos
  • Ajudar na tradução do material para o seu idioma
  • Realizar a palestra sobre esse projeto no seu JUG.

Quais referências estou utilizando?

Estamos utilizando como base diversos projetos existentes, inclusive, além da nomeclatura. Sempre que possível utilizaremos a mesma nomenclatura já existente em outras especificações para facilitar o aprendizado dos desenvolvedores Java.

Moneta 1.2 e nova JSR [Próximos passos]

Uma vez entregue a versão 1.1, que dentre diversas coisas corrige diversos bugs, o próximo passo será a modularização da money-api, tanto na sua API como na implementação. Atualmente lidamos com os seguintes pacotes:

  • javax.money – local aonde fica o core da API
  • javax.money.convert – Responsável por realizar a converção de valores, cotação
  • javax.money.format – Responsável pela formatação do Currency e MonetaryAmount
  • javax.money.spi – Service Provider Interface

Com essa nova proposta a ideia é tornar alguns pacotes opcionais:

  • javax.money : obrigatório
  • javax.money.convert: opcional
  • javax.money.format: opcional
  • javax.money.spi: opcional

Do lado da implementação, seguiríamos um passo semelhante, porém, dentro do módulo do convert ele será subdivido em cada implementação, ou seja, um módulo para convert IMF e um para convert ECB.

  • Módulo obrigatório
  • Módulo formatação
  • Módulo SPI
  • Módulo de convert IMF
  • Módulo de convert ECB

Lançamento do Moneta 1.1

 

A implementação de referência da JSR 354, money-api. Lança uma nova versão. O moneta, o nome da implementação de referência do money-api, vem algumas novidades além da correção de mais de vinte bugs. Dentre eles podemos destacar:

Além das correções foram adicionados novos métodos construtores para as implementações do MonetaryAmount.

  • zero(CurrencyUnit currency) – retorna um valor monetário igual a zero com a moeda informada.
  • ofMinor(CurrencyUnit currency, long amountMinor) – retorna o valor monetário a partir da moeda informada e do valor em centavos, por exemplo, o ofMinor(USD, 1234) retorna uma instância USD 12,34.
  • ofMinor(CurrencyUnit currency, long amountMinor, int factionDigits) – retorna o valor monetário a partir da moeda informada e do valor em centavos, por exemplo, o ofMinor(USD, 1234, 2) retorna uma instância USD 12,34. Diferente do método de criação anterior, utiliza o número de dígitos informado ao invés do número de dígitos da moeda.

 

MonetaryAmount money = Money.zero(dollar);
MonetaryAmount oneDollar = Money.ofMinor(dollar, 12_34);

MonetaryAmount money = FastMoney.zero(dollar);
MonetaryAmount oneDollar = FastMoney.ofMinor(dollar, 12_34);

MonetaryAmount money = RoundedMoney.zero(dollar);
MonetaryAmount oneDollar = RoundedMoney.ofMinor(dollar, 12_34);

Outro ponto é que a classe MonetaryUtil foi depreciada, o maior motivo é a duplicidade do nome, afinal, o termo util cabe qualquer coisa. Ao invés disso, foram criadas duas classes utilitárias:
A classe MonetaryOperators : utilitária da interface MonetaryOperator, nele contém diversas implementações interessantes que fazem a vida do desenvolvedor mais fácil, como por exemplo,

MonetaryAmount amount = ////USD 12.23
amount.with(MonetaryOperators.majorPart());//USD 12
amount.with(MonetaryOperators.minorPart());//USD 0.23
amount.with(MonetaryOperators.percent(10));//USD 1.223

MonetaryQueries: utilitária da interface MonetaryQuery, nele contém diversas implementações interessantes e que facilita a extração de informações do montante financeiro.

MonetaryAmount amount = //USD 12.32
amount.query(MonetaryQueries.convertMinorPart());//1232
amount.query(MonetaryQueries.extractMajorPart());//12
amount.query(MonetaryQueries.extractMinorPart());//32

Outra novidade está relacionado a cotação, agora é possível realizar buscas de cotação no IMF.

CurrencyUnit dollar = Monetary.getCurrency("USD");
CurrencyUnit real = Monetary.getCurrency("BRL");

MonetaryAmount money = FastMoney.of(10, dollar);
MonetaryAmount money2 = FastMoney.of(10, real);

LocalDate localDate = Year.of(2009).atMonth(Month.JANUARY).atDay(9);
ExchangeRateProvider provider = MonetaryConversions.getExchangeRateProvider(ExchangeRateType.IMF_HIST);
ConversionQuery query = ConversionQueryBuilder.of().setTermCurrency(dollar).set(localDate).build();

CurrencyConversion currencyConversion = provider.getCurrencyConversion(query);
MonetaryAmount result = currencyConversion.apply(money2);
MonetaryAmount monetaryAmount = money.add(result);

Também foi adicionado um novo recurso para formatar o MonetaryAmount.

MonetaryAmountFormat defaultFormat = MonetaryAmountDecimalFormatBuilder.newInstance().build();
MonetaryAmountFormat patternFormat = MonetaryAmountDecimalFormatBuilder.of("¤ ###,###.00").build();
MonetaryAmountFormat localeFormat = MonetaryAmountDecimalFormatBuilder.of(Locale.US).build();

CurrencyUnit currency = Monetary.getCurrency("BRL");
MonetaryAmount money = Money.of(12, currency);
String format = defaultFormat.format(money);//$12.00
MonetaryAmount moneyParsed = Money.parse(format, defaultFormat);//or using defafult.parse(format);

Com isso foi discutido sobre a nova versão do moneta, a versão 1.1. Nessa versão além de ter realizado diversas correções, foram adicionados novos recursos para formatação, cotação e deixar API cada vez mais fácil. Com essa versão finalizada, os esforços já foram iniciados para a próxima versão que o maior benefício é a modularização do moneta.

Novidades do CDI 2.0

cdi_herologo

A especificação de injeção de contexto e dependência, o CDI, certamente foi umas das grandes novidades que surgiram com o Java EE 6. Ele funciona basicamente como uma “cola” para o Java EE, realizando integrações entre os seus beans, recurso, dentro do servidor de aplicação. Nessa nova versão, o CDI 2.0, que está previsto para junho de 2016 teremos algumas novidades. E esse post tem o objetivo de dar uma visão delas. Vale lembrar como ainda se tem tempo para o lançamento muitas dessas novidades ainda estão sendo discutidas e codificadas.

  • A API foi subdividida, agora é possível utilizar uma pequena parte dela, ao invés, de todo o contexto. Assim, será possível rodar em dispositivos que tem menos memória e processamento, por exemplo, a IoT, plataformas embarcadas e até mesmo no Android. Sim, a ideia é realizar um CDI lite, assim irão o básico de injeção de dependência (definição de bean, @Inject, qualificadores, produtores, evento) até todos os recurso.
  • Melhorias nos eventos: definir ordem para os eventos além de poder lançar eventos de forma assíncrona.
  • O uso de programação orientada a aspecto para melhorar os interceptors e decoradores.

Para exemplificar o uso de CDI, será imaginado um simples aplicativo, venda de livros, utilizando apenas o Java SE. O primeiro passo será a criação de um projeto maven e será adicionado a dependência do CDI 2.0, ainda na fase de desenvolvimento, com a sua implementação de referência, o weld.

    <dependencies> 
        <dependency> 
            <groupId>org.jboss.weld.se</groupId> 
            <artifactId>weld-se-core</artifactId> 
            <version>3.0.0.Alpha15</version> 
        </dependency> 
    </dependencies>

Adicionado a dependência, o próximo passo é container do CDI, uma característica interessante é que ele implementa o AutoCloseable, recurso oriundo do Java 7 que permite que a JVM feche o recurso tão logo sai do bloco try. Assim ao executar o código abaixo é possível ver no log o Weld SE container STATIC_INSTANCE shut down.

import org.jboss.weld.environment.se.Weld; 
import javax.enterprise.inject.spi.CDI; 

public class App { 
    public static void main(String[] args) { 

        try (CDI<Object> container = new Weld().initialize()) { 

        } 
    } 
} 

É possível fazer as mesmas coisas como no Java EE, assim é possível realizar injeção de dependência apenas com a anotação @Inject.

public class PaymentMethod { 

    public void payment() { 
        System.out.println("Doing payment"); 
    } 
} 

public class Checkout { 

    @Inject 
    private PaymentMethod paymentMethod; 

    public void checkout() { 
        System.out.println("doing checkout "); 
        paymentMethod.payment(); 
    } 
} 

Retornando a classe App, é possível iniciar uma classe a partir do container do CDI, nesse exemplo será a classe Checkout. O CDI será responsável por iniciar a classe e injetar todas as suas dependências.

public class App { 
    public static void main(String[] args) { 

        try (CDI<Object> container = new Weld().initialize()) { 
            Checkout checkout = container.select(Checkout.class).get(); 
            checkout.checkout(); 
        } 
    } 
} 

Também é possível ensinar o CDI a criar uma instância utilizando o método produtor, por exemplo:

public class User implements Serializable {

private String name;

private String email;

public User(String name, String email) {
this.name = name;
this.email = email;
}

String getName() {
return name;
}

String getEmail() {
return email;
}

@Override
public String toString() {
final StringBuilder sb = new StringBuilder("User{");
sb.append("name='").append(name).append('\'');
sb.append(", email='").append(email).append('\'');
sb.append('}');
return sb.toString();
}
}

public class UserProducer {

@Produces
public User getUser() {
return new User("Otavio", "otaviojava@domain.com");
}
}

Nesse exemplo, a classe User não possui um construtor default, assim o CDI não sabe como instanciar, assim foi utilizado o @Produces dentro da classe UserProducer para ensinar o CDI como instanciar a classe usuário. Assim é possível injetá-lo, por exemplo, dentro da classe Checkout:

public class Checkout {

@Inject
private PaymentMethod paymentMethod;
@Inject
private User user;

public void checkout() {
System.out.println("doing checkout: " + user);
paymentMethod.payment();
}
}

O outro recurso que também estará disponível para SE são os qualificadores. Imagine que agora será possível ter dois métodos de pagamento:

  • Cartão de crédito
  • Dinheiro

Assim o método de pagamento passará a ser uma interface com duas implementações. Umas das maneiras para ensinar qual implementação o CDI utilizará seria a partir dos qualificadores.

public enum PaymentType {
CREDIT_CARD, CASH
}

@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface Payment {
PaymentType value();
}

public interface PaymentMethod {

void payment();
}

@Payment(PaymentType.CREDIT_CARD)
public class CreditCard implements PaymentMethod {
@Override
public void payment() {
System.out.println("Payment with credit card");
}
}

@Payment(PaymentType.CASH)
@Default
public class Cash implements PaymentMethod {
@Override
public void payment() {
System.out.println("Payment with cash");
}
}

Nesse exemplo, foi criado a anotação Payment que possui um atributo, o enum PaymentType, que define os tipos de pagamento. Na implementação Cash, além do qualificador ele possui a anotação @Default, quer dizer que caso não seja definido nenhuma qualificação o CDI o injetará como padrão.

@Inject
@Payment(PaymentType.CREDIT_CARD)
private PaymentMethod paymentMethod;

Será injetado a implementação de cartão de crédito.

@Inject
@Payment(PaymentType.CASH)
private PaymentMethod paymentMethod;

@Inject
private PaymentMethod paymentMethod;

Será injetado a implementação de pagamento em dinheiro.

Uma outra possibilidade dos qualificadores é definir qual instância será utilizada em tempo de execução, basta utilizar Instance com selector.

public class Checkout {

@Inject
@Any
private Instance payments;
@Inject
private User user;

public void checkout(PaymentType type) {
System.out.println("doing checkout: " + user);
PaymentMethod paymentMethod = payments.select(new PaymentSelector(type)).get();
paymentMethod.payment();
}
}

public class PaymentSelector extends AnnotationLiteral implements Payment {

private final PaymentType type;

PaymentSelector(PaymentType type) {
this.type = type;
}

@Override
public PaymentType value() {
return type;
}
}

Definido a forma de pagamento, o próximo passo será o envio de e-mail e entregar o livro. Assim, se pode trabalhar com eventos.

public class SendEmail {

public void sendEmail(@Observes User user) {
System.out.println("Sending email to user: " + user);
}
}

public class DeliveryBook {

public void delivery(@Observes User user) {
System.out.println("Delivering a book to: " + user);
}
}

public class Checkout {

@Inject
@Any
private Instance payments;
@Inject
private User user;
@Inject
private Event userEvent;

public void checkout(PaymentType type) {
System.out.println("doing checkout: " + user);
PaymentMethod paymentMethod = payments.select(new PaymentSelector(type)).get();
paymentMethod.payment();
userEvent.fire(user);
}
}

Um recurso possível agora no 2.0 é definir a ordem em que os eventos serão chamados.

public class DeliveryBook {

public void delivery(@Observes @Priority(Interceptor.Priority.APPLICATION+2) User user) {
System.out.println("Delivering a book to: " + user);
}
}

public class SendEmail {

public void sendEmail(@Observes @Priority(Interceptor.Priority.APPLICATION+1) User user) {
System.out.println("Sending email to user: " + user);
}
}

Outra possibilidade é realizar a chamada de forma assíncrona, para isso basta trocar a nos observadores para ObservesAsync e chamar o método do evento.

public class SendEmail {

public void sendEmail(@ObservesAsync User user) {
System.out.println("Sending email to user: " + user);
}
}

public class DeliveryBook {

public void delivery(@ObservesAsync User user) {
System.out.println("Delivering a book to: " + user);
}
}

public class Checkout {

@Inject
@Any
private Instance payments;
@Inject
private User user;
@Inject
private Event userEvent;

public void checkout(PaymentType type) {
System.out.println("doing checkout: " + user);
PaymentMethod paymentMethod = payments.select(new PaymentSelector(type)).get();
paymentMethod.payment();
userEvent.fireAsync(user);
}
}

Com isso foram apresentados os novos recursos do CDI 2.0, vale lembrar que muitos ainda estão sendo trabalhados, assim esperem por mais novidades até o dia do lançamento.

Link para o código

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