ZOFTINO.COM android and web dev tutorials

Google App Engine Java Application Example using Maven

Google App Engine allows you to easily build and deploy java based web applications to cloud using its environment and development tools. In this tutorial, you can learn how to build app engine applications using maven app engine plug-in and eclipse.

Table of Contents

Setup

Download and install JDK which is the latest version and supported by the App engine. At the time of writing this article, app engine supports Java 8.

Set JAVA_HOME environment variable to JDK folder and add bin folder in JDK to PATH variable.

Download and install the latest version of eclipse.

Download and install latest version of maven and add maven bin folder path to PATH variable.

Download the latest version of app engine SDK and extract it to any folder and add app engine SDK path to PATH variable.

Create Google Cloud Project and App Engine Project

Login to google cloud platform console using your google account, create cloud project and note the project id.

create google cloud project

Then select the project.

select app engine project

Then create app engine project by clicking language drop down, selecting language, clicking java and then selecting a region.

create app engine project

Creating App Engine Java Project using Maven

To create app engine project structure, run the below command. Change directory to the folder where you want to create the project on your machine.

You need to get your app engine project’s project id from google cloud platform console and use it as a value for -Dapplication-id option in the below command.

 mvn archetype:generate -Dappengine-version=1.9.64 -Djava8=true -DCloudSDK_Tooling=false -Dapplication-id=example-app-id -Dfilter=com.google.appengine.archetypes:

It prompts you to choose archetype with list of app engine archetype options. Enter the archetype which you are interested in, for example if you want to create an app to deploy on app engine standard environment, choose appengine-standard-archetype option by entering the number at prompt.

appengine maven project structure choose archetype

Then it prompts for archetype version to select, just hit enter to continue with pre selected option. Then enter groupId and artifactId for the project, hit enter to continue with pre selected version and package.

Then confirm by entering Y. It creates project structure with hello app engine servlet. Project root folder is created using artifactId, our example project root is appengineexample because that’s what entered for artifactId.

appengine maven project structure complete

pom XML

Here is the pom xml, which is created in project root folder, for your reference. It contains required dependencies and plugins. If you need additional jar, you need to add them as dependencies in this file.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>

  <groupId>com.tsaa.zoftino</groupId>
  <artifactId>appengineexample</artifactId>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <maven.compiler.showDeprecation>true</maven.compiler.showDeprecation>
    <archiveClasses>true</archiveClasses>
  </properties>

  <prerequisites>
    <maven>3.5</maven>
  </prerequisites>

  <dependencies>
    <!-- Compile/runtime dependencies -->
    <dependency>
      <groupId>com.google.appengine</groupId>
      <artifactId>appengine-api-1.0-sdk</artifactId>
      <version>1.9.64</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <type>jar</type>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
 

    <!-- Test Dependencies -->
    <dependency>
      <groupId>com.google.appengine</groupId>
      <artifactId>appengine-testing</artifactId>
      <version>1.9.64</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>com.google.appengine</groupId>
      <artifactId>appengine-api-stubs</artifactId>
      <version>1.9.64</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>com.google.appengine</groupId>
      <artifactId>appengine-tools-sdk</artifactId>
      <version>1.9.64</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>com.google.truth</groupId>
      <artifactId>truth</artifactId>
      <version>0.33</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.mockito</groupId>
      <artifactId>mockito-all</artifactId>
      <version>1.10.19</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <!-- for hot reload of the web application-->
    <outputDirectory>${project.build.directory}/${project.build.finalName}/WEB-INF/classes</outputDirectory>
    <plugins>
      <plugin>
        <groupId>com.google.appengine</groupId>
        <artifactId>appengine-maven-plugin</artifactId>
        <version>1.9.64</version>
        <configuration>
          <appId>zoftino-123</appId> <!-- Override appengine-web.xml <project> -->
          <version>1</version>
          <fullScanSeconds>1</fullScanSeconds>
          <retainUploadDir>true</retainUploadDir>
        </configuration>
      </plugin>

      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>versions-maven-plugin</artifactId>
        <version>2.3</version>
        <executions>
          <execution>
            <phase>compile</phase>
            <goals>
              <goal>display-dependency-updates</goal>
              <goal>display-plugin-updates</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <excludes>
            <exclude>javax.servlet:javax.servlet-api</exclude>
            <exclude>com.google.guava:guava</exclude> <!-- avoid android version -->
          </excludes>
        </configuration>
      </plugin>

      <plugin>
        <artifactId>maven-war-plugin</artifactId>
        <version>3.1.0</version>
      </plugin>

      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.6.1</version>
      </plugin>

      <plugin>
        <artifactId>maven-clean-plugin</artifactId>
        <version>3.0.0</version>
      </plugin>

      <plugin>
        <artifactId>maven-install-plugin</artifactId>
        <version>2.5.2</version>
      </plugin>

      <plugin>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.20</version>
      </plugin>

      <plugin>
        <artifactId>maven-site-plugin</artifactId>
        <version>3.6</version>
      </plugin>

      <plugin>
        <artifactId>maven-resources-plugin</artifactId>
        <version>3.0.2</version>
      </plugin>

      <plugin>
        <artifactId>maven-deploy-plugin</artifactId>
        <version>3.1</version>
      </plugin>

      <plugin>
        <artifactId>maven-enforcer-plugin</artifactId>
        <version>1.4.1</version>
        <executions>
          <execution>
            <id>enforce-maven</id>
            <goals>
              <goal>enforce</goal>
            </goals>
            <configuration>
              <rules>
                <requireMavenVersion>
                  <version>3.5</version>
                </requireMavenVersion>
                <requirePluginVersions>
                   <message>Best Practice is to always define plugin versions!</message>
                   <banLatest>true</banLatest>
                   <banRelease>true</banRelease>
                   <phases>clean,deploy,verify,appengine:run,appengine:deploy,appengine:update,appengine:devappaserver,site</phases>
                </requirePluginVersions>
              </rules>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

