Tutorial: Criar um aplicativo Web Java usando o Azure Cosmos DB e a API para NoSQL

APLICA-SE A: NoSQL

Este tutorial de aplicação Web do Java mostra-lhe como utilizar o serviço do Microsoft Azure Cosmos DB para armazenar e aceder a dados a partir de uma aplicação Java alojada em Aplicações Web do Serviço de Aplicações do Azure. Sem um cartão de crédito ou uma assinatura do Azure, você pode configurar uma conta gratuita Experimente o Azure Cosmos DB. Neste artigo, vai aprender:

  • Como criar uma aplicação JSP (JavaServer Pages) básica no Eclipse.
  • Como trabalhar com o serviço do Azure Cosmos DB com o SDK Java do Azure Cosmos DB.

Este tutorial de aplicação Java mostra-lhe como criar uma aplicação de gestão de tarefas baseada na Web que lhe permite criar, obter e marcar tarefas como concluídas, conforme mostrado na imagem que se segue. Cada uma das tarefas da lista ToDo é armazenada como documentos JSON no Azure Cosmos DB.

A minha aplicação Java da ToDo List

Gorjeta

Este tutorial do desenvolvimento de aplicações parte do princípio que tem experiência na utilização do Java. Se estiver familiarizado com o Java ou com as ferramentas dos pré-requisitos, recomendamos-lhe transferir todo o projeto todo a partir do GitHub e criá-lo utilizando as instruções no final deste artigo. Assim que o tiver criado, pode rever o artigo para obter conhecimentos aprofundados sobre o código no contexto do projeto.

Pré-requisitos para este tutorial de aplicação Web de Java

Antes de começar este tutorial de desenvolvimento de aplicação, tem de ter o seguinte:

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

Criar uma conta do Azure Cosmos DB

Comecemos por criar uma conta do Azure Cosmos DB. Se já tiver uma conta ou se estiver a utilizar o Emulador do Azure Cosmos DB para este tutorial, pode avançar para o Passo 2: Criar a aplicação Java JSP.

  1. A partir do menu do portal do Azure ou a partir da Home page, selecione Criar um recurso.

  2. Procure o 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 para NoSQL.

    O Azure Cosmos DB fornece várias APIs:

    • NoSQL, para dados de documentos
    • PostgreSQL
    • MongoDB, para dados de documentos
    • Apache Cassandra
    • Tabela
    • Apache Gremlin, para dados gráficos

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

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

    Definição valor Description
    Subscrição Nome da subscrição Selecione a subscrição do Azure que pretende utilizar para esta conta do Azure Cosmos DB.
    Grupo de Recursos Nome do grupo de recursos Selecione um grupo de recursos ou selecione Criar novo e, em seguida, introduza um nome exclusivo para o novo grupo de recursos.
    Nome da Conta Um nome exclusivo Insira um nome para identificar sua conta do Azure Cosmos DB. Uma vez que documents.azure.com é anexado ao nome que indicar para criar o URI, utilize um nome exclusivo. O nome pode conter apenas letras minúsculas, números e o caractere hífen (-). Deve ter de 3 a 44 caracteres.
    Location A região mais próxima dos seus utilizadores Selecione a localização geográfica para alojar a sua conta do Azure Cosmos DB. Utilize a localização mais próxima dos utilizadores para lhes dar o 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 de taxa de transferência provisionada. Selecione Serverless para criar uma conta no modo serverless .
    Aplicar desconto de nível gratuito do Azure Cosmos DB Candidatar-se ou Não aplicar Com o nível gratuito do Azure Cosmos DB, você obtém os primeiros 1000 RU/s e 25 GB de armazenamento gratuitamente 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 de taxa de transferência que pode ser provisionada nessa conta. Esse limite evita cobranças inesperadas relacionadas à 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 optar por participar ao criar a conta. Se você não vir a opção de aplicar o desconto de nível gratuito, outra conta na assinatura já foi habilitada com o nível gratuito.

    A captura de tela mostra a página Criar Conta do Azure Cosmos DB.

    Nota

    As seguintes opções não estarão disponíveis se você selecionar Serverless como o modo de Capacidade:

    • Aplicar Desconto de Escalão Gratuito
    • Limitar a taxa de transferência total da conta
  5. Na guia Distribuição Global, configure os seguintes detalhes. Você pode deixar os valores padrão para este início rápido:

    Definição valor Description
    Georredundância Desativar Habilite ou desative a distribuição global em sua conta emparelhando sua região com uma região par. Pode adicionar mais regiões à sua conta mais tarde.
    Escritas de várias regiões Desativar O recurso de gravação em 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 Disponibilidade Desativar As zonas de disponibilidade ajudam a melhorar ainda mais a disponibilidade e a resiliência do seu aplicativo.

    Nota

    As opções a seguir não estarão disponíveis se você selecionar Serverless como o modo de capacidade na página Noções básicas anterior:

    • Georredundância
    • Escritas 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 a política de backup periódico ou contínuo .
    • Encriptação. Use uma chave gerenciada pelo serviço ou uma chave gerenciada pelo cliente.
    • Etiquetas. As etiquetas são pares nome/valor que permitem categorizar recursos e ver faturação consolidada aplicando a mesma etiqueta a múltiplos recursos e grupos de recursos.
  7. Selecione Rever + criar.

  8. Reveja as definições da conta e, em seguida, selecione Criar. A criação da conta demora alguns minutos. Aguarde até que a página do portal apresente A implementação está concluída.

    A captura de tela mostra que sua implantação foi concluída.

  9. Selecione Ir para recurso para aceder à página da conta do Azure Cosmos DB.

    A captura de tela mostra a página da conta do Azure Cosmos DB.

