Tutoriel : Créer une application web Java à l’aide d’Azure Cosmos DB et de l’API pour NoSQL

S’APPLIQUE À : NoSQL

Ce didacticiel d’application web Java explique comment utiliser le service Microsoft Azure Cosmos DB pour stocker les données et y accéder à partir d’une application Java hébergée sur Azure App Service Web Apps. Sans carte de crédit ou abonnement Azure, vous pouvez configurer un compte d’essai Azure Cosmos DB gratuit. Cet article portera sur les éléments suivants :

Ce didacticiel d’application Java vous montre comment créer une application de gestion des tâches basée sur le web qui vous permet de créer, récupérer et marquer des tâches comme terminées, comme illustré dans l’image suivante. Chacune des tâches dans la liste ToDo est stockée sous forme de documents JSON dans Azure Cosmos DB.

Application Java My ToDo List

Conseil

Ce didacticiel de développement d’applications part du principe que vous avez déjà utilisé Java. Si vous débutez avec Java ou avec les outils requis, nous vous recommandons de télécharger l’ensemble du projet todo sur GitHub et de le générer à l’aide des instructions fournies à la fin de cet article. Une fois que vous l'avez créé, vous pouvez consulter l'article pour obtenir des informations sur le code dans le contexte du projet.

Conditions préalables à l’exécution de ce didacticiel d’application web Java

Avant de commencer ce didacticiel de développement d’applications, vous devez disposer des éléments suivants :

Si vous installez ces outils pour la première fois, coreservlets.com fournit un guide pas à pas de la procédure d’installation dans la section de démarrage rapide de son Tutoriel : Installation de TomCat7 et son utilisation avec Eclipse.

Création d’un compte Azure Cosmos DB

