From d834576f9f9d73c5786ec0eb701c11743756b8a1 Mon Sep 17 00:00:00 2001 From: Les Vogel Date: Tue, 19 Apr 2016 21:10:25 -0700 Subject: [PATCH 1/7] appengine/oauth2 sample 1. Also add minor formatting change to url fetch. --- .gitignore | 4 + appengine/oauth2/.gitignore | 7 ++ appengine/oauth2/README.md | 41 ++++++++ appengine/oauth2/pom.xml | 60 ++++++++++++ .../com/example/appengine/HelloServlet.java | 37 +++++++ .../com/example/appengine/Oauth2Filter.java | 97 +++++++++++++++++++ .../src/main/webapp/WEB-INF/appengine-web.xml | 20 ++++ .../oauth2/src/main/webapp/WEB-INF/web.xml | 27 ++++++ appengine/oauth2/src/main/webapp/index.html | 36 +++++++ appengine/urlfetch/pom.xml | 3 + pom.xml | 1 + 11 files changed, 333 insertions(+) create mode 100644 appengine/oauth2/.gitignore create mode 100644 appengine/oauth2/README.md create mode 100644 appengine/oauth2/pom.xml create mode 100644 appengine/oauth2/src/main/java/com/example/appengine/HelloServlet.java create mode 100644 appengine/oauth2/src/main/java/com/example/appengine/Oauth2Filter.java create mode 100644 appengine/oauth2/src/main/webapp/WEB-INF/appengine-web.xml create mode 100644 appengine/oauth2/src/main/webapp/WEB-INF/web.xml create mode 100644 appengine/oauth2/src/main/webapp/index.html diff --git a/.gitignore b/.gitignore index 86dc131d1b2..6731271b7d8 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,7 @@ dependency-reduced-pom.xml buildNumber.properties service-account.json + +# intellij +.idea/ +*.iml diff --git a/appengine/oauth2/.gitignore b/appengine/oauth2/.gitignore new file mode 100644 index 00000000000..471339729ae --- /dev/null +++ b/appengine/oauth2/.gitignore @@ -0,0 +1,7 @@ +# Eclipse files +.project +.classpath +.settings + +# Target folders +target/ diff --git a/appengine/oauth2/README.md b/appengine/oauth2/README.md new file mode 100644 index 00000000000..3f38cfba462 --- /dev/null +++ b/appengine/oauth2/README.md @@ -0,0 +1,41 @@ +# Google App Engine Standard Environment +## Oauth2 Sample + +This sample demonstrates using the Oauth2 apis to create an authenticaion filter. + +See the [Google App Engine standard environment documentation][ae-docs] for more +detailed instructions. + + +## Setup +1. In the [Cloud Developers Console](https://cloud.google.com/console) > API Manager > Credentials, +create a Oauth Client ID for a Web Application. You will need to provide an authroized JavaScript +origin. Typically, https://projectID.appspot.com. +1. Edit `src/main/webapp/index.html` and change `YOUR_CLIENT_ID_HERE.apps.googleusercontent.com` to +Client ID from the prior step. + +## Running locally +NOTE: The app can be run locally, but the Oauth2 APIs do not work with the development server. + + $ mvn appengine:devserver + +## Deploying + $ mvn appengine:update -Dappengine.appId=YOUR-PROJECT-ID -Dappengine.version=SOME-VERSION + +1. Using your browser, visit `https://YOUR-PROJECT-ID.appspot.com`, click Sign In. + +1. The Sign In process will then request some text from your app, and then display it, if +the id matches the list in `src/main/java/com/example/appengine/Oauth2Filter.java`. + +## Adding you to the list of valid users +NOTE: Typically, you would use this for Service Accounts, but user accounts work as well. + +1. Enable logging by uncommenting the context.log line in +`src/main/java/com/example/appengine/Oauth2Filter.java`, redeploy, and visit the page +1. Look at the logs in [Cloud Developers Console](https://cloud.google.com/console) > Logs. + +1. Add the `tokenAudience` to the `allowedClients`. + +1. Deploy and visit the page again. + +[ae-docs]: https://cloud.google.com/appengine/docs/java/ diff --git a/appengine/oauth2/pom.xml b/appengine/oauth2/pom.xml new file mode 100644 index 00000000000..b29ab18dad4 --- /dev/null +++ b/appengine/oauth2/pom.xml @@ -0,0 +1,60 @@ + + + 4.0.0 + war + 1.0-SNAPSHOT + com.example.appengine + appengine-oauth2 + + + 1.9.34 + 1.7 + 1.7 + + + com.google.cloud + doc-samples + 1.0.0 + ../.. + + + + + com.google.appengine + appengine-api-1.0-sdk + ${appengine.sdk.version} + + + javax.servlet + servlet-api + jar + provided + + + + + + ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + + com.google.appengine + appengine-maven-plugin + ${appengine.sdk.version} + + + + diff --git a/appengine/oauth2/src/main/java/com/example/appengine/HelloServlet.java b/appengine/oauth2/src/main/java/com/example/appengine/HelloServlet.java new file mode 100644 index 00000000000..9e4ebbe6f1e --- /dev/null +++ b/appengine/oauth2/src/main/java/com/example/appengine/HelloServlet.java @@ -0,0 +1,37 @@ +/** + * Copyright 2015 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.appengine; + +import java.io.IOException; +import java.io.PrintWriter; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +// [START example] +@SuppressWarnings("serial") +public class HelloServlet extends HttpServlet { + + @Override + public void doPost(final HttpServletRequest req, final HttpServletResponse resp) + throws IOException { + PrintWriter out = resp.getWriter(); + out.print("Hello, world"); // simple hello world response + } +} +// [END example] diff --git a/appengine/oauth2/src/main/java/com/example/appengine/Oauth2Filter.java b/appengine/oauth2/src/main/java/com/example/appengine/Oauth2Filter.java new file mode 100644 index 00000000000..299db8e0e65 --- /dev/null +++ b/appengine/oauth2/src/main/java/com/example/appengine/Oauth2Filter.java @@ -0,0 +1,97 @@ +/* + * 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.appengine; + +import com.google.appengine.api.oauth.OAuthRequestException; +import com.google.appengine.api.oauth.OAuthService; +import com.google.appengine.api.oauth.OAuthServiceFactory; +import com.google.appengine.api.oauth.OAuthServiceFailureException; +import com.google.appengine.api.users.User; +import com.google.appengine.api.utils.SystemProperty; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletResponse; + +import static com.google.appengine.api.utils.SystemProperty.environment; + +/** + * Filter to verify that request has a "Authorization: Bearer xxxx" header, + * and check if xxxx is authorized to use this app. + * + * Note - this is to demonstrate the OAuth2 APIs, as it is possible to lockdown some + * of your app's URL's using cloud console by adding service accounts to the project. + */ +public class Oauth2Filter implements Filter { + + private ServletContext context; + + @Override + public void init(final FilterConfig config) throws ServletException { + this.context = config.getServletContext(); + } + + // [START oauth2] + @Override + final void doFilter(final ServletRequest servletReq, final ServletResponse servletResp, + final FilterChain chain) throws IOException, ServletException { + HttpServletResponse resp = (HttpServletResponse) servletResp; + + OAuthService oauth = OAuthServiceFactory.getOAuthService(); + final String scope = "https://www.googleapis.com/auth/userinfo.email"; + Set allowedClients = new HashSet<>(); + + allowedClients.add("407408718192.apps.googleusercontent.com"); // list of client ids to allow + allowedClients.add("755878275993-j4k7emq6rlupctce1c28enpcrr50vfo1.apps.googleusercontent.com"); + + // Only check Oauth2 when in production, skip if run in development. + SystemProperty.Environment.Value env = environment.value(); + if (env == SystemProperty.Environment.Value.Production) { // APIs only work in Production + try { + User user = oauth.getCurrentUser(scope); // From "Authorization: Bearer" http req header + String tokenAudience = oauth.getClientId(scope); + + // The line below is commented out for privacy. +// context.log("tokenAudience: " + tokenAudience); // Account we match + + if (!allowedClients.contains(tokenAudience)) { + throw new OAuthRequestException("audience of token '" + tokenAudience + + "' is not in allowed list " + allowedClients); + } + } catch (OAuthRequestException ex) { + resp.sendError(HttpServletResponse.SC_NOT_FOUND); // Not allowed + return; + } catch (OAuthServiceFailureException ex) { + resp.sendError(HttpServletResponse.SC_NOT_FOUND); // some failure - reject + context.log("oauth2 failure", ex); + return; + } + } + chain.doFilter(servletReq, servletResp); // continue processing + } + // [END oauth2] + + @Override + public void destroy() { } + +} diff --git a/appengine/oauth2/src/main/webapp/WEB-INF/appengine-web.xml b/appengine/oauth2/src/main/webapp/WEB-INF/appengine-web.xml new file mode 100644 index 00000000000..c322e7d016e --- /dev/null +++ b/appengine/oauth2/src/main/webapp/WEB-INF/appengine-web.xml @@ -0,0 +1,20 @@ + + + + + + YOUR-PROJECT-ID + YOUR-VERSION-ID + true + diff --git a/appengine/oauth2/src/main/webapp/WEB-INF/web.xml b/appengine/oauth2/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..1884992f5f7 --- /dev/null +++ b/appengine/oauth2/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,27 @@ + + + + Oauth2Filter + com.example.appengine.Oauth2Filter + + + Oauth2Filter + /hello + + + + hello + com.example.appengine.HelloServlet + + + hello + /hello + + + + index.html + + diff --git a/appengine/oauth2/src/main/webapp/index.html b/appengine/oauth2/src/main/webapp/index.html new file mode 100644 index 00000000000..e5ac6b237ba --- /dev/null +++ b/appengine/oauth2/src/main/webapp/index.html @@ -0,0 +1,36 @@ + + + + + + + + + + +
+ + + diff --git a/appengine/urlfetch/pom.xml b/appengine/urlfetch/pom.xml index 09f67af79af..ee062bbb8b8 100644 --- a/appengine/urlfetch/pom.xml +++ b/appengine/urlfetch/pom.xml @@ -19,12 +19,14 @@ Copyright 2015 Google Inc. All Rights Reserved. 1.0-SNAPSHOT com.example.appengine appengine-URLFetch + com.google.cloud doc-samples 1.0.0 ../.. + javax.servlet @@ -38,6 +40,7 @@ Copyright 2015 Google Inc. All Rights Reserved. 20160212 + ${project.build.directory}/${project.build.finalName}/WEB-INF/classes diff --git a/pom.xml b/pom.xml index 3004a45fc99..741e29e6b06 100644 --- a/pom.xml +++ b/pom.xml @@ -49,6 +49,7 @@ appengine/mailgun appengine/mailjet appengine/memcache + appengine/oauth2 appengine/sendgrid appengine/static-files appengine/twilio From 1b008aa34d5dba27f6e0766e9e445d044a0490f8 Mon Sep 17 00:00:00 2001 From: Les Vogel Date: Tue, 19 Apr 2016 21:23:38 -0700 Subject: [PATCH 2/7] check style helping too much. --- .../src/main/java/com/example/appengine/Oauth2Filter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appengine/oauth2/src/main/java/com/example/appengine/Oauth2Filter.java b/appengine/oauth2/src/main/java/com/example/appengine/Oauth2Filter.java index 299db8e0e65..2fa0d391a21 100644 --- a/appengine/oauth2/src/main/java/com/example/appengine/Oauth2Filter.java +++ b/appengine/oauth2/src/main/java/com/example/appengine/Oauth2Filter.java @@ -53,7 +53,7 @@ public void init(final FilterConfig config) throws ServletException { // [START oauth2] @Override - final void doFilter(final ServletRequest servletReq, final ServletResponse servletResp, + public void doFilter(final ServletRequest servletReq, final ServletResponse servletResp, final FilterChain chain) throws IOException, ServletException { HttpServletResponse resp = (HttpServletResponse) servletResp; From e110bb3f03e1d33e3a9d4b560e5b80d5cf477988 Mon Sep 17 00:00:00 2001 From: Les Vogel Date: Tue, 19 Apr 2016 21:39:39 -0700 Subject: [PATCH 3/7] More check style goodness. --- .../main/java/com/example/appengine/Oauth2Filter.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/appengine/oauth2/src/main/java/com/example/appengine/Oauth2Filter.java b/appengine/oauth2/src/main/java/com/example/appengine/Oauth2Filter.java index 2fa0d391a21..17bca9e35a0 100644 --- a/appengine/oauth2/src/main/java/com/example/appengine/Oauth2Filter.java +++ b/appengine/oauth2/src/main/java/com/example/appengine/Oauth2Filter.java @@ -13,6 +13,8 @@ */ package com.example.appengine; +import static com.google.appengine.api.utils.SystemProperty.environment; + import com.google.appengine.api.oauth.OAuthRequestException; import com.google.appengine.api.oauth.OAuthService; import com.google.appengine.api.oauth.OAuthServiceFactory; @@ -33,8 +35,6 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletResponse; -import static com.google.appengine.api.utils.SystemProperty.environment; - /** * Filter to verify that request has a "Authorization: Bearer xxxx" header, * and check if xxxx is authorized to use this app. @@ -55,11 +55,12 @@ public void init(final FilterConfig config) throws ServletException { @Override public void doFilter(final ServletRequest servletReq, final ServletResponse servletResp, final FilterChain chain) throws IOException, ServletException { + final String scope = "https://www.googleapis.com/auth/userinfo.email"; + Set allowedClients = new HashSet<>(); + HttpServletResponse resp = (HttpServletResponse) servletResp; OAuthService oauth = OAuthServiceFactory.getOAuthService(); - final String scope = "https://www.googleapis.com/auth/userinfo.email"; - Set allowedClients = new HashSet<>(); allowedClients.add("407408718192.apps.googleusercontent.com"); // list of client ids to allow allowedClients.add("755878275993-j4k7emq6rlupctce1c28enpcrr50vfo1.apps.googleusercontent.com"); @@ -68,7 +69,7 @@ public void doFilter(final ServletRequest servletReq, final ServletResponse serv SystemProperty.Environment.Value env = environment.value(); if (env == SystemProperty.Environment.Value.Production) { // APIs only work in Production try { - User user = oauth.getCurrentUser(scope); // From "Authorization: Bearer" http req header + User user = oauth.getCurrentUser(scope); String tokenAudience = oauth.getClientId(scope); // The line below is commented out for privacy. From 26167f26538b4232b783a7a82acf0132289eb78a Mon Sep 17 00:00:00 2001 From: Les Vogel Date: Wed, 20 Apr 2016 17:55:27 -0700 Subject: [PATCH 4/7] Oauth2 updates, Guestbook MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Oauth2 - Tim and Brett’s comments - Mostly nits - HelloServlet now uses User GuestBook - Initial checkin, updated README remove .gitignore --- appengine/guestbook/README.md | 17 +++ appengine/guestbook/pom.xml | 96 ++++++++++++++++ .../java/com/example/guestbook/Greeting.java | 81 +++++++++++++ .../java/com/example/guestbook/Guestbook.java | 33 ++++++ .../java/com/example/guestbook/OfyHelper.java | 42 +++++++ .../guestbook/SignGuestbookServlet.java | 70 ++++++++++++ .../src/main/webapp/WEB-INF/appengine-web.xml | 10 ++ .../main/webapp/WEB-INF/logging.properties | 13 +++ .../guestbook/src/main/webapp/WEB-INF/web.xml | 35 ++++++ .../guestbook/src/main/webapp/guestbook.jsp | 106 ++++++++++++++++++ .../src/main/webapp/stylesheets/main.css | 4 + appengine/helloworld/.gitignore | 7 -- appengine/oauth2/.gitignore | 7 -- appengine/oauth2/pom.xml | 12 +- .../com/example/appengine/HelloServlet.java | 21 +++- .../com/example/appengine/Oauth2Filter.java | 26 ++--- appengine/oauth2/src/main/webapp/index.html | 19 +++- pom.xml | 5 +- 18 files changed, 566 insertions(+), 38 deletions(-) create mode 100644 appengine/guestbook/README.md create mode 100644 appengine/guestbook/pom.xml create mode 100644 appengine/guestbook/src/main/java/com/example/guestbook/Greeting.java create mode 100644 appengine/guestbook/src/main/java/com/example/guestbook/Guestbook.java create mode 100644 appengine/guestbook/src/main/java/com/example/guestbook/OfyHelper.java create mode 100644 appengine/guestbook/src/main/java/com/example/guestbook/SignGuestbookServlet.java create mode 100644 appengine/guestbook/src/main/webapp/WEB-INF/appengine-web.xml create mode 100644 appengine/guestbook/src/main/webapp/WEB-INF/logging.properties create mode 100644 appengine/guestbook/src/main/webapp/WEB-INF/web.xml create mode 100644 appengine/guestbook/src/main/webapp/guestbook.jsp create mode 100644 appengine/guestbook/src/main/webapp/stylesheets/main.css delete mode 100644 appengine/helloworld/.gitignore delete mode 100644 appengine/oauth2/.gitignore diff --git a/appengine/guestbook/README.md b/appengine/guestbook/README.md new file mode 100644 index 00000000000..9a0b84c9cff --- /dev/null +++ b/appengine/guestbook/README.md @@ -0,0 +1,17 @@ +# appengine-java-guestbook + +An App Engine guestbook using Java, Maven, and Objectify. + +Data access using [Objectify](https://github.com/objectify/objectify) + +Please ask questions on [Stackoverflow](http://stackoverflow.com/questions/tagged/google-app-engine) + +## Running Locally + +How do I, as a developer, start working on the project? + +1. `mvn clean appengine:devserver` + +## Deploying + +1. `mvn clean appengine:update -Dappengine.appId=PROJECT -Dappengine.version=VERSION` diff --git a/appengine/guestbook/pom.xml b/appengine/guestbook/pom.xml new file mode 100644 index 00000000000..44f67c1e25e --- /dev/null +++ b/appengine/guestbook/pom.xml @@ -0,0 +1,96 @@ + + + + 4.0.0 + war + 1.0-SNAPSHOT + + com.example.guestbook + appengine-guestbook + + 5.1.5 + 18.0 + + + com.google.cloud + doc-samples + 1.0.0 + ../.. + + + + + 3.3.9 + + + + + + + com.google.appengine + appengine-api-1.0-sdk + ${appengine.sdk.version} + + + javax.servlet + servlet-api + 2.5 + provided + + + jstl + jstl + 1.2 + + + + + com.google.guava + guava + ${guava.version} + + + com.googlecode.objectify + objectify + ${objectify.version} + + + + + + com.google.appengine + appengine-testing + ${appengine.sdk.version} + test + + + com.google.appengine + appengine-api-stubs + ${appengine.sdk.version} + test + + + + + + ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + + com.google.appengine + appengine-maven-plugin + ${appengine.sdk.version} + + false + + + + + + + + + diff --git a/appengine/guestbook/src/main/java/com/example/guestbook/Greeting.java b/appengine/guestbook/src/main/java/com/example/guestbook/Greeting.java new file mode 100644 index 00000000000..5aa82250a65 --- /dev/null +++ b/appengine/guestbook/src/main/java/com/example/guestbook/Greeting.java @@ -0,0 +1,81 @@ +/** + * Copyright 2014-2015 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 com.googlecode.objectify.Key; +import com.googlecode.objectify.annotation.Entity; +import com.googlecode.objectify.annotation.Id; +import com.googlecode.objectify.annotation.Index; +import com.googlecode.objectify.annotation.Parent; + +import java.lang.String; +import java.util.Date; +import java.util.List; + +/** + * The @Entity tells Objectify about our entity. We also register it in {@link OfyHelper} + * Our primary key @Id is set automatically by the Google Datastore for us. + * + * We add a @Parent to tell the object about its ancestor. We are doing this to support many + * guestbooks. Objectify, unlike the AppEngine library requires that you specify the fields you + * want to index using @Index. Only indexing the fields you need can lead to substantial gains in + * performance -- though if not indexing your data from the start will require indexing it later. + * + * NOTE - all the properties are PUBLIC so that can keep the code simple. + **/ +@Entity +public class Greeting { + @Parent Key theBook; + @Id public Long id; + + public String author_email; + public String author_id; + public String content; + @Index public Date date; + + /** + * Simple constructor just sets the date + **/ + public Greeting() { + date = new Date(); + } + + /** + * A convenience constructor + **/ + public Greeting(String book, String content) { + this(); + if( book != null ) { + theBook = Key.create(Guestbook.class, book); // Creating the Ancestor key + } else { + theBook = Key.create(Guestbook.class, "default"); + } + this.content = content; + } + + /** + * Takes all important fields + **/ + public Greeting(String book, String content, String id, String email) { + this(book, content); + author_email = email; + author_id = id; + } + +} +//[END all] diff --git a/appengine/guestbook/src/main/java/com/example/guestbook/Guestbook.java b/appengine/guestbook/src/main/java/com/example/guestbook/Guestbook.java new file mode 100644 index 00000000000..73786e00249 --- /dev/null +++ b/appengine/guestbook/src/main/java/com/example/guestbook/Guestbook.java @@ -0,0 +1,33 @@ +/** + * Copyright 2014-2015 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 com.googlecode.objectify.annotation.Entity; +import com.googlecode.objectify.annotation.Id; + +/** + * The @Entity tells Objectify about our entity. We also register it in + * OfyHelper.java -- very important. + * + * This is never actually created, but gives a hint to Objectify about our Ancestor key. + */ +@Entity +public class Guestbook { + @Id public String book; +} +//[END all] diff --git a/appengine/guestbook/src/main/java/com/example/guestbook/OfyHelper.java b/appengine/guestbook/src/main/java/com/example/guestbook/OfyHelper.java new file mode 100644 index 00000000000..3a2b95b7cc1 --- /dev/null +++ b/appengine/guestbook/src/main/java/com/example/guestbook/OfyHelper.java @@ -0,0 +1,42 @@ +/** + * Copyright 2014-2015 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 com.googlecode.objectify.Objectify; +import com.googlecode.objectify.ObjectifyFactory; +import com.googlecode.objectify.ObjectifyService; + +import javax.servlet.ServletContextListener; +import javax.servlet.ServletContextEvent; + +/** + * OfyHelper, a ServletContextListener, is setup in web.xml to run before a JSP is run. This is + * required to let JSP's access Ofy. + **/ +public class OfyHelper implements ServletContextListener { + public void contextInitialized(ServletContextEvent event) { + // This will be invoked as part of a warmup request, or the first user request if no warmup + // request. + ObjectifyService.register(Guestbook.class); + ObjectifyService.register(Greeting.class); + } + + public void contextDestroyed(ServletContextEvent event) { + // App Engine does not currently invoke this method. + } +} +//[END all] diff --git a/appengine/guestbook/src/main/java/com/example/guestbook/SignGuestbookServlet.java b/appengine/guestbook/src/main/java/com/example/guestbook/SignGuestbookServlet.java new file mode 100644 index 00000000000..47e6ea2768a --- /dev/null +++ b/appengine/guestbook/src/main/java/com/example/guestbook/SignGuestbookServlet.java @@ -0,0 +1,70 @@ +/** + * Copyright 2014-2015 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 com.google.appengine.api.datastore.DatastoreService; +import com.google.appengine.api.datastore.DatastoreServiceFactory; +import com.google.appengine.api.datastore.Entity; +import com.google.appengine.api.datastore.Key; +import com.google.appengine.api.datastore.KeyFactory; +import com.google.appengine.api.users.User; +import com.google.appengine.api.users.UserService; +import com.google.appengine.api.users.UserServiceFactory; + +import java.io.IOException; +import java.util.Date; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.googlecode.objectify.ObjectifyService; + +/** + * Form Handling Servlet + * Most of the action for this sample is in webapp/guestbook.jsp, which displays the + * {@link Greeting}'s. This servlet has one method + * {@link #doPost(<#HttpServletRequest req#>, <#HttpServletResponse resp#>)} which takes the form + * data and saves it. + */ +public class SignGuestbookServlet extends HttpServlet { + + // Process the http POST of the form + @Override + public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { + Greeting greeting; + + UserService userService = UserServiceFactory.getUserService(); + User user = userService.getCurrentUser(); // Find out who the user is. + + String guestbookName = req.getParameter("guestbookName"); + String content = req.getParameter("content"); + if (user != null) { + greeting = new Greeting(guestbookName, content, user.getUserId(), user.getEmail()); + } else { + greeting = new Greeting(guestbookName, content); + } + + // Use Objectify to save the greeting and now() is used to make the call synchronously as we + // will immediately get a new page using redirect and we want the data to be present. + ObjectifyService.ofy().save().entity(greeting).now(); + + resp.sendRedirect("/guestbook.jsp?guestbookName=" + guestbookName); + } +} +//[END all] diff --git a/appengine/guestbook/src/main/webapp/WEB-INF/appengine-web.xml b/appengine/guestbook/src/main/webapp/WEB-INF/appengine-web.xml new file mode 100644 index 00000000000..8bf645d1013 --- /dev/null +++ b/appengine/guestbook/src/main/webapp/WEB-INF/appengine-web.xml @@ -0,0 +1,10 @@ + + + your-app-id + ${appengine.app.version} + true + + + + + diff --git a/appengine/guestbook/src/main/webapp/WEB-INF/logging.properties b/appengine/guestbook/src/main/webapp/WEB-INF/logging.properties new file mode 100644 index 00000000000..a17206681f0 --- /dev/null +++ b/appengine/guestbook/src/main/webapp/WEB-INF/logging.properties @@ -0,0 +1,13 @@ +# A default java.util.logging configuration. +# (All App Engine logging is through java.util.logging by default). +# +# To use this configuration, copy it into your application's WEB-INF +# folder and add the following to your appengine-web.xml: +# +# +# +# +# + +# Set the default logging level for all loggers to WARNING +.level = WARNING diff --git a/appengine/guestbook/src/main/webapp/WEB-INF/web.xml b/appengine/guestbook/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..d5a23626f0c --- /dev/null +++ b/appengine/guestbook/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,35 @@ + + + + + + sign + com.example.guestbook.SignGuestbookServlet + + + + sign + /sign + + + + guestbook.jsp + + + + + + ObjectifyFilter + com.googlecode.objectify.ObjectifyFilter + + + ObjectifyFilter + /* + + + com.example.guestbook.OfyHelper + + + diff --git a/appengine/guestbook/src/main/webapp/guestbook.jsp b/appengine/guestbook/src/main/webapp/guestbook.jsp new file mode 100644 index 00000000000..5bdb96fbcab --- /dev/null +++ b/appengine/guestbook/src/main/webapp/guestbook.jsp @@ -0,0 +1,106 @@ +<%-- //[START all]--%> +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@ page import="com.google.appengine.api.users.User" %> +<%@ page import="com.google.appengine.api.users.UserService" %> +<%@ page import="com.google.appengine.api.users.UserServiceFactory" %> + +<%-- //[START imports]--%> +<%@ page import="com.example.guestbook.Greeting" %> +<%@ page import="com.example.guestbook.Guestbook" %> +<%@ page import="com.googlecode.objectify.Key" %> +<%@ page import="com.googlecode.objectify.ObjectifyService" %> +<%-- //[END imports]--%> + +<%@ page import="java.util.List" %> +<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> + + + + + + + + +<% + String guestbookName = request.getParameter("guestbookName"); + if (guestbookName == null) { + guestbookName = "default"; + } + pageContext.setAttribute("guestbookName", guestbookName); + UserService userService = UserServiceFactory.getUserService(); + User user = userService.getCurrentUser(); + if (user != null) { + pageContext.setAttribute("user", user); +%> + +

Hello, ${fn:escapeXml(user.nickname)}! (You can + sign out.)

+<% + } else { +%> +

Hello! + Sign in + to include your name with greetings you post.

+<% + } +%> + +<%-- //[START datastore]--%> +<% + // Create the correct Ancestor key + Key theBook = Key.create(Guestbook.class, guestbookName); + + // Run an ancestor query to ensure we see the most up-to-date + // view of the Greetings belonging to the selected Guestbook. + List greetings = ObjectifyService.ofy() + .load() + .type(Greeting.class) // We want only Greetings + .ancestor(theBook) // Anyone in this book + .order("-date") // Most recent first - date is indexed. + .limit(5) // Only show 5 of them. + .list(); + + if (greetings.isEmpty()) { +%> +

