From aa08246dd8a86723c4e2e4afd2d4c88d7b95802d Mon Sep 17 00:00:00 2001 From: Amy Unruh Date: Tue, 29 Jul 2014 14:33:37 -0700 Subject: [PATCH] READMEs and cleanup --- COPYING | 162 +++++++++++++ LICENSE | 201 ++++++++++++++++ README.md | 8 +- java_mvms_tutorial.html | 216 ++++++++++++++++++ java_mvms_tutorial.md | 192 ++++++++++++++++ stage1/nbactions.xml | 68 ++++++ stage1/pom.xml | 89 ++++++++ .../demos/guestbook/CaptchaServlet.java | 97 ++++++++ .../demos/guestbook/SignGuestbookServlet.java | 57 +++++ .../src/main/webapp/WEB-INF/appengine-web.xml | 37 +++ .../main/webapp/WEB-INF/logging.properties | 13 ++ stage1/src/main/webapp/WEB-INF/web.xml | 53 +++++ stage1/src/main/webapp/favicon.ico | Bin 0 -> 1150 bytes stage1/src/main/webapp/guestbook.jsp | 95 ++++++++ stage2/nbactions.xml | 68 ++++++ stage2/pom.xml | 89 ++++++++ .../demos/guestbook/CaptchaServlet.java | 99 ++++++++ .../demos/guestbook/FortuneInfo.java | 44 ++++ .../demos/guestbook/SignGuestbookServlet.java | 57 +++++ stage2/src/main/webapp/Dockerfile | 3 + .../src/main/webapp/WEB-INF/appengine-web.xml | 37 +++ .../main/webapp/WEB-INF/logging.properties | 13 ++ stage2/src/main/webapp/WEB-INF/web.xml | 53 +++++ stage2/src/main/webapp/favicon.ico | Bin 0 -> 1150 bytes stage2/src/main/webapp/guestbook.jsp | 99 ++++++++ stage3/nbactions.xml | 68 ++++++ stage3/pom.xml | 89 ++++++++ .../demos/guestbook/CaptchaServlet.java | 99 ++++++++ .../demos/guestbook/FortuneInfo.java | 44 ++++ .../demos/guestbook/SignGuestbookServlet.java | 57 +++++ stage3/src/main/webapp/Dockerfile | 11 + .../src/main/webapp/WEB-INF/appengine-web.xml | 37 +++ .../main/webapp/WEB-INF/logging.properties | 13 ++ stage3/src/main/webapp/WEB-INF/web.xml | 53 +++++ stage3/src/main/webapp/favicon.ico | Bin 0 -> 1150 bytes stage3/src/main/webapp/guestbook.jsp | 105 +++++++++ 36 files changed, 2423 insertions(+), 3 deletions(-) create mode 100644 COPYING create mode 100644 LICENSE create mode 100644 java_mvms_tutorial.html create mode 100644 java_mvms_tutorial.md create mode 100644 stage1/nbactions.xml create mode 100644 stage1/pom.xml create mode 100644 stage1/src/main/java/com/google/appengine/demos/guestbook/CaptchaServlet.java create mode 100644 stage1/src/main/java/com/google/appengine/demos/guestbook/SignGuestbookServlet.java create mode 100644 stage1/src/main/webapp/WEB-INF/appengine-web.xml create mode 100644 stage1/src/main/webapp/WEB-INF/logging.properties create mode 100644 stage1/src/main/webapp/WEB-INF/web.xml create mode 100644 stage1/src/main/webapp/favicon.ico create mode 100644 stage1/src/main/webapp/guestbook.jsp create mode 100644 stage2/nbactions.xml create mode 100644 stage2/pom.xml create mode 100644 stage2/src/main/java/com/google/appengine/demos/guestbook/CaptchaServlet.java create mode 100644 stage2/src/main/java/com/google/appengine/demos/guestbook/FortuneInfo.java create mode 100644 stage2/src/main/java/com/google/appengine/demos/guestbook/SignGuestbookServlet.java create mode 100644 stage2/src/main/webapp/Dockerfile create mode 100644 stage2/src/main/webapp/WEB-INF/appengine-web.xml create mode 100644 stage2/src/main/webapp/WEB-INF/logging.properties create mode 100644 stage2/src/main/webapp/WEB-INF/web.xml create mode 100644 stage2/src/main/webapp/favicon.ico create mode 100644 stage2/src/main/webapp/guestbook.jsp create mode 100644 stage3/nbactions.xml create mode 100644 stage3/pom.xml create mode 100644 stage3/src/main/java/com/google/appengine/demos/guestbook/CaptchaServlet.java create mode 100644 stage3/src/main/java/com/google/appengine/demos/guestbook/FortuneInfo.java create mode 100644 stage3/src/main/java/com/google/appengine/demos/guestbook/SignGuestbookServlet.java create mode 100644 stage3/src/main/webapp/Dockerfile create mode 100644 stage3/src/main/webapp/WEB-INF/appengine-web.xml create mode 100644 stage3/src/main/webapp/WEB-INF/logging.properties create mode 100644 stage3/src/main/webapp/WEB-INF/web.xml create mode 100644 stage3/src/main/webapp/favicon.ico create mode 100644 stage3/src/main/webapp/guestbook.jsp diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..32bc9dc --- /dev/null +++ b/COPYING @@ -0,0 +1,162 @@ + +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the +copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other +entities that control, are controlled by, or are under common control with +that entity. For the purposes of this definition, "control" means (i) the +power, direct or indirect, to cause the direction or management of such +entity, whether by contract or otherwise, or (ii) ownership of fifty percent +(50%) or more of the outstanding shares, or (iii) beneficial ownership of +such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation source, and +configuration files. + +"Object" form shall mean any form resulting from mechanical transformation +or translation of a Source form, including but not limited to compiled +object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, +made available under the License, as indicated by a copyright notice that is +included in or attached to the work (an example is provided in the Appendix +below). + +"Derivative Works" shall mean any work, whether in Source or Object form, +that is based on (or derived from) the Work and for which the editorial +revisions, annotations, elaborations, or other modifications represent, as a +whole, an original work of authorship. For the purposes of this License, +Derivative Works shall not include works that remain separable from, or +merely link (or bind by name) to the interfaces of, the Work and Derivative +Works thereof. + +"Contribution" shall mean any work of authorship, including the original +version of the Work and any modifications or additions to that Work or +Derivative Works thereof, that is intentionally submitted to Licensor for +inclusion in the Work by the copyright owner or by an individual or Legal +Entity authorized to submit on behalf of the copyright owner. For the +purposes of this definition, "submitted" means any form of electronic, +verbal, or written communication sent to the Licensor or its +representatives, including but not limited to communication on electronic +mailing lists, source code control systems, and issue tracking systems that +are managed by, or on behalf of, the Licensor for the purpose of discussing +and improving the Work, but excluding communication that is conspicuously +marked or otherwise designated in writing by the copyright owner as "Not a +Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on +behalf of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, +non-exclusive, no-charge, royalty-free, irrevocable copyright license to +reproduce, prepare Derivative Works of, publicly display, publicly perform, +sublicense, and distribute the Work and such Derivative Works in Source or +Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, +non-exclusive, no-charge, royalty-free, irrevocable (except as stated in +this section) patent license to make, have made, use, offer to sell, sell, +import, and otherwise transfer the Work, where such license applies only to +those patent claims licensable by such Contributor that are necessarily +infringed by their Contribution(s) alone or by combination of their +Contribution(s) with the Work to which such Contribution(s) was submitted. +If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this +License for that Work shall terminate as of the date such litigation is +filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and +in Source or Object form, provided that You meet the following conditions: + +a. You must give any other recipients of the Work or Derivative Works a copy +of this License; and + +b. You must cause any modified files to carry prominent notices stating that +You changed the files; and + +c. You must retain, in the Source form of any Derivative Works that You +distribute, all copyright, patent, trademark, and attribution notices from +the Source form of the Work, excluding those notices that do not pertain to +any part of the Derivative Works; and + +d. If the Work includes a "NOTICE" text file as part of its distribution, +then any Derivative Works that You distribute must include a readable copy +of the attribution notices contained within such NOTICE file, excluding +those notices that do not pertain to any part of the Derivative Works, in at +least one of the following places: within a NOTICE text file distributed as +part of the Derivative Works; within the Source form or documentation, if +provided along with the Derivative Works; or, within a display generated by +the Derivative Works, if and wherever such third-party notices normally +appear. The contents of the NOTICE file are for informational purposes only +and do not modify the License. You may add Your own attribution notices +within Derivative Works that You distribute, alongside or as an addendum to +the NOTICE text from the Work, provided that such additional attribution +notices cannot be construed as modifying the License. + +You may add Your own copyright statement to Your modifications and may +provide additional or different license terms and conditions for use, +reproduction, or distribution of Your modifications, or for any such +Derivative Works as a whole, provided Your use, reproduction, and +distribution of the Work otherwise complies with the conditions stated in +this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without +any additional terms or conditions. Notwithstanding the above, nothing +herein shall supersede or modify the terms of any separate license agreement +you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, except +as required for reasonable and customary use in describing the origin of the +Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in +writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any +warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or +FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining +the appropriateness of using or redistributing the Work and assume any risks +associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether +in tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to +in writing, shall any Contributor be liable to You for damages, including +any direct, indirect, special, incidental, or consequential damages of any +character arising as a result of this License or out of the use or inability +to use the Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all other +commercial damages or losses), even if such Contributor has been advised of +the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work +or Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such +obligations, You may act only on Your own behalf and on Your sole +responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any +liability incurred by, or claims asserted against, such Contributor by +reason of your accepting any such warranty or additional liability. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..04cb0d7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2013 Google Inc. + + 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. diff --git a/README.md b/README.md index c3af011..635a4ce 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -appengine-java-vm-guestbook-extras -================================== -App Engine Java Managed VMs example: multi-stage tutorial based on adding features to the 'guestbook'. +Copyright (C) 2014 Google Inc. + +This repo contains a tutorial that walks through use of Managed VMs and the new gcloud SDK for a Java app, based on an extended version of the "guestbook" app. It shows how you can test Managed VMs locally, as well as deploy using the new SDK; and shows how to "escape the sandbox" and use a non-default `Dockerfile`. + +See the tutorial 'java_mvms_tutorial' in this directory. diff --git a/java_mvms_tutorial.html b/java_mvms_tutorial.html new file mode 100644 index 0000000..d2df6b2 --- /dev/null +++ b/java_mvms_tutorial.html @@ -0,0 +1,216 @@ + + + + + + java_mvms_tutorial + + + + + +
+

