Skip to content

Commit 367e7bd

Browse files
committed
Add a Guestbook example for the standard environment that uses the Cloud
Datastore Java API.
1 parent 237e3aa commit 367e7bd

File tree

15 files changed

+746
-0
lines changed

15 files changed

+746
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# appengine/guestbook-cloud-datastore
2+
3+
An App Engine guestbook using Java, Maven, and the Cloud Datastore API via
4+
[google-cloud-java](https://github.com/GoogleCloudPlatform/google-cloud-java).
5+
6+
Please ask questions on [StackOverflow](http://stackoverflow.com/questions/tagged/google-app-engine).
7+
8+
## Running Locally
9+
10+
First, pick a project ID. You can create a project in the [Cloud Console] if you'd like, though this
11+
isn't necessary unless you'd like to deploy the sample.
12+
13+
Second, modify `Persistence.java`: replace `your-project-id-here` with the project ID you picked.
14+
15+
Then start the [Cloud Datastore Emulator](https://cloud.google.com/datastore/docs/tools/datastore-emulator):
16+
17+
gcloud beta emulators datastore start --project=YOUR_PROJECT_ID_HERE
18+
19+
Finally, in a new shell, [set the Datastore Emulator environmental variables](https://cloud.google.com/datastore/docs/tools/datastore-emulator#setting_environment_variables)
20+
and run
21+
22+
mvn clean appengine:devserver
23+
24+
## Deploying
25+
26+
Modify `appengine-web.xml` to reflect your app ID and version, then:
27+
28+
mvn clean appengine:update
+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<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">
3+
4+
<modelVersion>4.0.0</modelVersion>
5+
<packaging>war</packaging>
6+
<version>1.0-SNAPSHOT</version>
7+
8+
<groupId>com.example.appengine</groupId>
9+
<artifactId>appengine-guestbook-cloud-datastore</artifactId>
10+
<properties>
11+
<guava.version>19.0</guava.version>
12+
</properties>
13+
<parent>
14+
<groupId>com.google.cloud</groupId>
15+
<artifactId>doc-samples</artifactId>
16+
<version>1.0.0</version>
17+
<relativePath>../..</relativePath>
18+
</parent>
19+
20+
<!-- [START set_versions] -->
21+
<prerequisites>
22+
<maven>3.3.9</maven>
23+
</prerequisites>
24+
<!-- [END set_versions] -->
25+
26+
<dependencies>
27+
<!-- Compile/runtime dependencies -->
28+
<dependency>
29+
<groupId>com.google.appengine</groupId>
30+
<artifactId>appengine-api-1.0-sdk</artifactId>
31+
<version>${appengine.sdk.version}</version>
32+
</dependency>
33+
<dependency>
34+
<groupId>javax.servlet</groupId>
35+
<artifactId>servlet-api</artifactId>
36+
<version>2.5</version>
37+
<scope>provided</scope>
38+
</dependency>
39+
<dependency>
40+
<groupId>jstl</groupId>
41+
<artifactId>jstl</artifactId>
42+
<version>1.2</version>
43+
</dependency>
44+
45+
<dependency>
46+
<groupId>com.google.cloud</groupId>
47+
<artifactId>google-cloud</artifactId>
48+
<version>0.4.0</version>
49+
</dependency>
50+
51+
<dependency>
52+
<groupId>com.google.guava</groupId>
53+
<artifactId>guava</artifactId>
54+
<version>${guava.version}</version>
55+
</dependency>
56+
57+
<!-- Test Dependencies -->
58+
<dependency>
59+
<groupId>junit</groupId>
60+
<artifactId>junit</artifactId>
61+
<version>4.12</version>
62+
<scope>test</scope>
63+
</dependency>
64+
<dependency>
65+
<groupId>org.mockito</groupId>
66+
<artifactId>mockito-all</artifactId>
67+
<version>1.10.19</version>
68+
<scope>test</scope>
69+
</dependency>
70+
<dependency>
71+
<groupId>com.google.appengine</groupId>
72+
<artifactId>appengine-testing</artifactId>
73+
<version>${appengine.sdk.version}</version>
74+
<scope>test</scope>
75+
</dependency>
76+
<dependency>
77+
<groupId>com.google.appengine</groupId>
78+
<artifactId>appengine-api-stubs</artifactId>
79+
<version>${appengine.sdk.version}</version>
80+
<scope>test</scope>
81+
</dependency>
82+
<dependency>
83+
<groupId>com.google.appengine</groupId>
84+
<artifactId>appengine-tools-sdk</artifactId>
85+
<version>${appengine.sdk.version}</version>
86+
<scope>test</scope>
87+
</dependency>
88+
</dependencies>
89+
90+
<build>
91+
<!-- for hot reload of the web application-->
92+
<outputDirectory>${project.build.directory}/${project.build.finalName}/WEB-INF/classes</outputDirectory>
93+
<plugins>
94+
<plugin>
95+
<groupId>com.google.appengine</groupId>
96+
<artifactId>appengine-maven-plugin</artifactId>
97+
<version>${appengine.sdk.version}</version>
98+
<configuration>
99+
<enableJarClasses>false</enableJarClasses>
100+
<!-- Comment in the below snippet to bind to all IPs instead of just localhost -->
101+
<!-- address>0.0.0.0</address>
102+
<port>8080</port -->
103+
<!-- Comment in the below snippet to enable local debugging with a remote debugger
104+
like those included with Eclipse or IntelliJ -->
105+
<!-- jvmFlags>
106+
<jvmFlag>-agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=n</jvmFlag>
107+
</jvmFlags -->
108+
</configuration>
109+
</plugin>
110+
</plugins>
111+
</build>
112+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/**
2+
* Copyright 2016 Google Inc. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
//[START all]
18+
package com.example.guestbook;
19+
20+
import static com.example.guestbook.Persistence.getDatastore;
21+
22+
import com.google.cloud.datastore.DateTime;
23+
import com.google.cloud.datastore.Entity;
24+
import com.google.cloud.datastore.FullEntity;
25+
import com.google.cloud.datastore.FullEntity.Builder;
26+
import com.google.cloud.datastore.IncompleteKey;
27+
import com.google.cloud.datastore.Key;
28+
import java.util.Date;
29+
import java.util.Objects;
30+
31+
public class Greeting {
32+
private Guestbook book;
33+
34+
public Key key;
35+
36+
public String authorEmail;
37+
public String authorId;
38+
public String content;
39+
public Date date;
40+
41+
public Greeting() {
42+
date = new Date();
43+
}
44+
45+
public Greeting(String book, String content) {
46+
this();
47+
this.book = new Guestbook(book);
48+
this.content = content;
49+
}
50+
51+
public Greeting(String book, String content, String id, String email) {
52+
this(book, content);
53+
authorEmail = email;
54+
authorId = id;
55+
}
56+
57+
/**
58+
* Load greeting from Datastore entity
59+
*
60+
* @param entity
61+
*/
62+
public Greeting(Entity entity) {
63+
key = entity.hasKey() ? entity.key() : null;
64+
authorEmail = entity.contains("authorEmail") ? entity.getString("authorEmail") : null;
65+
authorId = entity.contains("authorId") ? entity.getString("authorId") : null;
66+
date = entity.contains("date") ? entity.getDateTime("date").toDate() : null;
67+
content = entity.contains("content") ? entity.getString("content") : null;
68+
}
69+
70+
public void save() {
71+
if (key == null) {
72+
key = getDatastore().allocateId(makeIncompleteKey()); // Give this greeting a unique ID
73+
}
74+
75+
Builder<Key> builder = FullEntity.builder(key);
76+
77+
if (authorEmail != null) {
78+
builder.set("authorEmail", authorEmail);
79+
}
80+
81+
if (authorId != null) {
82+
builder.set("authorId", authorId);
83+
}
84+
85+
builder.set("content", content);
86+
builder.set("date", DateTime.copyFrom(date));
87+
88+
getDatastore().put(builder.build());
89+
}
90+
91+
private IncompleteKey makeIncompleteKey() {
92+
// The book is our ancestor key.
93+
return Key.builder(book.getKey(), "Greeting").build();
94+
}
95+
96+
@Override
97+
public boolean equals(Object o) {
98+
if (this == o) {
99+
return true;
100+
}
101+
if (o == null || getClass() != o.getClass()) {
102+
return false;
103+
}
104+
Greeting greeting = (Greeting) o;
105+
return Objects.equals(key, greeting.key) &&
106+
Objects.equals(authorEmail, greeting.authorEmail) &&
107+
Objects.equals(authorId, greeting.authorId) &&
108+
Objects.equals(content, greeting.content) &&
109+
Objects.equals(date, greeting.date);
110+
}
111+
112+
@Override
113+
public int hashCode() {
114+
return Objects.hash(key, authorEmail, authorId, content, date);
115+
}
116+
}
117+
//[END all]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/**
2+
* Copyright 2016 Google Inc. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.guestbook;
18+
19+
import static com.example.guestbook.Persistence.getDatastore;
20+
import static com.example.guestbook.Persistence.getKeyFactory;
21+
import static com.google.cloud.datastore.StructuredQuery.OrderBy.desc;
22+
import static com.google.cloud.datastore.StructuredQuery.PropertyFilter.hasAncestor;
23+
24+
import com.google.cloud.datastore.Entity;
25+
import com.google.cloud.datastore.EntityQuery;
26+
import com.google.cloud.datastore.Key;
27+
import com.google.cloud.datastore.KeyFactory;
28+
import com.google.cloud.datastore.Query;
29+
import com.google.cloud.datastore.QueryResults;
30+
import com.google.common.collect.ImmutableList;
31+
import com.google.common.collect.ImmutableList.Builder;
32+
import java.util.List;
33+
34+
//[START all]
35+
public class Guestbook {
36+
private static final KeyFactory kf = getKeyFactory(Guestbook.class);
37+
38+
private final Key key;
39+
public final String book;
40+
41+
public Guestbook(String book) {
42+
this.book = book == null ? "default" : book;
43+
key = kf.newKey(this.book); // There is a 1:1 mapping between Guestbook names and Guestbook objects
44+
}
45+
46+
public Key getKey() {
47+
return key;
48+
}
49+
50+
public List<Greeting> getGreetings() {
51+
// This query requires the index defined in index.yaml to work because of the orderBy on date.
52+
EntityQuery query = Query.entityQueryBuilder()
53+
.kind("Greeting")
54+
.filter(hasAncestor(key))
55+
.orderBy(desc("date"))
56+
.limit(5)
57+
.build();
58+
59+
QueryResults<Entity> results = getDatastore().run(query);
60+
61+
Builder<Greeting> resultListBuilder = ImmutableList.builder();
62+
while (results.hasNext()) {
63+
resultListBuilder.add(new Greeting(results.next()));
64+
}
65+
66+
return resultListBuilder.build();
67+
}
68+
}
69+
//[END all]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/**
2+
* Copyright 2016 Google Inc. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.guestbook;
18+
19+
import com.google.cloud.datastore.Datastore;
20+
import com.google.cloud.datastore.DatastoreOptions;
21+
import com.google.cloud.datastore.KeyFactory;
22+
23+
import java.util.concurrent.atomic.AtomicReference;
24+
25+
//[START all]
26+
public class Persistence {
27+
private static AtomicReference<Datastore> datastore = new AtomicReference<>();
28+
29+
public static Datastore getDatastore() {
30+
if (datastore.get() == null) {
31+
datastore.set(DatastoreOptions.builder().projectId("your-project-id-here").build().service());
32+
}
33+
34+
return datastore.get();
35+
}
36+
37+
public static KeyFactory getKeyFactory(Class<?> c) {
38+
return getDatastore().newKeyFactory().kind(c.getSimpleName());
39+
}
40+
41+
public static void setDatastore(Datastore datastore) {
42+
Persistence.datastore.set(datastore);
43+
}
44+
}
45+
//[END all]

0 commit comments

Comments
 (0)