Deploying the latest Google Glass Mirror API Java Quick Start to AppEngine
The Google Glass team has a series of quick starts, a code kitchen sink, if you will, in a few different languages to get developers rapidly up to speed with the Mirror API, a web API (json/http) way of interacting with Google Glass. It comes in many code varieties, Java, Python, Ruby, Go, etc.
Originally, the Java version was designed to be deployed and run on Google's platform as a service, AppEngine (AE). After a while, a new version of the Java quick start was introduced, one that ran without the requirement of AppEngine - using maven for library dependency management and jetty, a sleek java based servlet runner, this new maven/jetty combo strips down what's needed to the bare minimum so developers can focus more on the code rather than on how to instrument or scaffold running on a PaaS. That's all well and good, until someone wants to run it on AppEngine. There're some good reasons to run on AppEngine (or another PaaS with similar functionality) - free hosting tier, easy deployment, and SSL for the webapp. SSL's important, because the Mirror API requires SSL for subscriptions - location and other updates from Glass.
There're a four things that need to be done to modify the existing Java Quick Start to deploy on AppEngine: Add an appengine-web.xml; Add a logging.properties file (optional); Modify the maven pom.xml file; and Change one source file.
This post won't cover how to set up a project on AppEngine or configure a Google API project to use the Mirror API, those are covered in Google's excellent on-line help. All of our changes are at my github: https://github.com/ghchinoy/mirror-quickstart-java
A requirement of AppEngine, an appengine-web.xml, has to be created in the src/main/webapp/WEB-INF folder of the code.
<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
<application>your-app-name-here</application>
<version>1</version>
<threadsafe>true</threadsafe>
<system-properties>
<property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
</system-properties>
<sessions-enabled>true</sessions-enabled>
</appengine-web-app>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
<application>your-app-name-here</application>
<version>1</version>
<threadsafe>true</threadsafe>
<system-properties>
<property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
</system-properties>
<sessions-enabled>true</sessions-enabled>
</appengine-web-app>
Optionally, add in a logging.properties file, added in the same WEB-INF directory, can be added to set the logging level on AE:
# Set the default logging level for all loggers to WARNING
.level = DEBUG
To the maven pom.xml file, we added a few properties, one dependency and the Eclipse maven AppEngine plugin (we're using Eclipse as our editor):
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<appengine.app.version>2</appengine.app.version>
<appengine.target.version>1.8.3</appengine.target.version>
</properties>
...
<!-- Appengine dependencies -->
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-api-1.0-sdk</artifactId>
<version>${appengine.target.version}</version>
</dependency>
...
<plugin>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-maven-plugin</artifactId>
<version>1.8.3</version>
</plugin>
Note, your local maven should be version 3.1.
The Java Quick Start reads a local properties file to get the API console client id and secret needed to authenticate with the API services. When deploying to AppEngine, the code can't reference source paths, it has to reference resource paths relative to the classloader. In the AuthUtil.java file, we commented out the reference to the file path and added in obtaining the file from a resource path.
private static final Logger LOG = Logger.getLogger(AuthUtil.class.getSimpleName());
/**
* Creates and returns a new {@link AuthorizationCodeFlow} for this app.
*/
public static AuthorizationCodeFlow newAuthorizationCodeFlow() throws IOException {
//FileInputStream authPropertiesStream = new FileInputStream("./src/main/resources/oauth.properties");
URL resource = AuthUtil.class.getResource("/oauth.properties");
File propertiesFile = new File("./src/main/resources/oauth.properties");
try {
propertiesFile = new File(resource.toURI());
//LOG.info("Able to find oauth properties from file.");
} catch (URISyntaxException e) {
LOG.info(e.toString());
LOG.info("Using default source path.");
}
FileInputStream authPropertiesStream = new FileInputStream(propertiesFile);
Properties authProperties = new Properties();
authProperties.load(authPropertiesStream);
Once that's done, we can now use two local methods of starting a server mvn jetty:run (the default from the quick start), mvn appengine:devserver (a local appengine development server) or mvn appengine:update - to push the Java Quick Start code to AppEngine.