Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Hosted-services / google-cloud

Google App Engine - Java Tutorial

4.91/5 (3 votes)
26 Nov 2014CC (Attr 3U)14 min read 16.1K  
By the end of the tutorial, you will have implemented a working application, a simple guest book that lets users post messages to a public message board.

This article is for our sponsors at CodeProject. These articles are intended to provide you with information on products and services that we consider useful and of value to developers

Introduction

Welcome to Google App Engine! Creating an App Engine application is easy, and only takes a few minutes. And it's free to start: upload your app and share it with users right away, at no charge and with no commitment required.

Google App Engine applications can be written in the Java, Python, Go or PHP programming languages. This tutorial covers Java. If you would prefer to use Python, Go or PHP to build your applications, see the Python, Go or PHP guides.

What you'll learn

In this tutorial, you will learn how to do the following:

  • Use Apache Maven to easily create an App Engine project with required layout and files.
  • Build out the project to add a simple app that lets the user post a greeting.
  • Integrate the app with Google Accounts for user authentication.
  • Save user data to Datastore with the user's email and store the greetings in a database.
  • Build and test your work on the local dev server.
  • Deploy the app to production App Engine.

By the end of the tutorial, you will have implemented a working application, a simple guest book that lets users post messages to a public message board.

Tutorial Setup

To complete this tutorial, you'll need the proper versions of Java and Maven.

Required Java version

We recommend using Java 7, preferably the Enterprise Edition.

Note: You might be able to use Java 8 if you use the Java 7 flags as follows: -source 1.7 -target 1.7

Downloading Java

If you don't have Java, follow these instructions to download the Java Development Kit (JDK) for Java version 7:

  1. Download and install it.

  2. Set your JAVA_HOME environment variable. If you are a bash user

    1. For a typical Linux installation, add a line similar to the following to your .bashrc file:

      export JAVA_HOME=/usr/local/tools/java/jdk1.7.0_45.jdk
    2. If you use Mac OSX and the default Terminal app, your shell session doesn't load .bashrc by default. So you may need to add a line similar to the following to your .bash_profile:

      [ -r ~/.bashrc ] && source ~/.bashrc
    3. If you use Mac OSX but don't use the default terminal app, for example, you use a terminal management app such as tmux, you may need to add a line similar to the following line to your .bashrc file:

      export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.7.0_45.jdk/Contents/Home

Required Maven version

This tutorial requires Apache Maven 3.1 or greater, so you'll need to download it if it isn't installed on your machine. To determine whether Maven is installed and which version you have, invoke the following command:

mvn -v

This command should display a long string of information beginning with something like Apache Maven 3.1.0.

If Maven isn't installed on your machine, you can download Maven from the Apache Maven website, and install it using the Apache Maven instructions.

The Google App Engine SDK and Maven

When you use Maven, you don't need to download the Google App Engine SDK. Maven does that for you. You'll also use Maven to test your app locally and upload (deploy) it to production App Engine.

Note: After you complete the tutorial, you can learn more about the App Engine Maven plugin capabilities by visiting App Engine Maven plugin goals.

Creating the Project

Important: The instructions in this tutorial assume that you are using a terminal on your local machine and running a browser on that same machine to test against the local development server.

To create a new project, you'll need to use an App Engine-provided Maven App Engine artifact called appengine-skeleton-archetype, as described in Creating the project. The App Engine Maven artifact is what creates the project layout and files required to deploy and run on App Engine.

After project creation, you'll need to configure the new project, as described in Configuring the project.

For more on using Maven with App Engine, see the Using Apache Maven Guide. As an alternative to Maven, you could instead use Eclipse via the Google Plugin For Eclipse or you could use Apache Ant.

Creating the project

