Backend API with Maven

We recommend that you use the latest version of this feature, which is renamed to Cloud Endpoints Frameworks for App Engine. This new version supports App Engine standard environment, provides lower latency, and has better integration with App Engine. For more details, see Migrating to 2.0.

In this tutorial, you'll go through a code walkthrough of a Maven project containing a complete Cloud Endpoints backend API and a sample web client that accesses the API. This sample demonstrates many of the core features supported for backend APIs:

  • A simple HTTP GET method that retrieves one of two canned responses based on the user's choice (Get Greeting).
  • A simple HTTP GET method that retrieves all of the canned responses (List Greeting).
  • A POST method that provides a user-supplied greeting and multiplier to the backend API which then returns the greeting repeated the number of times specified by the multiplier (Multiply Greeting).
  • An OAuth protected method that requires a signed-in user, which returns the user's email address (Authenticated Greeting).

This walkthrough focuses on the backend API, and doesn't go into detail about the web client included with the project. You can find a full description of the web client in the web client tutorial.

The UI provided by the web client included with the sample project looks like this:

Hello Endpoints UI

Objectives

A complete Hello Endpoints backend API that demonstrates common tasks, such as:

  • Handling HTTP GET requests
  • Handling HTTP POST requests
  • Protecting methods with OAuth 2.0
  • Deploying the backend API to production App Engine

Costs

App Engine has free a level of usage. If your total usage of App Engine is less than the limits specified in the App Engine free quota, there is no charge for doing this tutorial.

Before you begin

  1. Set up your environment and install the supported versions of Maven and Java as specified in Using Apache Maven and the App Engine Plugin.

  2. Create or select a Cloud Platform project in the Cloud Platform Console and then ensure that project includes an App Engine application:

    Go to App Engine

    The Dashboard opens if an App Engine application already exists in your project. Otherwise, you are prompted to choose the region where you want your App Engine application located.

  3. Make a note of your project ID because you'll need to use it later.

Cloning the sample project

Clone the Hello Endpoints sample from GitHub:

git clone https://github.com/GoogleCloudPlatform/appengine-endpoints-helloendpoints-java-maven

Alternatively, you can download the sample as a zip file and extract it.

Viewing the project layout and files

If you execute a tree command or equivalent on the directory appengine-endpoints-helloendpoints-java-maven, the following represents the structure of the project:

Maven Project Layout

You'll learn about these these files during the walkthrough:

File Description
appengine-web.xml Used to specify the project ID and the application version.
web.xml Does all the mappings required for the backend API servlet.
bootstrap Contains CSS for the sample web client. Not required, but used for this demo.
base.js The sample web client for this backend API.
Constants.java Contains client IDs used for clients and authorization.
Greetings.java The backend API servlet.
HelloGreeting.java The JavaBean used to send Greetings data through the endpoint.

Creating OAuth 2.0 client IDs for the backend

You need to create a client ID for each client, in this example for the web client. The client ID is added to the backend API (in Constants.java) and to the web client.

To create a client ID:

  1. Open the Credentials page for your project:
    Go to the Credentials page

  2. Click Create credentials > OAuth client ID.

  3. Click Configure consent screen

  4. Supply a product name, which you can change later, and click Save.

  5. Select Web application as the application type to display the settings for web clients.

  6. Specify a name for the web client.

  7. In the textbox labeled Authorized JavaScript origins, specify the App Engine URL of your backend API, for example, https://your_project_id.appspot.com, replacing your_project_id with your actual App Engine project ID. Be sure to specify https:// in the URL, not http.

  8. Click Create.

Note the client ID that is generated. This is the client ID you need to use in your backend and in your client application. You can always return to the Credentials page later to view the client ID.

Adding the client ID to backend API and to web client

To add the client ID to backend and client:

  1. Edit the file appengine-endpoints-helloendpoints-java-maven/src/main/java/com/example/helloendpoints/Constants.java

  2. For WEB_CLIENT_ID, replace the value replace this with your web client ID with the client ID, and save your changes. Ignore the constants for Android and iOS because you won't use these in this walkthrough.

  3. Edit the file appengine-endpoints-helloendpoints-java-maven/src/main/webapp/js/base.js.

  4. Edit the line starting with google.devrel.samples.hello.CLIENT_ID = so that it contains the client ID.

Adding the project ID to the application

You must add the project ID obtained when you created your project to your app before you can deploy.

To add the project ID:

  1. Edit the file appengine-endpoints-helloendpoints-java-maven/src/main/webapp/WEB-INF/appengine-web.xml.

  2. For <application>, replace the value your-app-id with your project ID.

  3. Save your changes.

Building and running the API locally

To build and run the backend API, and test it locally using the sample web client:

  1. In the root directory of the project, appengine-endpoints-helloendpoints-java-maven/ build the project by invoking the command

    mvn clean install
    

    Wait for the project to build. When the project successfully finishes, you will see a message similar to this one:

    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time: 14.846s
    [INFO] Finished at: Wed April 13 09:43:09 PDT 2016
    [INFO] Final Memory: 24M/331M
    
  2. Start the app in the local development server by invoking:

    mvn appengine:devserver
    
  3. In your browser, visit the URL http://localhost:8080 to view the web client app.

  4. In the Greeting ID text box, supply a value of 0 or 1, then click Submit. (The sample backend has only two stored messages.) You'll see hello world! or goodbye world!, depending on the value.

  5. Under List Greetings, click Submit to list out those same two stored greetings.

  6. Under Multiply Greetings, supply any text in the Greeting textbox and the number of repetitions in the Count textbox, then click Submit.

  7. Note that the Authenticated Greeting feature only works in deployment.

  1. Edit the file src/main/webapp/WEB-INF/appengine-web.xml to set <application> to the project ID you obtained earlier during Setup. (Or, look up the Client ID for web application in the Google Cloud Platform Console.)