Commençons par créer un compte Azure Cosmos DB. Si vous possédez déjà un compte ou si vous utilisez l’émulateur Azure Cosmos DB pour ce didacticiel, vous pouvez passer à l’étape 2 : Créer l’application JSP Java.

  1. Dans le menu du portail Azure ou dans la page d’accueil, sélectionnez Créer une ressource.

  2. Rechercher Azure Cosmos DB. Sélectionnez Créer>Azure Cosmos DB.

  3. Dans la page Création d’un compte Azure Cosmos DB, sélectionnez l’option Créer dans la section Azure Cosmos DB for NoSQL.

    Azure Cosmos DB fournit plusieurs API :

    • NoSQL, pour des données de document
    • PostgreSQL
    • MongoDB, pour des données de document
    • Apache Cassandra
    • Table de charge de travail
    • Apache Gremlin, pour des données graphiques

    Pour en savoir plus sur l’API pour NoSQL, consultez Bienvenue dans Azure Cosmos DB.

  4. Sur la page Créer un compte Azure Cosmos DB, entrez les paramètres de base du nouveau compte Azure Cosmos DB.

    Paramètre Valeur Description
    Abonnement Nom d’abonnement Sélectionnez l’abonnement Azure que vous souhaitez utiliser pour ce compte Azure Cosmos DB.
    Groupe de ressources Nom de groupe ressources Sélectionnez un groupe de ressources ou sélectionnez Créer, puis entrez un nom unique pour le nouveau groupe de ressources.
    Nom du compte Un nom unique Entrez un nom pour identifier votre compte Azure Cosmos DB. Étant donné que documents.azure.com est ajouté au nom que vous fournissez pour créer votre URI, utilisez un nom unique. Le nom ne peut contenir que des lettres minuscules, des chiffres et le caractère trait d’union (-). Il doit comporter entre 3 et 44 caractères.
    Emplacement La région la plus proche de vos utilisateurs Sélectionnez la zone géographique dans laquelle héberger votre compte Azure Cosmos DB. Utilisez l’emplacement le plus proche de vos utilisateurs pour leur donner l’accès le plus rapide possible aux données.
    Mode de capacité Débit approvisionné ou Serverless Sélectionnez Débit approvisionné pour créer un compte dans mode de débit approvisionné. Sélectionnez serverless pour créer un compte en mode serverless.
    Appliquer la remise de niveau gratuit Azure Cosmos DB Appliquer ou Ne pas appliquer Avec le niveau gratuit d’Azure Cosmos DB, vous recevez gratuitement 1000 RU/s et 25 Go de stockage dans un compte. Découvrez-en plus sur le niveau gratuit.
    Limiter le débit total du compte Sélectionné ou non Limiter la quantité totale de débit pouvant être approvisionné sur ce compte. Cette limite empêche les frais inattendus liés au débit approvisionné. Vous pouvez mettre à jour ou supprimer cette limite une fois votre compte créé.

    Vous pouvez avoir un seul compte Azure Cosmos DB de niveau gratuit par abonnement Azure et vous devez vous inscrire lors de la création du compte. Si vous ne voyez pas l’option permettant d’appliquer la remise de niveau gratuit, cela signifie qu’un autre compte dans l’abonnement a déjà été activé avec le niveau gratuit.

    Capture d’écran montrant la page Créer un compte Azure Cosmos DB.

    Notes

    Les options suivantes ne sont pas disponibles si vous sélectionnez Serverless comme Mode de capacité :

    • Appliquer la remise de niveau gratuit
    • Limiter le débit total du compte
  5. Sous l’onglet Distribution globale, configurez les informations suivantes. Pour ce démarrage rapide, vous pouvez conserver les valeurs par défaut :

    Paramètre Valeur Description
    Géoredondance Désactiver Activez ou désactivez la diffusion mondiale sur votre compte en appairant votre région avec une région correspondante. Vous pourrez ajouter d’autres régions à votre compte ultérieurement.
    Écritures multirégions Désactiver La fonctionnalité d’écritures multirégions vous permet de tirer parti du débit provisionné pour vos bases de données et conteneurs à travers le monde.
    Zones de disponibilité Désactiver Les zones de disponibilité vous permettent d’accroître la disponibilité et d’améliorer la résilience de votre application.

    Notes

    Les options suivantes ne sont pas disponibles si vous sélectionnez Serverless comme Mode de capacité sur la page Informations de base précédente :

    • Géo-redondance
    • Écritures multirégions
  6. Si vous le souhaitez, vous pouvez configurer des informations supplémentaires sous les onglets suivants :

    • Mise en réseau. Configurez l’accès à partir d’un réseau virtuel.
    • Stratégie de sauvegarde. Configurez une stratégie de sauvegarde périodique ou continue.
    • Chiffrement. Utilisez une clé gérée par le service ou une clé gérée par le client.
    • Tags (balises). Les étiquettes sont des paires nom/valeur qui vous permettent de catégoriser les ressources et d’afficher une facturation centralisée en appliquant la même étiquette à plusieurs ressources et groupes de ressources.
  7. Sélectionnez Revoir + créer.

  8. Passez en revue les paramètres du compte, puis sélectionnez Créer. La création du compte prend quelques minutes. Attendez que la page du portail affiche Votre déploiement est terminé.

    Capture d’écran montrant que votre déploiement est terminé.

  9. Sélectionnez Accéder à la ressource pour accéder à la page du compte Azure Cosmos DB.

    Capture d’écran montrant la page du compte Azure Cosmos DB.

Accédez à la page du compte Azure Cosmos DB, puis sélectionnez Clés. Copiez les valeurs à utiliser dans l’application web que vous créez ensuite.

Capture d’écran du portail Azure avec le bouton Clés mis en surbrillance dans la page du compte Azure Cosmos DB

Créer l’application JSP Java

Pour créer l'application JSP :

  1. Tout d’abord, nous allons créer un projet Java. Démarrez Eclipse, puis sélectionnez Fichier, Nouveau, puis Projet web dynamique. Si Project web dynamique ne figure pas dans la liste des projets disponibles, procédez comme suit : sélectionnez Fichier, Nouveau, puis Projet..., développez Web, sélectionnez Project web dynamique, puis cliquez sur Suivant.

    Développement d’applications Java JSP

  2. Entrez un nom de projet dans la zone Project name (Nom du projet) et sélectionnez Target Runtime (Runtime cible) dans le menu déroulant. Vous pouvez aussi sélectionner une valeur (par exemple, Apache Tomcat v7.0), puis sélectionnez Finish (Terminer). La sélection d'un runtime cible vous permet d'exécuter votre projet localement via Eclipse.

  3. Dans la vue de l'Explorateur de projets Eclipse, développez votre projet. Cliquez avec le bouton droit sur WebContent, sélectionnez Nouveau, puis Fichier JSP.

  4. Dans la boîte de dialogue New JSP File (Nouveau fichier JSP), nommez le fichier index.jsp. Gardez le nom du dossier parent WebContent, comme l’indique l’illustration ci-dessous, puis sélectionnez Next (Suivant).

    Créer un fichier JSP - Tutoriel d’application web Java

  5. Pour les besoins de ce didacticiel, dans la boîte de dialogue Select JSP Template (Sélectionner le modèle JSP), sélectionnez New JSP File (HTML) (Nouveau fichier JSP (HTML)) et sélectionnez Finish (Terminer).

  6. Quand le fichier index.jsp s'ouvre dans Eclipse, ajoutez du texte pour afficher Hello World! dans l'élément <body> existant. Le contenu <body> mis à jour doit se présenter de la façon suivante :

    <body>
      <% out.println("Hello World!"); %>
    </body>
    
  7. Enregistrez le fichier index.jsp.

  8. Si vous avez défini un runtime cible à l’étape 2, vous pouvez sélectionner Project (Projet), puis sur Run (Exécuter) pour exécuter votre application JSP localement :

    Hello World – Tutoriel sur les applications Java