Guestbook '${fn:escapeXml(guestbookName)}' has no messages.

+<% + } else { +%> +

Messages in Guestbook '${fn:escapeXml(guestbookName)}'.

+<% + // Look at all of our greetings + for (Greeting greeting : greetings) { + pageContext.setAttribute("greeting_content", greeting.content); + String author; + if (greeting.author_email == null) { + author = "An anonymous person"; + } else { + author = greeting.author_email; + String author_id = greeting.author_id; + if (user != null && user.getUserId().equals(author_id)) { + author += " (You)"; + } + } + pageContext.setAttribute("greeting_user", author); +%> +

${fn:escapeXml(greeting_user)} wrote:

+
${fn:escapeXml(greeting_content)}
+<% + } + } +%> + +
+
+
+ +
+<%-- //[END datastore]--%> +
+
+
+
+ + + +<%-- //[END all]--%> diff --git a/appengine/guestbook/src/main/webapp/stylesheets/main.css b/appengine/guestbook/src/main/webapp/stylesheets/main.css new file mode 100644 index 00000000000..05d72d5536d --- /dev/null +++ b/appengine/guestbook/src/main/webapp/stylesheets/main.css @@ -0,0 +1,4 @@ +body { + font-family: Verdana, Helvetica, sans-serif; + background-color: #FFFFCC; +} diff --git a/appengine/helloworld/.gitignore b/appengine/helloworld/.gitignore deleted file mode 100644 index 471339729ae..00000000000 --- a/appengine/helloworld/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# Eclipse files -.project -.classpath -.settings - -# Target folders -target/ diff --git a/appengine/oauth2/.gitignore b/appengine/oauth2/.gitignore deleted file mode 100644 index 471339729ae..00000000000 --- a/appengine/oauth2/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# Eclipse files -.project -.classpath -.settings - -# Target folders -target/ diff --git a/appengine/oauth2/pom.xml b/appengine/oauth2/pom.xml index b29ab18dad4..d21502a7803 100644 --- a/appengine/oauth2/pom.xml +++ b/appengine/oauth2/pom.xml @@ -1,5 +1,5 @@ + + appengine-oauth2 sample