Copyright (C) 2014 Google Inc.

+ +

Java Managed VMs Tutorial

+ +

The App Engine Managed VMs hosting environment lets you run App Engine applications on configurable Compute Engine Virtual Machines (VMs). This VM-based hosting environment offers more flexibility and provides more CPU and memory options. Applications that run on Managed VMs are not subject to Java and Python runtime restrictions, and they have access to all the Compute Engine machine types. You can also add third-party libraries and frameworks to your app. +Managed VMs instances are Docker-container-based, and with the Preview gcloud SDK, it is now possible to edit the Dockerfile configuration used by a module’s instances.

+ +

This tutorial walks through use of Managed VMs and the new gcloud SDK for a Java app; shows how you can test Managed VMs locally as well as deploy using the new SDK; and shows how to use a non-default Dockerfile.

+ +

The code for this tutorial is here: https://github.com/GoogleCloudPlatform/appengine-java-vm-guestbook-extras/. +It includes several stages of a sample app.

+ +

The first stage of the example shows how you can ‘escape the App Engine sandbox’ by using some Java libraries that don’t run on App Engine. +The second stage shows how you can edit a Managed VM module’s Dockerfile to further configure its instances. In this case, we’ll install a linux utility, and also write to the instances’ local filesystem. +The third stage shows how you can install Java 8 on your Managed VM instance via Dockerfile config.

+ +

Initial Setup

+ +

TBD: fix these links for external users

+ +

First, complete the following steps:

+ + + +

Gcloud Authentication

+ +

Be sure to first authenticate with:

+ +
gcloud auth login
+
+ +

Install Maven

+ +

This tutorial uses Maven to build its Java projects, so install Maven as necessary.

+ +

Grab the Sample Code

+ +

Then, grab the starter code that we’ll use for this tutorial, from this repo: https://user.git.corp.google.com/amyu/java-mvms-guestbook/. +This app uses as its starting point the (familiar to many) App Engine “guestbook” sample, but some extras are added that highlight the capabilities of Managed VMs. Here, we’ll assume familiarity with the basic Guestbook app and plunge into the new stuff.

+ +

Make sure that Docker is running before starting the next steps. You may also want to run: +export DOCKER_HOST=tcp://192.168.59.103:2375 +in your shell.

+ +

Stage 1 of the Sample App: Escape the Sandbox

+ +

With Managed VMs, you can run outside the traditional App Engine instance ‘sandbox’. +In this section of the tutorial, we’re going to use the java.awt.* package, which does not run on App Engine’s sandboxed instances, to build ‘captcha’ support for the guestbook app. +(In the next section, we’ll write to the file system, which also is not supported by a sandboxed instance).

+ +

Go to the stage1 directory of the downloaded sample. Take a look at src/main/webapp/WEB-INF/appengine-web.xml. You’ll see that it includes these settings:

+ +
<vm>true</vm>
+<vm-settings>
+    <setting name="machine_type" value="n1-standard-1"/>
+</vm-settings>
+<manual-scaling>
+    <instances>1</instances>
+</manual-scaling>
+
+ +

This indicates that this app module (the ‘default’ module, in this case) is a Managed VMs module.

+ +

While you’re looking at appengine-web.xml, go ahead and change the id to your app id. (This is not necessary for running locally using the development server, but is necessary for deployment).

+ +

Before running the app, take a quick look at the src/main/java/com/google/appengine/demos/guestbook/CaptchaServlet.java servlet, which is new. It uses the java.awt.* package to generate and serve up a ‘captcha’ image, putting the corresponding ‘captcha code’ in the Session.

+ +

guestbook.jsp displays the captcha image, and asks the user to type in the code. SignGuestbookServlet checks the submitted code against the value in the Session, and does not record the comment if the captcha code is incorrect.

+ +

Run Your App Locally

+ +

First, run your app locally. Build the app first:

+ +
mvn package
+
+ +

Then, from the stage1 directory, run:

+ +
<google-cloud-sdk>/bin/gcloud  preview app run target/guestbook-1.0-SNAPSHOT
+
+ +

