Tutorial: criar um aplicativo Web Java usando o Azure Cosmos DB e a API do NoSQL

APLICA-SE A: NoSQL

Este tutorial de aplicativo Web Java mostra a você como usar o serviço Microsoft Azure Cosmos DB para armazenar e acessar dados de um aplicativo Java hospedado nos Aplicativos Web do Serviço de Aplicativo do Azure. Sem um cartão de crédito ou uma assinatura do Azure, você pode configurar uma conta gratuita do Azure Cosmos DB. Neste artigo, você aprenderá:

  • Como compilar um aplicativo básico do JSP (JavaServer Pages) no Eclipse.
  • Como trabalhar com o serviço Azure Cosmos DB usando o SDK de Java do Azure Cosmos DB.

Este tutorial de aplicativo Java mostra como criar um aplicativo de gerenciamento de tarefas baseado na web que permite criar, recuperar e marcar tarefas como concluídas, conforme mostrado na imagem a seguir. Cada uma das tarefas na lista de tarefas é armazenada como documentos JSON no Azure Cosmos DB.

Aplicativo Java My ToDo List

Dica

Este tutorial de desenvolvimento de aplicativo presume que você tenha experiência anterior com o Java. Se você não estiver familiarizado com Java ou com as ferramentas de pré-requisito, recomendamos o download completo do projeto todo do GitHub e compilação dele usando as instruções no final deste artigo. Depois de compilá-lo, você poderá consultar o artigo para obter informações sobre o código no contexto do projeto.

Pré-requisitos para este tutorial de aplicativo Web Java

Antes de começar este tutorial de desenvolvimento de aplicativo, você deve ter:

Se você estiver instalando essas ferramentas pela primeira vez, o coreservlets.com fornecerá um passo a passo do processo de instalação na seção de início rápido do artigo Tutorial: Instalar TomCat7 e usá-lo com o Eclipse.

Criar uma conta do Azure Cosmos DB

Vamos começar criando uma conta do Azure Cosmos DB. Se você já tiver uma conta ou se estiver usando o Emulador do Azure Cosmos DB para este tutorial, pule para a Etapa 2: Criar o aplicativo JSP Java.

  1. No menu do portal do Azure ou na Home page, selecione Criar um recurso.

  2. Pesquise por Azure Cosmos DB. Selecione Criar>Azure Cosmos DB.

  3. Na página Criar uma conta do Azure Cosmos DB, selecione a opção Criar na seção Azure Cosmos DB for NoSQL.

    O Azure Cosmos DB fornece várias APIs:

    • NoSQL, para dados do documento
    • PostgreSQL
    • MongoDB, para dados do documento
    • Apache Cassandra
    • Tabela
    • Apache Gremlin, para dados do grafo

    Para saber mais sobre a API para NoSQL, confira Bem-vindo(a) ao Azure Cosmos DB.

  4. Na página Criar uma Conta do Azure Cosmos DB, insira as configurações básicas da nova conta do Azure Cosmos DB.

    Configuração Valor Descrição
    Subscription Nome da assinatura Selecione a assinatura do Azure que você deseja usar para essa conta do Azure Cosmos DB.
    Grupo de recursos Nome do grupo de recursos Selecione um grupo de recursos ou selecione Criar novo, então insira um nome exclusivo para o novo grupo de recursos.
    Nome da Conta Um nome exclusivo Insira um nome para identificar a conta do Azure Cosmos DB. Já que documents.Azure.com é acrescentado ao nome que você fornece para criar o URI, use um nome exclusivo. O nome pode conter apenas letras minúsculas, números e o caractere de hífen (-). Deve ter de 3 a 44 caracteres.
    Location A região mais próxima dos usuários Selecione uma localização geográfica para hospedar a sua conta do Azure Cosmos DB. Use a localização mais próxima dos usuários para fornecer a eles acesso mais rápido aos dados.
    Modo de capacidade Taxa de transferência provisionada ou sem servidor Selecione Taxa de transferência provisionada para criar uma conta no modo taxa de transferência provisionada. Selecione Sem servidor para criar uma conta no modo sem servidor.
    Aplicar o desconto por nível gratuito do Azure Cosmos DB Aplicar ou Não aplicar Com a camada gratuita do Azure Cosmos DB, você recebe os primeiros 1000 RU/s e 25 GB de armazenamento sem custos em uma conta. Saiba mais sobre o nível gratuito.
    Limitar a taxa de transferência total da conta Selecionado ou não Limite a quantidade total da taxa de transferência que pode ser provisionada nessa conta. Esse limite impede encargos inesperados relacionados à taxa de transferência provisionada. Você pode atualizar ou remover esse limite depois que sua conta for criada.

    Você pode ter até uma conta gratuita do Azure Cosmos DB por assinatura do Azure e deve aceitar ao criar a conta. Se você não vê a opção de aplicar o desconto por nível gratuito, outra conta da assinatura já foi habilitada com o nível gratuito.

    Captura de tela mostrando a página Criar Conta do Azure Cosmos DB.

    Observação

    As seguintes opções não estarão disponíveis se você selecionar Sem servidor como Modo de capacidade:

    • Aplicar desconto por nível gratuito
    • Limitar a taxa de transferência total da conta
  5. Na guia Distribuição global, configure os detalhes a seguir. Para este início rápido, é possível usar os valores padrão:

    Configuração Valor Descrição
    Redundância geográfica Desabilitar Habilite ou desabilite a distribuição global em sua conta emparelhando sua região com uma região de par. Você poderá adicionar mais regiões à sua conta posteriormente.
    Gravações de várias regiões Desabilitar A capacidade de gravação de várias regiões permite que você aproveite a taxa de transferência provisionada para seus bancos de dados e contêineres em todo o mundo.
    Zonas de Disponibilidades Desabilitar As Zonas de Disponibilidade ajudam a aprimorar a disponibilidade e a resiliência do seu aplicativo.

    Observação

    As seguintes opções não estarão disponíveis se você selecionar Sem servidor como Modo de capacidade na página anterior Noções básicas:

    • Redundância geográfica
    • Gravações de várias regiões
  6. Opcionalmente, você pode configurar mais detalhes nas seguintes guias:

    • Redes. Configure o acesso a partir de uma rede virtual.
    • Política de Backup. Configure uma política de backup periódica ou contínua.
    • Criptografia. Use uma chave gerenciada pelo serviço ou uma chave gerenciada pelo cliente.
    • Marcas. Marcas são pares nome/valor que permitem categorizar recursos e exibir a cobrança consolidada por meio da aplicação da mesma marca a vários recursos e grupos de recursos.
  7. Selecione Examinar + criar.

  8. Examine as configurações da conta e selecione Criar. São necessários alguns minutos para criar a conta. Aguarde até que a página do portal exiba Sua implantação está concluída.

    Captura de tela mostrando que sua implantação está concluída.

  9. Selecione Ir para recurso para ir para a página da conta do Azure Cosmos DB.

    Captura de tela mostrando a página da conta do Azure Cosmos DB.

