Skip to content

Commit 1426af0

Browse files
authored
Merge pull request #388 from GoogleCloudPlatform/wsh/standard-guestbook-sans-objectify
Standard guestbook using Cloud Datastore API rather than Objectify.
2 parents 237e3aa + 3778448 commit 1426af0

File tree

15 files changed

+758
-0
lines changed

15 files changed

+758
-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:run
23+
24+
## Deploying
25+
26+
Modify `appengine-web.xml` to reflect your app ID and version, then:
27+
28+
mvn clean appengine:deploy
+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xmlns="http://maven.apache.org/POM/4.0.0"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
6+
<modelVersion>4.0.0</modelVersion>
7+
<packaging>war</packaging>
8+
<version>1.0-SNAPSHOT</version>
9+
10+
<groupId>com.example.appengine</groupId>
11+
<artifactId>appengine-guestbook-cloud-datastore</artifactId>
12+
<properties>
13+
<guava.version>19.0</guava.version>
14+
</properties>
15+
<parent>
16+
<groupId>com.google.cloud</groupId>
17+
<artifactId>doc-samples</artifactId>
18+
<version>1.0.0</version>
19+
<relativePath>../..</relativePath>
20+
</parent>
21+
22+
<!-- [START set_versions] -->
23+
<prerequisites>
24+
<maven>3.3.9</maven>
25+
</prerequisites>
26+
<!-- [END set_versions] -->
27+
28+
<dependencies>
29+
<!-- Compile/runtime dependencies -->
30+
<dependency>
31+
<groupId>com.google.appengine</groupId>
32+
<artifactId>appengine-api-1.0-sdk</artifactId>
33+
<version>${appengine.sdk.version}</version>
34+
</dependency>
35+
<dependency>
36+
<groupId>javax.servlet</groupId>
37+
<artifactId>servlet-api</artifactId>
38+
<version>2.5</version>
39+
<scope>provided</scope>
40+
</dependency>
41+
<dependency>
42+
<groupId>jstl</groupId>
43+
<artifactId>jstl</artifactId>
44+
<version>1.2</version>
45+
</dependency>
46+
47+
<dependency>
48+
<groupId>com.google.cloud</groupId>
49+
<artifactId>google-cloud</artifactId>
50+
<version>0.4.0</version>
51+
</dependency>
52+
53+
<dependency>
54+
<groupId>com.google.guava</groupId>
55+
<artifactId>guava</artifactId>
56+
<version>${guava.version}</version>
57+
</dependency>
58+
59+
<!-- Test Dependencies -->
60+
<dependency>
61+
<groupId>junit</groupId>
62+
<artifactId>junit</artifactId>
63+
<version>4.12</version>
64+
<scope>test</scope>
65+
</dependency>
66+
<dependency>
67+
<groupId>org.mockito</groupId>
68+
<artifactId>mockito-all</artifactId>
69+
<version>1.10.19</version>
70+
<scope>test</scope>
71+
</dependency>
72+
<dependency>
73+
<groupId>com.google.appengine</groupId>
74+
<artifactId>appengine-testing</artifactId>
75+
<version>${appengine.sdk.version}</version>
76+
<scope>test</scope>
77+
</dependency>
78+
<dependency>
79+
<groupId>com.google.appengine</groupId>
80+
<artifactId>appengine-api-stubs</artifactId>
81+
<version>${appengine.sdk.version}</version>
82+
<scope>test</scope>
83+
</dependency>
84+
<dependency>
85+
<groupId>com.google.appengine</groupId>
86+
<artifactId>appengine-tools-sdk</artifactId>
87+
<version>${appengine.sdk.version}</version>
88+
<scope>test</scope>
89+
</dependency>
90+
</dependencies>
91+
92+
<build>
93+
<plugins>
94+
<plugin>
95+
<groupId>com.google.cloud.tools</groupId>
96+
<artifactId>appengine-maven-plugin</artifactId>
97+
<version>1.0.0</version>
98+
</plugin>
99+
</plugins>
100+
</build>
101+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/**
2+
* Copyright 2016 Google Inc. All Rights Reserved.
3+
*
4+
* <p>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5+
* except in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* <p>http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* <p>Unless required by applicable law or agreed to in writing, software distributed under the
10+
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
* express or implied. See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
15+
//[START all]
16+
package com.example.guestbook;
17+
18+
import static com.example.guestbook.Persistence.getDatastore;
19+
20+
import com.google.cloud.datastore.DateTime;
21+
import com.google.cloud.datastore.Entity;
22+
import com.google.cloud.datastore.FullEntity;
23+
import com.google.cloud.datastore.FullEntity.Builder;
24+
import com.google.cloud.datastore.IncompleteKey;
25+
import com.google.cloud.datastore.Key;
26+
import com.google.common.base.MoreObjects;
27+
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+
public String authorEmail;
36+
public String authorId;
37+
public String content;
38+
public Date date;
39+
40+
public Greeting() {
41+
date = new Date();
42+
}
43+
44+
public Greeting(String book, String content) {
45+
this();
46+
this.book = new Guestbook(book);
47+
this.content = content;
48+
}
49+
50+
public Greeting(String book, String content, String id, String email) {
51+
this(book, content);
52+
authorEmail = email;
53+
authorId = id;
54+
}
55+
56+
public Greeting(Entity entity) {
57+
key = entity.hasKey() ? entity.key() : null;
58+
authorEmail = entity.contains("authorEmail") ? entity.getString("authorEmail") : null;
59+
authorId = entity.contains("authorId") ? entity.getString("authorId") : null;
60+
date = entity.contains("date") ? entity.getDateTime("date").toDate() : null;
61+
content = entity.contains("content") ? entity.getString("content") : null;
62+
}
63+
64+
public void save() {
65+
if (key == null) {
66+
key = getDatastore().allocateId(makeIncompleteKey()); // Give this greeting a unique ID
67+
}
68+
69+
Builder<Key> builder = FullEntity.builder(key);
70+
71+
if (authorEmail != null) {
72+
builder.set("authorEmail", authorEmail);
73+
}
74+
75+
if (authorId != null) {
76+
builder.set("authorId", authorId);
77+
}
78+
79+
builder.set("content", content);
80+
builder.set("date", DateTime.copyFrom(date));
81+
82+
getDatastore().put(builder.build());
83+
}
84+
85+
private IncompleteKey makeIncompleteKey() {
86+
// The book is our ancestor key.
87+
return Key.builder(book.getKey(), "Greeting").build();
88+
}
89+
90+
@Override
91+
public boolean equals(Object o) {
92+
if (this == o) {
93+
return true;
94+
}
95+
if (o == null || getClass() != o.getClass()) {
96+
return false;
97+
}
98+
Greeting greeting = (Greeting) o;
99+
return Objects.equals(key, greeting.key)
100+
&& Objects.equals(authorEmail, greeting.authorEmail)
101+
&& Objects.equals(authorId, greeting.authorId)
102+
&& Objects.equals(content, greeting.content)
103+
&& Objects.equals(date, greeting.date);
104+
}
105+
106+
@Override
107+
public int hashCode() {
108+
return Objects.hash(key, authorEmail, authorId, content, date);
109+
}
110+
111+
@Override
112+
public String toString() {
113+
return MoreObjects.toStringHelper(this)
114+
.add("key", key)
115+
.add("authorEmail", authorEmail)
116+
.add("authorId", authorId)
117+
.add("content", content)
118+
.add("date", date)
119+
.add("book", book)
120+
.toString();
121+
}
122+
}
123+
//[END all]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/**
2+
* Copyright 2016 Google Inc. All Rights Reserved.
3+
*
4+
* <p>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5+
* except in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* <p>http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* <p>Unless required by applicable law or agreed to in writing, software distributed under the
10+
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
* express or implied. See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
package com.example.guestbook;
15+
16+
import static com.example.guestbook.Persistence.getDatastore;
17+
import static com.example.guestbook.Persistence.getKeyFactory;
18+
import static com.google.cloud.datastore.StructuredQuery.OrderBy.desc;
19+
import static com.google.cloud.datastore.StructuredQuery.PropertyFilter.hasAncestor;
20+
21+
import com.google.cloud.datastore.Entity;
22+
import com.google.cloud.datastore.EntityQuery;
23+
import com.google.cloud.datastore.Key;
24+
import com.google.cloud.datastore.KeyFactory;
25+
import com.google.cloud.datastore.Query;
26+
import com.google.cloud.datastore.QueryResults;
27+
import com.google.common.base.MoreObjects;
28+
import com.google.common.collect.ImmutableList;
29+
import com.google.common.collect.ImmutableList.Builder;
30+
31+
import java.util.List;
32+
import java.util.Objects;
33+
34+
//[START all]
35+
public class Guestbook {
36+
private static final KeyFactory keyFactory = getKeyFactory(Guestbook.class);
37+
private final Key key;
38+
39+
public final String book;
40+
41+
public Guestbook(String book) {
42+
this.book = book == null ? "default" : book;
43+
key =
44+
keyFactory.newKey(
45+
this.book); // There is a 1:1 mapping between Guestbook names and Guestbook objects
46+
}
47+
48+
public Key getKey() {
49+
return key;
50+
}
51+
52+
public List<Greeting> getGreetings() {
53+
// This query requires the index defined in index.yaml to work because of the orderBy on date.
54+
EntityQuery query =
55+
Query.entityQueryBuilder()
56+
.kind("Greeting")
57+
.filter(hasAncestor(key))
58+
.orderBy(desc("date"))
59+
.limit(5)
60+
.build();
61+
62+
QueryResults<Entity> results = getDatastore().run(query);
63+
64+
Builder<Greeting> resultListBuilder = ImmutableList.builder();
65+
while (results.hasNext()) {
66+
resultListBuilder.add(new Greeting(results.next()));
67+
}
68+
69+
return resultListBuilder.build();
70+
}
71+
72+
@Override
73+
public boolean equals(Object o) {
74+
if (this == o) {
75+
return true;
76+
}
77+
if (o == null || getClass() != o.getClass()) {
78+
return false;
79+
}
80+
Guestbook guestbook = (Guestbook) o;
81+
return Objects.equals(book, guestbook.book) && Objects.equals(key, guestbook.key);
82+
}
83+
84+
@Override
85+
public int hashCode() {
86+
return Objects.hash(book, key);
87+
}
88+
89+
@Override
90+
public String toString() {
91+
return MoreObjects.toStringHelper(this)
92+
.add("keyFactory", keyFactory)
93+
.add("book", book)
94+
.add("key", key)
95+
.toString();
96+
}
97+
}
98+
//[END all]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* Copyright 2016 Google Inc. All Rights Reserved.
3+
*
4+
* <p>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5+
* except in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* <p>http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* <p>Unless required by applicable law or agreed to in writing, software distributed under the
10+
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
* express or implied. See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
package com.example.guestbook;
15+
16+
import com.google.cloud.datastore.Datastore;
17+
import com.google.cloud.datastore.DatastoreOptions;
18+
import com.google.cloud.datastore.KeyFactory;
19+
20+
import java.util.concurrent.atomic.AtomicReference;
21+
22+
//[START all]
23+
public class Persistence {
24+
private static AtomicReference<Datastore> datastore = new AtomicReference<>();
25+
26+
public static Datastore getDatastore() {
27+
if (datastore.get() == null) {
28+
datastore.set(DatastoreOptions.builder().projectId("your-project-id-here").build().service());
29+
}
30+
31+
return datastore.get();
32+
}
33+
34+
public static void setDatastore(Datastore datastore) {
35+
Persistence.datastore.set(datastore);
36+
}
37+
38+
public static KeyFactory getKeyFactory(Class<?> c) {
39+
return getDatastore().newKeyFactory().kind(c.getSimpleName());
40+
}
41+
}
42+
//[END all]

0 commit comments

Comments
 (0)