Vá para a página da conta do Azure Cosmos DB e selecione Chaves. Copie os valores a serem usados no aplicativo Web que você criar a seguir.

Captura de ecrã do portal do Azure com o botão Chaves realçado na página da conta do Azure Cosmos DB

Criar o aplicativo Java JSP

Para criar a aplicação JSP:

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

    Desenvolvimento de aplicações de Java JSP

  2. Insira um nome de projeto na caixa Nome do projeto e, no menu suspenso Target Runtime, selecione opcionalmente um valor (por exemplo, Apache Tomcat v7.0) e, em seguida, selecione Concluir. Selecionar um tempo de execução de destino permite-lhe executar o seu projeto localmente através do Eclipse.

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

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

    Criar um novo ficheiro JSP - Tutorial de aplicações Web de Java

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

  6. Quando o arquivo index.jsp for aberto no Eclipse, adicione texto para exibir Hello World! dentro do elemento existente <body> . O conteúdo <body> atualizado deverá ser semelhante ao seguinte código:

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

  8. Se você definir um tempo de execução de destino na etapa 2, poderá selecionar Projeto e, em seguida , Executar para executar seu aplicativo JSP localmente:

    Olá, Mundo – Tutorial de Aplicações Java

Instalar o SQL Java SDK

A forma mais fácil para obter o SDK do SQL Java e as respetivas dependências é através do Apache Maven. Para fazer isso, você precisa converter seu projeto em um projeto Maven usando as seguintes etapas:

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

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

  3. No Explorador de Projeto, abra o ficheiro pom.xml.

  4. Na guia Dependências, no painel Dependências, 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, digite azure-cosmos.
    • Na caixa Versão, digite 4.11.0.

    Ou, você pode adicionar o XML de dependência para ID de Grupo e ID de Artefato diretamente ao arquivo pom.xml :

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

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

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

Adicionar um modelo

Primeiro, vamos definir um modelo dentro de um novo arquivo TodoItem.java. A TodoItem classe 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 Data Access Object (DAO)