Vá até a página da conta do Azure Cosmos DB e selecione Chaves. Copie os valores a serem usados no aplicativo Web que será criado em seguida.

Captura de tela do Portal do Azure com o botão Chaves realçado na página de conta do Azure Cosmos DB

Criar o aplicativo JSP Java

Para criar o aplicativo JSP:

  1. Primeiro, começaremos criando um projeto Java. Inicie o Eclipse, selecione Arquivo, Selecione Novo e, em seguida, Projeto Web Dinâmico. Se não vir o Projeto Web Dinâmico listado como um projeto disponível, faça o seguinte: selecione Arquivo, Novo, Projeto..., expanda Web, selecione Projeto Web Dinâmico e, em seguida, Avançar.

    Desenvolvimento de aplicativo Java JSP

  2. Digite um nome de projeto na caixa Nome do projeto e no menu suspenso Runtime de Destino, selecione, opcionalmente, um valor (por exemplo, Apache Tomcat v7.0) e selecione Concluir. Selecione um runtime de destino que permite que você execute seu projeto localmente por meio do Eclipse.

  3. No Eclipse, na exibição do Explorador de Projeto, expanda o seu projeto. Clique com o botão direito do mouse em WebContent, selecione Novoe, em seguida, Arquivo JSP.

  4. Na caixa de diálogo Novo Arquivo JSP, nomeie o arquivo como index.jsp. Mantenha a pasta pai como WebContent, conforme mostrado na ilustração a seguir e selecione Avançar.

    Criar um novo arquivo JSP – tutorial de aplicativo Web Java

  5. Na caixa de diálogo Selecionar modelo JSP, selecione esse tutorial Novo Arquivo JSP (html) e, em seguida, Concluir.

  6. Quando o arquivo index.jsp for aberto no Eclipse, adicione o texto para exibir Olá, Mundo! dentro do elemento <body> existente. A atualização <body> do conteúdo deve se parecer com o código a seguir:

    <body>
      <% out.println("Hello World!"); %>
    </body>
    
  7. Salve o arquivo index.jsp.

  8. Se definir um runtime de destino na etapa 2, você pode selecionar Projeto e Executar para executar o aplicativo JSP localmente:

    Olá, Mundo – Tutorial de aplicativo Java

Instalar o SDK Java do SQL

É a maneira mais fácil de obter o SDK do Java do SQL e suas dependências por meio do Apache Maven. Para fazer isso, você precisa converter o projeto em um projeto do Maven usando as etapas a seguir:

  1. Clique com o botão direito no projeto no Explorador de Projetos, selecione Configurar e, em seguida, Converter em Projeto Maven.

  2. Na janela Criar novo POM, aceite os padrões e selecione Concluir.

  3. No Explorador de projeto, abra o arquivo pom.xml.

  4. Na guia Dependências, no painel Dependênciase selecione Adicionar.

  5. Na janela Selecionar dependência , faça o seguinte:

    • Na caixa ID do Grupo, digite com.azure.
    • Na caixa ID do Artefato, insira azure-cosmos.
    • Na caixa Versão, insira 4.11.0.

    Ou adicione o XML de dependência da ID do Grupo e da ID do Artefato diretamente ao arquivo pom.xml:

    <dependency>
      <groupId>com.azure</groupId>
      <artifactId>azure-cosmos</artifactId>
      <version>4.11.0</version>
    </dependency>
    
  6. Clique em OK e o Maven instalará o SDK Java do SQL ou salvará o arquivo pom.xml.

Usar o serviço do Azure Cosmos DB em seu aplicativo Java

Agora, vamos adicionar os modelos, as exibições e os controladores ao seu aplicativo Web.

Adicionar um modelo

Primeiro, vamos definir um modelo em um novo arquivo TodoItem.java. A classe TodoItem define o esquema de um item junto com os métodos getter e setter:

package com.microsoft.azure.cosmos.sample.model;

//@Data
//@Builder
public class TodoItem {
    private String entityType;
    private String category;
    private boolean complete;
    private String id;
    private String name;

    public String getCategory() {
        return category;
    }

    public void setCategory(String category) {
        this.category = category;
    }