Installer le Kit de développement logiciel (SDK) Java SQL

Le moyen le plus simple d'extraire le Kit SDK Java SQL et ses dépendances est d'utiliser Apache Maven. Pour ce faire, vous devez convertir votre projet en projet Maven en effectuant les étapes suivantes :

  1. Cliquez avec le bouton droit sur votre projet dans l’Explorateur de projets, sélectionnez Configure (Configurer) et Convert to Maven Project (Convertir en projet Maven).

  2. Dans la fenêtre Create new POM (Créer un nouveau POM), acceptez les valeurs par défaut et sélectionnez Finish (Terminer).

  3. Dans l' Explorateur de projets, ouvrez le fichier pom.xml.

  4. Sous l’onglet Dependencies (Dépendances) du volet Dependencies (Dépendances), sélectionnez Add (Ajouter).

  5. Dans la fenêtre Select Dependency (Sélectionner une dépendance), procédez comme suit :

    • Dans la zone Group Id, entrez com.azure.
    • Dans la zone Artifact Id (ID d’artefact), entrez azure-cosmos.
    • Dans la zone Version, entrez 4.11.0.

    Sinon, vous pouvez ajouter le contenu XML de dépendance pour Group ID et Artifact ID directement dans le fichier pom.xml :

    <dependency>
      <groupId>com.azure</groupId>
      <artifactId>azure-cosmos</artifactId>
      <version>4.11.0</version>
    </dependency>
    
  6. Sélectionnez OK. Maven installe le kit SDK Java SQL ou enregistre le fichier pom.xml.

Utiliser le service Azure Cosmos DB dans votre application Java

Ajoutons à présent les modèles, les vues et les contrôleurs à votre application web.

Ajout d'un modèle

Tout d’abord, définissons un modèle dans un nouveau fichier TodoItem.java. La classe TodoItem définit le schéma d’un élément avec les méthodes getter et 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;
    }
    
    
}

Ajouter les classes d’objets d’accès aux données (DAO)

Créez un objet d’accès aux données (DAO) pour isoler la persistance des éléments ToDo dans Azure Cosmos DB. Pour enregistrer les éléments ToDo dans une collection, le client doit connaître la base de données et la collection de stockage (référencées par les liens réflexifs). En général, il est préférable de mettre en cache la collection et la base de données lorsque cela est possible afin d'éviter les allers-retours supplémentaires vers la base de données.

  1. Pour appeler le service Azure Cosmos DB, vous devez instancier un nouvel objet cosmosClient. En général, il est préférable de réutiliser l’objet cosmosClient plutôt que de construire un nouveau client à chaque demande suivante. Vous pouvez réutiliser le client en le définissant dans la classe cosmosClientFactory. Mettez à jour les valeurs HOST et MASTER_KEY que vous avez enregistrées au cours de étape 1. Remplacez la variable HOST par votre URI, et celle de MASTER_KEY par votre CLÉ PRIMAIRE. Utilisez le code suivant pour créer la classe CosmosClientFactory dans le fichier 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. Créez un fichier TodoDao.java et ajoutez la classe TodoDao pour créer, mettre à jour, lire et supprimer les éléments de tâche :

    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. Créez un fichier MockDao.java et ajoutez la classe MockDao ; cette classe implémente la classe TodoDao pour effectuer des opérations CRUD sur les éléments :

    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. Créez un fichier DocDbDao.java et ajoutez la classe DocDbDao. Cette classe définit le code permettant de rendre persistant les TodoItems dans le conteneur ; elle récupère votre base de données et la collection, si elle existe, ou en crée une si elle n’existe pas. Cet exemple utilise Gson pour sérialiser et désérialiser les objets POJO (Plain Old Java Object) TodoItem dans des documents JSON. Pour enregistrer les éléments ToDo dans une collection, le client doit connaître la base de données et la collection de stockage (référencées par les liens réflexifs). Cette classe définit également la fonction d’assistance pour récupérer les documents par un autre attribut (par exemple, « ID ») au lieu d’établir un lien réflexif. Vous pouvez utiliser la méthode d’assistance pour récupérer un document JSON TodoItem par son ID, et le désérialiser ensuite en POJO.

    Vous pouvez également utiliser l’objet client cosmosClient pour obtenir une collection ou une liste de TodoItems à l’aide d’une requête SQL. Enfin, vous définissez la méthode de suppression pour supprimer un TodoItem de votre liste. Le code suivant montre le contenu de la 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. Créez ensuite un fichier TodoDaoFactory.java et ajoutez la classe TodoDaoFactory qui crée un nouvel objet DocDbDao :

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

