Many apps need to do background processing outside of the context of a
web request. This tutorial creates a web app that lets users input text
to translate, and then displays a list of previous translations. The translation
is done in a background process to avoid blocking the user's request.

The following diagram illustrates the translation request process.

![Diagram of architecture.](https://docs.cloud.google.com/static/languages/images/background-event-architecture-cloud-run.svg)

Here is the sequence of events for how the tutorial app works:

1. Visit the web page to see a list of previous translations, stored in Firestore.
2. Request a translation of text by entering an HTML form.
3. The translation request is published to Pub/Sub.
4. A Cloud Run service subscribed to that Pub/Sub topic is triggered.
5. The Cloud Run service uses Cloud Translation to translate the text.
6. The Cloud Run service stores the result in Firestore.

This tutorial is intended for anyone who is interested in learning about
background processing with Google Cloud. No prior experience is
required with Pub/Sub, Firestore, Cloud Run.
However, to understand all of the code, some experience with
Java and HTML is helpful.

## Objectives

- Understand and deploy a Cloud Run service.
- Try the app.

## Costs


In this document, you use the following billable components of Google Cloud:


- [Cloud Run](https://docs.cloud.google.com/run/pricing)
- [Firestore](https://docs.cloud.google.com/firestore/pricing)
- [Pub/Sub](https://docs.cloud.google.com/pubsub/pricing)
- [Cloud Translation](https://docs.cloud.google.com/translate/pricing)


To generate a cost estimate based on your projected usage,
use the [pricing calculator](https://docs.cloud.google.com/products/calculator).
New Google Cloud users might be eligible for a [free trial](https://docs.cloud.google.com/free).

<br />

When you finish the tasks that are described in this document, you can avoid
continued billing by deleting the resources that you created. For more information, see
[Clean up](https://docs.cloud.google.com/java/getting-started/background-processing.md.txt#clean-up).

## Before you begin

1. Update `gcloud` components:

   ```bash
   gcloud components update
   ```
2. Prepare your development environment.
   [Go to the Java setup guide](https://docs.cloud.google.com/java/docs/setup.md.txt)

## Preparing the app

1. In your terminal window, clone the sample app repository to your local machine:

   ```bash
   git clone https://github.com/GoogleCloudPlatform/getting-started-java.git
   ```


   Alternatively, you can
   [download the sample](https://github.com/GoogleCloudPlatform/getting-started-java/archive/master.zip)
   as a zip file and extract it.
2. Change to the directory that contains the background processing sample code:

   ```bash
   cd getting-started-java/background
   ```

## Understanding the app

There are two main components for the web app:

- A Java HTTP server to handle web requests. The server has the following two endpoints:
  - `/translate`
    - `GET` (by using a web browser): Displays 10 most recent processed translation requests submitted by users.
    - `POST` (with a Pub/Sub subscription): Processes translation requests using the Cloud Translation API and stores the results in Firestore.
  - `/create`: The form to submit new translate requests.
- Service clients that process the translation requests submitted by the web form. There are three clients that work together:
  - Pub/Sub: When the web form is submitted by a user, the Pub/Sub client publishes a message with the request details. A subscription created in this tutorial relays those messages to the Cloud Run endpoint that you create to perform translations.
  - Translation: This client handles Pub/Sub requests by performing the translations.
  - Firestore: When the translation is complete, this client stores the request data along with the translation in Firestore. This client also reads the most recent requests on the main `/translate` endpoint.

## Understanding the Cloud Run code

- The Cloud Run app has dependencies on
  Firestore, Translation, and Pub/Sub.

  ```xml
  <dependency>
    <groupId>com.google.cloud</groupId>
    <artifactId>google-cloud-firestore</artifactId>
    <version>3.13.2</version>
  </dependency>
  <dependency>
    <groupId>com.google.cloud</groupId>
    <artifactId>google-cloud-translate</artifactId>
    <version>2.20.0</version>
  </dependency>

  <dependency>
    <groupId>com.google.cloud</groupId>
    <artifactId>google-cloud-pubsub</artifactId>
    <version>1.123.17</version>
  </dependency>
  ```
- The global Firestore, Translation, and
  Pub/Sub clients are initialized so they can be reused between
  invocations. That way, you don't have to initialize new clients for
  every invocation, which would slow down execution.

  ```java
  @WebListener("Creates Firestore and TranslateServlet service clients for reuse between requests.")
  public class BackgroundContextListener implements ServletContextListener {
    @Override
    public void contextDestroyed(javax.servlet.ServletContextEvent event) {}

    @Override
    public void contextInitialized(ServletContextEvent event) {
      String firestoreProjectId = System.getenv("FIRESTORE_CLOUD_PROJECT");
      Firestore firestore = (Firestore) event.getServletContext().getAttribute("firestore");
      if (firestore == null) {
        firestore =
            FirestoreOptions.getDefaultInstance().toBuilder()
                .setProjectId(firestoreProjectId)
                .build()
                .getService();
        event.getServletContext().setAttribute("firestore", firestore);
      }

      Translate translate = (Translate) event.getServletContext().getAttribute("translate");
      if (translate == null) {
        translate = TranslateOptions.getDefaultInstance().getService();
        event.getServletContext().setAttribute("translate", translate);
      }

      String topicId = System.getenv("PUBSUB_TOPIC");
      TopicName topicName = TopicName.of(firestoreProjectId, topicId);
      Publisher publisher = (Publisher) event.getServletContext().getAttribute("publisher");
      if (publisher == null) {
        try {
          publisher = Publisher.newBuilder(topicName).build();
          event.getServletContext().setAttribute("publisher", publisher);
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
  }
  ```
- The index handler (`/`) gets all existing translations from
  Firestore and fills an HTML template with the list:

  ```java
  @Override
  public void doGet(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {
    Firestore firestore = (Firestore) this.getServletContext().getAttribute("firestore");
    CollectionReference translations = firestore.collection("translations");
    QuerySnapshot snapshot;
    try {
      snapshot = translations.limit(10).get().get();
    } catch (InterruptedException | ExecutionException e) {
      throw new ServletException("Exception retrieving documents from Firestore.", e);
    }
    List<TranslateMessage> translateMessages = Lists.newArrayList();
    List<QueryDocumentSnapshot> documents = Lists.newArrayList(snapshot.getDocuments());
    documents.sort(Comparator.comparing(DocumentSnapshot::getCreateTime));

    for (DocumentSnapshot document : Lists.reverse(documents)) {
      String encoded = gson.toJson(document.getData());
      TranslateMessage message = gson.fromJson(encoded, TranslateMessage.class);
      message.setData(decode(message.getData()));
      translateMessages.add(message);
    }
    req.setAttribute("messages", translateMessages);
    req.setAttribute("page", "list");
    req.getRequestDispatcher("/base.jsp").forward(req, resp);
  }
  ```
- New translations are requested by submitting an HTML form. The request
  translation handler, registered at `/create`, parses
  the form submission, validates the request, and publishes a
  message to Pub/Sub:

  ```java
  @Override
  public void doPost(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {
    String text = req.getParameter("data");
    String sourceLang = req.getParameter("sourceLang");
    String targetLang = req.getParameter("targetLang");

    Enumeration<String> paramNames = req.getParameterNames();
    while (paramNames.hasMoreElements()) {
      String paramName = paramNames.nextElement();
      logger.warning("Param name: " + paramName + " = " + req.getParameter(paramName));
    }

    Publisher publisher = (Publisher) getServletContext().getAttribute("publisher");

    PubsubMessage pubsubMessage =
        PubsubMessage.newBuilder()
            .setData(ByteString.copyFromUtf8(text))
            .putAttributes("sourceLang", sourceLang)
            .putAttributes("targetLang", targetLang)
            .build();

    try {
      publisher.publish(pubsubMessage).get();
    } catch (InterruptedException | ExecutionException e) {
      throw new ServletException("Exception publishing message to topic.", e);
    }

    resp.sendRedirect("/");
  }
  ```
- The Pub/Sub subscription that you create forwards those requests
  to the Cloud Run endpoint, which parses the
  Pub/Sub message to get the text to translate and the desired
  target language. The Translation API then translates the string
  to the language you selected.

  ```java
  String body = req.getReader().lines().collect(Collectors.joining(System.lineSeparator()));

  PubSubMessage pubsubMessage = gson.fromJson(body, PubSubMessage.class);
  TranslateMessage message = pubsubMessage.getMessage();

  // Use Translate service client to translate the message.
  Translate translate = (Translate) this.getServletContext().getAttribute("translate");
  message.setData(decode(message.getData()));
  Translation translation =
      translate.translate(
          message.getData(),
          Translate.TranslateOption.sourceLanguage(message.getAttributes().getSourceLang()),
          Translate.TranslateOption.targetLanguage(message.getAttributes().getTargetLang()));
  ```
- The app stores the translation data in a new document it creates in
  Firestore.

  ```java
  // Use Firestore service client to store the translation in Firestore.
  Firestore firestore = (Firestore) this.getServletContext().getAttribute("firestore");

  CollectionReference translations = firestore.collection("translations");

  ApiFuture<WriteResult> setFuture = translations.document().set(message, SetOptions.merge());

  setFuture.get();
  resp.getWriter().write(translation.getTranslatedText());
  ```

## Deploying the Cloud Run app

1. Choose a Pub/Sub Topic Name and generate a
   Pub/Sub Verification Token using `uuidgen` or an online UUID
   generator such as [uuidgenerator.net](https://www.uuidgenerator.net/). This
   token will ensure that the Cloud Run endpoint only accepts
   requests from the Pub/Sub subscription you create.

   ```bash
   export PUBSUB_TOPIC=background-translate
   export PUBSUB_VERIFICATION_TOKEN=your-verification-token
   ```
2. Create a Pub/Sub topic:

   ```java
    gcloud pubsub topics create $PUBSUB_TOPIC
   ```
   - Replace `MY_PROJECT` in the `pom.xml` file with your Google Cloud project ID.
3. Build and deploy an image of your code to GCR (an image repository) with the
   [Jib](https://github.com/GoogleContainerTools/jib) Maven plugin.

   ```java
    mvn clean package jib:build
   ```
4. Deploy the app to Cloud Run:

   ```bash
   gcloud run deploy background --image gcr.io/MY_PROJECT/background \
         --platform managed --region us-central1 --memory 512M \
         --update-env-vars PUBSUB_TOPIC=$PUBSUB_TOPIC,PUBSUB_VERIFICATION_TOKEN=$PUBSUB_VERIFICATION_TOKEN
   ```

   Where `MY_PROJECT` is the name of the
   Google Cloud project you created. This command outputs the endpoint to
   which your Pub/Sub subscription pushes translation requests.
   Make note of this endpoint, because you will need it to create the
   Pub/Sub subscription, and you will visit the endpoint in a
   browser to request a new translation.

## Testing the app

After you've deployed the Cloud Run service, try requesting a
translation.

1. To view the app in your browser, go to the Cloud Run endpoint
   that you previously created.

   There is a page with an empty list of translations and a form to request
   new translations.
2. Click **+ Request Translation** , fill out the request form, and then click
   **Submit**.

3. Submission automatically brings you back to the `/translate` path, but the
   new translation might not appear yet. To refresh the page, click **Refresh**
   . There is a new
   row in the translation list. If you don't see a translation, wait a few more
   seconds and try again. If you still don't see a translation, see the
   next section about debugging the app.

## Debugging the app

If you cannot connect to your Cloud Run service or don't see new
translations, check the following:

- Check that the `gcloud run deploy` command successfully completed and
  didn't output any errors. If there were errors (for example,
  `message=Build failed`), fix them, and try running again.

- Check for errors in the logs:

  1. In the Google Cloud console, go to the Cloud Run page.

     [Go to Cloud Run page](https://console.cloud.google.com/run)
  2. Click the service name, **`background`**.

  3. Click **Logs**.

## Clean up

### Delete the project

> [!CAUTION]
> **Caution** : Deleting a project has the following effects:
>
> - **Everything in the project is deleted.** If you used an existing project for the tasks in this document, when you delete it, you also delete any other work you've done in the project.
> - **Custom project IDs are lost.** When you created this project, you might have created a custom project ID that you want to use in the future. To preserve the URLs that use the project ID, such as an `appspot.com` URL, delete selected resources inside the project instead of deleting the whole project.

1. In the Google Cloud console, go to the **Manage resources** page.

   [Go to Manage resources](https://console.cloud.google.com/iam-admin/projects)
2. In the project list, select the project that you want to delete, and then click **Delete**.
3. In the dialog, type the project ID, and then click **Shut down** to delete the project.

### Delete the Cloud Run services.

- Delete the Cloud Run services you created in this tutorial:

  ```bash
  gcloud run services delete --region=$region background
  ```

## What's next

- [Learn more about Cloud Run](https://docs.cloud.google.com/run/docs.md.txt).
- [Try Cloud Run](https://docs.cloud.google.com/run/docs/quickstarts/deploy-container.md.txt), which lets you run stateless containers on a fully managed environment or in your own Google Kubernetes Engine cluster.