Introduzione a Java

Questo tutorial è destinato a coloro che non hanno mai creato app nel cloud, ad esempio ingegneri e sviluppatori web che vogliono imparare i concetti chiave dello sviluppo di app nel contesto di Google Cloud.

Obiettivi

Costi

In questo documento vengono utilizzati i seguenti componenti fatturabili di Google Cloud:

Le istruzioni in questo documento sono progettate per mantenere l'utilizzo delle risorse entro i limiti del livello Always Free di Google Cloud. Per generare una stima dei costi in base all'utilizzo previsto, utilizza il Calcolatore prezzi. I nuovi utenti di Google Cloud possono essere idonei a una prova senza costi aggiuntivi.

Una volta completate le attività descritte in questo documento, puoi evitare la fatturazione continua eliminando le risorse che hai creato. Per ulteriori informazioni, consulta la pagina Pulizia.

Prima di iniziare

  1. Accedi al tuo account Google Cloud. Se non conosci Google Cloud, crea un account per valutare le prestazioni dei nostri prodotti in scenari reali. I nuovi clienti ricevono anche 300 $di crediti gratuiti per l'esecuzione, il test e il deployment dei carichi di lavoro.
  2. Nella pagina del selettore di progetti della console Google Cloud, seleziona o crea un progetto Google Cloud.

    Vai al selettore progetti

  3. Assicurati che la fatturazione sia attivata per il tuo progetto Google Cloud.

  4. Nella pagina del selettore di progetti della console Google Cloud, seleziona o crea un progetto Google Cloud.

    Vai al selettore progetti

  5. Assicurati che la fatturazione sia attivata per il tuo progetto Google Cloud.

  6. Per creare un database Firestore in modalità Native, segui questa procedura:
    1. In Cloud Console, vai alla pagina Visualizzatore negozio.
      Vai al visualizzatore Firestore
    2. Nella schermata Seleziona una modalità Cloud Firestore, fai clic su Seleziona modalità nativa.
    3. Seleziona una località per il tuo database Firestore. La località impostata corrisponde alla località predefinita delle risorse di Google Cloud per il tuo progetto Cloud . Questa località viene utilizzata per i servizi Google Cloud all'interno del tuo progetto Cloud che richiedono la geolocalizzazione. Nello specifico, si tratta del tuo bucket predefinito di Cloud Storage e della tua app Cloud Run.
    4. Fai clic su Crea database.
  7. Abilita le API Cloud Run Admin, Cloud Storage, Cloud Logging, and Error Reporting.

    Abilita le API

  8. Clona il repository di esempio e apri l'applicazione di esempio in Cloud Shell:
    Vai a Cloud Shell

    Cloud Shell fornisce l'accesso tramite riga di comando alle tue risorse Google Cloud direttamente dal browser.

  9. Per scaricare il codice di esempio e modificarlo nella directory dell'applicazione, fai clic su Continua.
  10. In Cloud Shell, configura lo strumento gcloud in modo da utilizzare il nuovo progetto Google Cloud:

    # Configure gcloud for your project
    gcloud config set project PROJECT_ID
    

    Sostituisci PROJECT_ID con l'ID progetto di Google Cloud che hai creato utilizzando Cloud Console.

    L'interfaccia a riga di comando di Google Cloud è il metodo principale per interagire con le risorse Google Cloud dalla riga di comando. In questo tutorial, utilizzerai lo strumento gcloud per eseguire il deployment dell'app e monitorarla.

Esecuzione dell'app

  1. Se utilizzi già Cloud Shell e lo hai configurato per utilizzare Java 11, aggiorna le alternative Java di shell, le variabili di ambiente JAVA_HOME e PATH specifiche di Java 8.
  2. Modifica nella directory bookshelf/1-cloud-run ed esegui l'app:
    GOOGLE_CLOUD_PROJECT=PROJECT_ID mvn -Plocal clean jetty:run-exploded
    
    Sostituisci PROJECT_ID con l'ID del progetto Google Cloud che hai creato.
  3. In Cloud Shell, fai clic su Anteprima web , e seleziona Anteprima sulla porta 8080. Compare una nuova finestra in cui viene eseguita l'app.