Ajout d'un contrôleur

Ajoutez le contrôleur TodoItemController à votre application. Dans ce projet, vous utilisez Project Lombok pour générer le constructeur, les accesseurs get, les méthodes setter et un générateur. Vous pouvez également écrire ce code manuellement ou faire en sorte que l’IDE le génère :

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

Créer un servlet

Ensuite, créez un servlet pour router les requêtes HTTP vers le contrôleur. Créez le fichier ApiServlet.java et définissez dessous le code suivant :

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

Relier le reste de l’application Java

Après l’agréable, passons à l’utile : la création d’une interface utilisateur rapide à associer à votre DAO.

  1. Vous avez besoin d’une interface utilisateur web à afficher à l’utilisateur. Réécrivons le fichier index.jsp créé précédemment avec le code suivant :

    <%@ 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. Terminons en écrivant le code JavaScript côté client pour lier l’interface utilisateur web et le 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. Maintenant, il ne nous reste qu'à tester l'application. Exécutez l'application localement et ajoutez des éléments Todo en renseignant le nom et la catégorie de l'élément, puis en cliquant sur Add Task(Ajouter une tâche). Après l’apparition de l’élément, vous pouvez le mettre à jour s’il est terminé en activant la case à cocher et en cliquant sur Mettre à jour les tâches.

Déployer votre application Java sur des sites web Azure

Les Sites Web Azure permettent de déployer facilement des applications Java en les exportant sous forme de fichiers WAR et en les chargeant par le biais du contrôle de code source (GIT, par exemple) ou par FTP.

  1. Pour exporter votre application en tant que fichier WAR, cliquez avec le bouton droit sur votre projet dans l’Explorateur de projets, sélectionnez Export (Exporter), puis WAR File (Fichier WAR).

  2. Dans la fenêtre WAR Export (Exportation WAR), procédez comme suit :

    • Dans la boîte de dialogue de projet web, entrez azure-cosmos-java-sample.
    • Dans la boîte de dialogue Destination, choisissez un emplacement d'enregistrement du fichier WAR.
    • Sélectionnez Terminer.
  3. Maintenant que vous disposez d’un fichier WAR, vous pouvez le charger tout simplement dans votre répertoire webapps sur les Sites Web Azure. Vous trouverez des instructions sur le chargement du fichier sur la page Ajouter une application Java à Azure App Service Web Apps. Après le téléchargement du fichier WAR dans le répertoire webapps, l’environnement d’exécution détecte que vous l’avez ajouté et le télécharge automatiquement.

  4. Pour consulter le produit terminé, accédez à http://YOUR\_SITE\_NAME.azurewebsites.net/azure-cosmos-java-sample/ et ajoutez vos tâches !

Obtenir le projet à partir de GitHub