Crie um Data Access Object (DAO) para abstrair os itens ToDo persistentes para o Azure Cosmos DB. Para guardar os itens ToDo numa coleção, o cliente tem de saber qual a base de dados e a coleção a manter (conforme referenciado em auto-ligações). Em geral, é melhor colocar em cache a base de dados e a coleção sempre que possível, para evitar tempos de retorno adicionais para a base de dados.

  1. Para invocar o serviço Azure Cosmos DB, você deve instanciar um novo cosmosClient objeto. Em geral, é melhor reutilizar o cosmosClient objeto em vez de construir um novo cliente para cada solicitação subsequente. Você pode reutilizar o cliente definindo-o dentro da cosmosClientFactory classe. Atualize os valores HOST e MASTER_KEY que você salvou na etapa 1. Substitua a variável HOST pelo URI e substitua o MASTER_KEY pela CHAVE PRIMÁRIA. Use o código a seguir para criar a CosmosClientFactory classe 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 novo arquivo TodoDao.java e adicione a TodoDao classe para criar, atualizar, ler e excluir os itens todo:

    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 novo arquivo MockDao.java e adicione a MockDao classe, essa classe implementa a TodoDao classe 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 novo arquivo de DocDbDao.java e adicione a DocDbDao classe. Essa classe define o código para persistir o TodoItems no contêiner, recupera seu banco de dados e coleção, se existir, ou cria um novo, se ele não existir. Este exemplo usa Gson para serializar e desserializar os POJOs (TodoItem Plain Old Java Objects) para documentos JSON. Para guardar os itens ToDo numa coleção, o cliente tem de saber qual a base de dados e a coleção a manter (conforme referenciado em auto-ligações). Esta classe também define a função auxiliar para recuperar os documentos por outro atributo (por exemplo, "ID") em vez de auto-link. Você pode usar o método auxiliar para recuperar um documento JSON TodoItem por ID e, em seguida, desserializá-lo para um POJO.

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

    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 novo arquivo de TodoDaoFactory.java e adicione a TodoDaoFactory classe que cria um novo 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, está a utilizar o Project Lombok para gerar o construtor, os getters, os setters e um construtor. Como alternativa, você pode escrever esse código manualmente ou fazer com que o IDE o gere:

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 sob ele:

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 terminamos os bits divertidos, tudo o que resta é construir uma interface de usuário rápida e conectá-la ao seu DAO.

  1. Você precisa de uma interface de usuário da Web para exibir ao usuário. Vamos reescrever o index.jsp que criamos 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 um JavaScript do lado do cliente para unir a interface do usuário da Web e o servlet:

    /**
     * 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, só falta testar a aplicação. Execute localmente a aplicação e adicione alguns itens Todo ao indicar o nome e a categoria do item. De seguida, clique em Adicionar Tarefa. Depois que o item for exibido, 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

Os Sites do Azure tornam a implementação de aplicações Java tão simples como a exportação da sua aplicação com um ficheiro WAR e ao carregá-lo através do controlo de código fonte (por exemplo, Git) ou FTP.

  1. Para exportar seu aplicativo como um arquivo WAR, clique com o botão direito do mouse em seu projeto no Project Explorer, selecione Exportar e, em seguida, selecione Arquivo WAR.

  2. Na janela Exportar WAR, efetue o seguinte procedimento:

    • Na caixa Projeto Web, insira azure-cosmos-java-sample.
    • Na caixa Destino, selecione um destino para guardar o ficheiro WAR.
    • Selecione Concluir.
  3. Agora que tem um ficheiro WAR, pode simplesmente carregá-lo no diretório webapps do seu Site do Azure. Para obter instruções sobre o carregamento do ficheiro, veja Adicionar uma aplicação Java às Aplicações Web do Serviço de Aplicações do Azure. Depois que o arquivo WAR for carregado no diretório webapps, o ambiente de tempo de execução detetará que você o adicionou e o carregará automaticamente.

  4. Para ver o seu produto concluído, navegue para http://YOUR\_SITE\_NAME.azurewebsites.net/azure-cosmos-java-sample/ e comece a adicionar as suas tarefas!

Obter o projeto a partir do GitHub

Todos os exemplos deste tutorial estão incluídos no projeto todo no GitHub. Para importar o projeto todo para o Eclipse, certifique-se de que o software e os recursos estão listados na secção Pré-requisitos e, de seguida, efetue o seguinte procedimento:

  1. Instalar Project Lombok. Lombok é utilizado para gerar construtores, getters e setters no projeto. Depois de ter transferido o ficheiro lombok.jar, faça duplo clique para instalá-lo ou instale-o a partir da linha de comandos.

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

  3. No Eclipse, no menu Arquivo , selecione Importar.

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

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

  6. Na tela Source Git Repository, na caixa URI, digite https://github.com/Azure-Samples/azure-cosmos-java-sql-api-todo-appe, em seguida, selecione Next.

  7. No ecrã Seleção de Ramo, certifique-se de que a opção principal está selecionada e, em seguida, selecione Seguinte.

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

  9. Na tela Selecione um assistente para usar para importar projetos, verifique se a opção Importar projetos existentes está selecionada e selecione Avançar.

  10. Na tela Importar Projetos, desmarque o projeto do Banco de Dados de Documentos e selecione Concluir. O projeto do DocumentDB contém o SDK Java do Azure Cosmos DB, que iremos adicionar como uma dependência.

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

  12. No Explorador de Projetos, clique com o botão direito do mouse no azure-cosmos-java-sample, selecione Caminho de Construção e, em seguida, selecione Configurar Caminho de Construção.

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

  14. Use a etapa 12 para abrir a janela Propriedades novamente e, no painel esquerdo, selecione Tempos de execução direcionados.

  15. Na tela Targeted Runtimes, selecione New, Apache Tomcat v7.0 e OK.

  16. Use a etapa 12 para abrir a janela Propriedades novamente e, no painel esquerdo, selecione Facetas do projeto.

  17. Na tela Facetas do projeto, selecione Módulo Web dinâmico e Java e, em seguida, selecione OK.

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

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

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

  21. Num browser, navegue para http://localhost:8080/azure-cosmos-java-sample/ e comece a adicionar à sua lista de tarefas. Tenha em atenção que se tiver alterado os valores de porta predefinidos, altere 8080 para o valor que selecionou.

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

Próximos passos

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