To create the project:

  1. You'll need a an application ID (project ID) in order to deploy your app to production App Engine. Create a new project as follows:

    1. Visit Google Developers Console in your web browser, and click Create Project.
    2. Supply the project name Guestbook and accept the project ID that is auto-generated for you.
    3. Click Create.

    Make a note of the project ID, since you'll need to supply it as the application ID in the next step.

    Note: Alternatively, you can use the ID from an existing project in the Google Developers Console if you want to use it for this project.

  2. Change to a directory where you want to build your project and invoke Maven as follows:

    mvn archetype:generate -Dappengine-version=1.9.15 -Dapplication-id=your-app-id -Dfilter=com.google.appengine.archetypes:

    replacing your-app-id with the project ID value you obtained in the previous step.

  3. Select from the artifact list by supplying the number corresponding to com.google.appengine.archetypes:appengine-skeleton-archetype.

  4. Select the most recent version from the displayed list of available archetype versions by accepting the default.

  5. When prompted to Define value for property 'groupId', supply the desired namespace for your app; to keep this tutorial in sync with the source files at GitHub, specify com.example.guestbook.

  6. When prompted to Define value for property 'artifactId', supply the desired project name; to keep this tutorial in sync with the source files at GitHub, use guestbook.

  7. When prompted to Define value for property 'version', accept the default value.

  8. When prompted to Define value for property 'package', supply your preferred package name (or accept the default). The generated Java files will have the package name you specify here. Note that the source files at GitHub use the package name com.example.guestbook, so you should use that as the package name if you want to keep in sync with the sample source.

  9. When prompted to confirm your choices, accept the default value (Y).

  10. Wait for the project to finish generating. then change directories to the new project directory.

  11. At this point you won't have any source code. But build the project anyway to download any required libraries by invoking

    mvn clean install
    
  12. Wait for the project to build. When the project successfully finishes you will see a message similar to this one:

    [INFO] --------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] --------------------------------------------------
    [INFO] Total time: 1:16.656s
    [INFO] Finished at: Mon Sep 15 11:42:22 PDT 2014
    [INFO] Final Memory: 16M/228M
    [INFO] --------------------------------------------------
    
  13. Examine the project layout:

    Image 1

    • You'll add your own application Java classes to src/main/java/...
    • You'll configure your application using the file src/main/webapp/WEB-INF/appengine.web.xml
    • You'll configure your application deployment using the file src/main/webapp/WEB-INF/web.xml

    We'll describe what to do inside these subdirectories later.

You are now ready to add your own application code and UI.

Adding Application Code and UI

In this part of the tutorial, we'll create an app that integrates with Google Accounts so users can sign in using their Google accounts. In App Engine, the integration with Google accounts is achieved via the App Engine Users service. We'll use this to personalize our application's greeting.