Tous les exemples de ce didacticiel sont inclus dans le projet todo sur GitHub. Pour importer le projet todo dans Eclipse, vérifiez d'abord que vous disposez des logiciels et ressources indiqués dans la section Configuration requise , puis procédez comme suit :

  1. Installez Project Lombok. Lombok est utilisé pour générer des constructeurs, des méthodes getter et des méthodes setter dans le projet. Une fois que vous avez téléchargé le fichier lombok.jar, double-cliquez dessus pour l'installer ou installez-le à partir de la ligne de commande.

  2. Si l'application Eclipse est ouverte, fermez-la et redémarrez-la pour charger Lombok.

  3. Dans Eclipse, dans le menu File (Fichier), sélectionnez Import (Importer).

  4. Dans la fenêtre Importer, sélectionnez Git, Projets de Git, puis Suivant.

  5. Dans l’écran Select Repository Source (Sélectionner une source de référentiel), sélectionnez Clone URI (URI du clone).

  6. Dans l’écran Dépôt Git source, dans la zone URI, entrez https://github.com/Azure-Samples/azure-cosmos-java-sql-api-todo-app, puis sélectionnez Suivant.

  7. Dans l’écran Branch Selection (Sélection d’une branche), vérifiez que main (principal) est sélectionné, puis sélectionnez Next (Suivant).

  8. Dans l’écran Local Destination (Destination locale), sélectionnez Browse (Parcourir) pour sélectionner un dossier dans lequel copier le référentiel, puis sélectionnez Next (Suivant).

  9. Dans l’écran Select a wizard to use for importing projects (Sélectionner un Assistant à utiliser pour l’importation de projets), vérifiez que Import existing projects (Importer des projets existants) est sélectionné, puis sélectionnez Next (Suivant).

  10. Dans l’écran Import Projects (Importer des projets), désélectionnez le projet DocumentDB, puis sélectionnez Finish (Terminer). Le projet DocumentDB contient le Kit de développement logiciel (SDK) Java Azure Cosmos DB, que nous allons ajouter comme dépendance à la place.

  11. Dans l’Explorateur de projets, accédez à azure-cosmos-java-sample\src\com.microsoft.azure.cosmos.sample.dao\DocumentClientFactory.java et remplacez les valeurs HOST et MASTER_KEY par l’URI et la CLÉ PRIMAIRE de votre compte Azure Cosmos DB, puis enregistrez le fichier. Pour plus d’informations, voir Étape 1. Créer un compte de base de données Azure Cosmos DB.

  12. Dans l’Explorateur de projets, cliquez avec le bouton droit sur azure-cosmos-java-sample, sélectionnez Build Path (Chemin de build), puis Configure Build Path (Configurer le chemin de build).

  13. Dans l’écran Java Build Path (Chemin de build Java), dans le volet droit, sélectionnez l’onglet Libraries (Bibliothèques), puis sélectionnez Add External JARs (Ajouter des JAR externes). Accédez à l’emplacement du fichier lombok.jar et sélectionnez Open (Ouvrir), puis OK.

  14. Utilisez l’étape 12 pour rouvrir la fenêtre Properties (Propriétés), puis, dans le volet de gauche, sélectionnez Targeted Runtimes (Runtime ciblés).

  15. Dans l’écran Targeted Runtimes (Runtime ciblés), sélectionnez New (Nouveau), Apache Tomcat v7.0, puis OK.

  16. Suivez l’étape 12 pour rouvrir la fenêtre Properties (Propriétés) et, dans le volet de gauche, sélectionnez Project Facets (Facettes du projet).

  17. Dans l’écran Project Facets (Facettes du projet), sélectionnez Dynamic Web Module (Module web dynamique) et Java, puis sélectionnez OK.

  18. Sous l’onglet Servers (Serveurs) en bas de l’écran, cliquez sur Tomcat v7.0 Server at localhost (Serveur Tomcat v7.0 dans localhost), puis sélectionnez Add and Remove (Ajouter et supprimer).

  19. Dans la fenêtre Add and Remove (Ajouter et supprimer), déplacez azure-cosmos-java-sample vers la zone Configured (Configuré), puis sélectionnez Finish (Terminer).

  20. Sous l’onglet Servers (Serveurs), cliquez avec le bouton droit sur Tomcat v7.0 Server at localhost (Serveur Tomcat v7.0 dans localhost), puis sélectionnez Restart (Redémarrer).

  21. Dans un navigateur, accédez à http://localhost:8080/azure-cosmos-java-sample/, puis commencez à ajouter à votre liste de tâches. Notez que si vous avez modifié les valeurs du port par défaut, définissez 8080 sur la valeur que vous avez sélectionnée.

  22. Pour déployer votre projet sur un site web Azure, consultez l'Étape 6. Déployez votre application sur les Sites Web Azure.

Étapes suivantes

Vous tentez d’effectuer une planification de la capacité pour une migration vers Azure Cosmos DB ? Vous pouvez utiliser les informations sur votre cluster de bases de données existant pour la planification de la capacité.