    public String getEntityType() {
        return entityType;
    }

    public void setEntityType(String entityType) {
        this.entityType = entityType;
    }

    public boolean isComplete() {
        return complete;
    }

    public void setComplete(boolean complete) {
        this.complete = complete;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    
}

Adicionar as classes de DAO (Objeto de acesso a dados)

Crie um DAO (Objeto de acesso a dados) para abstrair mantendo os itens ToDo no Azure Cosmos DB. Para salvar os itens das tarefas em uma coleção, o cliente precisa saber qual banco de dados e coleção manter (como referenciado por self-links). Em geral, é melhor armazenar o banco de dados e a coleção em cache sempre que possível para evitar viagens adicionais ao banco de dados.

  1. Para invocar o serviço do Azure Cosmos DB, você precisa criar uma instância de um novo cosmosClient. Em geral, é melhor reutilizar o objeto cosmosClient em vez de construir um novo cliente para cada solicitação posterior. Você pode reutilizar o cliente definindo-o dentro da classe cosmosClientFactory. Atualize os valores de HOST e MASTER_KEY que você salvou na Etapa 1. Substitua a variável HOST pelo URI e substitua a MASTER_KEY pela sua CHAVE PRIMÁRIA. Use o código a seguir para criar a classe CosmosClientFactory dentro do arquivo CosmosClientFactory.java:

    package com.microsoft.azure.cosmos.sample.dao;
    
    import com.azure.cosmos.ConsistencyLevel;
    import com.azure.cosmos.CosmosClient;
    import com.azure.cosmos.CosmosClientBuilder;
    
    public class CosmosClientFactory {
        private static final String HOST = "[ACCOUNT HOST NAME]";
        private static final String MASTER_KEY = "[ACCOUNT KEY]";
    
        private static CosmosClient cosmosClient = new CosmosClientBuilder()
                .endpoint(HOST)
                .key(MASTER_KEY)
                .consistencyLevel(ConsistencyLevel.EVENTUAL)
                .buildClient();
    
        public static CosmosClient getCosmosClient() {
            return cosmosClient;
        }
    
    }
    
  2. Crie um arquivo TodoDao.java e adicione a classe TodoDao para criar, atualizar, ler e excluir os itens de tarefas pendentes:

    package com.microsoft.azure.cosmos.sample.dao;
    
    import java.util.List;
    
    import com.microsoft.azure.cosmos.sample.model.TodoItem;
    
    public interface TodoDao {
        /**
         * @return A list of TodoItems
         */
        public List<TodoItem> readTodoItems();
    
        /**
         * @param todoItem
         * @return whether the todoItem was persisted.
         */
        public TodoItem createTodoItem(TodoItem todoItem);
    
        /**
         * @param id
         * @return the TodoItem
         */
        public TodoItem readTodoItem(String id);
    
        /**
         * @param id
         * @return the TodoItem
         */
        public TodoItem updateTodoItem(String id, boolean isComplete);
    
        /**
         *
         * @param id
         * @return whether the delete was successful.
         */
        public boolean deleteTodoItem(String id);
    }
    
  3. Crie um arquivo MockDao.java e adicione a classe MockDao, essa classe implementa a classe TodoDao para executar operações CRUD nos itens:

    package com.microsoft.azure.cosmos.sample.dao;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    import lombok.NonNull;
    
    import com.microsoft.azure.cosmos.sample.model.TodoItem;
    
    public class MockDao implements TodoDao {
        private final Map<String, TodoItem> todoItemMap;
    
        public MockDao() {
            todoItemMap = new HashMap<String, TodoItem>();
        }
    
        @Override
        public TodoItem createTodoItem(@NonNull TodoItem todoItem) {
            if (todoItem.getId() == null || todoItem.getId().isEmpty()) {
                todoItem.setId(generateId());
            }
            todoItemMap.put(todoItem.getId(), todoItem);
            return todoItem;
        }
    
        @Override
        public TodoItem readTodoItem(@NonNull String id) {
            return todoItemMap.get(id);
        }
    
        @Override
        public List<TodoItem> readTodoItems() {
            return new ArrayList<TodoItem>(todoItemMap.values());
        }
    
        @Override
        public TodoItem updateTodoItem(String id, boolean isComplete) {
            todoItemMap.get(id).setComplete(isComplete);
            return todoItemMap.get(id);
        }
    
        @Override
        public boolean deleteTodoItem(@NonNull String id) {
            todoItemMap.remove(id);
            return true;
        }
    
        private String generateId() {
            return new Integer(todoItemMap.size()).toString();
        }
    }
    
  4. Crie um arquivo DocDbDao.java e adicione a classe DocDbDao. Essa classe define o código para persistir os TodoItems no contêiner, recupera o banco de dados e a coleção, se existir, ou cria um se ele não existe. Este exemplo usa Gson para serializar e desserializar POJOs (TodoItem Plain Old Java Objects) para documentos JSON. Para salvar os itens das tarefas em uma coleção, o cliente precisa saber qual banco de dados e coleção manter (como referenciado por self-links). Essa classe também define a função auxiliar para recuperar os documentos por outro atributo (por exemplo, "ID") em vez de se autovincular. Você pode usar o método auxiliar para recuperar um documento TodoItem JSON pela ID e, em seguida, desserializá-lo para um POJO.

    Você também pode usar o objeto cliente cosmosClient para obter uma coleção ou uma lista de TodoItems usando uma consulta SQL. Por fim, você define o método de exclusão para excluir um TodoItem da sua lista. O código a seguir mostra o conteúdo da classe DocDbDao:

    package com.microsoft.azure.cosmos.sample.dao;
    
    import com.azure.cosmos.CosmosClient;
    import com.azure.cosmos.CosmosContainer;
    import com.azure.cosmos.CosmosDatabase;
    import com.azure.cosmos.CosmosException;
    import com.azure.cosmos.implementation.Utils;
    import com.azure.cosmos.models.CosmosContainerProperties;
    import com.azure.cosmos.models.CosmosContainerResponse;
    import com.azure.cosmos.models.CosmosDatabaseResponse;
    import com.azure.cosmos.models.CosmosItemRequestOptions;
    import com.azure.cosmos.models.CosmosQueryRequestOptions;
    import com.azure.cosmos.models.FeedResponse;
    import com.azure.cosmos.models.PartitionKey;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.node.ObjectNode;
    import com.google.gson.Gson;
    import com.microsoft.azure.cosmos.sample.model.TodoItem;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class DocDbDao implements TodoDao {
        // The name of our database.
        private static final String DATABASE_ID = "TestDB";
    
        // The name of our collection.
        private static final String CONTAINER_ID = "TestCollection";
    
        // We'll use Gson for POJO <=> JSON serialization for this example.
        private static Gson gson = new Gson();
    
        // The Cosmos DB Client
        private static CosmosClient cosmosClient = CosmosClientFactory
            .getCosmosClient();
    
        // The Cosmos DB database
        private static CosmosDatabase cosmosDatabase = null;
    
        // The Cosmos DB container
        private static CosmosContainer cosmosContainer = null;
    
        // For POJO/JsonNode interconversion
        private static final ObjectMapper OBJECT_MAPPER = Utils.getSimpleObjectMapper();
    
        @Override
        public TodoItem createTodoItem(TodoItem todoItem) {
            // Serialize the TodoItem as a JSON Document.
    
            JsonNode todoItemJson = OBJECT_MAPPER.valueToTree(todoItem);
    
            ((ObjectNode) todoItemJson).put("entityType", "todoItem");
    
            try {
                // Persist the document using the DocumentClient.
                todoItemJson =
                    getContainerCreateResourcesIfNotExist()
                        .createItem(todoItemJson)
                        .getItem();
            } catch (CosmosException e) {
                System.out.println("Error creating TODO item.\n");
                e.printStackTrace();
                return null;
            }
    
    
            try {
    
                return OBJECT_MAPPER.treeToValue(todoItemJson, TodoItem.class);
                //return todoItem;
            } catch (Exception e) {
                System.out.println("Error deserializing created TODO item.\n");
                e.printStackTrace();
    
                return null;
            }
    
        }
    
        @Override
        public TodoItem readTodoItem(String id) {
            // Retrieve the document by id using our helper method.
            JsonNode todoItemJson = getDocumentById(id);
    
            if (todoItemJson != null) {
                // De-serialize the document in to a TodoItem.
                try {
                    return OBJECT_MAPPER.treeToValue(todoItemJson, TodoItem.class);
                } catch (JsonProcessingException e) {
                    System.out.println("Error deserializing read TODO item.\n");
                    e.printStackTrace();
    
                    return null;
                }
            } else {
                return null;
            }
        }
    
        @Override
        public List<TodoItem> readTodoItems() {
    
            List<TodoItem> todoItems = new ArrayList<TodoItem>();
    
            String sql = "SELECT * FROM root r WHERE r.entityType = 'todoItem'";
            int maxItemCount = 1000;
            int maxDegreeOfParallelism = 1000;
            int maxBufferedItemCount = 100;
    
            CosmosQueryRequestOptions options = new CosmosQueryRequestOptions();
            options.setMaxBufferedItemCount(maxBufferedItemCount);
            options.setMaxDegreeOfParallelism(maxDegreeOfParallelism);
            options.setQueryMetricsEnabled(false);
    
            int error_count = 0;
            int error_limit = 10;
    
            String continuationToken = null;
            do {
    
                for (FeedResponse<JsonNode> pageResponse :
                    getContainerCreateResourcesIfNotExist()
                        .queryItems(sql, options, JsonNode.class)
                        .iterableByPage(continuationToken, maxItemCount)) {
    
                    continuationToken = pageResponse.getContinuationToken();
    
                    for (JsonNode item : pageResponse.getElements()) {
    
                        try {
                            todoItems.add(OBJECT_MAPPER.treeToValue(item, TodoItem.class));
                        } catch (JsonProcessingException e) {
                            if (error_count < error_limit) {
                                error_count++;
                                if (error_count >= error_limit) {
                                    System.out.println("\n...reached max error count.\n");
                                } else {
                                    System.out.println("Error deserializing TODO item JsonNode. " +
                                        "This item will not be returned.");
                                    e.printStackTrace();
                                }
                            }
                        }
    
                    }
                }
    
            } while (continuationToken != null);
    
            return todoItems;
        }
    
        @Override
        public TodoItem updateTodoItem(String id, boolean isComplete) {
            // Retrieve the document from the database
            JsonNode todoItemJson = getDocumentById(id);
    
            // You can update the document as a JSON document directly.
            // For more complex operations - you could de-serialize the document in
            // to a POJO, update the POJO, and then re-serialize the POJO back in to
            // a document.
            ((ObjectNode) todoItemJson).put("complete", isComplete);
    
            try {
                // Persist/replace the updated document.
                todoItemJson =
                    getContainerCreateResourcesIfNotExist()
                        .replaceItem(todoItemJson, id, new PartitionKey(id), new CosmosItemRequestOptions())
                        .getItem();
            } catch (CosmosException e) {
                System.out.println("Error updating TODO item.\n");
                e.printStackTrace();
                return null;
            }
    
            // De-serialize the document in to a TodoItem.
            try {
                return OBJECT_MAPPER.treeToValue(todoItemJson, TodoItem.class);
            } catch (JsonProcessingException e) {
                System.out.println("Error deserializing updated item.\n");
                e.printStackTrace();
    
                return null;
            }
        }
    
        @Override
        public boolean deleteTodoItem(String id) {
            // CosmosDB refers to documents by self link rather than id.
    
            // Query for the document to retrieve the self link.
            JsonNode todoItemJson = getDocumentById(id);
    
            try {
                // Delete the document by self link.
                getContainerCreateResourcesIfNotExist()
                    .deleteItem(id, new PartitionKey(id), new CosmosItemRequestOptions());
            } catch (CosmosException e) {
                System.out.println("Error deleting TODO item.\n");
                e.printStackTrace();
                return false;
            }
    
            return true;
        }
    
        /*
        
        private CosmosDatabase getTodoDatabase() {
            if (databaseCache == null) {
                // Get the database if it exists
                List<CosmosDatabase> databaseList = cosmosClient
                        .queryDatabases(
                                "SELECT * FROM root r WHERE r.id='" + DATABASE_ID
                                        + "'", null).getQueryIterable().toList();
    
                if (databaseList.size() > 0) {
                    // Cache the database object so we won't have to query for it
                    // later to retrieve the selfLink.
                    databaseCache = databaseList.get(0);
                } else {
                    // Create the database if it doesn't exist.
                    try {
                        CosmosDatabase databaseDefinition = new CosmosDatabase();
                        databaseDefinition.setId(DATABASE_ID);
    
                        databaseCache = cosmosClient.createDatabase(
                                databaseDefinition, null).getResource();
                    } catch (CosmosException e) {
                        // TODO: Something has gone terribly wrong - the app wasn't
                        // able to query or create the collection.
                        // Verify your connection, endpoint, and key.
                        e.printStackTrace();
                    }
                }
            }
    
            return databaseCache;
        }
    
        */
    
        private CosmosContainer getContainerCreateResourcesIfNotExist() {
    
            try {
    
                if (cosmosDatabase == null) {
                    CosmosDatabaseResponse cosmosDatabaseResponse = cosmosClient.createDatabaseIfNotExists(DATABASE_ID);
                    cosmosDatabase = cosmosClient.getDatabase(cosmosDatabaseResponse.getProperties().getId());
                }
    
            } catch (CosmosException e) {
                // TODO: Something has gone terribly wrong - the app wasn't
                // able to query or create the collection.
                // Verify your connection, endpoint, and key.
                System.out.println("Something has gone terribly wrong - " +
                    "the app wasn't able to create the Database.\n");
                e.printStackTrace();
            }
    
            try {
    
                if (cosmosContainer == null) {
                    CosmosContainerProperties properties = new CosmosContainerProperties(CONTAINER_ID, "/id");
                    CosmosContainerResponse cosmosContainerResponse = cosmosDatabase.createContainerIfNotExists(properties);
                    cosmosContainer = cosmosDatabase.getContainer(cosmosContainerResponse.getProperties().getId());
                }
    
            } catch (CosmosException e) {
                // TODO: Something has gone terribly wrong - the app wasn't
                // able to query or create the collection.
                // Verify your connection, endpoint, and key.
                System.out.println("Something has gone terribly wrong - " +
                    "the app wasn't able to create the Container.\n");
                e.printStackTrace();
            }
    
            return cosmosContainer;
        }
    
        private JsonNode getDocumentById(String id) {
    
            String sql = "SELECT * FROM root r WHERE r.id='" + id + "'";
            int maxItemCount = 1000;
            int maxDegreeOfParallelism = 1000;
            int maxBufferedItemCount = 100;
    
            CosmosQueryRequestOptions options = new CosmosQueryRequestOptions();
            options.setMaxBufferedItemCount(maxBufferedItemCount);
            options.setMaxDegreeOfParallelism(maxDegreeOfParallelism);
            options.setQueryMetricsEnabled(false);
    
            List<JsonNode> itemList = new ArrayList();
    
            String continuationToken = null;
            do {
                for (FeedResponse<JsonNode> pageResponse :
                    getContainerCreateResourcesIfNotExist()
                        .queryItems(sql, options, JsonNode.class)
                        .iterableByPage(continuationToken, maxItemCount)) {
    
                    continuationToken = pageResponse.getContinuationToken();
    
                    for (JsonNode item : pageResponse.getElements()) {
                        itemList.add(item);
                    }
                }
    
            } while (continuationToken != null);
    
            if (itemList.size() > 0) {
                return itemList.get(0);
            } else {
                return null;
            }
        }
    
    }
    