Compiling and Building Project

To compile the project, go to project folder (in our example, cd to appengineexample folder) and issue below command.

mvn clean package

You may get no-compiler error like shown below.

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.6.1:compile (default-compile) on project appengineexample: Compilation failure
[ERROR] No compiler is provided in this environment. Perhaps you are running on a JRE rather than a JDK?
app engine maven project compile error

To fix the issue, you need to make sure that JAVA_HOME is set to JDK folder (not JRE) and JAVA_HOME environment variable is added to your user profile environment variables.

If there are any corrupted jar files, you will get bad signature error like shown below.

[ERROR] error: error reading C:\Users\sr\.m2\repository\com\google\guava\guava\20.0\guava-20.0.jar; invalid LOC header (bad signature)

To fix this, you need to remove the version folder of the jar causing the issue from your maven repository and compile again using maven clean package command.

After successful compile and build, you will get output on the console like this.

[INFO] Processing war project
[INFO] Copying webapp resources [C:\ProjectNew\example\appengineexample\src\main\webapp]
[INFO] Webapp assembled in [222 msecs]
[INFO] Building war: C:\ProjectNew\example\appengineexample\target\appengineexample-1.0-SNAPSHOT.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 15.397 s
[INFO] Finished at: 2018-07-04T04:35:00+05:30
[INFO] ------------------------------------------------------------------------

Importing Project to Eclipse

Import the project to eclipse by clicking import menu item in the file menu.

import app engine project to eclipse

Then select maven project.

import app engine maven project to eclipse

Then select your maven project folder (for example appengineexample) and finish. Below is the project structure in eclipse.

after import app engine project in eclipse

Add code and go back to command line and do a build.

Deploying App to App Engine Dev Server

After development is done, you can compile and build your app using instructions mentioned in the previous sections. To deploy your app to app engine dev server, issue mvn appengine:devserver command on the command line, make sure that you change directory to project folder (appengineexample) before issuing the command to start the server.

mvn appengine:devserver
app engine app deployed to app engine dev server

You can access your app using URLs printed on the server console.

Deploying App Engine App to Cloud

After testing your app engine app on local dev server, you can deploy the app to app engine server on the cloud by using mvn appengine:update command, you need to change your directory to project root.

mvn appengine:update

It will deploy the app. Below is the last few lines of output of the command.

82% Uploaded 7 files.
84% Sending batch containing 7 file(s) totaling 49KB.
85% Initializing precompilation...
90% Deploying new version.
95% Will check again in 1 seconds.
98% Will check again in 2 seconds.
99% Will check again in 4 seconds.
99% Will check again in 8 seconds.
99% Closing update: new version is ready to start serving.
99% Uploading index definitions.

Update for module default completed successfully.
Success.
Temporary staging for module default directory left in C:\Users\srinu\AppData\Local\Temp\appcfg4387710237081627550.tmp
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 46.763 s
[INFO] Finished at: 2018-07-04T10:38:11+05:30
[INFO] ------------------------------------------------------------------------

You can access your app using following appspot url by replacing project id with your project id.

http://[YOUR_PROJECT_ID].appspot.com

You may get below error related to authentication while trying to deploy your app to app engine server on cloud.

Either the access code is invalid or the OAuth token is revoked.Details: invalid_grant

To fix this issue, you need to remove or rename .appcfg_oauth2_tokens_java file which exists in your user profile directory and then try to deploy again by executing the maven command. You will get google account login page, after entering your google account credentials, the next page will display token which you need to copy and enter on the command line to continue with the deployment.

Deploying Different Versions of an App Engine Service

App engine project can contain multiple services. Different versions of each service can be deployed to app engine. In the previous section, we deployed a service (war) which becomes the default service for that app engine project.

If you want to deploy a different version of the service after adding new functionality to your app, you need to increase version number in appengine-web.xml file which is located in your project src\main\webapp\WEB-INF folder. Then follow the deployment steps listed in previous section.

<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
    <application>myappprojectid</application>
    <version>2</version>
    <runtime>java8</runtime>

To access different versions of a service, you need to add version to the URL. For example, below url is for accessing the version 2 of default service with project id myappprojectid.

http://2.myappprojectid.appspot.com

Deploying Multiple App Engine Services

You may want to separate functionality in your app and create different war files. Multiple war file can be deployed to your app engine project. To deploy a second service (second war) under the same app engine project, create a new war project following the instructions detailed in the previous sections, import it to eclipse, add functionality and open appengine-web.xml file which exists in src\main\webapp\WEB-INF folder and add module element to it as shown below.

<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
    <application>myappprojectid</application>
    <version>1</version>
    <runtime>java8</runtime>
    ....
    <module>appenginesecondexample</module>
</appengine-web-app>

Then deploy the app to app engine following the same instructions. To access second module, add the module name to the url in addition to version and project id as shown below.

http://1.appenginesecondexample.myappprojectid.appspot.com

To deploy a service to backend instance, you just need to add instance-class element to appengine-web.xml file and specify backend instance.

<instance-class>B1</instance-class>

Like this, you can deploy multiple services to google app engine, please check google app engine documents for limitations, free quota and price.