Deploying to App Engine

After you finish testing, you can deploy to App Engine:

To deploy to App Engine:

  1. From the main project directory, helloendpoints/, invoke the command

    mvn appengine:update
    
  2. Follow the prompts: when you are presented with a browser window containing a code, copy it to the terminal window.

  3. Wait for the upload to finish, then visit the URL you specified above for the Authorized Javascript Origins (https://your_project_id.appspot.com).

    If you don't see the web client app, or if the client doesn't behave as expected, check for a successful deployment.

Hello Endpoints code walkthrough

In this part of the tutorial, you'll learn more about the code in the sample backend API.

Imports

The following imports are needed for the backend API, in Greetings.java:

package com.example.helloendpoints;

import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
import com.google.api.server.spi.response.NotFoundException;
import com.google.appengine.api.users.User;

import java.util.ArrayList;

import javax.inject.Named;

You must always import com.google.api.server.spi.config.Api because you must always annotate your API class with @Api, as shown in the snippet. This sample also imports com.google.api.server.spi.config.ApiMethod to illustrate its use in changing the name of a method, but this is optional; all public methods of the class with the @Api annotation are automatically exposed in the backend API.

The sample imports com.google.appengine.api.users.User because it has a method protected by OAuth 2.0. The import import javax.inject.Named is required for the request parameters.

API definition configuration using @Api

Let's look at the @Api annotation in Greetings.java:

/**
 * Defines v1 of a helloworld API, which provides simple "greeting" methods.
 */
@Api(name = "helloworld",
    version = "v1",
    scopes = {Constants.EMAIL_SCOPE},
    clientIds = {Constants.WEB_CLIENT_ID, Constants.ANDROID_CLIENT_ID, Constants.IOS_CLIENT_ID},
    audiences = {Constants.ANDROID_AUDIENCE}
)
public class Greetings {

  public static ArrayList<HelloGreeting> greetings = new ArrayList<HelloGreeting>();

  static {
    greetings.add(new HelloGreeting("hello world!"));
    greetings.add(new HelloGreeting("goodbye world!"));
  }

This API is implemented in the single class called Greetings, with the API name and version always required. If you need to create an API that exposes multiple classes, you must use the same @Api annotation for each class. The name and version would have to be identical for each class.

The sample protects a method by OAuth 2.0, which means that the scopes attribute is required; this must be set to the value https://www.googleapis.com/auth/userinfo.email, which is what Constants.EMAIL_SCOPE resolves to. This scope lets OAuth 2.0 work with Google Accounts.

Also, because of the OAuth 2.0 protection, you must supply a list of clients allowed to access the protected method in the clientIDs attribute. The sample suggests one way to do this, with lists of client IDs in the Constants.java file. That file also contains dummy values for Android and iOS clients to show that this client ID list could contain all the supported clients. Only clients in this list can access the protected method.

The audiences attribute is set to the backend API's web client. This attribute must be set if there are any Android clients; it is not used for non-Android clients.

Inside the class, notice the lines that set up an ArrayList of stored HelloGreeting (defined in HelloGreeting.java) JavaBean objects that are returned from the methods. In Endpoints, methods can only return Objects (treated as JavaBean objects) or a collection of Objects, which are converted to JSON to form the response.

A simple HTTP GET request

The following lines return text greetings. Both getGreeting and listGreeting are public, so they will be exposed in the backend API:

public HelloGreeting getGreeting(@Named("id") Integer id) throws NotFoundException {
  try {
    return greetings.get(id);
  } catch (IndexOutOfBoundsException e) {
    throw new NotFoundException("Greeting not found with an index: " + id);
  }
}

public ArrayList<HelloGreeting> listGreeting() {
  return greetings;
}

The getGreeting method serves an incoming HTTP GET request that has a numeric value indicating the user's choice of greeting:

  • A value of 0 returning the first value, hello world!.
  • A value of 1 returning the second value, goodbye world!.

All other values will return an error. Notice that the @Named attribute must be used for the incoming parameter since it is not an entity type.

The listGreeting method returns all the stored greetings in the array list.

A simple HTTP POST request

The following lines of code handle an incoming POST request containing a user-supplied greeting and integer, and returns the greeting repeated the number of times specified by the integer:

@ApiMethod(name = "greetings.multiply", httpMethod = "post")
public HelloGreeting insertGreeting(@Named("times") Integer times, HelloGreeting greeting) {
  HelloGreeting response = new HelloGreeting();
  StringBuilder responseBuilder = new StringBuilder();
  for (int i = 0; i < times; i++) {
    responseBuilder.append(greeting.getMessage());
  }
  response.setMessage(responseBuilder.toString());
  return response;
}

This method is annotated by @ApiMethod to override the default name that is generated by Endpoints. Notice that Endpoints prepends the class name (greetings, lowercase) to the method name when it generates the method name in the backend API: greetings.insertGreeting. When you override this value using the method annotation, the prepending does not take place, so you