  5. Em seguida, crie um arquivo TodoDaoFactory.java e adicione a classe TodoDaoFactory que cria um objeto DocDbDao:

    package com.microsoft.azure.cosmos.sample.dao;
    
    public class TodoDaoFactory {
        private static TodoDao myTodoDao = new DocDbDao();
    
        public static TodoDao getDao() {
            return myTodoDao;
        }
    }
    

Adicionar um controlador

Adicione o controlador TodoItemController ao seu aplicativo. Neste projeto, você está usando Project Lombok para gerar o construtor, os getters, os setters e um builder. Como alternativa, você pode escrever esse código manualmente ou o IDE pode gerá-lo:

package com.microsoft.azure.cosmos.sample.controller;

import java.util.List;
import java.util.UUID;

import lombok.NonNull;

import com.microsoft.azure.cosmos.sample.dao.TodoDao;
import com.microsoft.azure.cosmos.sample.dao.TodoDaoFactory;
import com.microsoft.azure.cosmos.sample.model.TodoItem;

public class TodoItemController {
    public static TodoItemController getInstance() {
        if (todoItemController == null) {
            todoItemController = new TodoItemController(TodoDaoFactory.getDao());
        }
        return todoItemController;
    }

    private static TodoItemController todoItemController;

    private final TodoDao todoDao;