Esecuzione del deployment dell'app in Cloud Run

Google Cloud offre varie opzioni per eseguire il codice. In questo esempio, viene utilizzato Cloud Run per eseguire il deployment di un'app scalabile in Google Cloud. Cloud Run non richiede la gestione dei server e scala automaticamente per supportare i picchi di traffico.

  1. Crea l'immagine utilizzando Jib:
    mvn package jib:build -Dimage=gcr.io/PROJECT_ID/bookshelf

    Sostituisci PROJECT_ID con l'ID del progetto Google Cloud che hai creato.

  2. Esegui il deployment dell'immagine:
    gcloud run deploy bookshelf --image gcr.io/PROJECT_ID/bookshelf \
    --platform managed --region us-central1 --allow-unauthenticated
    Sostituisci PROJECT_ID con l'ID del progetto Google Cloud che hai creato.

Una volta eseguito il deployment, restituisce un endpoint all'applicazione in esecuzione in Cloud Run, nel seguente formato:

https://bookshelf-abcdefghij-uc.a.run.app

Ora l'app è visualizzabile a questo link, che d'ora in poi verrà chiamato YOUR_CODE_RUN_URL. Nel browser web, inserisci l'URL per visualizzare l'app.

Home page dell'app Bookshelf

Rendere persistenti i dati con Firestore

Non puoi archiviare informazioni sulle istanze di Cloud Run poiché vengono perse al riavvio dell'istanza e non esistono quando vengono create nuove istanze. Puoi, invece, utilizzare un database in cui tutte le istanze eseguono operazioni di lettura e scrittura.

Google Cloud offre varie opzioni per archiviare i dati. In questo esempio viene utilizzato Firestore per archiviare i dati di ogni libro. Firestore è un database di documenti NoSQL serverless completamente gestito che consente di archiviare i dati e di eseguire query su di essi. Firestore si ridimensiona automaticamente per soddisfare le esigenze dell'app, con una scalabilità fino a zero quando non la utilizzi. Aggiungi il tuo primo libro.

  1. Dal tuo browser web, vai a YOUR_CODE_RUN_URL.
  2. Per creare un libro per l'applicazione di cui hai eseguito il deployment, fai clic su Add book (Aggiungi libro).

    Aggiungere un libro all'app Bookshelf
  3. Nel campo Title (Titolo), inserisci Moby Dick.
  4. Nel campo Author (Autore), inserisci Herman Melville.
  5. Fai clic su Salva. Adesso esiste una voce nell'applicazione Bookshelf.

    Voce per Moby Dick nell'app Bookshelf
  6. In Cloud Console, fai clic su Aggiorna per aggiornare la pagina di Firestore. I dati vengono visualizzati in Firestore. Ogni libro dell'app Bookshelf è archiviato come documento Firestore con un ID unico e tutti questi documenti sono archiviati in una raccolta Firestore. Ai fini di questo tutorial, la raccolta è chiamata "books" (libri). Esempio di documento Firestore.

Firestore archivia i libri mediante la libreria client di Firestore. Ecco un esempio di come recuperare un documento Firestore:

public class FirestoreDao implements BookDao {
  private CollectionReference booksCollection;

  public FirestoreDao() {
    Firestore firestore = FirestoreOptions.getDefaultInstance().getService();
    booksCollection = firestore.collection("books");
  }

  private Book documentToBook(DocumentSnapshot document) {
    Map<String, Object> data = document.getData();
    if (data == null) {
      System.out.println("No data in document " + document.getId());
      return null;
    }

    return new Book.Builder()
        .author((String) data.get(Book.AUTHOR))
        .description((String) data.get(Book.DESCRIPTION))
        .publishedDate((String) data.get(Book.PUBLISHED_DATE))
        .imageUrl((String) data.get(Book.IMAGE_URL))
        .createdBy((String) data.get(Book.CREATED_BY))
        .createdById((String) data.get(Book.CREATED_BY_ID))
        .title((String) data.get(Book.TITLE))
        .id(document.getId())
        .build();
  }

