Using Forms with Java

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/2-structured-data directory, enter this command to start a local web server:

    mvn -Plocal clean jetty:run-exploded -DprojectID=[YOUR-PROJECT-ID]
  2. In your web browser, navigate to http://localhost:8080

Deploying the app to the App Engine flexible environment

  1. Enter this command to deploy the app:

    mvn appengine:deploy -DprojectID=YOUR-PROJECT-ID
    
  2. In your web browser, enter this address. Replace [YOUR_PROJECT_ID] with your project ID:

    https://[YOUR_PROJECT_ID].appspot-preview.com
    

If you update your app, you can deploy the updated version by entering the same command you used to deploy the app the first time. The new deployment creates a new version of your app and promotes it to the default version. The older versions of your app remain, as do their associated VM instances. Be aware that all of these app versions and VM instances are billable resources.

You can reduce costs by deleting the non-default 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, there are two annotations. The @MultipartConfig annotation tells the Jetty Servlet container that the app uses multipart/form-data MIME type. for form data. The @WebServlet annotation identifies the class as a Servlet and specifies addional attributes. In this case, the Servlet has a name of create and a urlPattern of /create. Additional parameters include asyncSupported, which specifies that the container Servlet can process requests asyncronously, and loadOnStartup, which tells the container to load the servlet and execute its init() method:

@MultipartConfig
@WebServlet(name = "create", urlPatterns = {"/create"})

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}" enctype="multipart/form-data">

    <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 CloudStorageHelper and a BookDao. The form parameters are accessed by the req.getParameter() functions, and the page is redirected to /read to see what we just wrote. Session access and book creation will be discussed later:

@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
    IOException {
  BookDao dao = (BookDao) this.getServletContext().getAttribute("dao");
  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();
  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...