Then, visit the URL that the development server is running on (likely: http://localhost:8080). You should see a figure that looks like the following. The app is the (probably all-too-familiar) guestbook app, but with a ‘captcha’ image and field added. You must type in the captcha word correctly for the entry to be posted.

+ +

Guestbook with Captcha

+ +

Deploy Your App

+ +

Next, try deploying your app to production.

+ +

First, set the project you’re using with gcloud:

+ +
<preview_google-cloud-sdk>/bin/gcloud config set project <your-project>
+
+ +

Make sure that you’re using a Managed-VMs-enabled app, and have edited src/main/webapp/WEB-INF/appengine-web.xml to use that app id. (When you change the app id, remember to rebuild using mvn package). Then do:

+ +
<preview_google-cloud-sdk>/bin/gcloud preview app deploy \
+  --server preview.appengine.google.com target/guestbook-1.0-SNAPSHOT
+
+ +

This deployment is using the ‘default’ Dockerfile, which you can see in the <preview_google-cloud-sdk>/docker/dockerfiles directory. It contains just:

+ +
FROM google/appengine-java
+ADD . /home/vmagent/appengine-java-vmruntime/webapps/root
+
+ +

After deployment, go to your app: http://.appspot.com. +The app should work the same as it did with the local development server. You’ll see the captcha image— this code wouldn’t have worked with ‘regular’ App Engine instances!

+ +

Stage 2: Configure a Dockerfile for the app

+ +

In Stage 2 of this app, we will to use the linux ‘fortune’ program to autofill in the guestbook entries with ‘suggested text’, in case a guest has a case of writer’s block. You’ll find this version of the app in the stage2 directory.

+ +

These changes involve a couple of new things.

+ +

First, we need to install the ‘fortune’ program on our Managed VM instances, so that we can access it. We will do this by defining a Dockerfile for the app. +Then, we will define a new class (called FortuneInfo), that will exec this program, save the results to a new file, then read the file and serve up the results. +Take a quick look at FortuneInfo.java. Both the use of ProcessBuilder, and and the temp file writes to the local filesystem, would not work on ‘regular’ App Engine instances.

+ +

Guestbook with Fortunes

+ +

Take a look at the Dockerfile in the stage2/src/main/webapp directory. +It looks like this:

+ +
FROM google/appengine-java
+ADD . /home/vmagent/appengine-java-vmruntime/webapps/root
+
+RUN apt-get update && apt-get install -y fortunes
+
+ +

The file indicates to: start with the default java runtime docker image, and add to it an installation of the ‘fortunes’ program.

+ +

Build your app, via mvn package.

+ +

Run Your App Locally

+ +

As described above for Stage 1, build your app and run it locally:

+ +
<preview_google-cloud-sdk>/bin/gcloud  preview app run target/guestbook-1.0-SNAPSHOT
+
+ +

You should now see the guestbook entry field autofilled with a randomly-selected ‘fortune’.

+ +

Deploy Your App

+ +

Deploy your app using the same instructions as above for the Stage 1 version of the app:

+ +
<preview_google-cloud-sdk>/bin/gcloud preview app deploy \
+  --server preview.appengine.google.com target/guestbook-1.0-SNAPSHOT
+
+ +

It should look just the same as you saw in the local development server— again, with code that would not have run on sandboxed ‘regular’ App Engine instances.

+ +

Stage 3: Install Java 8 on Your Managed VM Instances

+ +

As a final stage of this tutorial, we will show how you can run your app using Java 8 (not yet supported on ‘regular’ App Engine instances), by adding additional commands to the app’s Dockerfile. You can find the code for this version of the app in the stage3 directory.

+ +

First, edit your Dockerfile to look like the following.

+ +
FROM google/appengine-java
+RUN apt-get update && apt-get install -y fortunes
+# Install java8
+RUN echo "deb http://ppa.launchpad.net/webupd8team/java/ubuntu precise main" | tee -a /etc/apt/sources.list
+RUN echo "deb-src http://ppa.launchpad.net/webupd8team/java/ubuntu precise main" | tee -a /etc/apt/sources.list
+RUN echo oracle-java8-installer shared/accepted-oracle-license-v1-1 select true | /usr/bin/debconf-set-selections
+RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys EEA14886
+RUN apt-get update
+RUN apt-get install -y oracle-java8-installer
+
+    ADD . /home/vmagent/appengine-java-vmruntime/webapps/root
+
+ +

Then, guestbook.jsp has been modified to grab the Java version from the system properties, and print it at the top of the page.

+ +

Local Testing and Deployment

+ +

The local testing and deployment process is the same as above. +This time, when you visit your app, you should see at the top of the page an indication of the java version you’re running. It should look something like the figure below.

+ +

Guestbook on Java 8

+ +

Summary

+ +

This tutorial walked through use of Managed VMs and the new gcloud SDK for a Java app, based on an extended version of the “guestbook” app. It showed how you can test Managed VMs locally, as well as deploy using the new SDK; and showed how to “escape the sandbox” and use a non-default Dockerfile.

+
+ + \ No newline at end of file diff --git a/java_mvms_tutorial.md b/java_mvms_tutorial.md new file mode 100644 index 0000000..290e04a --- /dev/null +++ b/java_mvms_tutorial.md @@ -0,0 +1,192 @@ + +Copyright (C) 2014 Google Inc. + +# Java Managed VMs Tutorial # + +The [App Engine Managed VMs](https://developers.google.com/appengine/docs/managed-vms/) hosting environment lets you run App Engine applications on configurable Compute Engine Virtual Machines (VMs). This VM-based hosting environment offers more flexibility and provides more CPU and memory options. Applications that run on Managed VMs are not subject to Java and Python runtime restrictions, and they have access to all the Compute Engine machine types. You can also add third-party libraries and frameworks to your app. +Managed VMs instances are [Docker](https://www.docker.com/)-container-based, and with the Preview gcloud SDK, it is now possible to edit the `Dockerfile` configuration used by a module's instances. + +This tutorial walks through use of Managed VMs and the new gcloud SDK for a Java app; shows how you can test Managed VMs locally as well as deploy using the new SDK; and shows how to use a non-default `Dockerfile`. + +The code for this tutorial is here: [https://github.com/GoogleCloudPlatform/appengine-java-vm-guestbook-extras/](https://github.com/GoogleCloudPlatform/appengine-java-vm-guestbook-extras/). +It includes several stages of a sample app. + +The first stage of the example shows how you can 'escape the App Engine sandbox' by using some Java libraries that don't run on App Engine. +The second stage shows how you can edit a Managed VM module's `Dockerfile` to further configure its instances. In this case, we'll install a linux utility, and also write to the instances' local filesystem. +The third stage shows how you can install Java 8 on your Managed VM instance via `Dockerfile` config. + +## Initial Setup ## + +**TBD: fix these links for external users** + +First, complete the following steps: + +- [Create your project](https://developers.google.com/appengine/docs/managed-vms/) and have it enabled for Managed VMs. +- Install Docker in a local VM, as [described here](tbd: pointer to correct docs). + **Important Note: make sure your VirtualBox VM has 4Gb or RAM (or more). Otherwise, the Java runtime will NOT start.** + (`boot2docker init -m 4096`) +- Download and install [the Preview build of the Google Cloud SDK](https://console.developers.google.com/m/cloudstorage/b/managed-vm-sdk/o/managed-vm-sdk-latest.zip), as [described here](tbd: pointer to docs). + +### Gcloud Authentication ### + +Be sure to first authenticate with: + + gcloud auth login + +### Install Maven ### + +This tutorial uses Maven to build its Java projects, so [install Maven](http://maven.apache.org/download.cgi) as necessary. + +### Grab the Sample Code ### + +Then, grab the starter code that we'll use for this tutorial, from this repo: [https://user.git.corp.google.com/amyu/java-mvms-guestbook/](https://user.git.corp.google.com/amyu/java-mvms-guestbook/). +This app uses as its starting point the (familiar to many) App Engine "guestbook" sample, but some extras are added that highlight the capabilities of Managed VMs. Here, we'll assume familiarity with the basic Guestbook app and plunge into the new stuff. + +**Make sure that Docker is running** before starting the next steps. You may also want to run: +`export DOCKER_HOST=tcp://192.168.59.103:2375` +in your shell. + + +## Stage 1 of the Sample App: Escape the Sandbox ## + +With Managed VMs, you can run outside the traditional App Engine instance 'sandbox'. +In this section of the tutorial, we're going to use the java.awt.* package, which does not run on App Engine's sandboxed instances, to build 'captcha' support for the guestbook app. +(In the next section, we'll write to the file system, which also is not supported by a sandboxed instance). + +Go to the `stage1` directory of the downloaded sample. Take a look at `src/main/webapp/WEB-INF/appengine-web.xml`. You'll see that it includes these settings: + + true + + + + + 1 + + +This indicates that this app module (the 'default' module, in this case) is a Managed VMs module, and indicates that one instance of this module version should be started. + +While you're looking at `appengine-web.xml`, go ahead and change the id to your app id. (This is not necessary for running locally using the development server, but is necessary for deployment). + +Before running the app, take a quick look at the `src/main/java/com/google/appengine/demos/guestbook/CaptchaServlet.java` servlet, which is new. It uses the java.awt.* package to generate and serve up a 'captcha' image, putting the corresponding 'captcha code' in the `Session`. + +`guestbook.jsp` displays the captcha image, and asks the user to type in the code. `SignGuestbookServlet` checks the submitted code against the value in the `Session`, and does not record the comment if the captcha code is incorrect. + +### Run Your App Locally ### + +First, run your app locally. Build the app first: + + mvn package + +Then, from the `stage1` directory, run: + + /bin/gcloud preview app run target/guestbook-1.0-SNAPSHOT + +Then, visit the URL that the development server is running on (likely: [http://localhost:8080](http://localhost:8080)). You should see a figure that looks like the following. The app is the (probably all-too-familiar) guestbook app, but with a 'captcha' image and field added. You must type in the captcha word correctly for the entry to be posted. + +Guestbook with Captcha + + +### Deploy Your App ### + +Next, try deploying your app to production. + +First, set the project you're using with `gcloud`: + + /bin/gcloud config set project + +Make sure that you're using a Managed-VMs-enabled app, and have edited `src/main/webapp/WEB-INF/appengine-web.xml` to use that app id. (When you change the app id, remember to rebuild using `mvn package`). Then do: + + /bin/gcloud preview app deploy \ + --server preview.appengine.google.com target/guestbook-1.0-SNAPSHOT + +This deployment is using the 'default' `Dockerfile`, which you can see in the `/docker/dockerfiles` directory. It contains just: + + FROM google/appengine-java + ADD . /home/vmagent/appengine-java-vmruntime/webapps/root + + +After deployment, go to your app: http://.appspot.com. +The app should work the same as it did with the local development server. You'll see the captcha image— this code wouldn't have worked with 'regular' App Engine instances! + +## Stage 2: Configure a Dockerfile for the app ## + +In Stage 2 of this app, we will to use the linux 'fortune' program to autofill in the guestbook entries with 'suggested text', in case a guest has a case of writer's block. You'll find this version of the app in the `stage2` directory. + +These changes involve a couple of new things. + +First, we need to install the 'fortune' program on our Managed VM instances, so that we can access it. We will do this by defining a `Dockerfile` for the app. +Then, we will define a new class (called `FortuneInfo`), that will exec this program, save the results to a new file, then read the file and serve up the results. +Take a quick look at `FortuneInfo.java`. Both the use of `ProcessBuilder`, and and the temp file writes to the local filesystem, would not work on 'regular' App Engine instances. + +Guestbook with Fortunes + +Take a look at the `Dockerfile` in the `stage2/src/main/webapp` directory. +It looks like this: + + FROM google/appengine-java + ADD . /home/vmagent/appengine-java-vmruntime/webapps/root + + RUN apt-get update && apt-get install -y fortunes + +The file indicates to: start with the default java runtime docker image, and add to it an installation of the 'fortunes' program. + +Build your app, via `mvn package`. + +### Run Your App Locally ### + +As described above for Stage 1, build your app and run it locally: + + /bin/gcloud preview app run target/guestbook-1.0-SNAPSHOT + +You should now see the guestbook entry field autofilled with a randomly-selected 'fortune'. + +### Deploy Your App ### + +Deploy your app using the same instructions as above for the Stage 1 version of the app: + + /bin/gcloud preview app deploy \ + --server preview.appengine.google.com target/guestbook-1.0-SNAPSHOT +It should look just the same as you saw in the local development server— again, with code that would not have run on sandboxed 'regular' App Engine instances. + +## Stage 3: Install Java 8 on Your Managed VM Instances ## + +As a final stage of this tutorial, we will show how you can run your app using Java 8 (not yet supported on 'regular' App Engine instances), by adding additional commands to the app's `Dockerfile`. You can find the code for this version of the app in the `stage3` directory. + +First, edit your Dockerfile to look like the following. + + FROM google/appengine-java + RUN apt-get update && apt-get install -y fortunes + # Install java8 + RUN echo "deb http://ppa.launchpad.net/webupd8team/java/ubuntu precise main" | tee -a /etc/apt/sources.list + RUN echo "deb-src http://ppa.launchpad.net/webupd8team/java/ubuntu precise main" | tee -a /etc/apt/sources.list + RUN echo oracle-java8-installer shared/accepted-oracle-license-v1-1 select true | /usr/bin/debconf-set-selections + RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys EEA14886 + RUN apt-get update + RUN apt-get install -y oracle-java8-installer + + ADD . /home/vmagent/appengine-java-vmruntime/webapps/root + + +Then, `guestbook.jsp` has been modified to grab the Java version from the system properties, and print it at the top of the page. + +### Local Testing and Deployment ### + +The local testing and deployment process is the same as above. +This time, when you visit your app, you should see at the top of the page an indication of the java version you're running. It should look something like the figure below. + +Guestbook on Java 8 + + +## Summary ## + +This tutorial walked through use of Managed VMs and the new gcloud SDK for a Java app, based on an extended version of the "guestbook" app. It showed how you can test Managed VMs locally, as well as deploy using the new SDK; and showed how to "escape the sandbox" and use a non-default `Dockerfile`. + + + + + + + + + + diff --git a/stage1/nbactions.xml b/stage1/nbactions.xml new file mode 100644 index 0000000..5b54b6f --- /dev/null +++ b/stage1/nbactions.xml @@ -0,0 +1,68 @@ + + + + + + CUSTOM-appengine:devserver + appengine:devserver + + appengine:devserver + + + + CUSTOM-appengine:update + appengine:update + + appengine:update + + + + CUSTOM-appengine:rollback + appengine:rollback + + appengine:rollback + + + + CUSTOM-appengine:update_cron + appengine:update_cron + + appengine:update_cron + + + + CUSTOM-appengine:update_dos + appengine:update_dos + + appengine:update_dos + + + + CUSTOM-appengine:update_indexes + appengine:update_indexes + + appengine:update_indexes + + + + CUSTOM-appengine:update_queues + appengine:update_queues + + appengine:update_queues + + + diff --git a/stage1/pom.xml b/stage1/pom.xml new file mode 100644 index 0000000..73145c3 --- /dev/null +++ b/stage1/pom.xml @@ -0,0 +1,89 @@ + + + + + + 4.0.0 + war + 1.0-SNAPSHOT + + com.google.appengine.demos + guestbook + + + 1.9.6 + UTF-8 + + + + + + com.google.appengine + appengine-api-1.0-sdk + ${appengine.target.version} + + + javax.servlet + servlet-api + 2.5 + provided + + + + + + + org.apache.maven.plugins + 2.5.1 + maven-compiler-plugin + + 1.7 + 1.7 + + + + + org.apache.maven.plugins + maven-war-plugin + 2.3 + + true + + + + ${basedir}/src/main/webapp/WEB-INF + true + WEB-INF + + + + + + + com.google.appengine + appengine-maven-plugin + ${appengine.target.version}-maven3.0 + + preview.appengine.google.com + + + + + + diff --git a/stage1/src/main/java/com/google/appengine/demos/guestbook/CaptchaServlet.java b/stage1/src/main/java/com/google/appengine/demos/guestbook/CaptchaServlet.java new file mode 100644 index 0000000..f3be80d --- /dev/null +++ b/stage1/src/main/java/com/google/appengine/demos/guestbook/CaptchaServlet.java @@ -0,0 +1,97 @@ +package com.google.appengine.demos.guestbook; + +import java.awt.Color; +import java.awt.Font; +import java.awt.GradientPaint; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.io.*; +import java.net.*; + +import java.util.Random; +import javax.imageio.ImageIO; +import javax.servlet.*; +import javax.servlet.http.*; + +// inspired by: http://zetcode.com/tutorials/jeetutorials/captcha/ + +public class CaptchaServlet extends HttpServlet { + + + protected void processRequest(HttpServletRequest request, + HttpServletResponse response) + throws ServletException, IOException { + + int width = 150; + int height = 50; + + char data[][] = { + { 'a', 'n', 'd', 'r', 'o', 'i', 'd' }, + { 'c', 'l', 'o', 'u', 'd' }, + { 'g', 'o', 'o', 'g', 'l', 'e' }, + { 'd', 'o', 'c', 'k', 'e', 'r' }, + { 'g', 'o', 'l', 'a', 'n', 'g' }, + { 'p', 'y', 't', 'h', 'o', 'n' }, + { 'c', 'o', 'm', 'p', 'u', 't', 'e' }, + { 'm', 'a', 'p', 's', 'a', 'p', 'i' } + }; + + + BufferedImage bufferedImage = new BufferedImage(width, height, + BufferedImage.TYPE_INT_RGB); + + Graphics2D g2d = bufferedImage.createGraphics(); + + Font font = new Font("Georgia", Font.BOLD, 18); + g2d.setFont(font); + + RenderingHints rh = new RenderingHints( + RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + + rh.put(RenderingHints.KEY_RENDERING, + RenderingHints.VALUE_RENDER_QUALITY); + + g2d.setRenderingHints(rh); + + GradientPaint gp = new GradientPaint(0, 0, + Color.red, 0, height/2, Color.black, true); + + g2d.setPaint(gp); + g2d.fillRect(0, 0, width, height); + + g2d.setColor(new Color(255, 153, 0)); + + Random r = new Random(); + int index = Math.abs(r.nextInt()) % 5; + + String captcha = String.copyValueOf(data[index]); + request.getSession().setAttribute("captcha", captcha ); + + int x = 0; + int y = 0; + + for (int i=0; ierrorerror in captcha." + + "
Try again."); + return; + } + } + else { + resp.getWriter().println("error in captcha"); + return; + } + + String guestbookName = req.getParameter("guestbookName"); + Key guestbookKey = KeyFactory.createKey("Guestbook", guestbookName); + String content = req.getParameter("content"); + Date date = new Date(); + Entity greeting = new Entity("Greeting", guestbookKey); + greeting.setProperty("user", user); + greeting.setProperty("date", date); + content = content.substring(0, Math.min(content.length(), 490)); + greeting.setProperty("content", content); + + DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); + datastore.put(greeting); + + resp.sendRedirect("/guestbook.jsp?guestbookName=" + guestbookName); + } +} diff --git a/stage1/src/main/webapp/WEB-INF/appengine-web.xml b/stage1/src/main/webapp/WEB-INF/appengine-web.xml new file mode 100644 index 0000000..ea01143 --- /dev/null +++ b/stage1/src/main/webapp/WEB-INF/appengine-web.xml @@ -0,0 +1,37 @@ + + + + + your-app-id + gbdocker + true + true + + + + + 1 + + + true + false + + + + + + diff --git a/stage1/src/main/webapp/WEB-INF/logging.properties b/stage1/src/main/webapp/WEB-INF/logging.properties new file mode 100644 index 0000000..e23a81e --- /dev/null +++ b/stage1/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 INFO +.level = INFO diff --git a/stage1/src/main/webapp/WEB-INF/web.xml b/stage1/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..d8533f0 --- /dev/null +++ b/stage1/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,53 @@ + + + + + + + /* + + + * + + + + + sign + com.google.appengine.demos.guestbook.SignGuestbookServlet + + + sign + /sign + + + + captcha + com.google.appengine.demos.guestbook.CaptchaServlet + + + captcha + /captcha/* + + + + guestbook.jsp + + diff --git a/stage1/src/main/webapp/favicon.ico b/stage1/src/main/webapp/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..0062ab413e73fa137f91cc1b082ce6a9bc992730 GIT binary patch literal 1150 zcmchVT}YE*6vvO#ObKxkm}F29kx=MGBotCtksqk6T__r47lF2m6b%yb3oXOSIbYlS zT0@&?=KPkK%d$<~me%IfxfOnO(T!aM-B`iXL)@7O-l`DN1e2iD~XBziRG#kh#!R zlB8E8it@c6mm2KdQt4SrtcazQq9_WzNfg|g7q>oV8HRho0@vImRu-q>c1&T}ZNrEn z8w_M!@>p|P*lUL-1J(u!w6ahr(+Hi9ZooSBf?u?cW7TQJtD)!6ceg^Ls^;x8CJa_y zf%=*VO;Z1ys*4oKB@ovui@-u#Hq6{5exOLqtJr&1mYlE|hcK)zgn3L4omPc*wSr%= z8_+Kc`Kie`Q0Tpfu$IM8wNYDwH_b8_xGb!Fa=_S@3&Y(2n7fMcvbUY_wU~QvMAZdK z^0GqD1uKoB1e>V`i?b%)Jv)KVjGx!#!(1kYZAgyE(SB%Kt6uq5SN`+}7YLJM^E%zeeoan^L zM+er|To@bdfV#d4+`}^7JU$FVz3jW7t&A1oPE$#Oia#jM!CG;cH`F9xacU4>zbwNv z(v8_^Bi`xc$PJ;B0@aimxf5om`sBbn^J*!q@>tZRP~WWpI{ozearr={bfhEqd}}Jv zK>=z^KlB$XBx>VDdCR&2bjiYnIr*n?DSu{fx8Qypof#;IMom2B2sJKw|89xT^OJ2e zEr^toK%ZJ)g{BNoPa4xBd`WiX!TwTk{wUFga8*Axhs;;~iN0-fp|&9>`oXlnwito? E4Z4s|oB#j- literal 0 HcmV?d00001 diff --git a/stage1/src/main/webapp/guestbook.jsp b/stage1/src/main/webapp/guestbook.jsp new file mode 100644 index 0000000..2ae1118 --- /dev/null +++ b/stage1/src/main/webapp/guestbook.jsp @@ -0,0 +1,95 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@ page import="java.util.List" %> +<%@ page import="com.google.appengine.api.users.User" %> +<%@ page import="com.google.appengine.api.users.UserService" %> +<%@ page import="com.google.appengine.api.users.UserServiceFactory" %> +<%@ page import="com.google.appengine.api.datastore.DatastoreServiceFactory" %> +<%@ page import="com.google.appengine.api.datastore.DatastoreService" %> +<%@ page import="com.google.appengine.api.datastore.Query" %> +<%@ page import="com.google.appengine.api.datastore.Entity" %> +<%@ page import="com.google.appengine.api.datastore.FetchOptions" %> +<%@ page import="com.google.appengine.api.datastore.Key" %> +<%@ page import="com.google.appengine.api.datastore.KeyFactory" %> + +<%@ 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.

+<% + } +%> + +<% + DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); + Key guestbookKey = KeyFactory.createKey("Guestbook", guestbookName); + // Run an ancestor query to ensure we see the most up-to-date + // view of the Greetings belonging to the selected Guestbook. + Query query = new Query("Greeting", guestbookKey).addSort("date", Query.SortDirection.DESCENDING); + List greetings = datastore.prepare(query).asList(FetchOptions.Builder.withLimit(5)); + if (greetings.isEmpty()) { + %> +

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