  @Override
  public String createBook(Book book) {
    String id = UUID.randomUUID().toString();
    DocumentReference document = booksCollection.document(id);
    Map<String, Object> data = Maps.newHashMap();

    data.put(Book.AUTHOR, book.getAuthor());
    data.put(Book.DESCRIPTION, book.getDescription());
    data.put(Book.PUBLISHED_DATE, book.getPublishedDate());
    data.put(Book.TITLE, book.getTitle());
    data.put(Book.IMAGE_URL, book.getImageUrl());
    data.put(Book.CREATED_BY, book.getCreatedBy());
    data.put(Book.CREATED_BY_ID, book.getCreatedById());
    try {
      document.set(data).get();
    } catch (InterruptedException | ExecutionException e) {
      e.printStackTrace();
    }

    return id;
  }

  @Override
  public Book readBook(String bookId) {
    try {
      DocumentSnapshot document = booksCollection.document(bookId).get().get();

      return documentToBook(document);
    } catch (InterruptedException | ExecutionException e) {
      e.printStackTrace();
    }
    return null;
  }

  @Override
  public void updateBook(Book book) {
    DocumentReference document = booksCollection.document(book.getId());
    Map<String, Object> data = Maps.newHashMap();

    data.put(Book.AUTHOR, book.getAuthor());
    data.put(Book.DESCRIPTION, book.getDescription());
    data.put(Book.PUBLISHED_DATE, book.getPublishedDate());
    data.put(Book.TITLE, book.getTitle());
    data.put(Book.IMAGE_URL, book.getImageUrl());
    data.put(Book.CREATED_BY, book.getCreatedBy());
    data.put(Book.CREATED_BY_ID, book.getCreatedById());
    try {
      document.set(data).get();
    } catch (InterruptedException | ExecutionException e) {
      e.printStackTrace();
    }
  }

  @Override
  public void deleteBook(String bookId) {
    try {
      booksCollection.document(bookId).delete().get();
    } catch (InterruptedException | ExecutionException e) {
      e.printStackTrace();
    }
  }

  private List<Book> documentsToBooks(List<QueryDocumentSnapshot> documents) {
    List<Book> resultBooks = new ArrayList<>();
    for (QueryDocumentSnapshot snapshot : documents) {
      resultBooks.add(documentToBook(snapshot));
    }
    return resultBooks;
  }

  @Override
  public Result<Book> listBooks(String startTitle) {
    Query booksQuery = booksCollection.orderBy("title").limit(10);
    if (startTitle != null) {
      booksQuery = booksQuery.startAfter(startTitle);
    }
    try {
      QuerySnapshot snapshot = booksQuery.get().get();
      List<Book> results = documentsToBooks(snapshot.getDocuments());
      String newCursor = null;
      if (results.size() > 0) {
        newCursor = results.get(results.size() - 1).getTitle();
      }
      return new Result<>(results, newCursor);
    } catch (InterruptedException | ExecutionException e) {
      e.printStackTrace();
    }
    return new Result<>(Lists.newArrayList(), null);
  }

  @Override
  public Result<Book> listBooksByUser(String userId, String startTitle) {
    Query booksQuery =
        booksCollection.orderBy("title").whereEqualTo(Book.CREATED_BY_ID, userId).limit(10);
    if (startTitle != null) {
      booksQuery = booksQuery.startAfter(startTitle);
    }
    try {
      QuerySnapshot snapshot = booksQuery.get().get();
      List<Book> results = documentsToBooks(snapshot.getDocuments());
      String newCursor = null;
      if (results.size() > 0) {
        newCursor = results.get(results.size() - 1).getTitle();
      }
      return new Result<>(results, newCursor);
    } catch (InterruptedException | ExecutionException e) {
      e.printStackTrace();
    }
    return new Result<>(Lists.newArrayList(), null);
  }
}

Per ulteriori informazioni sull'utilizzo di Firestore, consulta Aggiungere dati a Firestore.

Archiviare i caricamenti dei file in Cloud Storage

Dopo aver aggiunto un libro, devi aggiungere la rispettiva immagine di copertina. Non puoi archiviare i file nelle istanze. Un database non è la scelta giusta per i file di immagine. Utilizza, invece, Cloud Storage.