This is what the app will look like, in this part of the tutorial (we'll add more later):

Image 2

This application consists of these main logical "parts":

  • A JSP page that the user interacts with to make requests to the app.
  • A servlet named GuestbookServlet.java that prompts the user to log in and then displays a personalized greeting.

Note: This part of the tutorial is an "intermediate" version of the app we'll be building. It doesn't have the datastore logic and UI yet. So, if you look in the GitHub View Project links we provide, be sure to go to phase1, not to final, within the project.

Creating the UI using JSP

To create the UI:

  1. In guestbook/src/main/webapp, create a file named guestbook.jsp with the following contents:

    HTML
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%@ page import="com.google.appengine.api.users.User" %>
    <%@ page import="com.google.appengine.api.users.UserService" %>
    <%@ page import="com.google.appengine.api.users.UserServiceFactory" %>
    <%@ page import="java.util.List" %>
    <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
    
    <html>
    <head>
        <link type="text/css" rel="stylesheet" href="https://cloud.google.com/stylesheets/main.css"/>
    </head>
    
    <body>
    
    <%
        String guestbookName = request.getParameter("guestbookName");
        if (guestbookName == null) {
            guestbookName = "default";
        }
        pageContext.setAttribute("guestbookName", guestbookName);
        UserService userService = UserServiceFactory.getUserService();
        User user = userService.getCurrentUser();
        if (user != null) {
            pageContext.setAttribute("user", user);
    %>
    
    <p>Hello, ${fn:escapeXml(user.nickname)}! (You can
        <a href="<%= userService.createLogoutURL(request.getRequestURI()) %>">sign out</a>.)</p>
    <%
    } else {
    %>
    <p>Hello!
        <a href="<%= userService.createLoginURL(request.getRequestURI()) %>">Sign in</a>
        to include your name with greetings you post.</p>
    <%
        }
    %>
    
    
    <form action="/guestbook.jsp" method="get">
        <div><input type="text" name="guestbookName" value="${fn:escapeXml(guestbookName)}"/></div>
        <div><input type="submit" value="Switch Guestbook"/></div>
    </form>
    
    </body>
    </html>

    Notice the imports for the App Engine Users service. Also, by default, any file in webapp/ or in a subdirectory other than WEB-INF/ that has the s file suffix .jsp is automatically mapped to a URL path consisting of the path to the .jsp file, including the filename. This JSP will be mapped automatically to the URL /guestbook.jsp.

  2. In guestbook/src/main/webapp, create a directory named stylesheets, and create a file named main.css with the following contents:

    body {
        font-family: Verdana, Helvetica, sans-serif;
        background-color: #FFFFCC;
    }
  3. Proceed to web.xml configuration, described next.

Configuring web.xml

To configure the web.xml file:

  1. In guestbook/src/main/webapp/WEB-INF, open web.xml in a text editor, and replace the contents of the file with the following:

    XML
    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE web-app PUBLIC
     "-//Oracle Corporation//DTD Web Application 2.3//EN"
     "http://java.sun.com/dtd/web-app_2_3.dtd">
    
    <web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5">
        <servlet>
            <servlet-name>guestbook</servlet-name>
            <servlet-class>com.example.guestbook.GuestbookServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>guestbook</servlet-name>
            <url-pattern>/guestbook</url-pattern>
        </servlet-mapping>
        <welcome-file-list>
            <welcome-file>guestbook.jsp</welcome-file>
        </welcome-file-list>
    </web-app>

    This configuration maps the servlet to its serving location and specifies the JSP file you created to be the application home page. For more information about the web.xml file and how to use it, see the Deployment Descriptor page.

  2. Proceed to servlet creation, described next.

Creating the servlet GuestbookServlet.java

App Engine Java applications use the Java Servlet API to interact with the web server. An HTTP servlet is an application class that can process and respond to web requests. This class extends either the javax.servlet.GenericServlet class or the javax.servlet.http.HttpServlet class.

To create the servlet:

  1. In guestbook/src/main/java, create the subdirectories com/example/guestbook by invoking the following command (in a Linux or Mac OSX terminal window):

    mkdir -p com/example/guestbook
  2. In guestbook/src/main/java/com/example/guestbook, create a file named GuestbookServlet.java.

  3. Add the following contents to the file:

    Python
    package com.example.guestbook;
    
    import com.google.appengine.api.users.User;
    import com.google.appengine.api.users.UserService;
    import com.google.appengine.api.users.UserServiceFactory;
    
    import java.io.IOException;
    import java.util.Properties;
    
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class GuestbookServlet extends HttpServlet {
      @Override
      public void doGet(HttpServletRequest req, HttpServletResponse resp)
          throws IOException {
        if (req.getParameter("testing") == null) {
          resp.setContentType("text/plain");
          resp.getWriter().println("Hello, this is a testing servlet. \n\n");
          Properties p = System.getProperties();
          p.list(resp.getWriter());
    
        } else {
          UserService userService = UserServiceFactory.getUserService();
          User currentUser = userService.getCurrentUser();
    
          if (currentUser != null) {
            resp.setContentType("text/plain");
            resp.getWriter().println("Hello, " + currentUser.getNickname());
          } else {
            resp.sendRedirect(userService.createLoginURL(req.getRequestURI()));
          }
        }
      }
    }

    The servlet checks whether the user is logged on. If the user is logged on, the servlet displays a personalized greeting; otherwise the user is redirected to the logon page.

    Note: The development server knows how to simulate the Google Accounts sign-in facility. So when this app runs on your local machine, the redirect goes to a page where you can enter any email address to simulate an account sign-in. However, when the app runs on production App Engine, the redirect goes to the actual Google Accounts screen.

  4. You project should look like this:

    Image 3

    Your app is now ready to build and test the app locally on the development server, which is described next.

Building and testing the app

To build and test the app:

  1. Change directory to guestbook, and invoke the command:

    mvn clean install

    Wait for the build to complete.

  2. Run the app in the development server on your local machine by invoking this command from /guestbook:

    mvn appengine:devserver

    Wait for the success message, which looks something like this:

    [INFO] INFO: The admin console is running at http://localhost:8080/_ah/admin
    [INFO] Aug 18, 2014 5:35:04 PM com.google.appengine.tools.development.DevAppServerImpl doStart
    [INFO] INFO: Dev App Server is now running
  3. In a browser that is running on the same machine as your terminal window, visit localhost:8080 to access the app on your local machine. If prompted, click Sign in.

    Note: If you get a runtime error when you first visit localhost:8080, referring to a restricted class, for example, java.lang.NoClassDefFoundError: java.nio.charset.StandardCharsets is a restricted class., check the settings for guestbook/pom.xml. The App Engine version must be set to the most recent App Engine SDK version: 1.9.15. If you get a 403 error, check the web.xml to make sure the file is configured correctly as described above under "Configuring web.xml".

  4. In the login form supply an email or accept the test@example.com dummy email and click Log In. (When run locally, there is no validity check.)

  5. Observe that the greeting now displays your email address.

  6. You have successfully created a simple Java App Engine app. You are ready to do something a bit more useful next, adding the ability to accept and store user posts in a database.

Storing Data in Datastore

In this part of the tutorial, we'll extend the simple application created earlier by adding UI that allows the user to POST greetings and display previously posted greetings. To support the UI, we'll add a new servlet that handles the POST interaction with the database to store the data.

The database we are using is App Engine Datastore, which is a NoSQL, schema-less database available to your app without any further sign-up or activation. Because Datastore is schema-less, you don't need to define objects in the database in advance before you start writing to Datastore. You simply import the required Datastore classes via your import statements and write your data. (For a complete description of this powerful storage system, visit the App Engine Datastore pages.)

Note: This tutorial uses the low-level Datastore API, for the sake of simplicity. App Engine Datastore also includes implementations of the Java Data Objects (JDO) and Java Persistence API (JPA) interfaces, if you wish to use those. You could alternatively use Objectify or Slim3.

This is what the app will look like after we add the UI and support for Datastore:

Image 4

We'll make these changes to our existing application :

  • Edit the JSP page to allow users to post greetings to the datastore and to display all the greetings currently stored.
  • Create a new servlet named SignGuestbookServlet.java that handles the interactions with Datastore.
  • Add required entries in web.xml so requests can be routed to the new servlet.

Note: This part of the tutorial is the completed, or "final", version of the app we'll be building, including the datastore logic and UI. So, if you look in the GitHub View Project links we provide, be sure to go to final, not to phase1, within the project.

Adding data POST and display UI to JSP

To add the new UI pieces:

  1. In guestbook/src/main/webapp, open guestbook.jsp in an editor to add the following imports to the imports section:

    HTML
    <%@ page import="com.google.appengine.api.datastore.DatastoreService" %>
    <%@ page import="com.google.appengine.api.datastore.DatastoreServiceFactory" %>
    <%@ page import="com.google.appengine.api.datastore.Entity" %>
    <%@ page import="com.google.appengine.api.datastore.FetchOptions" %>
    <%@ page import="com.google.appengine.api.datastore.Key" %>
    <%@ page import="com.google.appengine.api.datastore.KeyFactory" %>
    <%@ page import="com.google.appengine.api.datastore.Query" %>
  2. Just above the line <form action="/guestbook.jsp" method="get"> add the following code:

    ASP.NET
    <%
        DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
        Key guestbookKey = KeyFactory.createKey("Guestbook", guestbookName);
        // Run an ancestor query to ensure we see the most up-to-date
        // view of the Greetings belonging to the selected Guestbook.
        Query query = new Query("Greeting", guestbookKey).addSort("date", Query.SortDirection.DESCENDING);
        List<Entity> greetings = datastore.prepare(query).asList(FetchOptions.Builder.withLimit(5));
        if (greetings.isEmpty()) {
    %>
    <p>Guestbook '${fn:escapeXml(guestbookName)}' has no messages.</p>
    <%
    } else {
    %>
    <p>Messages in Guestbook '${fn:escapeXml(guestbookName)}'.</p>
    <%
        for (Entity greeting : greetings) {
            pageContext.setAttribute("greeting_content",
                    greeting.getProperty("content"));
            if (greeting.getProperty("user") == null) {
    %>
    <p>An anonymous person wrote:</p>
    <%
    } else {
        pageContext.setAttribute("greeting_user",
                greeting.getProperty("user"));
    %>
    <p><b>${fn:escapeXml(greeting_user.nickname)}</b> wrote:</p>
    <%
        }
    %>
    <blockquote>${fn:escapeXml(greeting_content)}</blockquote>
    <%
            }
        }
    %>
    
    <form action="/sign" method="post">
        <div><textarea name="content" rows="3" cols="60"></textarea></div>
        <div><input type="submit" value="Post Greeting"/></div>
        <input type="hidden" name="guestbookName" value="${fn:escapeXml(guestbookName)}"/>
    </form>

    The query-related code results in a query when the page is loaded; Datastore is searched for all greetings currently stored for this app in Datastore, and lists them in the UI.

    The form-related part of this code sends a POST request containing a new greeting from the user. That POST is handled by the post handler servlet SignGuestbookServlet.java, which you will create in the next procedure.

Creating the servlet SignGuestbookServlet.java

To create the servlet:

  1. In guestbook/src/main/java/com/example/guestbook, create a file named SignGuestbookServlet.java.

  2. Add the following contents to the file:

    Python
    package com.example.guestbook;
    
    import com.google.appengine.api.datastore.DatastoreService;
    import com.google.appengine.api.datastore.DatastoreServiceFactory;
    import com.google.appengine.api.datastore.Entity;
    import com.google.appengine.api.datastore.Key;
    import com.google.appengine.api.datastore.KeyFactory;
    import com.google.appengine.api.users.User;
    import com.google.appengine.api.users.UserService;
    import com.google.appengine.api.users.UserServiceFactory;
    
    import java.io.IOException;
    import java.util.Date;
    
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class SignGuestbookServlet extends HttpServlet {
      @Override
      public void doPost(HttpServletRequest req, HttpServletResponse resp)
          throws IOException {
        UserService userService = UserServiceFactory.getUserService();
        User user = userService.getCurrentUser();
    
        String guestbookName = req.getParameter("guestbookName");
        Key guestbookKey = KeyFactory.createKey("Guestbook", guestbookName);
        String content = req.getParameter("content");
        Date date = new Date();
        Entity greeting = new Entity("Greeting", guestbookKey);
        greeting.setProperty("user", user);
        greeting.setProperty("date", date);
        greeting.setProperty("content", content);
    
        DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
        datastore.put(greeting);
    
        resp.sendRedirect("/guestbook.jsp?guestbookName=" + guestbookName);
      }
    }

    This servlet is the POST handler for the greetings posted by users. It takes the the greeting (content) from the incoming request and stores it in Datastore as an entity called Greeting, and stores properties along with the Greeting, such as the user who posted the greeting and the date it was posted. (For more information about entities in App Engine, see Entities, Properties, and Keys).

  3. Open guestbook/src/main/webapp/WEB-INF/web.xml and ensure the new servlets have URLs mapped. The final version should look like this:

    XML
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
        <servlet>
            <servlet-name>sign</servlet-name>
            <servlet-class>com.example.guestbook.SignGuestbookServlet</servlet-class>
        </servlet>
        <servlet>
            <servlet-name>guestbook</servlet-name>
            <servlet-class>com.example.guestbook.GuestbookServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>sign</servlet-name>
            <url-pattern>/sign</url-pattern>
        </servlet-mapping>
        <servlet-mapping>
            <servlet-name>guestbook</servlet-name>
            <url-pattern>/guestbook</url-pattern>
        </servlet-mapping>
        <welcome-file-list>
            <welcome-file>guestbook.jsp</welcome-file>
        </welcome-file-list>
    </web-app>
  4. You are ready to build and test the app locally on the development server, which is described next.

Building and testing the app

To build and test the app:

  1. Change directory to guestbook, and invoke the command:

    mvn clean install

    Wait for the build to complete.

  2. Run the app in the development server by invoking this command:

    mvn appengine:devserver

    Wait for the success message, which looks something like this:

    INFO] INFO: The admin console is running at http://localhost:8080/_ah/admin
    [INFO] Aug 18, 2014 5:35:04 PM com.google.appengine.tools.development.DevAppServerImpl doStart
    [INFO] INFO: Dev App Server is now running
  3. In a browser running on the same machine as your terminal window, visit localhost:8080 to access the app. If you aren't already signed-in, click Sign in, supply an email, and click Log In.

  4. Supply a text message in the textbox and click Post Greeting. Observe that the greeting is now displayed in the greetings list under your email name.