    TodoItemController(TodoDao todoDao) {
        this.todoDao = todoDao;
    }

    public TodoItem createTodoItem(@NonNull String name,
            @NonNull String category, boolean isComplete) {
        TodoItem todoItem = new TodoItem();
        
        todoItem.setName(name);
        todoItem.setCategory(category);
        todoItem.setComplete(isComplete);
        todoItem.setId(UUID.randomUUID().toString());

        return todoDao.createTodoItem(todoItem);
    }

    public boolean deleteTodoItem(@NonNull String id) {
        return todoDao.deleteTodoItem(id);
    }

    public TodoItem getTodoItemById(@NonNull String id) {
        return todoDao.readTodoItem(id);
    }

    public List<TodoItem> getTodoItems() {
        return todoDao.readTodoItems();
    }

    public TodoItem updateTodoItem(@NonNull String id, boolean isComplete) {
        return todoDao.updateTodoItem(id, isComplete);
    }
}

Criar um servlet

Em seguida, crie um servlet para rotear solicitações HTTP para o controlador. Crie o arquivo ApiServlet.java e defina o seguinte código abaixo dele:

package com.microsoft.azure.cosmos.sample;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.gson.Gson;
import com.microsoft.azure.cosmos.sample.controller.TodoItemController;

/**
 * API Frontend Servlet
 */
@WebServlet("/api")
public class ApiServlet extends HttpServlet {
    // API Keys
    public static final String API_METHOD = "method";

    // API Methods
    public static final String CREATE_TODO_ITEM = "createTodoItem";
    public static final String GET_TODO_ITEMS = "getTodoItems";
    public static final String UPDATE_TODO_ITEM = "updateTodoItem";

    // API Parameters
    public static final String TODO_ITEM_ID = "todoItemId";
    public static final String TODO_ITEM_NAME = "todoItemName";
    public static final String TODO_ITEM_CATEGORY = "todoItemCategory";
    public static final String TODO_ITEM_COMPLETE = "todoItemComplete";

    public static final String MESSAGE_ERROR_INVALID_METHOD = "{'error': 'Invalid method'}";

    private static final long serialVersionUID = 1L;
    private static final Gson gson = new Gson();

    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {

        String apiResponse = MESSAGE_ERROR_INVALID_METHOD;

        TodoItemController todoItemController = TodoItemController
                .getInstance();

        String id = request.getParameter(TODO_ITEM_ID);
        String name = request.getParameter(TODO_ITEM_NAME);
        String category = request.getParameter(TODO_ITEM_CATEGORY);
        String itemComplete = request.getParameter(TODO_ITEM_COMPLETE);
        boolean isComplete = itemComplete!= null && itemComplete.equalsIgnoreCase("true");

        switch (request.getParameter(API_METHOD)) {
        case CREATE_TODO_ITEM:
            apiResponse = gson.toJson(todoItemController.createTodoItem(name,
                    category, isComplete));
            break;
        case GET_TODO_ITEMS:
            apiResponse = gson.toJson(todoItemController.getTodoItems());
            break;
        case UPDATE_TODO_ITEM:
            apiResponse = gson.toJson(todoItemController.updateTodoItem(id,
                    isComplete));
            break;
        default:
            break;
        }

        response.setCharacterEncoding("UTF-8");
        response.getWriter().println(apiResponse);
    }

    @Override
    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

Conecte o restante do aplicativo Java

Agora que concluímos a parte divertida, tudo que restou é criar uma interface do usuário rápida e conectá-la ao seu DAO.

  1. Você precisará de uma interface do usuário da Web para a exibição ao usuário. Vamos reescrever o index.jsp criado anteriormente com o seguinte código:

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge;" />
      <title>Azure Cosmos Java Sample</title>
    
      <!-- Bootstrap -->
      <link href="//ajax.aspnetcdn.com/ajax/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
    
      <style>
        /* Add padding to body for fixed nav bar */
        body {
          padding-top: 50px;
        }
      </style>
    </head>
    <body>
      <!-- Nav Bar -->
      <div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
        <div class="container">
          <div class="navbar-header">
            <a class="navbar-brand" href="#">My Tasks</a>
          </div>
        </div>
      </div>
    
      <!-- Body -->
      <div class="container">
        <h1>My ToDo List</h1>
    
        <hr/>
    
        <!-- The ToDo List -->
        <div class = "todoList">
          <table class="table table-bordered table-striped" id="todoItems">
            <thead>
              <tr>
                <th>Name</th>
                <th>Category</th>
                <th>Complete</th>
              </tr>
            </thead>
            <tbody>
            </tbody>
          </table>
    
          <!-- Update Button -->
          <div class="todoUpdatePanel">
            <form class="form-horizontal" role="form">
              <button type="button" class="btn btn-primary">Update Tasks</button>
            </form>
          </div>
    
        </div>
    
        <hr/>
    
