Esercitazione: Creare un'applicazione Web Java usando Azure Cosmos DB e API for NoSQL
SI APPLICA A: NoSQL
Questa esercitazione sull'applicazione Web Java illustra come usare il servizio Microsoft Azure Cosmos DB per archiviare i dati e accedervi da un'applicazione Java ospitata in app Web del servizio app di Azure. È possibile configurare un account Azure Cosmos DB di prova gratuito anche senza una carta di credito o una sottoscrizione di Azure. Contenuto dell'articolo:
- Come creare un'applicazione JavaServer Pages (JSP) di base in Eclipse.
- Come usare il servizio Azure Cosmos DB tramite Azure Cosmos DB Java SDK.
Questa esercitazione illustra come creare un'applicazione di gestione delle attività basata su Web che consente di creare, recuperare e contrassegnare le attività come completate, come illustrato nella figura seguente. Tutte le attività nell'elenco attività vengono archiviate come documenti JSON in Azure Cosmos DB.
Suggerimento
Questa esercitazione sullo sviluppo dell’applicazione presuppone che l'utente abbia già acquisito familiarità con l'uso di Java. Se non si ha alcuna esperienza riguardo a Java o agli strumenti richiesti come prerequisiti, è consigliabile scaricare il progetto todo completo da GitHub e creare l'applicazione usando le istruzioni alla fine di questo articolo. Una volta creata la soluzione, è possibile leggere l'articolo per approfondire il codice nel contesto del progetto.
Prerequisiti per questa esercitazione sull'applicazione Web Java
Prima di iniziare questa esercitazione sullo sviluppo dell’applicazione, è necessario disporre di quanto segue:
Se non si ha una sottoscrizione di Azure, è possibile configurare un account Azure Cosmos DB di prova gratuito anche senza una carta di credito o una sottoscrizione di Azure.
È possibile provare gratuitamente Azure Cosmos DB senza una sottoscrizione di Azure e senza impegno. In alternativa, è possibile creare un account di livello gratuito di Azure Cosmos DB, con i primi 1.000 UR/s e 25 GB di spazio di archiviazione gratuiti. È anche possibile usare l'Emulatore di Azure Cosmos DB con l'URI
https://localhost:8081
. Per la chiave da usare con l'emulatore, vedere Autenticazione delle richieste.Un sito Web di Azure con Java Runtime Environment (ad esempio Tomcat o Jetty) abilitato.
Se questi strumenti vengono installati per la prima volta, coreservlets.com fornisce una procedura dettagliata del processo di installazione nella sezione di avvio rapido dell'articolo relativo all'esercitazione sull'installazione di TomCat7 e il relativo uso con Eclipse.
Creare un account Azure Cosmos DB
Creare prima di tutto un account Azure Cosmos DB. Se si ha già un account o si usa l'emulatore Azure Cosmos DB per questa esercitazione, è possibile proseguire con il Passaggio 2: Creare l'applicazione Java JSP.
Nel menu del portale di Azure o dalla pagina Home selezionare Crea una risorsa.
Cercare Azure Cosmos DB. Selezionare Crea>Azure Cosmos DB.
Nella pagina Creare un account Azure Cosmos DB, selezionare l'opzione Crea all'interno della sezione Azure Cosmos DB for NoSQL.
Azure Cosmos DB offre diverse API:
- NoSQL, per i dati del documento
- PostgreSQL
- MongoDB, per i dati del documento
- Apache Cassandra
- Tabella
- Apache Gremlin, per i dati del grafo
Per ulteriori informazioni sulla API per NoSQL, vedere Introduzione ad Azure Cosmos DB.
Nella pagina Crea account Azure Cosmos DB, immettere le impostazioni base per il nuovo account Azure Cosmos DB.
Impostazione valore Descrizione Abbonamento Nome della sottoscrizione Selezionare la sottoscrizione di Azure da usare per l'account Azure Cosmos DB. Gruppo di risorse Nome gruppo di risorse Selezionare un gruppo di risorse oppure fare clic su Crea nuovo, quindi immettere un nome univoco per il nuovo gruppo di risorse. Nome conto Un nome univoco Immettere un nome per identificare l'account Azure Cosmos DB. Dato che al nome specificato viene aggiunto documents.azure.com per creare l'URI, usare un nome univoco. Il nome può contenere solo lettere minuscole, numeri e il trattino (-). Deve essere di 3-44 caratteri. Ufficio Area più vicina ai propri utenti Selezionare una posizione geografica in cui ospitare l'account Azure Cosmos DB. Usare la località più vicina agli utenti per offrire loro la massima velocità di accesso ai dati. Modalità di capacità Velocità effettiva con provisioning o Serverless Selezionare Provisioning velocità effettiva per creare un account in modalità Provisioning velocità effettiva. Selezionare Serverless per creare un account in modalità Serverless. Applicare lo sconto del livello gratuito di Azure Cosmos DB Applicare o non applicare Il livello gratuito di Azure Cosmos DB offre i primi 1000 UR/sec e 25 GB di spazio di archiviazione gratuiti per account. Altre informazioni sul livello gratuito. Limitare la velocità effettiva totale dell'account Selezionato o non selezionato È possibile limitare la misura totale di velocità effettiva di provisioning in questo account. Questo limite impedisce addebiti imprevisti correlati alla velocità effettiva con provisioning. È possibile aggiornare o rimuovere questo limite dopo la creazione dell'account. È possibile avere al massimo un account Azure Cosmos DB di livello gratuito per ogni sottoscrizione di Azure ed è necessario acconsentire esplicitamente durante la creazione dell'account. Se l'opzione di applicare lo sconto per il livello gratuito non è visualizzata, significa che un altro account nella sottoscrizione è già stato abilitato per il livello gratuito.
Nota
Le opzioni seguenti non sono disponibili se si seleziona Serverless come modalità di capacità:
- Applica sconto livello gratuito
- Limitare la velocità effettiva totale dell'account
Nella scheda Distribuzione globale configurare i dettagli seguenti. Per questo avvio rapido è possibile lasciare i valori predefiniti:
Impostazione valore Descrizione Ridondanza geografica Disabilita Abilitare o disabilitare la distribuzione globale nell'account associando la propria area a un'altra area. È possibile aggiungere altre aree al proprio account in un secondo momento. Scritture in più aree Disabilita La funzionalità Scritture in più aree consente di sfruttare la velocità effettiva di cui è stato effettuato il provisioning per i database e i contenitori in tutto il mondo. Zone di disponibilità Disabilita Le zone di disponibilità consentono di migliorare ulteriormente la disponibilità e la resilienza dell'applicazione. Nota
Le seguenti opzioni non sono disponibili se si seleziona Serverless come modalità di capacità nella pagina precedente Basic:
- Ridondanza geografica
- Scritture in più aree
Facoltativamente, è possibile configurare altri dettagli nelle schede seguenti:
- Reti. Configurare accesso da una rete virtuale.
- Criteri di backup. Configurare criteri di backup periodici o continui.
- Crittografia. Usare una chiave gestita dal servizio o una chiave gestita dal cliente.
- Tag. I tag sono coppie nome-valore che consentono di classificare le risorse e visualizzare dati di fatturazione consolidati tramite l'applicazione dello stesso tag a più risorse e gruppi di risorse.
Selezionare Rivedi e crea.
Esaminare le impostazioni dell'account e quindi selezionare Crea. La creazione dell'account richiede alcuni minuti. Attendere che la pagina del portale visualizzi La distribuzione è stata completata.
Selezionare Vai alla risorsa per passare alla pagina dell'account Azure Cosmos DB.
Passare alla pagina dell'account Azure Cosmos DB e selezionare Chiavi. Copiare i valori da usare nell'applicazione Web che verrà creata successivamente.
Creare l'applicazione Java JSP
Per creare l'applicazione JSP:
Si inizierà con la creazione di un progetto Java. Avviare Eclipse, quindi selezionare File, Nuovo e quindi Progetto Web dinamico. Se Progetto Web dinamico non è elencato come progetto disponibile, eseguire le operazioni seguenti: selezionare File, selezionare Nuovo, selezionare Progetto..., espandere Web, scegliere Progetto Web dinamico e selezionare Avanti.
Immettere un nome per il progetto nella casella Nome progetto e dal menu a discesa Runtime di destinazione selezionare facoltativamente un valore, ad esempio Apache Tomcat v7.0, e quindi selezionare Fine. Se si seleziona un runtime di destinazione, sarà possibile eseguire il progetto in locale tramite Eclipse.
Nella vista Project Explorer di Eclipse espandere il progetto. Fare clic con il pulsante destro del mouse su WebContent, scegliere Nuovo e quindi File JSP.
Nella finestra di dialogo New JSP File (Nuovo file JSP) assegnare al file il nome index.jsp. Mantenere il nome WebContent per la cartella padre, come illustrato di seguito, e quindi selezionare Avanti.
Per le finalità di questa esercitazione, nella finestra di dialogo Seleziona modello JSP selezionare Nuovo file JSP (html), quindi selezionare Fine.
Quando si apre il file index.jsp in Eclipse, aggiungere il testo per visualizzare Hello World! all'interno dell'elemento
<body>
esistente. Il contenuto<body>
aggiornato dovrebbe avere un aspetto analogo al codice seguente:<body> <% out.println("Hello World!"); %> </body>
Salvare il file index.jsp.
Se nel passaggio 2 è stato impostato un runtime di destinazione, è possibile selezionare Progetto, quindi Esegui per eseguire l'applicazione JSP in locale:
Installare Java SDK SQL
Il modo più semplice per inserire Java SDK SQL e le relative dipendenze è tramite Apache Maven. A tale scopo, sarà necessario convertire il progetto in un progetto Maven completando i passaggi seguenti:
Fare clic con il pulsante destro del mouse sul progetto in Esplora progetti, selezionare Configura e quindi selezionare Converti in progetto Maven.
Nella finestra Crea nuovo POM accettare le impostazioni predefinite e selezionare Fine.
In Project Explorer, aprire il file pom.xml.
Nella scheda Dipendenze, nel riquadro Dipendenze, selezionare Aggiungi.
Nella finestra Select Dependency (Seleziona dipendenza) eseguire le operazioni seguenti:
- Nella casella ID gruppo immettere
com.azure
. - Nella casella ID artefatto immettere
azure-cosmos
. - Nella casella Versione immettere
4.11.0
.
In alternativa, aggiungere l'XML della dipendenza per ID gruppo e ID artefatto direttamente nel file pom.xml:
<dependency> <groupId>com.azure</groupId> <artifactId>azure-cosmos</artifactId> <version>4.11.0</version> </dependency>
- Nella casella ID gruppo immettere
Selezionare OK e Maven installerà SQL Java SDK o salverà il file pom.xml.
Usare il servizio Azure Cosmos DB in un'applicazione Java
Aggiungere ora i modelli, le visualizzazioni e i controller all'applicazione Web.
Aggiungi un modello
Per prima cosa, definire un modello all'interno di un nuovo file TodoItem.java. La classe TodoItem
definisce lo schema di un elemento insieme ai metodi 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;
}
}
Aggiungere le classi DAO (Data Access Object)
Creare un oggetto DAO (Data Access Object) per astrarre in modo persistente gli elementi ToDo in Azure Cosmos DB. Per salvare gli elementi ToDo in una raccolta, il client deve conoscere i database e le raccolte da rendere permanenti (in base al riferimento dei collegamenti automatici). È in genere preferibile memorizzare nella cache database e raccolte appena possibile, in modo da evitare episodi di round trip del database.
Per richiamare il servizio Azure Cosmos DB, è necessario creare un'istanza di un nuovo oggetto
cosmosClient
. È in genere preferibile riutilizzare l'oggettocosmosClient
invece di creare un nuovo client per ogni richiesta successiva. È possibile riutilizzare il client definendolo all'interno della classecosmosClientFactory
. Aggiornare i valori HOST e MASTER_KEY salvati nel passaggio 1. Sostituire la variabile HOST con il proprio URI, e sostituire MASTER_KEY con la propria chiave primaria. Usare il codice seguente per creare la classeCosmosClientFactory
nel file di 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; } }
Creare un nuovo file TodoDao.java e aggiungere la classe
TodoDao
per creare, aggiornare, leggere ed eliminare gli elementi 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); }
Creare un nuovo file MockDao.java e aggiungere la classe
MockDao
, che implementa la classeTodoDao
per eseguire operazioni CRUD sugli elementi: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(); } }
Creare un nuovo file DocDbDao.java e aggiungere la classe
DocDbDao
. Questa classe definisce il codice per la persistenza di elementi TodoItem nel contenitore, recupera il database e la raccolta, se esistono, o ne crea di nuovi in caso contrario. Questo esempio usa Gson per serializzare e deserializzare gli oggetti POJO (Plain Old Java Object) di TodoItem in documenti JSON. Per salvare gli elementi ToDo in una raccolta, il client deve conoscere i database e le raccolte da rendere permanenti (in base al riferimento dei collegamenti automatici). Questa classe definisce anche la funzione helper per recuperare i documenti in base a un altro attributo, ad esempio "ID") invece che con il collegamento automatico. È possibile usare il metodo helper per recuperare un documento JSON TodoItem in base all'ID e quindi deserializzarlo in POJO.È inoltre possibile usare l'oggetto client
cosmosClient
per ottenere una raccolta o un elenco di elementi TodoItem tramite una query SQL. Infine, definire il metodo delete per eliminare un elemento TodoItem dall'elenco. Il codice seguente illustra il contenuto della classeDocDbDao
: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; } } }
Successivamente, creare un nuovo file TodoDaoFactory.java e aggiungere la classe
TodoDaoFactory
, che crea un nuovo oggetto DocDbDao:package com.microsoft.azure.cosmos.sample.dao; public class TodoDaoFactory { private static TodoDao myTodoDao = new DocDbDao(); public static TodoDao getDao() { return myTodoDao; } }
Aggiungere un controller
Aggiungere il controller TodoItemController all'applicazione. In questo progetto viene usato Project Lombok per generare il costruttore, getter, setter e un generatore. In alternativa, è possibile scrivere il codice manualmente o farlo generare mediante IDE:
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);
}
}
Creare un servlet
Creare quindi un servlet per instradare le richieste HTTP al controller. Creare il file ApiServlet.java e definire il codice seguente al suo interno:
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);
}
}
Collegare il resto dell'app Java
Dopo aver completato questi passaggi, è necessario creare un'interfaccia utente intuitiva e collegarla all'oggetto DAO.
Sarà necessaria un'interfaccia utente Web da visualizzare all'utente. A tale scopo, riscrivere il file index.jsp creato in precedenza con il codice seguente:
<%@ 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>
Infine, scrivere codice JavaScript lato client per il collegamento dell'interfaccia utente Web con il 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(); });
è necessario testare l'applicazione. Eseguire l'applicazione in locale e aggiungere alcuni elementi Todo specificando i valori relativi al nome e alla categoria dell'elemento e selezionando Add Task (Aggiungi attività). Quando l'elemento viene visualizzato, è possibile aggiornarne lo stato attivando o disattivando la relativa casella di controllo e facendo clic su Update Tasks (Aggiorna attività).
Distribuire l'applicazione Java in Siti Web di Azure
Con Siti Web di Azure la procedura di distribuzione di applicazioni Java è molto semplice e consiste nell'esportazione di un'applicazione come file con WAR e nel relativo caricamento tramite controllo del codice sorgente (ad esempio Git) o FTP.
Per esportare l'applicazione come file WAR, fare clic con il pulsante destro del mouse sul progetto in Esplora progetti, selezionare Esporta e quindi selezionare File WAR.
Nella finestra WAR Export eseguire le operazioni seguenti:
- Nella casella Progetto Web immettere azure-cosmos-java-sample.
- Nella casella Destination scegliere una destinazione in cui salvare il file WAR.
- Selezionare Fine.
A questo punto è sufficiente caricare il file nella directory webapps di Siti Web di Azure. Per istruzioni sul caricamento del file, vedere Distribuire l'applicazione di esempio nelle app Web di Servizio app di Azure. Dopo aver caricato il file WAR nella directory webapps, l'ambiente di runtime rileverà che il file è stato aggiunto e lo caricherà automaticamente.
Per visualizzare il prodotto finito, passare a
http://YOUR\_SITE\_NAME.azurewebsites.net/azure-cosmos-java-sample/
e iniziare ad aggiungere le attività.
Ottenere il progetto da GitHub
Tutti gli esempi in questa esercitazione sono inclusi nel progetto todo su GitHub. Per importare il progetto todo in Eclipse, assicurarsi di avere il software e le risorse elencate nella sezione Prerequisiti , quindi eseguire le operazioni seguenti:
Installare Project Lombok. Lombok viene usato per generare costruttori, getter e setter nel progetto. Dopo aver scaricato il file lombok.jar, fare doppio clic per installarlo o eseguire l'installazione dalla riga di comando.
Se Eclipse è aperto, chiuderlo e riavviarlo per caricare Lombok.
In Eclipse dal menu File, selezionare Importa.
Nella finestra Importa selezionare Git, selezionare Progetti da Git e quindi selezionare Avanti.
Nella schermata Seleziona origine repository selezionare Clona URI.
Nella schermata Repository Git di origine, nella casella URI, immettere https://github.com/Azure-Samples/azure-cosmos-java-sql-api-todo-app e quindi selezionare Avanti.
Nella schermata Selezione ramo assicurarsi che sia selezionata l'opzione principale e quindi selezionare Avanti.
Nella schermata Destinazione locale selezionare Sfoglia per selezionare una cartella in cui sia possibile copiare il repository e quindi selezionare Avanti.
Nella schermata Selezionare una procedura guidata per l'importazione di progetti assicurarsi che l'opzione Importa progetti esistenti sia selezionata e quindi selezionare Avanti.
Nella schermata Importa progettideselezionare il progetto DocumentDB e quindi selezionare Fine. Il progetto DocumentDB contiene Azure Cosmos DB Java SDK, che verrà aggiunto invece come dipendenza.
In Esplora progetti passare ad azure-cosmos-java-sample\src\com.microsoft.azure.cosmos.sample.dao\DocumentClientFactory.java e sostituire i valori HOST e MASTER_KEY con i valori URI e PRIMARY KEY dell'account Azure Cosmos DB, quindi salvare il file. Per altre informazioni, vedere Passaggio 1. Creare un account di database Azure Cosmos DB.
In Esplora progetti fare clic con il pulsante destro del mouse su azure-cosmos-java-sample, selezionare Percorso compilazione e quindi selezionare Configura percorso compilazione.
Nella schermata Percorso compilazione Java, nel riquadro a destra selezionare la scheda Librerie e quindi selezionare Aggiungi JAR esterni. Passare al percorso del file lombok.jar e selezionareApri e quindi selezionare OK.
Vedere il passaggio 12 per aprire nuovamente la finestra Proprietà e quindi nel riquadro a sinistra selezionare Runtime di destinazione.
Nella schermata Runtime di destinazione selezionare Nuovo, selezionare Apache Tomcat v7.0 e quindi selezionare OK.
Vedere il passaggio 12 per aprire nuovamente la finestra Proprietà e quindi nel riquadro a sinistra selezionare Facet di progetto.
Nella schermata Facet di progetto selezionare Modulo Web dinamico e Java e quindi selezionare OK.
Nella scheda Server nella parte inferiore della schermata fare clic con il pulsante destro del mouse su Tomcat v7.0 Server in localhost e quindi selezionare Aggiungi e rimuovi.
Nella finestra Aggiungi e rimuovi spostare azure-cosmos-java-sample nella casella Configurato e quindi selezionare Fine.
Nella scheda Server fare clic con il pulsante destro del mouse su Tomcat v7.0 Server in localhost e quindi selezionare Riavvia.
In un browser passare a
http://localhost:8080/azure-cosmos-java-sample/
e iniziare ad aggiungere voci all'elenco attività. Si noti che se sono stati modificati i valori di porta predefiniti, è necessario modificare la porta 8080 con il valore selezionato.Per distribuire il progetto in un sito web di Azure, vedere il passaggio 6. Distribuire l'applicazione in Siti Web di Azure.
Passaggi successivi
Si sta tentando di pianificare la capacità per una migrazione ad Azure Cosmos DB? È possibile usare le informazioni del cluster di database esistente per la pianificazione della capacità.
- Se si conosce solo il numero di vCore e server nel cluster di database esistente, leggere le informazioni sulla stima delle unità richieste con vCore o vCPU
- Se si conosce la frequenza delle richieste tipiche per il carico di lavoro corrente del database, leggere le informazioni sulla stima delle unità richieste con lo strumento di pianificazione della capacità di Azure Cosmos DB