Creating indexes

When you run the development server and exercise your app, indexes required for operation in production App Engine are automatically generated in guestbook/target/guestbook-1.0-SNAPSHOT/WEB-INF/appengine-generated/datastore-indexes-auto.xml. You'll also notice the file local_db.binat that same location: this is local storage for the development server to persist your app data between development server sessions. The file datastore-indexes-auto.xml is automatically uploaded along with your app; local_db.bin is not uploaded.

Important: Be aware that running mvn clean install clears the datastore-indexes-auto.xml file; if you run it on your app prior to uploading to production App Engine, you won't get required indexes and you'll experience a runtime error.

For complete information about indexes, see Datastore Indexes.

Uploading Your Application

After you finish testing and running the app on the development server, which also generates required indexes, you need to upload the app to production App Engine.

Important: you must generate any required indexes before uploading your app.

To upload the app:

  1. Open the file guestbook/target/guestbook-1.0-SNAPSHOT/WEB-INF/appengine-generated/datastore-indexes-auto.xml in an editor and make sure that it contains the following:

    <datastore-indexes>
        <datastore-index kind="Greeting" ancestor="true" source="manual">
            <property name="date" direction="desc"/>
        </datastore-index>
    </datastore-indexes>

    Typically, you have to change source="auto" to source="manual".

    Note: If needed indexes are not uploaded, your app may fail at runtime. However, in this case, you can address this by examining the error message: the needed indexes are included in the error message. Copy those index entries from the error message into datastore-indexes-auto.xml and run the update command again.

  2. Change directory to guestbook, and invoke Maven as follows:

    mvn appengine:update
  3. The first time you upload, you'll be prompted to supply login information and you will be led through the login process via the browser. Follow the prompts. You may need to copy the success code from the browser into the command line at the end of the login process. (This information is remembered after you do it once.)

  4. Wait for the upload to complete. If the upload succeeds, you'll see a message similar to this one:

    98% Uploading index definitions.
    
    Update for module default completed successfully.
    Success.
    Cleaning up temporary files for module default...
  5. In your browser, visit the url https://<project-ID>.appspot.com to run the application hosted on App Engine, replacing <project-ID> with your project ID.

 

Except as otherwise noted, the code samples of this page is licensed under the Apache 2.0 License.

License

This article, along with any associated source code and files, is licensed under The Creative Commons Attribution 3.0 Unported License