        <!-- Item Input Form -->
        <div class="todoForm">
          <form class="form-horizontal" role="form">
            <div class="form-group">
              <label for="inputItemName" class="col-sm-2">Task Name</label>
              <div class="col-sm-10">
                <input type="text" class="form-control" id="inputItemName" placeholder="Enter name">
              </div>
            </div>
    
            <div class="form-group">
              <label for="inputItemCategory" class="col-sm-2">Task Category</label>
              <div class="col-sm-10">
                <input type="text" class="form-control" id="inputItemCategory" placeholder="Enter category">
              </div>
            </div>
    
            <button type="button" class="btn btn-primary">Add Task</button>
          </form>
        </div>
    
      </div>
    
      <!-- Placed at the end of the document so the pages load faster -->
      <script src="//ajax.aspnetcdn.com/ajax/jQuery/jquery-2.1.1.min.js"></script>
      <script src="//ajax.aspnetcdn.com/ajax/bootstrap/3.2.0/bootstrap.min.js"></script>
      <script src="assets/todo.js"></script>
    </body>
    </html>
    
  2. Finalmente, escreva algum JavaScript do lado do cliente para unir o servlet e a interface do usuário da Web:

    /**
     * ToDo App
     */
    
    var todoApp = {
      /*
       * API methods to call Java backend.
       */
      apiEndpoint: "api",
    
      createTodoItem: function(name, category, isComplete) {
        $.post(todoApp.apiEndpoint, {
            "method": "createTodoItem",
            "todoItemName": name,
            "todoItemCategory": category,
            "todoItemComplete": isComplete
          },
          function(data) {
            var todoItem = data;
            todoApp.addTodoItemToTable(todoItem.id, todoItem.name, todoItem.category, todoItem.complete);
          },
          "json");
      },
    
      getTodoItems: function() {
        $.post(todoApp.apiEndpoint, {
            "method": "getTodoItems"
          },
          function(data) {
            var todoItemArr = data;
            $.each(todoItemArr, function(index, value) {
              todoApp.addTodoItemToTable(value.id, value.name, value.category, value.complete);
            });
          },
          "json");
      },
    
      updateTodoItem: function(id, isComplete) {
        $.post(todoApp.apiEndpoint, {
            "method": "updateTodoItem",
            "todoItemId": id,
            "todoItemComplete": isComplete
          },
          function(data) {},
          "json");
      },
    
      /*
       * UI Methods
       */
      addTodoItemToTable: function(id, name, category, isComplete) {
        var rowColor = isComplete ? "active" : "warning";
    
        todoApp.ui_table().append($("<tr>")
          .append($("<td>").text(name))
          .append($("<td>").text(category))
          .append($("<td>")
            .append($("<input>")
              .attr("type", "checkbox")
              .attr("id", id)
              .attr("checked", isComplete)
              .attr("class", "isComplete")
            ))
          .addClass(rowColor)
        );
      },
    
      /*
       * UI Bindings
       */
      bindCreateButton: function() {
        todoApp.ui_createButton().click(function() {
          todoApp.createTodoItem(todoApp.ui_createNameInput().val(), todoApp.ui_createCategoryInput().val(), false);
          todoApp.ui_createNameInput().val("");
          todoApp.ui_createCategoryInput().val("");
        });
      },
    
      bindUpdateButton: function() {
        todoApp.ui_updateButton().click(function() {
          // Disable button temporarily.
          var myButton = $(this);
          var originalText = myButton.text();
          $(this).text("Updating...");
          $(this).prop("disabled", true);
    
          // Call api to update todo items.
          $.each(todoApp.ui_updateId(), function(index, value) {
            todoApp.updateTodoItem(value.name, value.value);
            $(value).remove();
          });
    
          // Re-enable button.
          setTimeout(function() {
            myButton.prop("disabled", false);
            myButton.text(originalText);
          }, 500);
        });
      },
    
      bindUpdateCheckboxes: function() {
        todoApp.ui_table().on("click", ".isComplete", function(event) {
          var checkboxElement = $(event.currentTarget);
          var rowElement = $(event.currentTarget).parents('tr');
          var id = checkboxElement.attr('id');
          var isComplete = checkboxElement.is(':checked');
    
          // Togle table row color
          if (isComplete) {
            rowElement.addClass("active");
            rowElement.removeClass("warning");
          } else {
            rowElement.removeClass("active");
            rowElement.addClass("warning");
          }
    
          // Update hidden inputs for update panel.
          todoApp.ui_updateForm().children("input[name='" + id + "']").remove();
    
          todoApp.ui_updateForm().append($("<input>")
            .attr("type", "hidden")
            .attr("class", "updateComplete")
            .attr("name", id)
            .attr("value", isComplete));
    
        });
      },
    
      /*
       * UI Elements
       */
      ui_createNameInput: function() {
        return $(".todoForm #inputItemName");
      },
    
      ui_createCategoryInput: function() {
        return $(".todoForm #inputItemCategory");
      },
    
      ui_createButton: function() {
        return $(".todoForm button");
      },
    
      ui_table: function() {
        return $(".todoList table tbody");
      },
    
      ui_updateButton: function() {
        return $(".todoUpdatePanel button");
      },
    
      ui_updateForm: function() {
        return $(".todoUpdatePanel form");
      },
    
      ui_updateId: function() {
        return $(".todoUpdatePanel .updateComplete");
      },
    
      /*
       * Install the TodoApp
       */
      install: function() {
        todoApp.bindCreateButton();
        todoApp.bindUpdateButton();
        todoApp.bindUpdateCheckboxes();
    
        todoApp.getTodoItems();
      }
    };
    
    $(document).ready(function() {
      todoApp.install();
    });
    
