Skip to content

Commit

Permalink
Add a Guestbook example for the standard environment that uses the Cloud
Browse files Browse the repository at this point in the history
Datastore Java API.
  • Loading branch information
wsh committed Oct 26, 2016
1 parent 237e3aa commit 367e7bd
Show file tree
Hide file tree
Showing 15 changed files with 746 additions and 0 deletions.
28 changes: 28 additions & 0 deletions appengine/guestbook-cloud-datastore/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# appengine/guestbook-cloud-datastore

An App Engine guestbook using Java, Maven, and the Cloud Datastore API via
[google-cloud-java](https://github.com/GoogleCloudPlatform/google-cloud-java).

Please ask questions on [StackOverflow](http://stackoverflow.com/questions/tagged/google-app-engine).

## Running Locally

First, pick a project ID. You can create a project in the [Cloud Console] if you'd like, though this
isn't necessary unless you'd like to deploy the sample.

Second, modify `Persistence.java`: replace `your-project-id-here` with the project ID you picked.

Then start the [Cloud Datastore Emulator](https://cloud.google.com/datastore/docs/tools/datastore-emulator):

gcloud beta emulators datastore start --project=YOUR_PROJECT_ID_HERE

Finally, in a new shell, [set the Datastore Emulator environmental variables](https://cloud.google.com/datastore/docs/tools/datastore-emulator#setting_environment_variables)
and run

mvn clean appengine:devserver

## Deploying

Modify `appengine-web.xml` to reflect your app ID and version, then:

mvn clean appengine:update
112 changes: 112 additions & 0 deletions appengine/guestbook-cloud-datastore/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<?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.example.appengine</groupId>
<artifactId>appengine-guestbook-cloud-datastore</artifactId>
<properties>
<guava.version>19.0</guava.version>
</properties>
<parent>
<groupId>com.google.cloud</groupId>
<artifactId>doc-samples</artifactId>
<version>1.0.0</version>
<relativePath>../..</relativePath>
</parent>

<!-- [START set_versions] -->
<prerequisites>
<maven>3.3.9</maven>
</prerequisites>
<!-- [END set_versions] -->

<dependencies>
<!-- Compile/runtime dependencies -->
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-api-1.0-sdk</artifactId>
<version>${appengine.sdk.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>

<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud</artifactId>
<version>0.4.0</version>
</dependency>

<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>

<!-- Test Dependencies -->
<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>
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-testing</artifactId>
<version>${appengine.sdk.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-api-stubs</artifactId>
<version>${appengine.sdk.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-tools-sdk</artifactId>
<version>${appengine.sdk.version}</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>${appengine.sdk.version}</version>
<configuration>
<enableJarClasses>false</enableJarClasses>
<!-- Comment in the below snippet to bind to all IPs instead of just localhost -->
<!-- address>0.0.0.0</address>
<port>8080</port -->
<!-- Comment in the below snippet to enable local debugging with a remote debugger
like those included with Eclipse or IntelliJ -->
<!-- jvmFlags>
<jvmFlag>-agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=n</jvmFlag>
</jvmFlags -->
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/**
* Copyright 2016 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

//[START all]
package com.example.guestbook;

import static com.example.guestbook.Persistence.getDatastore;

import com.google.cloud.datastore.DateTime;
import com.google.cloud.datastore.Entity;
import com.google.cloud.datastore.FullEntity;
import com.google.cloud.datastore.FullEntity.Builder;
import com.google.cloud.datastore.IncompleteKey;
import com.google.cloud.datastore.Key;
import java.util.Date;
import java.util.Objects;

public class Greeting {
private Guestbook book;

public Key key;

public String authorEmail;
public String authorId;
public String content;
public Date date;

public Greeting() {
date = new Date();
}

public Greeting(String book, String content) {
this();
this.book = new Guestbook(book);
this.content = content;
}

public Greeting(String book, String content, String id, String email) {
this(book, content);
authorEmail = email;
authorId = id;
}

/**
* Load greeting from Datastore entity
*
* @param entity
*/
public Greeting(Entity entity) {
key = entity.hasKey() ? entity.key() : null;
authorEmail = entity.contains("authorEmail") ? entity.getString("authorEmail") : null;
authorId = entity.contains("authorId") ? entity.getString("authorId") : null;
date = entity.contains("date") ? entity.getDateTime("date").toDate() : null;
content = entity.contains("content") ? entity.getString("content") : null;
}

public void save() {
if (key == null) {
key = getDatastore().allocateId(makeIncompleteKey()); // Give this greeting a unique ID
}

Builder<Key> builder = FullEntity.builder(key);

if (authorEmail != null) {
builder.set("authorEmail", authorEmail);
}

if (authorId != null) {
builder.set("authorId", authorId);
}

builder.set("content", content);
builder.set("date", DateTime.copyFrom(date));

getDatastore().put(builder.build());
}

private IncompleteKey makeIncompleteKey() {
// The book is our ancestor key.
return Key.builder(book.getKey(), "Greeting").build();
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Greeting greeting = (Greeting) o;
return Objects.equals(key, greeting.key) &&
Objects.equals(authorEmail, greeting.authorEmail) &&
Objects.equals(authorId, greeting.authorId) &&
Objects.equals(content, greeting.content) &&
Objects.equals(date, greeting.date);
}

@Override
public int hashCode() {
return Objects.hash(key, authorEmail, authorId, content, date);
}
}
//[END all]
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/**
* Copyright 2016 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.example.guestbook;

import static com.example.guestbook.Persistence.getDatastore;
import static com.example.guestbook.Persistence.getKeyFactory;
import static com.google.cloud.datastore.StructuredQuery.OrderBy.desc;
import static com.google.cloud.datastore.StructuredQuery.PropertyFilter.hasAncestor;

import com.google.cloud.datastore.Entity;
import com.google.cloud.datastore.EntityQuery;
import com.google.cloud.datastore.Key;
import com.google.cloud.datastore.KeyFactory;
import com.google.cloud.datastore.Query;
import com.google.cloud.datastore.QueryResults;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import java.util.List;

//[START all]
public class Guestbook {
private static final KeyFactory kf = getKeyFactory(Guestbook.class);

private final Key key;
public final String book;

public Guestbook(String book) {
this.book = book == null ? "default" : book;
key = kf.newKey(this.book); // There is a 1:1 mapping between Guestbook names and Guestbook objects
}

public Key getKey() {
return key;
}

public List<Greeting> getGreetings() {
// This query requires the index defined in index.yaml to work because of the orderBy on date.
EntityQuery query = Query.entityQueryBuilder()
.kind("Greeting")
.filter(hasAncestor(key))
.orderBy(desc("date"))
.limit(5)
.build();

QueryResults<Entity> results = getDatastore().run(query);

Builder<Greeting> resultListBuilder = ImmutableList.builder();
while (results.hasNext()) {
resultListBuilder.add(new Greeting(results.next()));
}

return resultListBuilder.build();
}
}
//[END all]
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Copyright 2016 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.example.guestbook;

import com.google.cloud.datastore.Datastore;
import com.google.cloud.datastore.DatastoreOptions;
import com.google.cloud.datastore.KeyFactory;

import java.util.concurrent.atomic.AtomicReference;

//[START all]
public class Persistence {
private static AtomicReference<Datastore> datastore = new AtomicReference<>();

public static Datastore getDatastore() {
if (datastore.get() == null) {
datastore.set(DatastoreOptions.builder().projectId("your-project-id-here").build().service());
}

return datastore.get();
}

public static KeyFactory getKeyFactory(Class<?> c) {
return getDatastore().newKeyFactory().kind(c.getSimpleName());
}

public static void setDatastore(Datastore datastore) {
Persistence.datastore.set(datastore);
}
}
//[END all]
Loading

0 comments on commit 367e7bd

Please sign in to comment.