+ <% + } else { + %> +

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

+ <% + for (Entity greeting : greetings) { + pageContext.setAttribute("greeting_content", + greeting.getProperty("content")); + if (greeting.getProperty("user") == null) { + %> +

An anonymous person wrote:

+ <% + } else { + pageContext.setAttribute("greeting_user", + greeting.getProperty("user")); + %> +

${fn:escapeXml(greeting_user.nickname)} wrote:

+ <% + } + %> +
${fn:escapeXml(greeting_content)}
+ <% + } + } +%> + +
+
+
Enter the word below:
+
+ +
+
+ +
+
+
+
+ + + diff --git a/stage2/nbactions.xml b/stage2/nbactions.xml new file mode 100644 index 0000000..5b54b6f --- /dev/null +++ b/stage2/nbactions.xml @@ -0,0 +1,68 @@ + + + + + + CUSTOM-appengine:devserver + appengine:devserver + + appengine:devserver + + + + CUSTOM-appengine:update + appengine:update + + appengine:update + + + + CUSTOM-appengine:rollback + appengine:rollback + + appengine:rollback + + + + CUSTOM-appengine:update_cron + appengine:update_cron + + appengine:update_cron + + + + CUSTOM-appengine:update_dos + appengine:update_dos + + appengine:update_dos + + + + CUSTOM-appengine:update_indexes + appengine:update_indexes + + appengine:update_indexes + + + + CUSTOM-appengine:update_queues + appengine:update_queues + + appengine:update_queues + + + diff --git a/stage2/pom.xml b/stage2/pom.xml new file mode 100644 index 0000000..73145c3 --- /dev/null +++ b/stage2/pom.xml @@ -0,0 +1,89 @@ + + + + + + 4.0.0 + war + 1.0-SNAPSHOT + + com.google.appengine.demos + guestbook + + + 1.9.6 + UTF-8 + + + + + + com.google.appengine + appengine-api-1.0-sdk + ${appengine.target.version} + + + javax.servlet + servlet-api + 2.5 + provided + + + + + + + org.apache.maven.plugins + 2.5.1 + maven-compiler-plugin + + 1.7 + 1.7 + + + + + org.apache.maven.plugins + maven-war-plugin + 2.3 + + true + + + + ${basedir}/src/main/webapp/WEB-INF + true + WEB-INF + + + + + + + com.google.appengine + appengine-maven-plugin + ${appengine.target.version}-maven3.0 + + preview.appengine.google.com + + + + + + diff --git a/stage2/src/main/java/com/google/appengine/demos/guestbook/CaptchaServlet.java b/stage2/src/main/java/com/google/appengine/demos/guestbook/CaptchaServlet.java new file mode 100644 index 0000000..b1e5c65 --- /dev/null +++ b/stage2/src/main/java/com/google/appengine/demos/guestbook/CaptchaServlet.java @@ -0,0 +1,99 @@ +package com.google.appengine.demos.guestbook; + +import java.awt.Color; +import java.awt.Font; +import java.awt.GradientPaint; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.io.*; +import java.net.*; + +import java.util.Random; +import javax.imageio.ImageIO; +import javax.servlet.*; +import javax.servlet.http.*; + +// inspired by: http://zetcode.com/tutorials/jeetutorials/captcha/ + +public class CaptchaServlet extends HttpServlet { + + + protected void processRequest(HttpServletRequest request, + HttpServletResponse response) + throws ServletException, IOException { + + int width = 150; + int height = 50; + + String test = FortuneInfo.getInfo(); + + char data[][] = { + { 'a', 'n', 'd', 'r', 'o', 'i', 'd' }, + { 'c', 'l', 'o', 'u', 'd' }, + { 'g', 'o', 'o', 'g', 'l', 'e' }, + { 'd', 'o', 'c', 'k', 'e', 'r' }, + { 'g', 'o', 'l', 'a', 'n', 'g' }, + { 'p', 'y', 't', 'h', 'o', 'n' }, + { 'c', 'o', 'm', 'p', 'u', 't', 'e' }, + { 'm', 'a', 'p', 's', 'a', 'p', 'i' } + }; + + + BufferedImage bufferedImage = new BufferedImage(width, height, + BufferedImage.TYPE_INT_RGB); + + Graphics2D g2d = bufferedImage.createGraphics(); + + Font font = new Font("Georgia", Font.BOLD, 18); + g2d.setFont(font); + + RenderingHints rh = new RenderingHints( + RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + + rh.put(RenderingHints.KEY_RENDERING, + RenderingHints.VALUE_RENDER_QUALITY); + + g2d.setRenderingHints(rh); + + GradientPaint gp = new GradientPaint(0, 0, + Color.red, 0, height/2, Color.black, true); + + g2d.setPaint(gp); + g2d.fillRect(0, 0, width, height); + + g2d.setColor(new Color(255, 153, 0)); + + Random r = new Random(); + int index = Math.abs(r.nextInt()) % 5; + + String captcha = String.copyValueOf(data[index]); + request.getSession().setAttribute("captcha", captcha ); + + int x = 0; + int y = 0; + + for (int i=0; ierrorerror in captcha." + + "
Try again."); + return; + } + } + else { + resp.getWriter().println("error in captcha"); + return; + } + + String guestbookName = req.getParameter("guestbookName"); + Key guestbookKey = KeyFactory.createKey("Guestbook", guestbookName); + String content = req.getParameter("content"); + Date date = new Date(); + Entity greeting = new Entity("Greeting", guestbookKey); + greeting.setProperty("user", user); + greeting.setProperty("date", date); + content = content.substring(0, Math.min(content.length(), 490)); + greeting.setProperty("content", content); + + DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); + datastore.put(greeting); + + resp.sendRedirect("/guestbook.jsp?guestbookName=" + guestbookName); + } +} diff --git a/stage2/src/main/webapp/Dockerfile b/stage2/src/main/webapp/Dockerfile new file mode 100644 index 0000000..7d47dda --- /dev/null +++ b/stage2/src/main/webapp/Dockerfile @@ -0,0 +1,3 @@ +FROM google/appengine-java +RUN apt-get update && apt-get install -y fortunes +ADD . /home/vmagent/appengine-java-vmruntime/webapps/root diff --git a/stage2/src/main/webapp/WEB-INF/appengine-web.xml b/stage2/src/main/webapp/WEB-INF/appengine-web.xml new file mode 100644 index 0000000..ea01143 --- /dev/null +++ b/stage2/src/main/webapp/WEB-INF/appengine-web.xml @@ -0,0 +1,37 @@ + + + + + your-app-id + gbdocker + true + true + + + + + 1 + + + true + false + + + + + + diff --git a/stage2/src/main/webapp/WEB-INF/logging.properties b/stage2/src/main/webapp/WEB-INF/logging.properties new file mode 100644 index 0000000..e23a81e --- /dev/null +++ b/stage2/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 INFO +.level = INFO diff --git a/stage2/src/main/webapp/WEB-INF/web.xml b/stage2/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..d8533f0 --- /dev/null +++ b/stage2/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,53 @@ + + + + + + + /* + + + * + + + + + sign + com.google.appengine.demos.guestbook.SignGuestbookServlet + + + sign + /sign + + + + captcha + com.google.appengine.demos.guestbook.CaptchaServlet + + + captcha + /captcha/* + + + + guestbook.jsp + + diff --git a/stage2/src/main/webapp/favicon.ico b/stage2/src/main/webapp/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..0062ab413e73fa137f91cc1b082ce6a9bc992730 GIT binary patch literal 1150 zcmchVT}YE*6vvO#ObKxkm}F29kx=MGBotCtksqk6T__r47lF2m6b%yb3oXOSIbYlS zT0@&?=KPkK%d$<~me%IfxfOnO(T!aM-B`iXL)@7O-l`DN1e2iD~XBziRG#kh#!R zlB8E8it@c6mm2KdQt4SrtcazQq9_WzNfg|g7q>oV8HRho0@vImRu-q>c1&T}ZNrEn z8w_M!@>p|P*lUL-1J(u!w6ahr(+Hi9ZooSBf?u?cW7TQJtD)!6ceg^Ls^;x8CJa_y zf%=*VO;Z1ys*4oKB@ovui@-u#Hq6{5exOLqtJr&1mYlE|hcK)zgn3L4omPc*wSr%= z8_+Kc`Kie`Q0Tpfu$IM8wNYDwH_b8_xGb!Fa=_S@3&Y(2n7fMcvbUY_wU~QvMAZdK z^0GqD1uKoB1e>V`i?b%)Jv)KVjGx!#!(1kYZAgyE(SB%Kt6uq5SN`+}7YLJM^E%zeeoan^L zM+er|To@bdfV#d4+`}^7JU$FVz3jW7t&A1oPE$#Oia#jM!CG;cH`F9xacU4>zbwNv z(v8_^Bi`xc$PJ;B0@aimxf5om`sBbn^J*!q@>tZRP~WWpI{ozearr={bfhEqd}}Jv zK>=z^KlB$XBx>VDdCR&2bjiYnIr*n?DSu{fx8Qypof#;IMom2B2sJKw|89xT^OJ2e zEr^toK%ZJ)g{BNoPa4xBd`WiX!TwTk{wUFga8*Axhs;;~iN0-fp|&9>`oXlnwito? E4Z4s|oB#j- literal 0 HcmV?d00001 diff --git a/stage2/src/main/webapp/guestbook.jsp b/stage2/src/main/webapp/guestbook.jsp new file mode 100644 index 0000000..8438e24 --- /dev/null +++ b/stage2/src/main/webapp/guestbook.jsp @@ -0,0 +1,99 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@ page import="java.util.List" %> +<%@ page import="com.google.appengine.api.users.User" %> +<%@ page import="com.google.appengine.api.users.UserService" %> +<%@ page import="com.google.appengine.api.users.UserServiceFactory" %> +<%@ page import="com.google.appengine.api.datastore.DatastoreServiceFactory" %> +<%@ page import="com.google.appengine.api.datastore.DatastoreService" %> +<%@ page import="com.google.appengine.api.datastore.Query" %> +<%@ page import="com.google.appengine.api.datastore.Entity" %> +<%@ page import="com.google.appengine.api.datastore.FetchOptions" %> +<%@ page import="com.google.appengine.api.datastore.Key" %> +<%@ page import="com.google.appengine.api.datastore.KeyFactory" %> +<%@ page import="com.google.appengine.demos.guestbook.FortuneInfo" %> + +<%@ 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); + String fortune = FortuneInfo.getInfo(); + pageContext.setAttribute("fortune", fortune); + 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.