  3. Agora tudo o que resta é testar o aplicativo. Executar o aplicativo localmente e adicionar alguns itens de tarefas, preenchendo o nome do item e a categoria e clicando em Adicionar tarefa. Quando o item aparecer, você poderá atualizar se ele está concluído alternando a caixa de seleção e clicando em Atualizar Tarefas.

Implantar seu aplicativo Java em sites do Azure

Sites do Azure tornam a implantação de aplicativos Java tão simples quanto a exportação de seu aplicativo como um arquivo WAR e por carregamento ou por meio do controle de origem (por exemplo, Git) ou FTP.

  1. Para exportar seu aplicativo como um arquivo WAR, clique com o botão direito em seu projeto no Explorador de Projeto, selecione Exportar e, em seguida, Arquivo WAR.

  2. Na janela Exportar WAR , faça o seguinte:

    • Na caixa do projeto Web, insira azure-cosmos-java-sample.
    • Na caixa Destino, escolha um destino para salvar o arquivo WAR.
    • Selecione Concluir.
  3. Agora que tem um arquivo WAR em mãos, você pode simplesmente carregá-lo no seu diretório webapps do site do Azure. Para obter instruções sobre como carregar o arquivo, confira Adicionar um aplicativo Java aos Aplicativos Web do Serviço de Aplicativo do Azure. Uma vez carregado o arquivo WAR na pasta webapps, o ambiente de runtime detectará que você o adicionou e o carregará automaticamente.

  4. Para exibir seu produto final, navegue até http://YOUR\_SITE\_NAME.azurewebsites.net/azure-cosmos-java-sample/ e comece a adicionar tarefas!

Obtenha o projeto do GitHub

Todos os exemplos neste tutorial foram incluídos no projeto tarefas no GitHub. Para importar o projeto de tarefas no Eclipse, certifique-se de ter o software e os recursos listados na seção pré-requisitos e, em seguida, faça o seguinte:

  1. Instalar Project Lombok. Lombok é usado para gerar construtores, getters e setters no projeto. Depois que você baixou o arquivo lombok.jar, clique duas vezes nele para instalá-lo ou instalá-lo a partir da linha de comando.

  2. Se o Eclipse estiver aberto, feche-o e reinicie-o para carregar o Lombok.

  3. No Eclipse, no menu Arquivo, clique em Importar.

  4. Na janela Importar, selecione Git, Projetos do Git e, em seguida, Avançar.

  5. Na tela Selecionar origem de repositório, selecione Clonar URI.

  6. Na tela Repositório Git de Origem, na caixa URI, digite https://github.com/Azure-Samples/azure-cosmos-java-sql-api-todo-appe, em seguida, clique em Avançar.

  7. Na tela Seleção de Branch, verifique se principal está selecionado e clique em Avançar.

  8. Na tela Destino Local, clique em Procurar para selecionar uma pasta onde o repositório possa ser copiado e clique em Avançar.

  9. Na tela Selecionar um assistente a ser usado para importar projetos, verifique se Importar projetos existentes está selecionado e selecione Avançar.

  10. Na tela Importar projetos, desmarque o projeto do DocumentDB e selecione Concluir. O projeto DocumentDB contém o SDK Java do Azure Cosmos DB, que adicionaremos como uma dependência em seu lugar.

  11. No Explorador de Projeto, navegue para azure-cosmos-java-sample\src\com.microsoft.azure.cosmos.sample.dao\DocumentClientFactory.java e substitua os valores HOST e MASTER_KEY pela URI e a CHAVE PRIMÁRIA da sua conta do Azure Cosmos DB e salve o arquivo. Para obter mais informações, confira Etapa 1. Criar uma conta de banco de dados do Azure Cosmos DB.

  12. Em Explorador de Projeto, clique com o botão direito do mouse em azure-cosmos-java-sample, selecione Caminho de Build e, em seguida, Configurar Caminho de Build.

  13. Na tela Caminho de Build Java, no painel direito, selecione a guia Bibliotecas e selecione Adicionar JARs Externos. Navegue até o local do arquivo lombok.jar, selecioneAbrir e, em seguida,OK.

  14. Use a Etapa 12 para abrir a janela Propriedades novamente e, no painel esquerdo, selecione Runtimes Direcionados.

  15. Na tela Runtimes Direcionados, selecione Novo, Apache Tomcat v7.0 e, em seguida, OK.

  16. Use a Etapa 12 para abrir a janela Propriedades novamente e, no painel esquerdo, clique em Facetas do Projeto.

  17. Na tela Facetas do Projeto, selecione Módulo da Web Dinâmico e Java e clique em OK.

  18. Na guia Servidores na parte inferior da tela, clique com o botão direito do mouse em Servidor Tomcat v7.0 no localhost e clique em Adicionar e Remover.

  19. Na janela Adicionar e Remover, mova azure-cosmos-java-sample para a caixa Configurado e clique em Concluir.

  20. Na guia Servidores, clique com o botão direito do mouse em Servidor Tomcat v7.0 no localhost e clique em Reiniciar.

  21. Em um navegador, navegue até http://localhost:8080/azure-cosmos-java-sample/ e comece a adicionar à sua lista de tarefas. Observe que, se você alterou os valores da porta padrão, altere a 8080 para o valor selecionado.

  22. Para implantar o projeto em um site do Azure, consulte a Etapa 6. Implante o aplicativo nos Sites do Azure.

Próximas etapas

Tentando fazer um planejamento de capacidade para uma migração para o Microsoft Azure Cosmos DB? Você pode usar informações sobre o cluster de banco de dados existente para fazer isso.