Cloud Storage è il principale archivio di blob per Google Cloud. Puoi utilizzare Cloud Storage per archiviare gli asset dell'app che vuoi condividere in Google Cloud. Per utilizzare Cloud Storage, devi creare un bucket Cloud Storage, ovvero un container di base che ospiti i tuoi dati.

  1. In Cloud Console, vai alla pagina Browser Cloud Storage.

    Vai alla pagina Browser Cloud Storage

  2. Fai clic su Crea bucket.
  3. Nella finestra di dialogo Crea bucket, inserisci un nome per il bucket aggiungendo l'ID del progetto Google Cloud alla stringa _bucket, in modo che il nome sia simile a YOUR_PROJECT_ID_bucket. Questo nome è soggetto ai requisiti per i nomi dei bucket. Puoi lasciare invariati i valori predefiniti di tutti gli altri campi.
  4. Fai clic su Crea.
  5. Una volta creato il bucket, gli oggetti devono essere resi accessibili pubblicamente per poter essere visualizzati dagli utenti. Per rendere gli oggetti accessibili pubblicamente, consulta la pagina Rendere pubblici i dati.
  6. Fai clic su Edit book (Modifica libro) e seleziona un'immagine da caricare come copertina del libro. Ad esempio, puoi utilizzare questa immagine di dominio pubblico:
    Copertina di Moby Dick
  7. Fai clic su Save (Salva). Viene visualizzata la home page, dove è presente una voce per la tua app Bookshelf.
    Voce per Moby Dick nell'app Bookshelf

L'applicazione Bookshelf invia i file caricati a Cloud Storage mediante la libreria client di Cloud Storage.

public class CloudStorageHelper {

  private final Logger logger = Logger.getLogger(CloudStorageHelper.class.getName());
  private static Storage storage = null;

  static {
    storage = StorageOptions.getDefaultInstance().getService();
  }

  /**
   * Uploads a file to Google Cloud Storage to the bucket specified in the BUCKET_NAME environment
   * variable, appending a timestamp to end of the uploaded filename.
   */
  public String uploadFile(FileItemStream fileStream, final String bucketName)
      throws IOException, ServletException {
    checkFileExtension(fileStream.getName());

    System.out.println("FileStream name: " + fileStream.getName() + "\nBucket name: " + bucketName);

    DateTimeFormatter dtf = DateTimeFormat.forPattern("-YYYY-MM-dd-HHmmssSSS");
    DateTime dt = DateTime.now(DateTimeZone.UTC);
    String dtString = dt.toString(dtf);
    final String fileName = fileStream.getName() + dtString;

    // the inputstream is closed by default, so we don't need to close it here
    @SuppressWarnings("deprecation")
    BlobInfo blobInfo =
        storage.create(
            BlobInfo.newBuilder(bucketName, fileName)
                // Modify access list to allow all users with link to read file
                .setAcl(new ArrayList<>(Arrays.asList(Acl.of(User.ofAllUsers(), Role.READER))))
                .build(),
            fileStream.openStream());
    logger.log(
        Level.INFO, "Uploaded file {0} as {1}", new Object[] {fileStream.getName(), fileName});
    // return the public download link
    return blobInfo.getMediaLink();
  }

  /** Checks that the file extension is supported. */
  private void checkFileExtension(String fileName) throws ServletException {
    if (fileName != null && !fileName.isEmpty() && fileName.contains(".")) {
      String[] allowedExt = {".jpg", ".jpeg", ".png", ".gif"};
      for (String ext : allowedExt) {
        if (fileName.endsWith(ext)) {
          return;
        }
      }
      throw new ServletException("file must be an image");
    }
  }
}

Esegui la pulizia

Per evitare che al tuo Account Google Cloud vengano addebitati costi relativi alle risorse utilizzate in questo tutorial, elimina il progetto che contiene le risorse oppure mantieni il progetto ed elimina le singole risorse.

Elimina il progetto

  1. Nella console Google Cloud, vai alla pagina Gestisci risorse.

    Vai a Gestisci risorse

  2. Nell'elenco dei progetti, seleziona il progetto che vuoi eliminare, quindi fai clic su Elimina.
  3. Nella finestra di dialogo, digita l'ID del progetto e fai clic su Chiudi per eliminare il progetto.

Passaggi successivi