+<% + } +%> + +<% + DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); + Key guestbookKey = KeyFactory.createKey("Guestbook", guestbookName); + // Run an ancestor query to ensure we see the most up-to-date + // view of the Greetings belonging to the selected Guestbook. + Query query = new Query("Greeting", guestbookKey).addSort("date", Query.SortDirection.DESCENDING); + List greetings = datastore.prepare(query).asList(FetchOptions.Builder.withLimit(5)); + if (greetings.isEmpty()) { + %> +

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

+ <% + } else { + %> +

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

+ <% + for (Entity greeting : greetings) { + pageContext.setAttribute("greeting_content", + greeting.getProperty("content")); + if (greeting.getProperty("user") == null) { + %> +

An anonymous person wrote:

+ <% + } else { + pageContext.setAttribute("greeting_user", + greeting.getProperty("user")); + %> +

${fn:escapeXml(greeting_user.nickname)} wrote:

+ <% + } + %> +
${fn:escapeXml(greeting_content)}
+ <% + } + } +%> + +
+
+
Enter the word below:
+
+ +
+
+ +
+
+
+
+ + + diff --git a/stage3/nbactions.xml b/stage3/nbactions.xml new file mode 100644 index 0000000..5b54b6f --- /dev/null +++ b/stage3/nbactions.xml @@ -0,0 +1,68 @@ + + + + + + CUSTOM-appengine:devserver + appengine:devserver + + appengine:devserver + + + + CUSTOM-appengine:update + appengine:update + + appengine:update + + + + CUSTOM-appengine:rollback + appengine:rollback + + appengine:rollback + + + + CUSTOM-appengine:update_cron + appengine:update_cron + + appengine:update_cron + + + + CUSTOM-appengine:update_dos + appengine:update_dos + + appengine:update_dos + + + + CUSTOM-appengine:update_indexes + appengine:update_indexes + + appengine:update_indexes + + + + CUSTOM-appengine:update_queues + appengine:update_queues + + appengine:update_queues + + + diff --git a/stage3/pom.xml b/stage3/pom.xml new file mode 100644 index 0000000..73145c3 --- /dev/null +++ b/stage3/pom.xml @@ -0,0 +1,89 @@ + + + + + + 4.0.0 + war + 1.0-SNAPSHOT + + com.google.appengine.demos + guestbook + + + 1.9.6 + UTF-8 + + + + + + com.google.appengine + appengine-api-1.0-sdk + ${appengine.target.version} + + + javax.servlet + servlet-api + 2.5 + provided + + + + + + + org.apache.maven.plugins + 2.5.1 + maven-compiler-plugin + + 1.7 + 1.7 + + + + + org.apache.maven.plugins + maven-war-plugin + 2.3 + + true + + + + ${basedir}/src/main/webapp/WEB-INF + true + WEB-INF + + + + + + + com.google.appengine + appengine-maven-plugin + ${appengine.target.version}-maven3.0 + + preview.appengine.google.com + + + + + + diff --git a/stage3/src/main/java/com/google/appengine/demos/guestbook/CaptchaServlet.java b/stage3/src/main/java/com/google/appengine/demos/guestbook/CaptchaServlet.java new file mode 100644 index 0000000..b1e5c65 --- /dev/null +++ b/stage3/src/main/java/com/google/appengine/demos/guestbook/CaptchaServlet.java @@ -0,0 +1,99 @@ +package com.google.appengine.demos.guestbook; + +import java.awt.Color; +import java.awt.Font; +import java.awt.GradientPaint; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.io.*; +import java.net.*; + +import java.util.Random; +import javax.imageio.ImageIO; +import javax.servlet.*; +import javax.servlet.http.*; + +// inspired by: http://zetcode.com/tutorials/jeetutorials/captcha/ + +public class CaptchaServlet extends HttpServlet { + + + protected void processRequest(HttpServletRequest request, + HttpServletResponse response) + throws ServletException, IOException { + + int width = 150; + int height = 50; + + String test = FortuneInfo.getInfo(); + + char data[][] = { + { 'a', 'n', 'd', 'r', 'o', 'i', 'd' }, + { 'c', 'l', 'o', 'u', 'd' }, + { 'g', 'o', 'o', 'g', 'l', 'e' }, + { 'd', 'o', 'c', 'k', 'e', 'r' }, + { 'g', 'o', 'l', 'a', 'n', 'g' }, + { 'p', 'y', 't', 'h', 'o', 'n' }, + { 'c', 'o', 'm', 'p', 'u', 't', 'e' }, + { 'm', 'a', 'p', 's', 'a', 'p', 'i' } + }; + + + BufferedImage bufferedImage = new BufferedImage(width, height, + BufferedImage.TYPE_INT_RGB); + + Graphics2D g2d = bufferedImage.createGraphics(); + + Font font = new Font("Georgia", Font.BOLD, 18); + g2d.setFont(font); + + RenderingHints rh = new RenderingHints( + RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + + rh.put(RenderingHints.KEY_RENDERING, + RenderingHints.VALUE_RENDER_QUALITY); + + g2d.setRenderingHints(rh); + + GradientPaint gp = new GradientPaint(0, 0, + Color.red, 0, height/2, Color.black, true); + + g2d.setPaint(gp); + g2d.fillRect(0, 0, width, height); + + g2d.setColor(new Color(255, 153, 0)); + + Random r = new Random(); + int index = Math.abs(r.nextInt()) % 5; + + String captcha = String.copyValueOf(data[index]); + request.getSession().setAttribute("captcha", captcha ); + + int x = 0; + int y = 0; + + for (int i=0; ierrorerror in captcha." + + "
Try again."); + return; + } + } + else { + resp.getWriter().println("error in captcha"); + return; + } + + String guestbookName = req.getParameter("guestbookName"); + Key guestbookKey = KeyFactory.createKey("Guestbook", guestbookName); + String content = req.getParameter("content"); + Date date = new Date(); + Entity greeting = new Entity("Greeting", guestbookKey); + greeting.setProperty("user", user); + greeting.setProperty("date", date); + content = content.substring(0, Math.min(content.length(), 490)); + greeting.setProperty("content", content); + + DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); + datastore.put(greeting); + + resp.sendRedirect("/guestbook.jsp?guestbookName=" + guestbookName); + } +} diff --git a/stage3/src/main/webapp/Dockerfile b/stage3/src/main/webapp/Dockerfile new file mode 100644 index 0000000..e385f29 --- /dev/null +++ b/stage3/src/main/webapp/Dockerfile @@ -0,0 +1,11 @@ +FROM google/appengine-java +RUN apt-get update && apt-get install -y fortunes +# Install java8 +RUN echo "deb http://ppa.launchpad.net/webupd8team/java/ubuntu precise main" | tee -a /etc/apt/sources.list +RUN echo "deb-src http://ppa.launchpad.net/webupd8team/java/ubuntu precise main" | tee -a /etc/apt/sources.list +RUN echo oracle-java8-installer shared/accepted-oracle-license-v1-1 select true | /usr/bin/debconf-set-selections +RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys EEA14886 +RUN apt-get update +RUN apt-get install -y oracle-java8-installer + +ADD . /home/vmagent/appengine-java-vmruntime/webapps/root diff --git a/stage3/src/main/webapp/WEB-INF/appengine-web.xml b/stage3/src/main/webapp/WEB-INF/appengine-web.xml new file mode 100644 index 0000000..ea01143 --- /dev/null +++ b/stage3/src/main/webapp/WEB-INF/appengine-web.xml @@ -0,0 +1,37 @@ + + + + + your-app-id + gbdocker + true + true + + + + + 1 + + + true + false + + + + + + diff --git a/stage3/src/main/webapp/WEB-INF/logging.properties b/stage3/src/main/webapp/WEB-INF/logging.properties new file mode 100644 index 0000000..e23a81e --- /dev/null +++ b/stage3/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 INFO +.level = INFO diff --git a/stage3/src/main/webapp/WEB-INF/web.xml b/stage3/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..d8533f0 --- /dev/null +++ b/stage3/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,53 @@ + + + + + + + /* + + + * + + + + + sign + com.google.appengine.demos.guestbook.SignGuestbookServlet + + + sign + /sign + + + + captcha + com.google.appengine.demos.guestbook.CaptchaServlet + + + captcha + /captcha/* + + + + guestbook.jsp + + diff --git a/stage3/src/main/webapp/favicon.ico b/stage3/src/main/webapp/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..0062ab413e73fa137f91cc1b082ce6a9bc992730 GIT binary patch literal 1150 zcmchVT}YE*6vvO#ObKxkm}F29kx=MGBotCtksqk6T__r47lF2m6b%yb3oXOSIbYlS zT0@&?=KPkK%d$<~me%IfxfOnO(T!aM-B`iXL)@7O-l`DN1e2iD~XBziRG#kh#!R zlB8E8it@c6mm2KdQt4SrtcazQq9_WzNfg|g7q>oV8HRho0@vImRu-q>c1&T}ZNrEn z8w_M!@>p|P*lUL-1J(u!w6ahr(+Hi9ZooSBf?u?cW7TQJtD)!6ceg^Ls^;x8CJa_y zf%=*VO;Z1ys*4oKB@ovui@-u#Hq6{5exOLqtJr&1mYlE|hcK)zgn3L4omPc*wSr%= z8_+Kc`Kie`Q0Tpfu$IM8wNYDwH_b8_xGb!Fa=_S@3&Y(2n7fMcvbUY_wU~QvMAZdK z^0GqD1uKoB1e>V`i?b%)Jv)KVjGx!#!(1kYZAgyE(SB%Kt6uq5SN`+}7YLJM^E%zeeoan^L zM+er|To@bdfV#d4+`}^7JU$FVz3jW7t&A1oPE$#Oia#jM!CG;cH`F9xacU4>zbwNv z(v8_^Bi`xc$PJ;B0@aimxf5om`sBbn^J*!q@>tZRP~WWpI{ozearr={bfhEqd}}Jv zK>=z^KlB$XBx>VDdCR&2bjiYnIr*n?DSu{fx8Qypof#;IMom2B2sJKw|89xT^OJ2e zEr^toK%ZJ)g{BNoPa4xBd`WiX!TwTk{wUFga8*Axhs;;~iN0-fp|&9>`oXlnwito? E4Z4s|oB#j- literal 0 HcmV?d00001 diff --git a/stage3/src/main/webapp/guestbook.jsp b/stage3/src/main/webapp/guestbook.jsp new file mode 100644 index 0000000..e3bf9c5 --- /dev/null +++ b/stage3/src/main/webapp/guestbook.jsp @@ -0,0 +1,105 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@ page import="java.util.List" %> +<%@ page import="com.google.appengine.api.users.User" %> +<%@ page import="com.google.appengine.api.users.UserService" %> +<%@ page import="com.google.appengine.api.users.UserServiceFactory" %> +<%@ page import="com.google.appengine.api.datastore.DatastoreServiceFactory" %> +<%@ page import="com.google.appengine.api.datastore.DatastoreService" %> +<%@ page import="com.google.appengine.api.datastore.Query" %> +<%@ page import="com.google.appengine.api.datastore.Entity" %> +<%@ page import="com.google.appengine.api.datastore.FetchOptions" %> +<%@ page import="com.google.appengine.api.datastore.Key" %> +<%@ page import="com.google.appengine.api.datastore.KeyFactory" %> +<%@ page import="com.google.appengine.demos.guestbook.FortuneInfo" %> +<%@ page import="com.google.appengine.api.utils.SystemProperty" %> + + +<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> + + + + + + + + +<% + String guestbookName = request.getParameter("guestbookName"); + if (guestbookName == null) { + guestbookName = "default"; + } + String version = SystemProperty.applicationVersion.get(); + String javaVersion = System.getProperty("java.version"); + pageContext.setAttribute("javaversion", javaVersion); + pageContext.setAttribute("guestbookName", guestbookName); + String fortune = FortuneInfo.getInfo(); + pageContext.setAttribute("fortune", fortune); + UserService userService = UserServiceFactory.getUserService(); + User user = userService.getCurrentUser(); + if (user != null) { + pageContext.setAttribute("user", user); +%> +

Java Version: ${fn:escapeXml(javaversion)}.

+

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

+<% + } else { +%> +

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

+<% + } +%> + +<% + DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); + Key guestbookKey = KeyFactory.createKey("Guestbook", guestbookName); + // Run an ancestor query to ensure we see the most up-to-date + // view of the Greetings belonging to the selected Guestbook. + Query query = new Query("Greeting", guestbookKey).addSort("date", Query.SortDirection.DESCENDING); + List greetings = datastore.prepare(query).asList(FetchOptions.Builder.withLimit(5)); + if (greetings.isEmpty()) { + %> +

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

+ <% + } else { + %> +

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

+ <% + for (Entity greeting : greetings) { + pageContext.setAttribute("greeting_content", + greeting.getProperty("content")); + if (greeting.getProperty("user") == null) { + %> +

An anonymous person wrote:

+ <% + } else { + pageContext.setAttribute("greeting_user", + greeting.getProperty("user")); + %> +

${fn:escapeXml(greeting_user.nickname)} wrote:

+ <% + } + %> +
${fn:escapeXml(greeting_content)}
+ <% + } + } +%> + +
+
+
Enter the word below:
+
+ +
+
+ +
+
+
+
+ + +