Using Forms with Java for the App Engine Standard Environment

This part of the Bookshelf tutorial for Java shows how to access form data from Servlets.

This page is part of a multi-page tutorial. To start from the beginning and see instructions for setting up, go to Java Bookshelf App.

Running the app on your local machine

To run the app locally:

  1. In the getting-started-java/bookshelf-standard/2-structured-data directory, enter this command to start a local web server:

    mvn -Plocal clean appengine:devserver
  2. In your web browser, navigate to http://localhost:8080

Deploying the app to the App Engine standard environment

To deploy to the App Engine standard environment:

  1. Make sure you have fully exercised the Bookshelf application locally, especially creating at least one book and then clicking My Books. This creates a required Cloud Datastore index that is uploaded with Bookshelf. Note that building the application using clean deletes that local index, so be sure to do this on the build of the application that you are going to deploy.
  2. In the getting-started-java/bookshelf-standard/2-structured-data directory, enter this command to deploy the app:
    mvn appengine:update -Dappengine.appId=[YOUR-PROJECT-ID] -Dappengine.version=[YOUR-VERSION]
    Replace [YOUR-PROJECT-ID] with your project ID and [YOUR-VERSION] with your version, for example, 1, or 2, or some other or some other string value you want to use.
  3. In your web browser, enter this address:
    https://[YOUR-PROJECT-ID].appspot.com
    Replace [YOUR-PROJECT-ID] with your project ID.

After you update your app, you can redeploy the updated version by entering the same command you used to deploy the app the first time, specifying the same project ID and version: this overwrites the currently deployed app. If you specify a different version string in the update command line, the new deployment creates a new version of your app and promotes it to be the currently serving version.

You can reduce costs by deleting the non-serving versions of your app.

To delete an app version:

  1. In the GCP Console, go to the App Engine Versions page.

    Go to the Versions page

  2. Click the checkbox next to the non-default app version you want to delete.
  3. Click the Delete button at the top of the page to delete the app version.

For complete information about cleaning up billable resources, see the Cleaning up section in the final step of this tutorial.

Handling user submissions with forms

The add/edit form allows users to add and edit book submissions within the app.

Image of add/edit Form

When the user's browser requests /create, the Servlet Container Engine loads the CreateBookServlet and calls the doGet method. Then the request is forwarded to the /base.jsp servlet, which includes the /form.jsp servlet. After the user submits the form, the doPost method of CreateBookServlet is called.

In CreateBookServlet.java, the doGet method processes the request for a form and sets up attributes to be used by the JSP file:

@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
    IOException {
  req.setAttribute("action", "Add");          // Part of the Header in form.jsp
  req.setAttribute("destination", "create");  // The urlPattern to invoke (this Servlet)
  req.setAttribute("page", "form");           // Tells base.jsp to include form.jsp
  req.getRequestDispatcher("/base.jsp").forward(req, resp);
}

The base.jsp file serves as the foundation for the app's HTML pages. It starts by including the JSP Standard Tag Library. JSTL core tags are identified by tags starting with <c: and provide Variable, Flow control, URL management and miscellaneous support. JSTL functions are identified by tags starting with <fn: and provide Collections and String manipulation libraries. Here is a summary of JSTL tags used by the Bookshelf Application:

JSTL tag Description
<c:if ... Evaluate expression test and include content if true.
<c:choose> Conditional tag that establishes context for c:when and c:otherwise.
<c:when ... Include content if test is true.
<c:otherwise> If c:when is not true, include this content.
<c:import url="" /> Include the contents of the url within the page.
<c:out value="" /> Evaluate the expression for value and display an encoded version.
${fn:escapeXml()} Escape special characters for webpages.


<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<html lang="en">
  <head>
    <title>Bookshelf - Java on Google Cloud Platform</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
  </head>
  <body>
    <div class="navbar navbar-default">
      <div class="container">
        <div class="navbar-header">
          <div class="navbar-brand">Bookshelf</div>
        </div>
        <ul class="nav navbar-nav">
          <li><a href="/">Books</a></li>
          <c:if test="${isAuthConfigured}"><li><a href="/books/mine">My Books</a></li></c:if>
        </ul>
        <p class="navbar-text navbar-right">
          <c:choose>
          <c:when test="${not empty token}">
          <!-- using pageContext requires jsp-api artifact in pom.xml -->
          <a href="/logout">
            <c:if test="${not empty userImageUrl}">
              <img class="img-circle" src="${fn:escapeXml(userImageUrl)}" width="24">
            </c:if>
            ${fn:escapeXml(userEmail)}
          </a>
          </c:when>
          <c:when test="${isAuthConfigured}">
          <a href="/login">Login</a>
          </c:when>
          </c:choose>
        </p>
      </div>
    </div>
    <c:import url="/${page}.jsp" />
  </body>
</html>

The HTML form is created using JavaServer Pages, which is like a Java template engine, but runs as Servlets. The following JSP code specifies that the form include text input fields for Title, Author, Date Published, Description, Cover Image, and a Save button:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>
<div class="container">
  <h3>
    <c:out value="${action}" /> book
  </h3>

  <form method="POST" action="${destination}">

    <div class="form-group">
      <label for="title">Title</label>
      <input type="text" name="title" id="title" value="${fn:escapeXml(book.title)}" class="form-control" />
    </div>

    <div class="form-group">
      <label for="author">Author</label>
      <input type="text" name="author" id="author" value="${fn:escapeXml(book.author)}" class="form-control" />
    </div>

    <div class="form-group">
      <label for="publishedDate">Date Published</label>
      <input type="text" name="publishedDate" id="publishedDate" value="${fn:escapeXml(book.publishedDate)}" class="form-control" />
    </div>

    <div class="form-group">
      <label for="description">Description</label>
      <textarea name="description" id="description" class="form-control">${fn:escapeXml(book.description)}</textarea>
    </div>

    <div class="form-group ${isCloudStorageConfigured ? '' : 'hidden'}">
      <label for="image">Cover Image</label>
      <input type="file" name="file" id="file" class="form-control" />
    </div>

    <div class="form-group hidden">
      <label for="imageUrl">Cover Image URL</label>
      <input type="hidden" name="id" value="${book.id}" />
      <input type="text" name="imageUrl" id="imageUrl" value="${fn:escapeXml(book.imageUrl)}" class="form-control" />
    </div>

    <button type="submit" class="btn btn-success">Save</button>
  </form>
</div>

Form submission

The doPost method creates a Book object, reading in parameters from the form by the req.getParameter() calls, and supplies the Book object to a new BookDao object that adds the new Book entity to the data store. After the book is saved to the data store, the page is redirected to /read so the user can see what was just saved to data store. Session access and book creation will be discussed later:

@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
    IOException {
  Book book = new Book.Builder()
      .author(req.getParameter("author"))   // form parameter
      .description(req.getParameter("description"))
      .publishedDate(req.getParameter("publishedDate"))
      .title(req.getParameter("title"))
      .imageUrl(null)
      .build();

  BookDao dao = (BookDao) this.getServletContext().getAttribute("dao");
  try {
    Long id = dao.createBook(book);
    resp.sendRedirect("/read?id=" + id.toString());   // read what we just wrote
  } catch (Exception e) {
    throw new ServletException("Error creating book", e);
  }
}

Send feedback about...