Repository: GoogleCloudPlatform/getting-started-java Branch: main Commit: 5135af00b4aa Files: 219 Total size: 527.1 KB Directory structure: gitextract_3yeindkx/ ├── .github/ │ ├── CODEOWNERS │ ├── blunderbuss.yml │ └── sync-repo-settings.yaml ├── .gitignore ├── .gitmodules ├── .kokoro/ │ ├── java11/ │ │ ├── common.cfg │ │ ├── continuous.cfg │ │ ├── periodic.cfg │ │ └── presubmit.cfg │ ├── java8/ │ │ ├── common.cfg │ │ ├── continuous.cfg │ │ ├── periodic.cfg │ │ └── presubmit.cfg │ ├── lint/ │ │ ├── common.cfg │ │ └── presubmit.cfg │ ├── tests/ │ │ ├── run_diff_only.sh │ │ ├── run_lint.sh │ │ └── run_tests.sh │ └── trampoline.sh ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── appengine-standard-java8/ │ ├── deployAll.sh │ ├── helloworld-gae-javasdk-tools/ │ │ ├── README.md │ │ ├── build.gradle │ │ ├── gradle/ │ │ │ └── wrapper/ │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ ├── pom.xml │ │ ├── settings.gradle │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── example/ │ │ │ │ └── appengine/ │ │ │ │ └── java8/ │ │ │ │ └── HelloAppEngine.java │ │ │ └── webapp/ │ │ │ ├── WEB-INF/ │ │ │ │ └── appengine-web.xml │ │ │ └── index.jsp │ │ └── test/ │ │ └── java/ │ │ └── com/ │ │ └── example/ │ │ └── appengine/ │ │ └── java8/ │ │ └── HelloAppEngineTest.java │ ├── kotlin-appengine-standard/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── nbactions.xml │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── kotlin/ │ │ │ └── HomeController.kt │ │ └── webapp/ │ │ └── WEB-INF/ │ │ ├── appengine-web.xml │ │ └── logging.properties │ ├── kotlin-sb-appengine-standard/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── nbactions.xml │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── kotlin/ │ │ │ └── org/ │ │ │ └── jetbrains/ │ │ │ └── kotlin/ │ │ │ └── demo/ │ │ │ ├── Application.kt │ │ │ ├── Greeting.kt │ │ │ └── GreetingController.kt │ │ └── webapp/ │ │ └── WEB-INF/ │ │ ├── appengine-web.xml │ │ └── logging.properties │ └── kotlin-spark-appengine-standard/ │ ├── .gitignore │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ ├── kotlin/ │ │ ├── MainApp.kt │ │ └── SparkInitFilter.kt │ └── webapp/ │ └── WEB-INF/ │ ├── appengine-web.xml │ └── logging.properties ├── background/ │ ├── README.md │ ├── pom.xml │ ├── sample_message.json │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── getstarted/ │ │ │ └── background/ │ │ │ ├── functions/ │ │ │ │ ├── CreateServlet.java │ │ │ │ └── TranslateServlet.java │ │ │ ├── objects/ │ │ │ │ ├── PubSubMessage.java │ │ │ │ ├── TranslateAttributes.java │ │ │ │ └── TranslateMessage.java │ │ │ └── util/ │ │ │ └── BackgroundContextListener.java │ │ └── webapp/ │ │ ├── base.jsp │ │ ├── form.jsp │ │ └── list.jsp │ └── test/ │ └── java/ │ └── com/ │ └── getstarted/ │ └── background/ │ └── UserJourneyTestIT.java ├── bookshelf/ │ └── 1-cloud-run/ │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── com/ │ │ └── example/ │ │ └── getstarted/ │ │ ├── basicactions/ │ │ │ ├── CreateBookServlet.java │ │ │ ├── DeleteBookServlet.java │ │ │ ├── ErrorsBookServlet.java │ │ │ ├── ListBookServlet.java │ │ │ ├── ReadBookServlet.java │ │ │ └── UpdateBookServlet.java │ │ ├── daos/ │ │ │ ├── BookDao.java │ │ │ └── FirestoreDao.java │ │ ├── objects/ │ │ │ ├── Book.java │ │ │ └── Result.java │ │ └── util/ │ │ ├── BookshelfContextListener.java │ │ └── CloudStorageHelper.java │ └── webapp/ │ ├── WEB-INF/ │ │ └── web.xml │ ├── base.jsp │ ├── form.jsp │ ├── list.jsp │ └── view.jsp ├── bookshelf-standard/ │ ├── 2-structured-data/ │ │ ├── README.md │ │ ├── jenkins.sh │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── example/ │ │ │ └── getstarted/ │ │ │ ├── basicactions/ │ │ │ │ ├── CreateBookServlet.java │ │ │ │ ├── DeleteBookServlet.java │ │ │ │ ├── ListBookServlet.java │ │ │ │ ├── ReadBookServlet.java │ │ │ │ └── UpdateBookServlet.java │ │ │ ├── daos/ │ │ │ │ ├── BookDao.java │ │ │ │ ├── CloudSqlDao.java │ │ │ │ └── DatastoreDao.java │ │ │ ├── objects/ │ │ │ │ ├── Book.java │ │ │ │ └── Result.java │ │ │ └── util/ │ │ │ └── DatastoreSessionFilter.java │ │ └── webapp/ │ │ ├── WEB-INF/ │ │ │ ├── appengine-web.xml │ │ │ ├── logging.properties │ │ │ └── web.xml │ │ ├── base.jsp │ │ ├── form.jsp │ │ ├── list.jsp │ │ └── view.jsp │ ├── 3-binary-data/ │ │ ├── README.md │ │ ├── jenkins.sh │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── example/ │ │ │ └── getstarted/ │ │ │ ├── basicactions/ │ │ │ │ ├── CreateBookServlet.java │ │ │ │ ├── DeleteBookServlet.java │ │ │ │ ├── ListBookServlet.java │ │ │ │ ├── ReadBookServlet.java │ │ │ │ └── UpdateBookServlet.java │ │ │ ├── daos/ │ │ │ │ ├── BookDao.java │ │ │ │ ├── CloudSqlDao.java │ │ │ │ └── DatastoreDao.java │ │ │ ├── objects/ │ │ │ │ ├── Book.java │ │ │ │ └── Result.java │ │ │ └── util/ │ │ │ ├── CloudStorageHelper.java │ │ │ └── DatastoreSessionFilter.java │ │ └── webapp/ │ │ ├── WEB-INF/ │ │ │ ├── appengine-web.xml │ │ │ ├── logging.properties │ │ │ └── web.xml │ │ ├── base.jsp │ │ ├── form.jsp │ │ ├── list.jsp │ │ └── view.jsp │ ├── 4-auth/ │ │ ├── README.md │ │ ├── jenkins.sh │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── example/ │ │ │ └── getstarted/ │ │ │ ├── auth/ │ │ │ │ ├── ListByUserFilter.java │ │ │ │ ├── LoginServlet.java │ │ │ │ ├── LogoutFilter.java │ │ │ │ └── LogoutServlet.java │ │ │ ├── basicactions/ │ │ │ │ ├── CreateBookServlet.java │ │ │ │ ├── DeleteBookServlet.java │ │ │ │ ├── ListBookServlet.java │ │ │ │ ├── ListByUserServlet.java │ │ │ │ ├── ReadBookServlet.java │ │ │ │ └── UpdateBookServlet.java │ │ │ ├── daos/ │ │ │ │ ├── BookDao.java │ │ │ │ ├── CloudSqlDao.java │ │ │ │ └── DatastoreDao.java │ │ │ ├── objects/ │ │ │ │ ├── Book.java │ │ │ │ └── Result.java │ │ │ └── util/ │ │ │ ├── CloudStorageHelper.java │ │ │ └── DatastoreSessionFilter.java │ │ └── webapp/ │ │ ├── WEB-INF/ │ │ │ ├── appengine-web.xml │ │ │ ├── datastore-indexes.xml │ │ │ ├── logging.properties │ │ │ └── web.xml │ │ ├── base.jsp │ │ ├── form.jsp │ │ ├── list.jsp │ │ └── view.jsp │ └── 5-logging/ │ ├── README.md │ ├── jenkins.sh │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── com/ │ │ └── example/ │ │ └── getstarted/ │ │ ├── auth/ │ │ │ ├── ListByUserFilter.java │ │ │ ├── LoginServlet.java │ │ │ ├── LogoutFilter.java │ │ │ └── LogoutServlet.java │ │ ├── basicactions/ │ │ │ ├── CreateBookServlet.java │ │ │ ├── DeleteBookServlet.java │ │ │ ├── ListBookServlet.java │ │ │ ├── ListByUserServlet.java │ │ │ ├── ReadBookServlet.java │ │ │ └── UpdateBookServlet.java │ │ ├── daos/ │ │ │ ├── BookDao.java │ │ │ ├── CloudSqlDao.java │ │ │ └── DatastoreDao.java │ │ ├── objects/ │ │ │ ├── Book.java │ │ │ └── Result.java │ │ └── util/ │ │ ├── CloudStorageHelper.java │ │ └── DatastoreSessionFilter.java │ └── webapp/ │ ├── WEB-INF/ │ │ ├── appengine-web.xml │ │ ├── datastore-indexes.xml │ │ ├── logging.properties │ │ └── web.xml │ ├── base.jsp │ ├── form.jsp │ ├── list.jsp │ └── view.jsp ├── codecov.yml ├── gce/ │ ├── README.md │ ├── config/ │ │ └── base/ │ │ ├── etc/ │ │ │ └── java-util-logging.properties │ │ ├── modules/ │ │ │ └── gce.mod │ │ └── resources/ │ │ └── jetty-logging.properties │ ├── makeProject │ ├── pom.xml │ ├── scripts/ │ │ └── startup-script.sh │ └── src/ │ ├── main/ │ │ ├── appengine/ │ │ │ └── app.yaml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── example/ │ │ │ └── getstarted/ │ │ │ └── basicactions/ │ │ │ └── HelloworldController.java │ │ └── webapp/ │ │ └── WEB-INF/ │ │ └── web.xml │ └── test/ │ └── java/ │ └── com/ │ └── example/ │ └── getstarted/ │ └── basicactions/ │ └── UserJourneyTestIT.java ├── helloworld-jsp/ │ ├── README.md │ ├── build.gradle │ ├── eclipse-launch-profiles/ │ │ ├── AppEngineDeploy.launch │ │ └── AppEngineRun.launch │ ├── gradle/ │ │ └── wrapper/ │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── nbactions.xml │ ├── pom.xml │ └── src/ │ └── main/ │ ├── appengine/ │ │ └── app.yaml │ ├── java/ │ │ └── org/ │ │ └── example/ │ │ └── appengine/ │ │ └── hello/ │ │ └── HelloInfo.java │ └── webapp/ │ ├── WEB-INF/ │ │ ├── logging.properties │ │ └── web.xml │ └── hello.jsp ├── mvnw.cmd └── renovate.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/CODEOWNERS ================================================ # Code owners file. # This file controls who is tagged for review for any given pull request. # The java-samples-reviewers team is the default owner for anything not # explicitly taken by someone else. * @GoogleCloudPlatform/java-samples-reviewers ================================================ FILE: .github/blunderbuss.yml ================================================ assign_issues: - GoogleCloudPlatform/java-samples-reviewers assign_prs: - GoogleCloudPlatform/java-samples-reviewers ================================================ FILE: .github/sync-repo-settings.yaml ================================================ rebaseMergeAllowed: true squashMergeAllowed: true mergeCommitAllowed: false branchProtectionRules: - pattern: main isAdminEnforced: false requiredStatusCheckContexts: - 'Kokoro CI - Java 8' - 'Kokoro CI - Java 11' - 'Kokoro CI - Lint' - 'cla/google' requiredApprovingReviewCount: 1 requiresCodeOwnerReviews: true requiresStrictStatusChecks: true permissionRules: - team: java-samples-reviewers permission: push - team: yoshi-java permission: push - team: devrel-java-admin permission: admin - team: yoshi-admins permission: admin ================================================ FILE: .gitignore ================================================ # Eclipse files .project .classpath .settings # IntelliJ IDEA .Idea *.iml .idea/ # Target folders target/ build/ out/ .gradle/ bin/ ## Vim ## # swap [._]*.s[a-w][a-z] [._]s[a-w][a-z] # session Session.vim # temporary .netrwhist *~ # auto-generated tag files tags ## Secrets ## client-secret.json **/pom.xml.versionsBackup ================================================ FILE: .gitmodules ================================================ ================================================ FILE: .kokoro/java11/common.cfg ================================================ # Copyright 2019 Google LLC # # 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. # Format: //devtools/kokoro/config/proto/build.proto # Build timeout of 5 hours timeout_mins: 300 # Download trampoline resources. gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" # Use the trampoline script to run in docker. build_file: "getting-started-java/.kokoro/trampoline.sh" action { define_artifacts { regex: "**/*sponge_log.xml" } } # Set the JAVA VERSION env var. env_vars: { key: "JAVA_VERSION" value: "1.8,11" } # Configure the docker image for kokoro-trampoline. env_vars: { key: "TRAMPOLINE_IMAGE" value: "gcr.io/cloud-devrel-kokoro-resources/java11" } ================================================ FILE: .kokoro/java11/continuous.cfg ================================================ # Copyright 2019 Google LLC # # 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. # Format: //devtools/kokoro/config/proto/build.proto # Tell the trampoline which tests to run. env_vars: { key: "TRAMPOLINE_BUILD_FILE" value: "github/getting-started-java/.kokoro/tests/run_tests.sh" } ================================================ FILE: .kokoro/java11/periodic.cfg ================================================ # Copyright 2019 Google LLC # # 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. # Format: //devtools/kokoro/config/proto/build.proto # Tell the trampoline which build file to use. env_vars: { key: "TRAMPOLINE_BUILD_FILE" value: "github/getting-started-java/.kokoro/tests/run_tests.sh" } ================================================ FILE: .kokoro/java11/presubmit.cfg ================================================ # Copyright 2019 Google LLC # # 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. # Format: //devtools/kokoro/config/proto/build.proto # Tell the trampoline which build file to use. env_vars: { key: "TRAMPOLINE_BUILD_FILE" value: "github/getting-started-java/.kokoro/tests/run_diff_only.sh" } ================================================ FILE: .kokoro/java8/common.cfg ================================================ # Copyright 2019 Google LLC # # 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. # Format: //devtools/kokoro/config/proto/build.proto # Build timeout of 5 hours timeout_mins: 300 # Download trampoline resources. gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" # Use the trampoline script to run in docker. build_file: "getting-started-java/.kokoro/trampoline.sh" action { define_artifacts { regex: "**/*sponge_log.xml" } } # Set the JAVA VERSION env var. env_vars: { key: "JAVA_VERSION" value: "1.8" } # Configure the docker image for kokoro-trampoline. env_vars: { key: "TRAMPOLINE_IMAGE" value: "gcr.io/cloud-devrel-kokoro-resources/java8" } ================================================ FILE: .kokoro/java8/continuous.cfg ================================================ # Copyright 2019 Google LLC # # 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. # Format: //devtools/kokoro/config/proto/build.proto # Tell trampoline which tests to run. env_vars: { key: "TRAMPOLINE_BUILD_FILE" value: "github/getting-started-java/.kokoro/tests/run_tests.sh" } ================================================ FILE: .kokoro/java8/periodic.cfg ================================================ # Copyright 2019 Google LLC # # 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. # Format: //devtools/kokoro/config/proto/build.proto # Tell the trampoline which build file to use. env_vars: { key: "TRAMPOLINE_BUILD_FILE" value: "github/getting-started-java/.kokoro/tests/run_tests.sh" } ================================================ FILE: .kokoro/java8/presubmit.cfg ================================================ # Copyright 2019 Google LLC # # 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. # Format: //devtools/kokoro/config/proto/build.proto # Tell the trampoline which build file to use. env_vars: { key: "TRAMPOLINE_BUILD_FILE" value: "github/getting-started-java/.kokoro/tests/run_diff_only.sh" } ================================================ FILE: .kokoro/lint/common.cfg ================================================ # Copyright 2020 Google LLC # # 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. # Format: //devtools/kokoro/config/proto/build.proto # Use the trampoline to bounce the script into docker. gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" build_file: "getting-started-java/.kokoro/trampoline.sh" env_vars: { key: "TRAMPOLINE_IMAGE" value: "gcr.io/cloud-devrel-kokoro-resources/java8" } env_vars: { key: "TRAMPOLINE_BUILD_FILE" value: "github/getting-started-java/.kokoro/tests/run_lint.sh" } # Access btlr binaries used in the tests gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/btlr" # Upload logs to result-store action { define_artifacts { regex: "**/*sponge_log.xml" } } ================================================ FILE: .kokoro/lint/presubmit.cfg ================================================ # Copyright 2020 Google LLC # # 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. # Format: //devtools/kokoro/config/proto/build.proto # Tell the trampoline which build file to use. env_vars: { key: "GIT_DIFF" value: "origin/main... ." } ================================================ FILE: .kokoro/tests/run_diff_only.sh ================================================ #!/usr/bin/env bash # Copyright 2017 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. mydir="${0%/*}" "$mydir"/run_tests.sh --only-diff ================================================ FILE: .kokoro/tests/run_lint.sh ================================================ #!/usr/bin/env bash # Copyright 2020 Google LLC # # 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. # `-e` enables the script to automatically fail when a command fails # `-o pipefail` sets the exit code to the rightmost comment to exit with a non-zero set -eo pipefail # If on kokoro, add btlr to the path and cd into repo root if [ -n "$KOKORO_GFILE_DIR" ]; then bltr_dir="$KOKORO_GFILE_DIR/v0.0.1/" chmod +x "${bltr_dir}"btlr export PATH="$PATH:$bltr_dir" cd github/getting-started-java || exit fi opts=() if [ -n "$GIT_DIFF" ]; then opts+=( "--git-diff" "$GIT_DIFF" ) fi btlr "${opts[@]}" run "**/pom.xml" -- mvn -P lint --quiet --batch-mode checkstyle:check ================================================ FILE: .kokoro/tests/run_tests.sh ================================================ #!/bin/bash # Copyright 2017 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. # `-e` enables the script to automatically fail when a command fails # `-o pipefail` sets the exit code to the rightmost comment to exit with a non-zero set -eo pipefail # Enables `**` to include files nested inside sub-folders shopt -s globstar SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # `--script-debug` can be added make local testing of this script easier if [[ $* == *--script-debug* ]]; then SCRIPT_DEBUG="true" JAVA_VERSION="1.8" else SCRIPT_DEBUG="false" fi # `--only-diff` will only run tests on projects container changes from the main branch. if [[ $* == *--only-diff* ]]; then ONLY_DIFF="true" else ONLY_DIFF="false" fi # Verify Java versions have been specified if [[ -z ${JAVA_VERSION+x} ]]; then echo -e "'JAVA_VERSION' env var should be a comma delimited list of valid java versions." exit 1 fi if [[ "$SCRIPT_DEBUG" != "true" ]]; then # Update `gcloud` and log versioning for debugging apt update && apt -y upgrade google-cloud-sdk echo "********** GCLOUD INFO ***********" gcloud -v echo "********** MAVEN INFO ***********" mvn -v echo "********** GRADLE INFO ***********" gradle -v # Setup required env variables export GOOGLE_CLOUD_PROJECT=java-docs-samples-testing export GOOGLE_APPLICATION_CREDENTIALS=${KOKORO_GFILE_DIR}/secrets/java-docs-samples-service-account.json # Grab latest version of secrets mkdir -p "${KOKORO_GFILE_DIR}/secrets" gcloud secrets versions access latest --secret="java-docs-samples-service-account" > "$GOOGLE_APPLICATION_CREDENTIALS" gcloud secrets versions access latest --secret="java-firestore-samples-secrets" > "${KOKORO_GFILE_DIR}/secrets/java-firestore-samples-secrets.txt" # Execute secret file contents source "${KOKORO_GFILE_DIR}/secrets/java-firestore-samples-secrets.txt" # Activate service account gcloud auth activate-service-account \ --key-file="$GOOGLE_APPLICATION_CREDENTIALS" \ --project="$GOOGLE_CLOUD_PROJECT" cd github/getting-started-java fi echo -e "\n******************** TESTING PROJECTS ********************" # Switch to 'fail at end' to allow all tests to complete before exiting. set +e # Use RTN to return a non-zero value if the test fails. RTN=0 ROOT=$(pwd) git config --global --add safe.directory $PWD # Find all POMs in the repository (may break on whitespace). for file in **/pom.xml; do cd "$ROOT" # Navigate to the project folder. file=$(dirname "$file") cd "$file" # If $DIFF_ONLY is true, skip projects without changes. if [[ "$ONLY_DIFF" = "true" ]]; then git diff --quiet origin/main.. . CHANGED=$? if [[ "$CHANGED" -eq 0 ]]; then # echo -e "\n Skipping $file: no changes in folder.\n" continue fi fi echo "------------------------------------------------------------" echo "- testing $file" echo "------------------------------------------------------------" # Fail the tests if no Java version was found. POM_JAVA=$(grep -oP '(?<=).*?(?=)' pom.xml) ALLOWED_VERSIONS=("1.8" "11") # shellcheck disable=SC2199 # shellcheck disable=SC2076 if [[ "$POM_JAVA" = "" ]] || [[ ! "${ALLOWED_VERSIONS[@]}" =~ "${POM_JAVA}" ]]; then RTN=1 echo -e "\n Testing failed: Unable to determine Java version. Please set in pom:" echo -e "\n" echo -e " 1.8" echo -e " 1.8" echo -e "\n" continue fi # Skip tests that don't have the correct Java version. # shellcheck disable=SC2076 if ! [[ ",$JAVA_VERSION," =~ ",$POM_JAVA," ]]; then echo -e "\n Skipping tests: Java version ($POM_JAVA) not required ($JAVA_VERSION)\n" continue fi # Use maven to execute the tests for the project. mvn --quiet --batch-mode --fail-at-end clean verify \ -Dfile.encoding="UTF-8" \ -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn \ -Dmaven.test.redirectTestOutputToFile=true \ -Dbigtable.projectID="${GOOGLE_CLOUD_PROJECT}" \ -Dbigtable.instanceID=instance EXIT=$? if [[ $EXIT -ne 0 ]]; then RTN=1 echo -e "\n Testing failed: Maven returned a non-zero exit code. \n" else echo -e "\n Testing completed.\n" fi done exit "$RTN" ================================================ FILE: .kokoro/trampoline.sh ================================================ #!/bin/bash # Copyright 2017 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. python3 "${KOKORO_GFILE_DIR}/trampoline_v1.py" ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Code of Conduct As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality. Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery * Personal attacks * Trolling or insulting/derogatory comments * Public or private harassment * Publishing other's private information, such as physical or electronic addresses, without explicit permission * Other unethical or unprofessional conduct. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team. This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/) ================================================ FILE: CONTRIBUTING.md ================================================ # How to become a contributor and submit your own code ## Contributor License Agreements We'd love to accept your sample apps and patches! Before we can take them, we have to jump a couple of legal hurdles. Please fill out either the individual or corporate Contributor License Agreement (CLA): * If you are an individual writing original source code and you're sure you own the intellectual property, then you'll need to sign an [individual CLA](https://developers.google.com/open-source/cla/individual). * If you work for a company that wants to allow you to contribute your work, then you'll need to sign a [corporate CLA](https://developers.google.com/open-source/cla/corporate). Follow either of the two links above to access the appropriate CLA and instructions for how to sign and return it. Once we receive it, we'll be able to accept your pull requests. ## Contributing A Patch 1. Submit an issue describing your proposed change to the repository in question. 2. The repository owner will respond to your issue promptly. 3. If your proposed change is accepted, and you haven't already done so, sign a CLA (see details above). 4. Fork the desired repo, then develop and test your code changes. 5. Ensure that your code adheres to the existing style in the sample to which you are contributing. Refer to the [Google Java Style Guide](http://google.github.io/styleguide/javaguide.html) and the [Google Cloud Platform Community Style Guide](https://cloud.google.com/community/tutorials/styleguide) for the recommended coding standards for this organization. 6. Ensure that your code has an appropriate set of unit tests which all pass. 7. Submit a pull request. ================================================ FILE: LICENSE ================================================ 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 [yyyy] [name of copyright owner] 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. ================================================ FILE: README.md ================================================ # Getting started on Google Cloud Platform for Java® [![CircleCI Build Status](https://circleci.com/gh/GoogleCloudPlatform/getting-started-java.svg?style=shield&circle-token=51b789e102291cbeae6817678d02da0f4cf25f1f)](https://circleci.com/gh/GoogleCloudPlatform/getting-started-java) [![Coverage Status](https://codecov.io/gh/GoogleCloudPlatform/getting-started-java/branch/main/graph/badge.svg)](https://codecov.io/gh/GoogleCloudPlatform/getting-started-java) The code for the samples is contained in individual folders on this repository. Follow the instructions at [Getting Started on Google Cloud Platform for Java](https://cloud.google.com/java/) or the README files in each folder for instructions on how to run locally and deploy. Managed VMs on Google Cloud Platform use the [Java Servlets](http://www.oracle.com/technetwork/java/overview-137084.html) & [Java Server Pages](http://www.oracle.com/technetwork/java/index-jsp-138231.html) on [Jetty](http://www.eclipse.org/jetty/). 1. [Helloworld-servlet](helloworld-servlet) Servlet based Hello World app 1. [HelloWorld-jsp](helloworld-jsp) Java Server Pages based Hello World app 1. [Bookshelf](bookshelf) A full featured app that demonstrates Authentication and CRUD operations for [Cloud Datastore](https://cloud.google.com/datastore/docs/concepts/overview?hl=en) and [Cloud SQL](https://cloud.google.com/sql/docs/introduction). ## Google Cloud Samples To browse ready to use code samples check [Google Cloud samples](https://cloud.google.com/docs/samples). ## Contributing changes * See [CONTRIBUTING.md](CONTRIBUTING.md) ## Licensing * See [LICENSE](LICENSE) Java is a registered trademark of Oracle Corporation and/or its affiliates. ================================================ FILE: appengine-standard-java8/deployAll.sh ================================================ #!/bin/bash # Copyright 2017 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. # set -x # set -v # gcloud config configurations activate qa for app in "helloworld" "kotlin-appengine-standard" \ "kotlin-sb-appengine-standard" \ "springboot-appengine-standard" "kotlin-spark-appengine-standard" \ "sparkjava-appengine-standard" do (cd "${app}" sed --in-place='.xx' "s/<\/runtime>/<\/runtime>${app}<\/service>/" \ src/main/webapp/WEB-INF/appengine-web.xml mvn -B --fail-at-end -q package appengine:deploy -Dapp.deploy.version="1" \ -Dapp.stage.quickstart=true -Dapp.deploy.force=true -Dapp.deploy.promote=true \ -Dapp.deploy.projectId="${GOOGLE_CLOUD_PROJECT}" -DskipTests=true mv src/main/webapp/WEB-INF/appengine-web.xml.xx src/main/webapp/WEB-INF/appengine-web.xml) done echo "STATUS: ${?}" ================================================ FILE: appengine-standard-java8/helloworld-gae-javasdk-tools/README.md ================================================ HelloWorld for App Engine Standard (Java 8) using the App Engine Java SDK tooling ============================ This sample demonstrates how to deploy an application on Google App Engine. See the [Google App Engine standard environment documentation][ae-docs] for more detailed instructions. [ae-docs]: https://cloud.google.com/appengine/docs/java/ * [Java 8](http://www.oracle.com/technetwork/java/javase/downloads/index.html) * [Maven](https://maven.apache.org/download.cgi) (at least 3.5) * [Gradle](https://gradle.org/gradle-download/) (optional) * [Google Cloud SDK](https://cloud.google.com/sdk/) (aka gcloud) ## Setup • Download and initialize the [Cloud SDK](https://cloud.google.com/sdk/) ``` gcloud init ``` * Create an App Engine app within the current Google Cloud Project ``` gcloud app create ``` * In the `pom.xml`, update the [App Engine Maven Plugin](https://cloud.google.com/appengine/docs/standard/java/tools/maven-reference) with your Google Cloud Project Id: ``` com.google.cloud.tools appengine-maven-plugin 2.3.0 GCLOUD_CONFIG GCLOUD_CONFIG ``` **Note:** `GCLOUD_CONFIG` is a special version for autogenerating an App Engine version. Change this field to specify a specific version name. ## Maven ### Running locally mvn clean package appengine:run To use visit: http://localhost:8080/ ### Deploying Update `src/main/webapp/WEB-INF/appengine-web.xml` `` tag with the Project ID. mvn clean package appengine:deploy To use visit: https://YOUR-PROJECT-ID.appspot.com ## Gradle For more information see the [plugin project](https://github.com/GoogleCloudPlatform/gradle-appengine-plugin#gradle-app-engine-plugin-) on github. ### Running locally gradle appengineRun If you do not have gradle installed, you can run using `./gradlew appengineRun`. To use visit: http://localhost:8080/ ### Deploying Update `src/main/webapp/WEB-INF/appengine-web.xml` `` tag with the Project ID. gradle appengineUpdate If you do not have gradle installed, you can deploy using `./gradlew appengineDeploy`. To use visit: https://1-dot-YOUR-PROJECT-ID.appspot.com This is using version-dot-project naming. ## Testing mvn verify or gradle test As you add / modify the source code (`src/main/java/...`) it's very useful to add [unit testing](https://cloud.google.com/appengine/docs/java/tools/localunittesting) to (`src/main/test/...`). The following resources are quite useful: * [Junit4](http://junit.org/junit4/) * [Mockito](http://mockito.org/) * [Truth](http://google.github.io/truth/) ================================================ FILE: appengine-standard-java8/helloworld-gae-javasdk-tools/build.gradle ================================================ // Copyright 2017 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. // [START gradle] buildscript { // Configuration for building repositories { jcenter() // Bintray's repository - a fast Maven Central mirror & more mavenCentral() } dependencies { classpath 'com.google.appengine:gradle-appengine-plugin:+' // latest App Engine Gradle tasks } } repositories { // repositories for Jar's you access in your code mavenCentral() jcenter() } apply plugin: 'java' // standard Java tasks apply plugin: 'war' // standard Web Archive plugin apply plugin: 'appengine' // App Engine tasks dependencies { appengineSdk 'com.google.appengine:appengine-java-sdk:1.9.93' compile 'com.google.appengine:appengine-api-1.0-sdk:+' // Latest App Engine Api's providedCompile 'javax.servlet:javax.servlet-api:4.0.1' compile 'jstl:jstl:1.2' // Add your dependencies here. // compile 'com.google.cloud:google-cloud:+' // Latest Cloud API's http://googlecloudplatform.github.io/google-cloud-java testCompile 'junit:junit:4.13.2' testCompile 'com.google.truth:truth:1.2.0' testCompile 'org.mockito:mockito-all:1.10.19' testCompile 'com.google.appengine:appengine-testing:+' testCompile 'com.google.appengine:appengine-api-stubs:+' testCompile 'com.google.appengine:appengine-tools-sdk:+' } // Always run unit tests appengineUpdate.dependsOn test appengineStage.dependsOn test // [START model] appengine { // App Engine tasks configuration downloadSdk = true } test { useJUnit() testLogging.showStandardStreams = true beforeTest { descriptor -> logger.lifecycle("test: " + descriptor + " Running") } onOutput { descriptor, event -> logger.lifecycle("test: " + descriptor + ": " + event.message ) } afterTest { descriptor, result -> logger.lifecycle("test: " + descriptor + ": " + result ) } } // [END model] group = "com.example.appengine_standard_java8" // Generated output GroupId version = "1.0-SNAPSHOT" // Version in generated output sourceCompatibility = 1.8 // App Engine Flexible uses Java 8 targetCompatibility = 1.8 // App Engine Flexible uses Java 8 // [END gradle] ================================================ FILE: appengine-standard-java8/helloworld-gae-javasdk-tools/gradle/wrapper/gradle-wrapper.properties ================================================ #Tue Jun 13 16:53:48 PDT 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip ================================================ FILE: appengine-standard-java8/helloworld-gae-javasdk-tools/gradlew ================================================ #!/usr/bin/env sh ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn ( ) { echo "$*" } die ( ) { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin, switch paths to Windows format before running java if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=$((i+1)) done case $i in (0) set -- ;; (1) set -- "$args0" ;; (2) set -- "$args0" "$args1" ;; (3) set -- "$args0" "$args1" "$args2" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Escape application args save ( ) { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } APP_ARGS=$(save "$@") # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then cd "$(dirname "$0")" fi exec "$JAVACMD" "$@" ================================================ FILE: appengine-standard-java8/helloworld-gae-javasdk-tools/gradlew.bat ================================================ @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS= @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: appengine-standard-java8/helloworld-gae-javasdk-tools/pom.xml ================================================ 4.0.0 war 1.0-SNAPSHOT com.example.appengine_standard_java8 helloworld_gae_tooling com.google.cloud.samples shared-configuration 1.2.0 1.8 1.8 UTF-8 UTF-8 true true false com.google.appengine appengine-api-1.0-sdk 2.0.15 javax.servlet javax.servlet-api 4.0.1 jar provided jstl jstl 1.2 com.google.appengine appengine-testing 2.0.15 test com.google.appengine appengine-api-stubs 2.0.15 test com.google.appengine appengine-tools-sdk 2.0.15 test com.google.truth truth 1.2.0 test junit junit 4.13.2 test org.mockito mockito-core 4.5.0 test ${project.build.directory}/${project.build.finalName}/WEB-INF/classes com.google.cloud.tools appengine-maven-plugin 2.4.4 GCLOUD_CONFIG GCLOUD_CONFIG 1 maven-compiler-plugin 3.11.0 1.8 1.8 org.codehaus.mojo versions-maven-plugin 2.11.0 compile display-dependency-updates display-plugin-updates maven-war-plugin 3.4.0 maven-clean-plugin 3.3.2 maven-install-plugin 3.1.1 maven-surefire-plugin 3.1.2 maven-site-plugin 3.12.1 maven-resources-plugin 3.3.0 maven-deploy-plugin 3.0.0 maven-enforcer-plugin 3.1.0 enforce-maven enforce 3.5 Best Practice is to always define plugin versions! true true clean,deploy,verify,appengine:run,appengine:deploy,appengine:update,appengine:devappaserver,site ================================================ FILE: appengine-standard-java8/helloworld-gae-javasdk-tools/settings.gradle ================================================ /* * This settings file was generated by the Gradle 'init' task. * * The settings file is used to specify which projects to include in your build. * In a single project build this file can be empty or even removed. * * Detailed information about configuring a multi-project build in Gradle can be found * in the user guide at https://docs.gradle.org/3.5/userguide/multi_project_builds.html */ /* // To declare projects as part of a multi-project build use the 'include' method include 'shared' include 'api' include 'services:webservice' */ rootProject.name = 'helloworld_gae_tooling' ================================================ FILE: appengine-standard-java8/helloworld-gae-javasdk-tools/src/main/java/com/example/appengine/java8/HelloAppEngine.java ================================================ /* * Copyright 2017 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. */ package com.example.appengine.java8; // [START example] import com.google.appengine.api.utils.SystemProperty; import java.io.IOException; import java.util.Properties; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // With @WebServlet annotation the webapp/WEB-INF/web.xml is no longer required. @WebServlet(name = "HelloAppEngine", value = "/hello") public class HelloAppEngine extends HttpServlet { @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { Properties properties = System.getProperties(); response.setContentType("text/plain"); response.getWriter().println("Hello App Engine - Standard using " + SystemProperty.version.get() + " Java " + properties.get("java.specification.version")); } public static String getInfo() { return "Version: " + System.getProperty("java.version") + " OS: " + System.getProperty("os.name") + " User: " + System.getProperty("user.name"); } } // [END example] ================================================ FILE: appengine-standard-java8/helloworld-gae-javasdk-tools/src/main/webapp/WEB-INF/appengine-web.xml ================================================ helloworld-gae java8 true ================================================ FILE: appengine-standard-java8/helloworld-gae-javasdk-tools/src/main/webapp/index.jsp ================================================ <%-- ~ Copyright 2017 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. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page import="com.example.appengine.java8.HelloAppEngine" %> Hello App Engine Standard Java 8

Hello App Engine -- Java 8!

This is <%= HelloAppEngine.getInfo() %>.

Available Servlets:
Hello App Engine
================================================ FILE: appengine-standard-java8/helloworld-gae-javasdk-tools/src/test/java/com/example/appengine/java8/HelloAppEngineTest.java ================================================ /* * Copyright 2017 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. */ package com.example.appengine.java8; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.when; import com.google.appengine.tools.development.testing.LocalServiceTestHelper; import java.io.PrintWriter; import java.io.StringWriter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.Mock; import org.mockito.MockitoAnnotations; /** * Unit tests for {@link HelloAppEngine}. */ // [START example] @RunWith(JUnit4.class) public class HelloAppEngineTest { private static final String FAKE_URL = "fake.fk/hello"; // Set up a helper so that the ApiProxy returns a valid environment for local testing. private final LocalServiceTestHelper helper = new LocalServiceTestHelper(); @Mock private HttpServletRequest mockRequest; @Mock private HttpServletResponse mockResponse; private StringWriter responseWriter; private HelloAppEngine servletUnderTest; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); helper.setUp(); // Set up some fake HTTP requests when(mockRequest.getRequestURI()).thenReturn(FAKE_URL); // Set up a fake HTTP response. responseWriter = new StringWriter(); when(mockResponse.getWriter()).thenReturn(new PrintWriter(responseWriter)); servletUnderTest = new HelloAppEngine(); } @After public void tearDown() { helper.tearDown(); } @Test public void doGetWritesResponse() throws Exception { servletUnderTest.doGet(mockRequest, mockResponse); // We expect our hello world response. assertThat(responseWriter.toString()) .contains("Hello App Engine - Standard "); } @Test public void helloInfoTest() { String result = HelloAppEngine.getInfo(); assertThat(result) .containsMatch("^Version:\\s+.+OS:\\s+.+User:\\s"); } } // [END example] ================================================ FILE: appengine-standard-java8/kotlin-appengine-standard/.gitignore ================================================ hotspot.log *.iml *.ipr *.iws .gradle/ build/ target/ classes/ /var pom.xml.versionsBackup test-output/ /atlassian-ide-plugin.xml .idea .DS_Store .classpath .settings .project temp-testng-customsuite.xml test-output .externalToolBuilders *~ ================================================ FILE: appengine-standard-java8/kotlin-appengine-standard/README.md ================================================ App Engine Java Kotlin Servlet 3.1 with Java8 === ## Sample Servlet 3.1 written in Kotlin for use with App Engine Java8 Standard. See the [Google App Engine standard environment documentation][ae-docs] for more detailed instructions. [ae-docs]: https://cloud.google.com/appengine/docs/java/ * [Java 8](http://www.oracle.com/technetwork/java/javase/downloads/index.html) * [Maven](https://maven.apache.org/download.cgi) (at least 3.5) * [Google Cloud SDK](https://cloud.google.com/sdk/) (aka gcloud command line tool) ## Setup * Download and initialize the [Cloud SDK](https://cloud.google.com/sdk/) ``` gcloud init ``` * Create an App Engine app within the current Google Cloud Project ``` gcloud app create ``` * In the `pom.xml`, update the [App Engine Maven Plugin](https://cloud.google.com/appengine/docs/standard/java/tools/maven-reference) with your Google Cloud Project Id: ``` com.google.cloud.tools appengine-maven-plugin 2.3.0 GCLOUD_CONFIG GCLOUD_CONFIG ``` **Note:** `GCLOUD_CONFIG` is a special version for autogenerating an App Engine version. Change this field to specify a specific version name. ## Maven ### Running locally `mvn package appengine:run` To use visit: http://localhost:8080/ ### Deploying `mvn package appengine:deploy` To use visit: https://YOUR-PROJECT-ID.appspot.com ## Testing `mvn verify` As you add / modify the source code (`src/main/java/...`) it's very useful to add [unit testing](https://cloud.google.com/appengine/docs/java/tools/localunittesting) to (`src/main/test/...`). The following resources are quite useful: * [Junit4](http://junit.org/junit4/) * [Mockito](http://mockito.org/) * [Truth](http://google.github.io/truth/) For further information, consult the [Java App Engine](https://developers.google.com/appengine/docs/java/overview) documentation. ================================================ FILE: appengine-standard-java8/kotlin-appengine-standard/nbactions.xml ================================================ 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 run war ear ejb appengine:devserver true ================================================ FILE: appengine-standard-java8/kotlin-appengine-standard/pom.xml ================================================ 4.0.0 war 1.0-SNAPSHOT com.google.appengine.demos kotlin-appengine-standard com.google.cloud.samples shared-configuration 1.2.0 1.8 1.3.72 1.8 1.8 com.google.appengine appengine-api-1.0-sdk 2.0.15 jstl jstl 1.2 javax.servlet javax.servlet-api 4.0.1 jar org.jetbrains.kotlin kotlin-stdlib 1.6.0 target/${project.artifactId}-${project.version}/WEB-INF/classes ${project.basedir}/src/main/kotlin ${project.basedir}/src/test/kotlin kotlin-maven-plugin org.jetbrains.kotlin 1.5.31 compile compile compile test-compile test-compile test-compile org.apache.maven.plugins maven-war-plugin 3.3.2 true false com.google.cloud.tools appengine-maven-plugin 2.4.4 GCLOUD_CONFIG GCLOUD_CONFIG maven-compiler-plugin 3.11.0 1.8 1.8 ================================================ FILE: appengine-standard-java8/kotlin-appengine-standard/src/main/kotlin/HomeController.kt ================================================ // See https://github.com/JetBrains/kotlin-examples/blob/master/LICENSE package org.jetbrains.kotlin.demo import javax.servlet.annotation.WebServlet import javax.servlet.http.HttpServlet import javax.servlet.http.HttpServletRequest import javax.servlet.http.HttpServletResponse @WebServlet(name = "Hello", value = ["/"]) class HomeController : HttpServlet() { override fun doGet(req: HttpServletRequest, res: HttpServletResponse) { res.writer.write("Hello, World! I am a Servlet 3.1 running on Java8 App Engine Standard, and written in Kotlin...") } } ================================================ FILE: appengine-standard-java8/kotlin-appengine-standard/src/main/webapp/WEB-INF/appengine-web.xml ================================================ true java8 ================================================ FILE: appengine-standard-java8/kotlin-appengine-standard/src/main/webapp/WEB-INF/logging.properties ================================================ # Copyright 2017 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. # 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 ================================================ FILE: appengine-standard-java8/kotlin-sb-appengine-standard/.gitignore ================================================ hotspot.log *.iml *.ipr *.iws .gradle/ build/ target/ classes/ /var pom.xml.versionsBackup test-output/ /atlassian-ide-plugin.xml .idea .DS_Store .classpath .settings .project temp-testng-customsuite.xml test-output .externalToolBuilders *~ ================================================ FILE: appengine-standard-java8/kotlin-sb-appengine-standard/README.md ================================================ App Engine Java SpringBoot Kotlin application === ## Sample SpringBoot application written in Kotlin for use with App Engine Java8 Standard. See the [Google App Engine standard environment documentation][ae-docs] for more detailed instructions. [ae-docs]: https://cloud.google.com/appengine/docs/java/ * [Java 8](http://www.oracle.com/technetwork/java/javase/downloads/index.html) * [Maven](https://maven.apache.org/download.cgi) (at least 3.5) * [Google Cloud SDK](https://cloud.google.com/sdk/) (aka gcloud command line tool) ## Setup * Download and initialize the [Cloud SDK](https://cloud.google.com/sdk/) ``` gcloud init ``` * Create an App Engine app within the current Google Cloud Project ``` gcloud app create ``` * In the `pom.xml`, update the [App Engine Maven Plugin](https://cloud.google.com/appengine/docs/standard/java/tools/maven-reference) with your Google Cloud Project Id: ``` com.google.cloud.tools appengine-maven-plugin 2.3.0 GCLOUD_CONFIG GCLOUD_CONFIG ``` **Note:** `GCLOUD_CONFIG` is a special version for autogenerating an App Engine version. Change this field to specify a specific version name. ## Maven ### Running locally `mvn package appengine:run` To use visit: http://localhost:8080/ ### Deploying `mvn package appengine:deploy` To use visit: https://YOUR-PROJECT-ID.appspot.com ## Testing `mvn verify` As you add / modify the source code (`src/main/java/...`) it's very useful to add [unit testing](https://cloud.google.com/appengine/docs/java/tools/localunittesting) to (`src/main/test/...`). The following resources are quite useful: * [Junit4](http://junit.org/junit4/) * [Mockito](http://mockito.org/) * [Truth](http://google.github.io/truth/) For further information, consult the [Java App Engine](https://developers.google.com/appengine/docs/java/overview) documentation. ================================================ FILE: appengine-standard-java8/kotlin-sb-appengine-standard/nbactions.xml ================================================ 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 run war ear ejb appengine:devserver true ================================================ FILE: appengine-standard-java8/kotlin-sb-appengine-standard/pom.xml ================================================ 4.0.0 war 1.0-SNAPSHOT com.google.appengine.demos kotlin-sb-appengine-standard com.google.cloud.samples shared-configuration 1.2.0 1.8 1.6.0 1.8 1.8 com.google.appengine appengine-api-1.0-sdk 2.0.15 jstl jstl 1.2 org.springframework.boot spring-boot-starter-web 3.1.1 org.slf4j jul-to-slf4j org.springframework.boot spring-boot-starter-tomcat javax.servlet javax.servlet-api 4.0.1 jar org.jetbrains.kotlin kotlin-stdlib-jdk8 ${kotlin.version} org.jetbrains.kotlin kotlin-reflect ${kotlin.version} org.jetbrains.kotlin kotlin-maven-allopen ${kotlin.version} target/${project.artifactId}-${project.version}/WEB-INF/classes ${project.basedir}/src/main/kotlin ${project.basedir}/src/test/kotlin kotlin-maven-plugin org.jetbrains.kotlin ${kotlin.version} spring ${java.version} compile compile compile test-compile test-compile test-compile org.jetbrains.kotlin kotlin-maven-allopen ${kotlin.version} org.apache.maven.plugins maven-war-plugin 3.3.2 true false com.google.cloud.tools appengine-maven-plugin 2.4.4 GCLOUD_CONFIG GCLOUD_CONFIG maven-compiler-plugin 3.11.0 ${java.version} ${java.version} ================================================ FILE: appengine-standard-java8/kotlin-sb-appengine-standard/src/main/kotlin/org/jetbrains/kotlin/demo/Application.kt ================================================ // See https://github.com/JetBrains/kotlin-examples/blob/master/LICENSE package org.jetbrains.kotlin.demo import org.springframework.boot.SpringApplication import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.web.servlet.support.SpringBootServletInitializer @SpringBootApplication class Application : SpringBootServletInitializer() { } fun main(args: Array) { SpringApplication.run(Application::class.java, *args) } ================================================ FILE: appengine-standard-java8/kotlin-sb-appengine-standard/src/main/kotlin/org/jetbrains/kotlin/demo/Greeting.kt ================================================ // See https://github.com/JetBrains/kotlin-examples/blob/master/LICENSE package org.jetbrains.kotlin.demo data class Greeting(val id: Long, val content: String) ================================================ FILE: appengine-standard-java8/kotlin-sb-appengine-standard/src/main/kotlin/org/jetbrains/kotlin/demo/GreetingController.kt ================================================ // See https://github.com/JetBrains/kotlin-examples/blob/master/LICENSE package org.jetbrains.kotlin.demo import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController import java.util.concurrent.atomic.AtomicLong @RestController class GreetingController { val counter = AtomicLong() @GetMapping("/greeting") fun greeting(@RequestParam(value = "name", defaultValue = "World") name: String) = Greeting(counter.incrementAndGet(), "Hello, $name, from a SpringBoot Application written in Kotlin, running on Google App Engine Java8 Standard...") } ================================================ FILE: appengine-standard-java8/kotlin-sb-appengine-standard/src/main/webapp/WEB-INF/appengine-web.xml ================================================ true java8 ================================================ FILE: appengine-standard-java8/kotlin-sb-appengine-standard/src/main/webapp/WEB-INF/logging.properties ================================================ # Copyright 2017 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. # 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 ================================================ FILE: appengine-standard-java8/kotlin-spark-appengine-standard/.gitignore ================================================ hotspot.log *.iml *.ipr *.iws .gradle/ build/ target/ classes/ /var pom.xml.versionsBackup test-output/ /atlassian-ide-plugin.xml .idea .DS_Store .classpath .settings .project temp-testng-customsuite.xml test-output .externalToolBuilders *~ ================================================ FILE: appengine-standard-java8/kotlin-spark-appengine-standard/README.md ================================================ App Engine SparkJava Kotlin with Java8 === ## Sample SparkJava application written in Kotlin for use with App Engine Java8 Standard. For Spark Kotlin documentation, see [Spark Kotlin](https://github.com/perwendel/spark-kotlin/). See the [Google App Engine standard environment documentation][ae-docs] for more detailed instructions. [ae-docs]: https://cloud.google.com/appengine/docs/java/ * [Java 8](http://www.oracle.com/technetwork/java/javase/downloads/index.html) * [Maven](https://maven.apache.org/download.cgi) (at least 3.5) * [Google Cloud SDK](https://cloud.google.com/sdk/) (aka gcloud command line tool) ## Setup * Download and initialize the [Cloud SDK](https://cloud.google.com/sdk/) ``` gcloud init ``` * Create an App Engine app within the current Google Cloud Project ``` gcloud app create ``` * In the `pom.xml`, update the [App Engine Maven Plugin](https://cloud.google.com/appengine/docs/standard/java/tools/maven-reference) with your Google Cloud Project Id: ``` com.google.cloud.tools appengine-maven-plugin 2.3.0 GCLOUD_CONFIG GCLOUD_CONFIG ``` **Note:** `GCLOUD_CONFIG` is a special version for autogenerating an App Engine version. Change this field to specify a specific version name. ## Maven ### Running locally `mvn package appengine:run` To use visit: http://localhost:8080/ ### Deploying `mvn package appengine:deploy` To use visit: https://YOUR-PROJECT-ID.appspot.com ## Testing `mvn verify` As you add / modify the source code (`src/main/java/...`) it's very useful to add [unit testing](https://cloud.google.com/appengine/docs/java/tools/localunittesting) to (`src/main/test/...`). The following resources are quite useful: * [Junit4](http://junit.org/junit4/) * [Mockito](http://mockito.org/) * [Truth](http://google.github.io/truth/) For further information, consult the [Java App Engine](https://developers.google.com/appengine/docs/java/overview) documentation. ================================================ FILE: appengine-standard-java8/kotlin-spark-appengine-standard/pom.xml ================================================ 4.0.0 war 1.0-SNAPSHOT com.google.appengine.demos kotlin-spark-appengine-standard com.google.cloud.samples shared-configuration 1.2.0 1.8 1.8 com.google.appengine appengine-api-1.0-sdk 2.0.15 jstl jstl 1.2 javax.servlet javax.servlet-api 4.0.1 jar org.jetbrains.kotlin kotlin-stdlib-jre8 1.2.71 com.sparkjava spark-kotlin 1.0.0-alpha org.eclipse.jetty jetty-server org.eclipse.jetty jetty-io org.eclipse.jetty jetty-security org.eclipse.jetty jetty-webapp org.eclipse.jetty jetty-util org.eclipse.jetty jetty-client org.eclipse.jetty.websocket websocket-api org.eclipse.jetty.websocket websocket-client org.eclipse.jetty.websocket websocket-common org.eclipse.jetty.websocket websocket-server org.slf4j slf4j-api org.eclipse.jetty.websocket websocket-servlet org.slf4j slf4j-api 2.0.11 target/${project.artifactId}-${project.version}/WEB-INF/classes ${project.basedir}/src/main/kotlin ${project.basedir}/src/test/kotlin kotlin-maven-plugin org.jetbrains.kotlin 1.5.31 compile compile compile test-compile test-compile test-compile org.apache.maven.plugins maven-war-plugin 3.3.2 true com.google.cloud.tools appengine-maven-plugin 2.4.4 GCLOUD_CONFIG GCLOUD_CONFIG maven-compiler-plugin 3.11.0 1.8 1.8 ================================================ FILE: appengine-standard-java8/kotlin-spark-appengine-standard/src/main/kotlin/MainApp.kt ================================================ /** * Copyright 2017 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. */ import spark.kotlin.Http import spark.kotlin.ignite import spark.servlet.SparkApplication /** * Example usage of spark-kotlin. * See https://github.com/perwendel/spark-kotlin */ class MainApp : SparkApplication { override fun init() { val http: Http = ignite() http.get("/") { """Hello Spark Kotlin running on Java8 App Engine Standard.

You can try /hello

or /saymy/:name

or redirect

or /nothing""" } http.get("/hello") { "Hello Spark Kotlin running on Java8 App Engine Standard." } http.get("/nothing") { status(404) "Oops, we couldn't find what you're looking for." } http.get("/saymy/:name") { params(":name") } http.get("/redirect") { redirect("/hello"); } } } ================================================ FILE: appengine-standard-java8/kotlin-spark-appengine-standard/src/main/kotlin/SparkInitFilter.kt ================================================ /** * Copyright 2017 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. */ import javax.servlet.annotation.WebFilter import javax.servlet.annotation.WebInitParam import spark.servlet.SparkFilter // Use Servlet annotation to define the Spark filter without web.xml: @WebFilter( filterName = "SparkInitFilter", urlPatterns = arrayOf("/*"), initParams = arrayOf( WebInitParam( name = "applicationClass", value = "MainApp") )) class SparkInitFilter : SparkFilter() { } ================================================ FILE: appengine-standard-java8/kotlin-spark-appengine-standard/src/main/webapp/WEB-INF/appengine-web.xml ================================================ true java8 ================================================ FILE: appengine-standard-java8/kotlin-spark-appengine-standard/src/main/webapp/WEB-INF/logging.properties ================================================ # Copyright 2017 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. # 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 ================================================ FILE: background/README.md ================================================ # Background Processing App on Cloud Run Tutorial Contains the code for using Cloud Firestore, Cloud Translate, and Cloud Pub/Sub. This is part of the [getting started experience](https://cloud.google.com/java/getting-started). ### Running Locally To run your project locally: * Choose a Pub/Sub Topic Name and generate a Pub/Sub Verification Token using `uuidgen` or an online UUID generator such as [uuidgenerator.net](https://www.uuidgenerator.net/). export PUBSUB_TOPIC= export PUBSUB_VERIFICATION_TOKEN= export FIRESTORE_CLOUD_PROJECT= * Create a Pub/Sub topic: gcloud pubsub topics create $PUBSUB_TOPIC * Run with the Jetty Maven plugin: mvn jetty:run-war **Note**: If you run into an error about `Invalid Credentials`, you may have to run: gcloud auth application-default login * Navigate to http://localhost:8080/ * Click `+ Request Translation`, and fill out the form using a phrase, a source language code ("en" for English) and a target language code (e.g. "es" for Spanish). * Click `Submit`. This will submit the request to your Pub/Sub topic and redirect you back to the list page. You will see that nothing has changed. This because there is no subscription on that Pub/Sub topic yet. Since you can't set up a Pub/Sub push subscription to post requests to `localhost`, you can instead send a manual request with `curl` (from a second terminal, in the `getting-started-java/background` directory): curl -H "Content-Type: application/json" -i --data @sample_message.json \ "localhost:8080/pubsub/push?token=$PUBSUB_VERIFICATION_TOKEN" Refresh `http://localhost:8080` now and you will see a translated entry in the list. ### Deploying to Cloud Run To build your image: * Update the parameters in `pom.xml`: * Replace `MY_PROJECT` with your project ID. * Build and deploy to your GCR with the [Jib][jib] Maven plugin. mvn clean package jib:build * Deploy the app to Cloud Run: gcloud beta run deploy background --image gcr.io//background \ --platform managed --region us-central1 --memory 512M \ --update-env-vars PUBSUB_TOPIC=$PUBSUB_TOPIC,PUBSUB_VERIFICATION_TOKEN=$PUBSUB_VERIFICATION_TOKEN Where is the name of the project you created. * Create a Pub/Sub Subscription that will send requests to the Cloud Run endpoint created with the previous command: gcloud pubsub subscriptions create \ --topic $PUBSUB_TOPIC --push-endpoint \ /translate?token=$PUBSUB_VERIFICATION_TOKEN \ --ack-deadline 30 This command will output a link to visit the page, hereafter called . * Now fill out the `+ Request Translation` form again, this time at . When you click `Submit` it will redirect you back to /translate. * The new request will take a moment to show, so refresh after a minute or two. [jib]: https://github.com/GoogleContainerTools/jib ### Architecture The flow of translation requests fits together as such: * When the `+ Request Translation` form is submitted, it posts a message to the Pub/Sub topic you created with the Text as (encoded) data, and the source/target language codes as attributes. * The Subscription you created receives this data and pushes it to the Cloud Run endpoint (with a POST request to /translate). * The /translate endpoint processes POST requests (that include the correct PUBSUB_VERIFICATION_TOKEN) by performing the Translate request and saving the result in Firestore. * When you visit the Cloud Run endpoint, it reads the past 10 requests from Firestore and shows them in a table at the `/` or `/translate` endpoints. ================================================ FILE: background/pom.xml ================================================ 4.0.0 war com.example.getstarted background-processing 1.0-SNAPSHOT com.google.cloud.samples shared-configuration 1.2.0 MY_PROJECT false UTF-8 1.8 1.8 true true false false 9.4.51.v20230217 com.google.cloud google-cloud-firestore 3.13.2 com.google.cloud google-cloud-translate 2.20.0 com.google.cloud google-cloud-pubsub 1.123.17 javax.servlet javax.servlet-api 4.0.1 com.google.guava guava 33.1.0-jre compile io.opencensus opencensus-contrib-http-util 0.31.1 jstl jstl 1.2 junit junit 4.13.2 test org.seleniumhq.selenium selenium-server 4.0.0-alpha-2 org.seleniumhq.selenium selenium-chrome-driver 4.10.0 background-processing ${project.build.directory}/${project.build.finalName}/WEB-INF/classes org.eclipse.jetty jetty-maven-plugin ${jetty.version} com.google.cloud.tools jib-maven-plugin 3.4.0 gcr.io/${gcloud.appId}/background ================================================ FILE: background/sample_message.json ================================================ { "message":{ "data":"Hello world!", "attributes":{ "sourceLang":"en", "targetLang":"es" }, "messageId":"181789827785065", "publishTime":"2018-01-06T00:41:01.839Z" } } ================================================ FILE: background/src/main/java/com/getstarted/background/functions/CreateServlet.java ================================================ /* Copyright 2019 Google LLC * * 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.getstarted.background.functions; import com.google.cloud.pubsub.v1.Publisher; import com.google.protobuf.ByteString; import com.google.pubsub.v1.PubsubMessage; import java.io.IOException; import java.util.Enumeration; import java.util.concurrent.ExecutionException; import java.util.logging.Logger; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** Servlet for the Translation Request form. */ @WebServlet( name = "create", urlPatterns = {"/create"}) public class CreateServlet extends HttpServlet { private static Logger logger = Logger.getLogger(CreateServlet.class.getName()); @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setAttribute("action", "Add"); req.setAttribute("destination", "create"); req.setAttribute("page", "form"); req.getRequestDispatcher("/base.jsp").forward(req, resp); } // [START getting_started_background_app_request] @Override public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String text = req.getParameter("data"); String sourceLang = req.getParameter("sourceLang"); String targetLang = req.getParameter("targetLang"); Enumeration paramNames = req.getParameterNames(); while (paramNames.hasMoreElements()) { String paramName = paramNames.nextElement(); logger.warning("Param name: " + paramName + " = " + req.getParameter(paramName)); } Publisher publisher = (Publisher) getServletContext().getAttribute("publisher"); PubsubMessage pubsubMessage = PubsubMessage.newBuilder() .setData(ByteString.copyFromUtf8(text)) .putAttributes("sourceLang", sourceLang) .putAttributes("targetLang", targetLang) .build(); try { publisher.publish(pubsubMessage).get(); } catch (InterruptedException | ExecutionException e) { throw new ServletException("Exception publishing message to topic.", e); } resp.sendRedirect("/"); } // [END getting_started_background_app_request] } ================================================ FILE: background/src/main/java/com/getstarted/background/functions/TranslateServlet.java ================================================ /* Copyright 2019 Google LLC * * 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.getstarted.background.functions; import com.getstarted.background.objects.PubSubMessage; import com.getstarted.background.objects.TranslateMessage; import com.google.api.core.ApiFuture; import com.google.cloud.firestore.CollectionReference; import com.google.cloud.firestore.DocumentSnapshot; import com.google.cloud.firestore.Firestore; import com.google.cloud.firestore.QueryDocumentSnapshot; import com.google.cloud.firestore.QuerySnapshot; import com.google.cloud.firestore.SetOptions; import com.google.cloud.firestore.WriteResult; import com.google.cloud.translate.Translate; import com.google.cloud.translate.Translation; import com.google.common.collect.Lists; import com.google.gson.Gson; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Base64; import java.util.Comparator; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet( name = "translate", urlPatterns = {"/", "/translate"}) public class TranslateServlet extends HttpServlet { private static final Gson gson = new Gson(); private static final String PUBSUB_VERIFICATION_TOKEN = System.getenv("PUBSUB_VERIFICATION_TOKEN"); // [START getting_started_background_app_list] @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Firestore firestore = (Firestore) this.getServletContext().getAttribute("firestore"); CollectionReference translations = firestore.collection("translations"); QuerySnapshot snapshot; try { snapshot = translations.limit(10).get().get(); } catch (InterruptedException | ExecutionException e) { throw new ServletException("Exception retrieving documents from Firestore.", e); } List translateMessages = Lists.newArrayList(); List documents = Lists.newArrayList(snapshot.getDocuments()); documents.sort(Comparator.comparing(DocumentSnapshot::getCreateTime)); for (DocumentSnapshot document : Lists.reverse(documents)) { String encoded = gson.toJson(document.getData()); TranslateMessage message = gson.fromJson(encoded, TranslateMessage.class); message.setData(decode(message.getData())); translateMessages.add(message); } req.setAttribute("messages", translateMessages); req.setAttribute("page", "list"); req.getRequestDispatcher("/base.jsp").forward(req, resp); } // [END getting_started_background_app_list] /** * Handle a posted message from Pubsub. * * @param req The message Pubsub posts to this process. * @param resp Not used. */ @Override public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { // Block requests that don't contain the proper verification token. String pubsubVerificationToken = PUBSUB_VERIFICATION_TOKEN; if (req.getParameter("token").compareTo(pubsubVerificationToken) != 0) { resp.setStatus(HttpServletResponse.SC_BAD_REQUEST); return; } // [START getting_started_background_translate_string] String body = req.getReader().lines().collect(Collectors.joining(System.lineSeparator())); PubSubMessage pubsubMessage = gson.fromJson(body, PubSubMessage.class); TranslateMessage message = pubsubMessage.getMessage(); // Use Translate service client to translate the message. Translate translate = (Translate) this.getServletContext().getAttribute("translate"); message.setData(decode(message.getData())); Translation translation = translate.translate( message.getData(), Translate.TranslateOption.sourceLanguage(message.getAttributes().getSourceLang()), Translate.TranslateOption.targetLanguage(message.getAttributes().getTargetLang())); // [END getting_started_background_translate_string] message.setTranslatedText(translation.getTranslatedText()); try { // [START getting_started_background_translate] // Use Firestore service client to store the translation in Firestore. Firestore firestore = (Firestore) this.getServletContext().getAttribute("firestore"); CollectionReference translations = firestore.collection("translations"); ApiFuture setFuture = translations.document().set(message, SetOptions.merge()); setFuture.get(); resp.getWriter().write(translation.getTranslatedText()); // [END getting_started_background_translate] } catch (InterruptedException | ExecutionException e) { throw new ServletException("Exception storing data in Firestore.", e); } } private String decode(String data) throws UnsupportedEncodingException { return new String(Base64.getDecoder().decode(data), "UTF-8"); } } ================================================ FILE: background/src/main/java/com/getstarted/background/objects/PubSubMessage.java ================================================ /* Copyright 2019 Google LLC * * 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.getstarted.background.objects; // [START getting_started_background_pub_sub_message] public class PubSubMessage { private TranslateMessage message; private String subscription; public TranslateMessage getMessage() { return message; } public void setMessage(TranslateMessage message) { this.message = message; } public String getSubscription() { return subscription; } public void setSubscription(String subscription) { this.subscription = subscription; } } // [END getting_started_background_pub_sub_message] ================================================ FILE: background/src/main/java/com/getstarted/background/objects/TranslateAttributes.java ================================================ /* Copyright 2019 Google LLC * * 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.getstarted.background.objects; // [START getting_started_background_translate_attributes] public class TranslateAttributes { private String sourceLang; private String targetLang; public String getSourceLang() { return sourceLang; } public void setSourceLang(String sourceLang) { this.sourceLang = sourceLang; } public String getTargetLang() { return targetLang; } public void setTargetLang(String targetLang) { this.targetLang = targetLang; } } // [END getting_started_background_translate_attributes] ================================================ FILE: background/src/main/java/com/getstarted/background/objects/TranslateMessage.java ================================================ /* Copyright 2019 Google LLC * * 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.getstarted.background.objects; // [START getting_started_background_translate_message] public class TranslateMessage { public String data; public TranslateAttributes attributes; public String messageId; public String publishTime; public String translatedText; public String sourceLang = "en"; public String targetLang = "en"; public String getData() { return data; } public void setData(String data) { this.data = data; } public TranslateAttributes getAttributes() { return attributes; } public void setAttributes(TranslateAttributes attributes) { this.attributes = attributes; } public String getMessageId() { return messageId; } public void setMessageId(String messageId) { this.messageId = messageId; } public String getPublishTime() { return publishTime; } public void setPublishTime(String publishTime) { this.publishTime = publishTime; } public String getTranslatedText() { return translatedText; } public void setTranslatedText(String translatedText) { this.translatedText = translatedText; } public String getSourceLang() { return sourceLang; } public void setSourceLang(String sourceLang) { this.sourceLang = sourceLang; } public String getTargetLang() { return targetLang; } public void setTargetLang(String targetLang) { this.targetLang = targetLang; } } // [END getting_started_background_translate_message] ================================================ FILE: background/src/main/java/com/getstarted/background/util/BackgroundContextListener.java ================================================ /* Copyright 2019 Google LLC * * 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.getstarted.background.util; import com.google.cloud.ServiceOptions; import com.google.cloud.firestore.Firestore; import com.google.cloud.firestore.FirestoreOptions; import com.google.cloud.pubsub.v1.Publisher; import com.google.cloud.translate.Translate; import com.google.cloud.translate.TranslateOptions; import com.google.pubsub.v1.TopicName; import java.io.IOException; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; // [START background_context_listener] @WebListener("Creates Firestore and TranslateServlet service clients for reuse between requests.") public class BackgroundContextListener implements ServletContextListener { @Override public void contextDestroyed(javax.servlet.ServletContextEvent event) {} @Override public void contextInitialized(ServletContextEvent event) { String firestoreProjectId = System.getenv("FIRESTORE_CLOUD_PROJECT"); Firestore firestore = (Firestore) event.getServletContext().getAttribute("firestore"); if (firestore == null) { firestore = FirestoreOptions.getDefaultInstance().toBuilder() .setProjectId(firestoreProjectId) .build() .getService(); event.getServletContext().setAttribute("firestore", firestore); } Translate translate = (Translate) event.getServletContext().getAttribute("translate"); if (translate == null) { translate = TranslateOptions.getDefaultInstance().getService(); event.getServletContext().setAttribute("translate", translate); } String topicId = System.getenv("PUBSUB_TOPIC"); TopicName topicName = TopicName.of(firestoreProjectId, topicId); Publisher publisher = (Publisher) event.getServletContext().getAttribute("publisher"); if (publisher == null) { try { publisher = Publisher.newBuilder(topicName).build(); event.getServletContext().setAttribute("publisher", publisher); } catch (IOException e) { e.printStackTrace(); } } } } // [END background_context_listener] ================================================ FILE: background/src/main/webapp/base.jsp ================================================ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> Background Processing - Java on Google Cloud Platform

================================================ FILE: background/src/main/webapp/form.jsp ================================================ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>

book

See language codes

================================================ FILE: background/src/main/webapp/list.jsp ================================================ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>

Translations

Request translation

No translations found.

Timestamp Message Source Language Target Language Translation
${message.publishTime} ${fn:escapeXml(message.data)} ${message.attributes.sourceLang} ${message.attributes.targetLang} ${fn:escapeXml(message.translatedText)}
================================================ FILE: background/src/test/java/com/getstarted/background/UserJourneyTestIT.java ================================================ /* Copyright 2019 Google LLC * * 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.getstarted.background; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import com.google.cloud.firestore.Firestore; import com.google.cloud.firestore.FirestoreOptions; import com.google.cloud.firestore.QueryDocumentSnapshot; import java.time.Duration; import java.util.List; import java.util.concurrent.ExecutionException; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeDriverService; import org.openqa.selenium.remote.service.DriverService; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; /* * I can't figure out how to test server-side logging in a selenium test, so just confidence-check * that it hasn't broken anything. */ @RunWith(JUnit4.class) @SuppressWarnings("checkstyle:AbbreviationAsWordInName") @Ignore("Issue #498") public class UserJourneyTestIT { private static final String TEXT = "Hello World!"; private static final String SOURCE_LANG_CODE = "en"; private static final String TARGET_LANG_CODE = "es"; private static DriverService service; private WebDriver driver; @BeforeClass public static void setupClass() throws Exception { service = ChromeDriverService.createDefaultService(); service.start(); } @AfterClass public static void tearDownClass() throws ExecutionException, InterruptedException { // Clear the firestore list if we're not using the local emulator Firestore firestore = FirestoreOptions.getDefaultInstance().getService(); for (QueryDocumentSnapshot docSnapshot : firestore.collection("translations").get().get().getDocuments()) { try { docSnapshot.getReference().delete().get(); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } service.stop(); } @Before public void setup() { driver = new ChromeDriver(); } @After public void tearDown() { driver.quit(); } private WebElement checkLandingPage() { WebElement button = driver.findElement(By.cssSelector("a.btn")); assertEquals("Request Translation", button.getText()); WebElement heading = driver.findElement(By.cssSelector("body>.container h3")); assertEquals("Translations", heading.getText()); List list = driver.findElements(By.cssSelector("body>.container tr")); assertEquals("Should be no entries in translation list.", 1, list.size()); return button; } private void checkRequestTranslationPage() { List inputContainers = driver.findElements(By.cssSelector("form .form-group")); assertTrue("Should have more than 2 inputs", inputContainers.size() > 2); assertEquals( "First input should be Text", "Text", inputContainers.get(0).findElement(By.tagName("label")).getText()); assertEquals( "Second input should be Source Language Code", "Source Language Code", inputContainers.get(1).findElement(By.tagName("label")).getText()); assertEquals( "Third input should be Target Language Code", "Target Language Code", inputContainers.get(2).findElement(By.tagName("label")).getText()); } private void submitForm() { driver.findElement(By.cssSelector("[name=data]")).sendKeys(UserJourneyTestIT.TEXT); driver .findElement(By.cssSelector("[name=sourceLang]")) .sendKeys(UserJourneyTestIT.SOURCE_LANG_CODE); driver .findElement(By.cssSelector("[name=targetLang]")) .sendKeys(UserJourneyTestIT.TARGET_LANG_CODE); driver.findElement(By.cssSelector("button[type=submit]")).submit(); } @Test public void userJourney() { // Do selenium tests on the deployed version, if applicable String endpoint = "http://localhost:8080"; System.out.println("Testing endpoint: " + endpoint); driver.get(endpoint); try { WebElement button = checkLandingPage(); button.click(); new WebDriverWait(driver, 10L) // 10 seconds .until(ExpectedConditions.urlMatches(".*/create$")::apply); checkRequestTranslationPage(); submitForm(); new WebDriverWait(driver, 10L) // 10 seconds .until(ExpectedConditions.urlMatches(".*/")::apply); } catch (Exception e) { System.err.println(driver.getPageSource()); throw e; } } } ================================================ FILE: bookshelf/1-cloud-run/README.md ================================================ # Bookshelf App for Java on Cloud Run Tutorial Contains the code for using Cloud Firestore. This is part of a [Bookshelf tutorial](https://cloud.google.com/java/getting-started). Most users can get this running by updating the parameters in `pom.xml`. You'll also need to [create a bucket][create-bucket] in Google Cloud Storage, referred to below as `MY_BUCKET`. [create-bucket]: https://cloud.google.com/storage/docs/creating-buckets ### Running Locally To run your project locally: * Set the `BOOKSHELF_BUCKET` environment variable: export BOOKSHELF_BUCKET= Where is the bucket you created above. * Run with the Jetty Maven plugin: mvn jetty:run **Note**: If you run into an error about `Invalid Credentials`, you may have to run: gcloud auth application-default login ### Deploying to Cloud Run To build your image: * Update the parameters in `pom.xml`: * Replace `MY_PROJECT` with your project ID. * Build and deploy to your GCR with [Jib][jib] Maven plugin. mvn clean package jib:build * Deploy the app to Cloud Run: gcloud run deploy bookshelf --image gcr.io//bookshelf \ --region us-central1 --memory 512M \ --update-env-vars BOOKSHELF_BUCKET="" Where is the name of the project you created. This command will output a link to visit the page. [jib]: https://github.com/GoogleContainerTools/jib [configure-memory]: https://cloud.google.com/run/docs/configuring/memory-limits ================================================ FILE: bookshelf/1-cloud-run/pom.xml ================================================ 4.0.0 war com.example.getstarted bookshelf-cloud-run 1.0-SNAPSHOT com.google.cloud.samples shared-configuration 1.2.0 MY_PROJECT false UTF-8 1.8 1.8 true true false 9.4.51.v20230217 javax.servlet javax.servlet-api 4.0.1 com.google.cloud google-cloud-firestore 3.13.2 joda-time joda-time 2.12.6 com.google.guava guava 33.1.0-jre compile io.opencensus opencensus-contrib-http-util 0.31.1 jstl jstl 1.2 com.google.cloud google-cloud-storage 2.23.0 commons-fileupload commons-fileupload 1.5 commons-io commons-io 2.13.0 bookshelf-cloud-run ${project.build.directory}/${project.build.finalName}/WEB-INF/classes org.eclipse.jetty jetty-maven-plugin ${jetty.version} com.google.cloud.tools jib-maven-plugin 3.4.0 jetty:10.0.9-jre11 java,-jar,/usr/local/jetty/start.jar gcr.io/${gcloud.appId}/bookshelf org.apache.maven.plugins maven-war-plugin 3.3.2 false ================================================ FILE: bookshelf/1-cloud-run/src/main/java/com/example/getstarted/basicactions/CreateBookServlet.java ================================================ /* Copyright 2019 Google LLC * * 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.getstarted.basicactions; // [START bookshelf_create_servlet] import com.example.getstarted.daos.BookDao; import com.example.getstarted.objects.Book; import com.example.getstarted.util.CloudStorageHelper; import com.google.common.base.Strings; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.commons.fileupload.FileItemIterator; import org.apache.commons.fileupload.FileItemStream; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.fileupload.util.Streams; @SuppressWarnings("serial") @WebServlet( name = "create", urlPatterns = {"/create"}) public class CreateBookServlet extends HttpServlet { private static final Logger logger = Logger.getLogger(CreateBookServlet.class.getName()); @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setAttribute("action", "Add"); req.setAttribute("destination", "create"); req.setAttribute("page", "form"); req.getRequestDispatcher("/base.jsp").forward(req, resp); } @Override public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { assert ServletFileUpload.isMultipartContent(req); CloudStorageHelper storageHelper = (CloudStorageHelper) getServletContext().getAttribute("storageHelper"); String newImageUrl = null; Map params = new HashMap(); try { FileItemIterator iter = new ServletFileUpload().getItemIterator(req); while (iter.hasNext()) { FileItemStream item = iter.next(); if (item.isFormField()) { params.put(item.getFieldName(), Streams.asString(item.openStream())); } else if (!Strings.isNullOrEmpty(item.getName())) { newImageUrl = storageHelper.uploadFile( item, System.getenv("BOOKSHELF_BUCKET")); } } } catch (FileUploadException e) { throw new IOException(e); } String createdByString = ""; String createdByIdString = ""; HttpSession session = req.getSession(); if (session.getAttribute("userEmail") != null) { // Does the user have a logged in session? createdByString = (String) session.getAttribute("userEmail"); createdByIdString = (String) session.getAttribute("userId"); } Book book = new Book.Builder() .author(params.get("author")) .description(params.get("description")) .publishedDate(params.get("publishedDate")) .title(params.get("title")) .imageUrl(null == newImageUrl ? params.get("imageUrl") : newImageUrl) .createdBy(createdByString) .createdById(createdByIdString) .build(); BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); String id = dao.createBook(book); logger.log(Level.INFO, "Created book {0}", book); resp.sendRedirect("/read?id=" + id); } } // [END bookshelf_create_servlet] ================================================ FILE: bookshelf/1-cloud-run/src/main/java/com/example/getstarted/basicactions/DeleteBookServlet.java ================================================ /* Copyright 2019 Google LLC * * 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.getstarted.basicactions; // [START bookshelf_delete_servlet] import com.example.getstarted.daos.BookDao; import java.io.IOException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @SuppressWarnings("serial") @WebServlet( name = "delete", urlPatterns = {"/delete"}) public class DeleteBookServlet extends HttpServlet { @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { String id = req.getParameter("id"); BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); dao.deleteBook(id); resp.sendRedirect("/books"); } } // [END bookshelf_delete_servlet] ================================================ FILE: bookshelf/1-cloud-run/src/main/java/com/example/getstarted/basicactions/ErrorsBookServlet.java ================================================ /* Copyright 2019 Google LLC * * 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.getstarted.basicactions; // [START bookshelf_errors_servlet] import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @SuppressWarnings("serial") @WebServlet( name = "errors", urlPatterns = {"/errors"}) public class ErrorsBookServlet extends HttpServlet { @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException { throw new ServletException("Expected exception."); } } // [END bookshelf_errors_servlet] ================================================ FILE: bookshelf/1-cloud-run/src/main/java/com/example/getstarted/basicactions/ListBookServlet.java ================================================ /* Copyright 2019 Google LLC * * 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.getstarted.basicactions; // [START bookshelf_list_books_servlet] import com.example.getstarted.daos.BookDao; import com.example.getstarted.objects.Book; import com.example.getstarted.objects.Result; import java.io.IOException; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // a url pattern of "" makes this servlet the root servlet @SuppressWarnings("serial") @WebServlet( name = "list", urlPatterns = {"", "/books"}, loadOnStartup = 1) public class ListBookServlet extends HttpServlet { private static final Logger logger = Logger.getLogger(ListBookServlet.class.getName()); @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); String startCursor = req.getParameter("cursor"); List books = null; String endCursor = null; try { Result result = dao.listBooks(startCursor); logger.log(Level.INFO, "Retrieved list of all books"); books = result.getResult(); endCursor = result.getCursor(); } catch (Exception e) { throw new ServletException("Error listing books", e); } req.getSession().getServletContext().setAttribute("books", books); StringBuilder bookNames = new StringBuilder(); for (Book book : books) { bookNames.append(book.getTitle()).append(" "); } logger.log(Level.INFO, "Loaded books: " + bookNames.toString()); req.setAttribute("cursor", endCursor); req.setAttribute("page", "list"); req.getRequestDispatcher("/base.jsp").forward(req, resp); } } // [END bookshelf_list_books_servlet] ================================================ FILE: bookshelf/1-cloud-run/src/main/java/com/example/getstarted/basicactions/ReadBookServlet.java ================================================ /* Copyright 2019 Google LLC * * 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.getstarted.basicactions; // [START bookshelf_read_servlet] import com.example.getstarted.daos.BookDao; import com.example.getstarted.objects.Book; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @SuppressWarnings("serial") @WebServlet( name = "read", urlPatterns = {"/read"}) public class ReadBookServlet extends HttpServlet { private final Logger logger = Logger.getLogger(ReadBookServlet.class.getName()); @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String id = req.getParameter("id"); BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); Book book = dao.readBook(id); logger.log(Level.INFO, "Read book with id {0}", id); req.setAttribute("book", book); req.setAttribute("page", "view"); req.getRequestDispatcher("/base.jsp").forward(req, resp); } } // [END bookshelf_read_servlet] ================================================ FILE: bookshelf/1-cloud-run/src/main/java/com/example/getstarted/basicactions/UpdateBookServlet.java ================================================ /* Copyright 2019 Google LLC * * 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.getstarted.basicactions; // [START bookshelf_update_servlet] import com.example.getstarted.daos.BookDao; import com.example.getstarted.objects.Book; import com.example.getstarted.util.CloudStorageHelper; import com.google.common.base.Strings; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileItemIterator; import org.apache.commons.fileupload.FileItemStream; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.fileupload.util.Streams; @SuppressWarnings("serial") @WebServlet( name = "update", urlPatterns = {"/update"}) public class UpdateBookServlet extends HttpServlet { @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException { BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); try { Book book = dao.readBook(req.getParameter("id")); req.setAttribute("book", book); req.setAttribute("action", "Edit"); req.setAttribute("destination", "update"); req.setAttribute("page", "form"); req.getRequestDispatcher("/base.jsp").forward(req, resp); } catch (Exception e) { throw new ServletException("Error loading book for editing", e); } } @Override public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); assert ServletFileUpload.isMultipartContent(req); CloudStorageHelper storageHelper = (CloudStorageHelper) getServletContext().getAttribute("storageHelper"); String newImageUrl = null; Map params = new HashMap(); try { FileItemIterator iter = new ServletFileUpload().getItemIterator(req); while (iter.hasNext()) { FileItemStream item = iter.next(); if (item.isFormField()) { params.put(item.getFieldName(), Streams.asString(item.openStream())); } else if (!Strings.isNullOrEmpty(item.getName())) { newImageUrl = storageHelper.uploadFile( item, System.getenv("BOOKSHELF_BUCKET")); } } } catch (FileUploadException e) { throw new IOException(e); } Book oldBook = dao.readBook(params.get("id")); Book book = new Book.Builder() .author(params.get("author")) .description(params.get("description")) .publishedDate(params.get("publishedDate")) .title(params.get("title")) .imageUrl(null == newImageUrl ? params.get("imageUrl") : newImageUrl) .id(params.get("id")) .createdBy(oldBook.getCreatedBy()) .createdById(oldBook.getCreatedById()) .build(); dao.updateBook(book); resp.sendRedirect("/read?id=" + params.get("id")); } } // [END bookshelf_update_servlet] ================================================ FILE: bookshelf/1-cloud-run/src/main/java/com/example/getstarted/daos/BookDao.java ================================================ /* Copyright 2019 Google LLC * * 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.getstarted.daos; import com.example.getstarted.objects.Book; import com.example.getstarted.objects.Result; // [START bookshelf_base_dao] public interface BookDao { String createBook(Book book); Book readBook(String bookId); void updateBook(Book book); void deleteBook(String bookId); Result listBooks(String startCursor); Result listBooksByUser(String userId, String startCursor); } // [END bookshelf_base_dao] ================================================ FILE: bookshelf/1-cloud-run/src/main/java/com/example/getstarted/daos/FirestoreDao.java ================================================ /* Copyright 2019 Google LLC * * 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.getstarted.daos; import com.example.getstarted.objects.Book; import com.example.getstarted.objects.Result; import com.google.cloud.firestore.CollectionReference; import com.google.cloud.firestore.DocumentReference; import com.google.cloud.firestore.DocumentSnapshot; import com.google.cloud.firestore.Firestore; import com.google.cloud.firestore.FirestoreOptions; import com.google.cloud.firestore.Query; import com.google.cloud.firestore.QueryDocumentSnapshot; import com.google.cloud.firestore.QuerySnapshot; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.ExecutionException; // [START bookshelf_firestore_client] public class FirestoreDao implements BookDao { private CollectionReference booksCollection; public FirestoreDao() { Firestore firestore = FirestoreOptions.getDefaultInstance().getService(); booksCollection = firestore.collection("books"); } // [START bookshelf_firestore_document_to_book] private Book documentToBook(DocumentSnapshot document) { Map data = document.getData(); if (data == null) { System.out.println("No data in document " + document.getId()); return null; } return new Book.Builder() .author((String) data.get(Book.AUTHOR)) .description((String) data.get(Book.DESCRIPTION)) .publishedDate((String) data.get(Book.PUBLISHED_DATE)) .imageUrl((String) data.get(Book.IMAGE_URL)) .createdBy((String) data.get(Book.CREATED_BY)) .createdById((String) data.get(Book.CREATED_BY_ID)) .title((String) data.get(Book.TITLE)) .id(document.getId()) .build(); } // [END bookshelf_firestore_document_to_book] // [START bookshelf_firestore_create_book] @Override public String createBook(Book book) { String id = UUID.randomUUID().toString(); DocumentReference document = booksCollection.document(id); Map data = Maps.newHashMap(); data.put(Book.AUTHOR, book.getAuthor()); data.put(Book.DESCRIPTION, book.getDescription()); data.put(Book.PUBLISHED_DATE, book.getPublishedDate()); data.put(Book.TITLE, book.getTitle()); data.put(Book.IMAGE_URL, book.getImageUrl()); data.put(Book.CREATED_BY, book.getCreatedBy()); data.put(Book.CREATED_BY_ID, book.getCreatedById()); try { document.set(data).get(); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } return id; } // [END bookshelf_firestore_create_book] // [START bookshelf_firestore_read] @Override public Book readBook(String bookId) { try { DocumentSnapshot document = booksCollection.document(bookId).get().get(); return documentToBook(document); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } return null; } // [END bookshelf_firestore_read] // [START bookshelf_firestore_update] @Override public void updateBook(Book book) { DocumentReference document = booksCollection.document(book.getId()); Map data = Maps.newHashMap(); data.put(Book.AUTHOR, book.getAuthor()); data.put(Book.DESCRIPTION, book.getDescription()); data.put(Book.PUBLISHED_DATE, book.getPublishedDate()); data.put(Book.TITLE, book.getTitle()); data.put(Book.IMAGE_URL, book.getImageUrl()); data.put(Book.CREATED_BY, book.getCreatedBy()); data.put(Book.CREATED_BY_ID, book.getCreatedById()); try { document.set(data).get(); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } // [END bookshelf_firestore_update] // [START bookshelf_firestore_delete] @Override public void deleteBook(String bookId) { try { booksCollection.document(bookId).delete().get(); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } // [END bookshelf_firestore_delete] // [START bookshelf_firestore_documents_to_books] private List documentsToBooks(List documents) { List resultBooks = new ArrayList<>(); for (QueryDocumentSnapshot snapshot : documents) { resultBooks.add(documentToBook(snapshot)); } return resultBooks; } // [END bookshelf_firestore_documents_to_books] // [START bookshelf_firestore_list_books] @Override public Result listBooks(String startTitle) { Query booksQuery = booksCollection.orderBy("title").limit(10); if (startTitle != null) { booksQuery = booksQuery.startAfter(startTitle); } try { QuerySnapshot snapshot = booksQuery.get().get(); List results = documentsToBooks(snapshot.getDocuments()); String newCursor = null; if (results.size() > 0) { newCursor = results.get(results.size() - 1).getTitle(); } return new Result<>(results, newCursor); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } return new Result<>(Lists.newArrayList(), null); } // [END bookshelf_firestore_list_books] // [START bookshelf_firestore_list_by_user] @Override public Result listBooksByUser(String userId, String startTitle) { Query booksQuery = booksCollection.orderBy("title").whereEqualTo(Book.CREATED_BY_ID, userId).limit(10); if (startTitle != null) { booksQuery = booksQuery.startAfter(startTitle); } try { QuerySnapshot snapshot = booksQuery.get().get(); List results = documentsToBooks(snapshot.getDocuments()); String newCursor = null; if (results.size() > 0) { newCursor = results.get(results.size() - 1).getTitle(); } return new Result<>(results, newCursor); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } return new Result<>(Lists.newArrayList(), null); } // [END bookshelf_firestore_list_by_user] } // [END bookshelf_firestore_client] ================================================ FILE: bookshelf/1-cloud-run/src/main/java/com/example/getstarted/objects/Book.java ================================================ /* Copyright 2019 Google LLC * * 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.getstarted.objects; // [START bookshelf_book] public class Book { private String title; private String author; private String createdBy; private String createdById; private String publishedDate; private String description; private String id; private String imageUrl; public static final String AUTHOR = "author"; public static final String CREATED_BY = "createdBy"; public static final String CREATED_BY_ID = "createdById"; public static final String DESCRIPTION = "description"; public static final String ID = "id"; public static final String PUBLISHED_DATE = "publishedDate"; public static final String TITLE = "title"; public static final String IMAGE_URL = "imageUrl"; // We use a Builder pattern here to simplify and standardize construction of // Book objects. private Book(Builder builder) { this.title = builder.title; this.author = builder.author; this.createdBy = builder.createdBy; this.createdById = builder.createdById; this.publishedDate = builder.publishedDate; this.description = builder.description; this.id = builder.id; this.imageUrl = builder.imageUrl; } public static class Builder { private String title; private String author; private String createdBy; private String createdById; private String publishedDate; private String description; private String id; private String imageUrl; public Builder title(String title) { this.title = title; return this; } public Builder author(String author) { this.author = author; return this; } public Builder createdBy(String createdBy) { this.createdBy = createdBy; return this; } public Builder createdById(String createdById) { this.createdById = createdById; return this; } public Builder publishedDate(String publishedDate) { this.publishedDate = publishedDate; return this; } public Builder description(String description) { this.description = description; return this; } public Builder id(String id) { this.id = id; return this; } public Builder imageUrl(String imageUrl) { this.imageUrl = imageUrl; return this; } public Book build() { return new Book(this); } } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public String getCreatedBy() { return createdBy; } public void setCreatedBy(String createdBy) { this.createdBy = createdBy; } public String getCreatedById() { return createdById; } public void setCreatedById(String createdById) { this.createdById = createdById; } public String getPublishedDate() { return publishedDate; } public void setPublishedDate(String publishedDate) { this.publishedDate = publishedDate; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getImageUrl() { return imageUrl; } public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; } @Override public String toString() { return "Title: " + title + ", Author: " + author + ", Published date: " + publishedDate + ", Added by: " + createdBy; } } // [END bookshelf_book] ================================================ FILE: bookshelf/1-cloud-run/src/main/java/com/example/getstarted/objects/Result.java ================================================ /* Copyright 2019 Google LLC * * 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.getstarted.objects; import java.util.List; // [START bookshelf_result] public class Result { private String cursor; private List result; public String getCursor() { return cursor; } public void setCursor(String cursor) { this.cursor = cursor; } public List getResult() { return result; } public void setResult(List result) { this.result = result; } public Result(List result, String cursor) { this.result = result; this.cursor = cursor; } public Result(List result) { this.result = result; this.cursor = null; } } // [END bookshelf_result] ================================================ FILE: bookshelf/1-cloud-run/src/main/java/com/example/getstarted/util/BookshelfContextListener.java ================================================ /* Copyright 2019 Google LLC * * 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.getstarted.util; import com.example.getstarted.daos.BookDao; import com.example.getstarted.daos.FirestoreDao; import com.google.common.base.Strings; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; @WebListener("Creates BookDao and other servlet context objects for reuse.") public class BookshelfContextListener implements ServletContextListener { @Override public void contextDestroyed(javax.servlet.ServletContextEvent event) { } @Override public void contextInitialized(ServletContextEvent event) { // This function is called when the application starts and will safely set a few required // context attributes such as the BookDao. BookDao dao = (BookDao) event.getServletContext().getAttribute("dao"); if (dao == null) { dao = new FirestoreDao(); event.getServletContext().setAttribute("dao", dao); } Boolean isCloudStorageConfigured = (Boolean) event.getServletContext() .getAttribute("isCloudStorageConfigured"); if (isCloudStorageConfigured == null) { event.getServletContext() .setAttribute( "isCloudStorageConfigured", !Strings.isNullOrEmpty(System.getenv("BOOKSHELF_BUCKET"))); } CloudStorageHelper storageHelper = (CloudStorageHelper) event.getServletContext().getAttribute( "storageHelper"); if (storageHelper == null) { storageHelper = new CloudStorageHelper(); event.getServletContext().setAttribute("storageHelper", storageHelper); } } } ================================================ FILE: bookshelf/1-cloud-run/src/main/java/com/example/getstarted/util/CloudStorageHelper.java ================================================ /* * Copyright 2019 Google LLC * * 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.getstarted.util; import com.google.cloud.storage.Acl; import com.google.cloud.storage.Acl.Role; import com.google.cloud.storage.Acl.User; import com.google.cloud.storage.BlobInfo; import com.google.cloud.storage.Storage; import com.google.cloud.storage.StorageOptions; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.ServletException; import org.apache.commons.fileupload.FileItemStream; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; // [START bookshelf_cloud_storage_client] public class CloudStorageHelper { private final Logger logger = Logger.getLogger(CloudStorageHelper.class.getName()); private static Storage storage = null; // [START bookshelf_cloud_storage_client_init] static { storage = StorageOptions.getDefaultInstance().getService(); } // [END bookshelf_cloud_storage_client_init] // [START bookshelf_cloud_storage_client_upload_file] /** * Uploads a file to Google Cloud Storage to the bucket specified in the BUCKET_NAME environment * variable, appending a timestamp to end of the uploaded filename. */ public String uploadFile(FileItemStream fileStream, final String bucketName) throws IOException, ServletException { checkFileExtension(fileStream.getName()); System.out.println("FileStream name: " + fileStream.getName() + "\nBucket name: " + bucketName); DateTimeFormatter dtf = DateTimeFormat.forPattern("-YYYY-MM-dd-HHmmssSSS"); DateTime dt = DateTime.now(DateTimeZone.UTC); String dtString = dt.toString(dtf); final String fileName = fileStream.getName() + dtString; // the inputstream is closed by default, so we don't need to close it here @SuppressWarnings("deprecation") BlobInfo blobInfo = storage.create( BlobInfo.newBuilder(bucketName, fileName) // Modify access list to allow all users with link to read file .setAcl(new ArrayList<>(Arrays.asList(Acl.of(User.ofAllUsers(), Role.READER)))) .build(), fileStream.openStream()); logger.log( Level.INFO, "Uploaded file {0} as {1}", new Object[] {fileStream.getName(), fileName}); // return the public download link return blobInfo.getMediaLink(); } // [END bookshelf_cloud_storage_client_upload_file] // [START bookshelf_cloud_storage_client_check_file_extension] /** Checks that the file extension is supported. */ private void checkFileExtension(String fileName) throws ServletException { if (fileName != null && !fileName.isEmpty() && fileName.contains(".")) { String[] allowedExt = {".jpg", ".jpeg", ".png", ".gif"}; for (String ext : allowedExt) { if (fileName.endsWith(ext)) { return; } } throw new ServletException("file must be an image"); } } // [END bookshelf_cloud_storage_client_check_file_extension] } // [END bookshelf_cloud_storage_client] ================================================ FILE: bookshelf/1-cloud-run/src/main/webapp/WEB-INF/web.xml ================================================ list com.example.getstarted.basicactions.ListBookServlet 1 list / /books create com.example.getstarted.basicactions.CreateBookServlet create /create update com.example.getstarted.basicactions.UpdateBookServlet update /update read com.example.getstarted.basicactions.ReadBookServlet read /read delete com.example.getstarted.basicactions.DeleteBookServlet delete /delete ================================================ FILE: bookshelf/1-cloud-run/src/main/webapp/base.jsp ================================================ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> Bookshelf - Java on Google Cloud Platform ================================================ FILE: bookshelf/1-cloud-run/src/main/webapp/form.jsp ================================================ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>

book

================================================ FILE: bookshelf/1-cloud-run/src/main/webapp/list.jsp ================================================ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> ================================================ FILE: bookshelf/1-cloud-run/src/main/webapp/view.jsp ================================================ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>

Book

${fn:escapeXml(book.title)} ${fn:escapeXml(book.publishedDate)}

By ${fn:escapeXml(not empty book.author?book.author:'Unknown')}

${fn:escapeXml(book.description)}

Added by ${fn:escapeXml(not empty book.createdBy?book.createdBy:'Anonymous')}
================================================ FILE: bookshelf-standard/2-structured-data/README.md ================================================ mvn package appengine:deploymvn package appengine:deploymvn package appengine:deploy# Bookshelf App for Java on App Engine Standard Tutorial ## Structured Data Contains the code for using Cloud Datastore and Cloud SQL v2. This is part of a [Bookshelf tutorial](https://cloud.google.com/java/getting-started/tutorial-app). Most users can get this running by updating the parameters in `pom.xml`. ### Running Locally mvn -Plocal clean appengine:devserver ### Deploying to App Engine Standard * In the `pom.xml`, update the [App Engine Maven Plugin](https://cloud.google.com/appengine/docs/standard/java/tools/maven-reference) with your Google Cloud Project Id: ``` com.google.cloud.tools appengine-maven-plugin 2.3.0 GCLOUD_CONFIG bookshelf ``` * Deploy your App mvn package appengine:deploy Visit it at http://bookshelf..appspot.com ================================================ FILE: bookshelf-standard/2-structured-data/jenkins.sh ================================================ #!/usr/bin/env bash # Copyright 2017 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. # Fail on non-zero return and print command to stdout set -xe # Jenkins provides values for GOOGLE_CLOUD_PROJECT and GOOGLE_VERSION_ID # Make sure it works on GAE7 # Deploy and run selenium tests mvn clean appengine:update verify \ -Pselenium \ -Dappengine.appId="${GOOGLE_CLOUD_PROJECT}" \ -Dappengine.version="${GOOGLE_VERSION_ID}" \ -Dappengine.additionalParams="--service_account_json_key_file=${GOOGLE_APPLICATION_CREDENTIALS}" # Make sure it deploys on GAE8 FILE_PATH=src/main/webapp/WEB-INF/appengine-web.xml sed -i'.bak' '/java8' "${FILE_PATH}" # Restore the backup after we're done trap 'mv "${FILE_PATH}"{.bak,}' EXIT # Deploy and run selenium tests mvn clean appengine:update verify \ -Pselenium \ -Dappengine.appId="${GOOGLE_CLOUD_PROJECT}" \ -Dappengine.version="${GOOGLE_VERSION_ID}" \ -Dappengine.additionalParams="--service_account_json_key_file=${GOOGLE_APPLICATION_CREDENTIALS}" ================================================ FILE: bookshelf-standard/2-structured-data/pom.xml ================================================ 4.0.0 war com.example.standard.gettingstarted bookshelf-standard-2 1.0-SNAPSHOT com.google.cloud.samples shared-configuration 1.2.0 datastore bookshelf DATABASE-connectionName-HERE root MYSQL-ROOT-PASSWORD-HERE UTF-8 1.8 1.8 true true true local com.google.cloud.sql mysql-socket-factory 1.6.1 mysql mysql-connector-java 8.0.30 com.google.api-client google-api-client-appengine 2.0.0 com.google.appengine appengine-api-1.0-sdk 2.0.15 javax.servlet javax.servlet-api 4.0.1 provided javax.servlet jsp-api 2.0 jstl jstl 1.2 taglibs standard 1.1.2 com.google.guava guava 33.1.0-jre compile joda-time joda-time 2.12.6 ${project.build.directory}/${project.build.finalName}/WEB-INF/classes com.google.cloud.tools appengine-maven-plugin 2.4.4 GCLOUD_CONFIG bookshelf org.apache.maven.plugins maven-compiler-plugin 3.11.0 ================================================ FILE: bookshelf-standard/2-structured-data/src/main/java/com/example/getstarted/basicactions/CreateBookServlet.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.basicactions; import com.example.getstarted.daos.BookDao; import com.example.getstarted.objects.Book; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // [START example] @SuppressWarnings("serial") public class CreateBookServlet extends HttpServlet { // [START setup] @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setAttribute("action", "Add"); // Part of the Header in form.jsp req.setAttribute("destination", "create"); // The urlPattern to invoke (this Servlet) req.setAttribute("page", "form"); // Tells base.jsp to include form.jsp req.getRequestDispatcher("/base.jsp").forward(req, resp); } // [END setup] // [START formpost] @Override public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // [START bookBuilder] Book book = new Book.Builder() .author(req.getParameter("author")) // form parameter .description(req.getParameter("description")) .publishedDate(req.getParameter("publishedDate")) .title(req.getParameter("title")) .imageUrl(null) .build(); // [END bookBuilder] BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); try { Long id = dao.createBook(book); resp.sendRedirect("/read?id=" + id.toString()); // read what we just wrote } catch (Exception e) { throw new ServletException("Error creating book", e); } } // [END formpost] } // [END example] ================================================ FILE: bookshelf-standard/2-structured-data/src/main/java/com/example/getstarted/basicactions/DeleteBookServlet.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.basicactions; import com.example.getstarted.daos.BookDao; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // [START example] @SuppressWarnings("serial") public class DeleteBookServlet extends HttpServlet { @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Long id = Long.decode(req.getParameter("id")); BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); try { dao.deleteBook(id); resp.sendRedirect("/books"); } catch (Exception e) { throw new ServletException("Error deleting book", e); } } } // [END example] ================================================ FILE: bookshelf-standard/2-structured-data/src/main/java/com/example/getstarted/basicactions/ListBookServlet.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.basicactions; import com.example.getstarted.daos.BookDao; import com.example.getstarted.daos.CloudSqlDao; import com.example.getstarted.daos.DatastoreDao; import com.example.getstarted.objects.Book; import com.example.getstarted.objects.Result; import java.io.IOException; import java.sql.SQLException; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // [START example] @SuppressWarnings("serial") public class ListBookServlet extends HttpServlet { @Override public void init() throws ServletException { BookDao dao = null; // Creates the DAO based on the Context Parameters String storageType = this.getServletContext().getInitParameter("bookshelf.storageType"); switch (storageType) { case "datastore": dao = new DatastoreDao(); break; case "cloudsql": try { // Use this url when using dev appserver, but connecting to Cloud SQL String connect = this.getServletContext().getInitParameter("sql.urlRemote"); if (connect.contains("localhost")) { // Use this url when using a local mysql server connect = this.getServletContext().getInitParameter("sql.urlLocal"); } else if (System.getProperty("com.google.appengine.runtime.version") .startsWith("Google App Engine/")) { // Use this url when on App Engine, connecting to Cloud SQL. // Uses a special adapter because of the App Engine sandbox. connect = this.getServletContext().getInitParameter("sql.urlRemoteGAE"); } dao = new CloudSqlDao(connect); } catch (SQLException e) { throw new ServletException("SQL error", e); } break; default: throw new IllegalStateException( "Invalid storage type. Check if bookshelf.storageType property is set."); } this.getServletContext().setAttribute("dao", dao); } @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); String startCursor = req.getParameter("cursor"); List books = null; String endCursor = null; try { Result result = dao.listBooks(startCursor); books = result.result; endCursor = result.cursor; } catch (Exception e) { throw new ServletException("Error listing books", e); } req.getSession().getServletContext().setAttribute("books", books); StringBuilder bookNames = new StringBuilder(); for (Book book : books) { bookNames.append(book.getTitle() + " "); } req.setAttribute("cursor", endCursor); req.setAttribute("page", "list"); req.getRequestDispatcher("/base.jsp").forward(req, resp); } } // [END example] ================================================ FILE: bookshelf-standard/2-structured-data/src/main/java/com/example/getstarted/basicactions/ReadBookServlet.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.basicactions; import com.example.getstarted.daos.BookDao; import com.example.getstarted.objects.Book; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // [START example] @SuppressWarnings("serial") public class ReadBookServlet extends HttpServlet { @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { Long id = Long.decode(req.getParameter("id")); BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); try { Book book = dao.readBook(id); req.setAttribute("book", book); req.setAttribute("page", "view"); req.getRequestDispatcher("/base.jsp").forward(req, resp); } catch (Exception e) { throw new ServletException("Error reading book", e); } } } // [END example] ================================================ FILE: bookshelf-standard/2-structured-data/src/main/java/com/example/getstarted/basicactions/UpdateBookServlet.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.basicactions; import com.example.getstarted.daos.BookDao; import com.example.getstarted.objects.Book; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // [START example] @SuppressWarnings("serial") public class UpdateBookServlet extends HttpServlet { @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); try { Book book = dao.readBook(Long.decode(req.getParameter("id"))); req.setAttribute("book", book); req.setAttribute("action", "Edit"); req.setAttribute("destination", "update"); req.setAttribute("page", "form"); req.getRequestDispatcher("/base.jsp").forward(req, resp); } catch (Exception e) { throw new ServletException("Error loading book for editing", e); } } @Override public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); try { // [START bookBuilder] Book book = new Book.Builder() .author(req.getParameter("author")) .description(req.getParameter("description")) .id(Long.decode(req.getParameter("id"))) .publishedDate(req.getParameter("publishedDate")) .title(req.getParameter("title")) .build(); // [END bookBuilder] dao.updateBook(book); resp.sendRedirect("/read?id=" + req.getParameter("id")); } catch (Exception e) { throw new ServletException("Error updating book", e); } } } // [END example] ================================================ FILE: bookshelf-standard/2-structured-data/src/main/java/com/example/getstarted/daos/BookDao.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.daos; import com.example.getstarted.objects.Book; import com.example.getstarted.objects.Result; import java.sql.SQLException; // [START example] public interface BookDao { Long createBook(Book book) throws SQLException; Book readBook(Long bookId) throws SQLException; void updateBook(Book book) throws SQLException; void deleteBook(Long bookId) throws SQLException; Result listBooks(String startCursor) throws SQLException; } // [END example] ================================================ FILE: bookshelf-standard/2-structured-data/src/main/java/com/example/getstarted/daos/CloudSqlDao.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.daos; import com.example.getstarted.objects.Book; import com.example.getstarted.objects.Result; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; // [START example] public class CloudSqlDao implements BookDao { // [START constructor] private String sqlUrl; /** * A data access object for Bookshelf using a Google Cloud SQL server for storage. */ public CloudSqlDao(final String url) throws SQLException { sqlUrl = url; final String createTableSql = "CREATE TABLE IF NOT EXISTS books2 ( id INT NOT NULL " + "AUTO_INCREMENT, author VARCHAR(255), createdBy VARCHAR(255), createdById VARCHAR(255), " + "description VARCHAR(255), publishedDate VARCHAR(255), title VARCHAR(255), imageUrl " + "VARCHAR(255), PRIMARY KEY (id))"; try (Connection conn = DriverManager.getConnection(sqlUrl)) { conn.createStatement().executeUpdate(createTableSql); } } // [END constructor] // [START create] @Override public Long createBook(Book book) throws SQLException { final String createBookString = "INSERT INTO books2 " + "(author, createdBy, createdById, description, publishedDate, title, imageUrl) " + "VALUES (?, ?, ?, ?, ?, ?, ?)"; try (Connection conn = DriverManager.getConnection(sqlUrl); final PreparedStatement createBookStmt = conn.prepareStatement(createBookString, Statement.RETURN_GENERATED_KEYS)) { createBookStmt.setString(1, book.getAuthor()); createBookStmt.setString(2, book.getCreatedBy()); createBookStmt.setString(3, book.getCreatedById()); createBookStmt.setString(4, book.getDescription()); createBookStmt.setString(5, book.getPublishedDate()); createBookStmt.setString(6, book.getTitle()); createBookStmt.setString(7, book.getImageUrl()); createBookStmt.executeUpdate(); try (ResultSet keys = createBookStmt.getGeneratedKeys()) { keys.next(); return keys.getLong(1); } } } // [END create] // [START read] @Override public Book readBook(Long bookId) throws SQLException { final String readBookString = "SELECT * FROM books2 WHERE id = ?"; try (Connection conn = DriverManager.getConnection(sqlUrl); PreparedStatement readBookStmt = conn.prepareStatement(readBookString)) { readBookStmt.setLong(1, bookId); try (ResultSet keys = readBookStmt.executeQuery()) { keys.next(); return new Book.Builder() .author(keys.getString(Book.AUTHOR)) .createdBy(keys.getString(Book.CREATED_BY)) .createdById(keys.getString(Book.CREATED_BY_ID)) .description(keys.getString(Book.DESCRIPTION)) .id(keys.getLong(Book.ID)) .publishedDate(keys.getString(Book.PUBLISHED_DATE)) .title(keys.getString(Book.TITLE)) .imageUrl(keys.getString(Book.IMAGE_URL)) .build(); } } } // [END read] // [START update] @Override public void updateBook(Book book) throws SQLException { final String updateBookString = "UPDATE books2 SET author = ?, createdBy = ?, createdById = ?, " + "description = ?, publishedDate = ?, title = ?, imageUrl = ? WHERE id = ?"; try (Connection conn = DriverManager.getConnection(sqlUrl); PreparedStatement updateBookStmt = conn.prepareStatement(updateBookString)) { updateBookStmt.setString(1, book.getAuthor()); updateBookStmt.setString(2, book.getCreatedBy()); updateBookStmt.setString(3, book.getCreatedById()); updateBookStmt.setString(4, book.getDescription()); updateBookStmt.setString(5, book.getPublishedDate()); updateBookStmt.setString(6, book.getTitle()); updateBookStmt.setString(7, book.getImageUrl()); updateBookStmt.setLong(8, book.getId()); updateBookStmt.executeUpdate(); } } // [END update] // [START delete] @Override public void deleteBook(Long bookId) throws SQLException { final String deleteBookString = "DELETE FROM books2 WHERE id = ?"; try (Connection conn = DriverManager.getConnection(sqlUrl); PreparedStatement deleteBookStmt = conn.prepareStatement(deleteBookString)) { deleteBookStmt.setLong(1, bookId); deleteBookStmt.executeUpdate(); } } // [END delete] // [START listbooks] @Override public Result listBooks(String cursor) throws SQLException { int offset = 0; if (cursor != null && !cursor.equals("")) { offset = Integer.parseInt(cursor); } final String listBooksString = "SELECT SQL_CALC_FOUND_ROWS author, createdBy, createdById, " + "description, id, publishedDate, title, imageUrl FROM books2 ORDER BY title ASC " + "LIMIT 10 OFFSET ?"; try (Connection conn = DriverManager.getConnection(sqlUrl); PreparedStatement listBooksStmt = conn.prepareStatement(listBooksString)) { listBooksStmt.setInt(1, offset); List resultBooks = new ArrayList<>(); try (ResultSet rs = listBooksStmt.executeQuery()) { while (rs.next()) { Book book = new Book.Builder() .author(rs.getString(Book.AUTHOR)) .createdBy(rs.getString(Book.CREATED_BY)) .createdById(rs.getString(Book.CREATED_BY_ID)) .description(rs.getString(Book.DESCRIPTION)) .id(rs.getLong(Book.ID)) .publishedDate(rs.getString(Book.PUBLISHED_DATE)) .title(rs.getString(Book.TITLE)) .imageUrl(rs.getString(Book.IMAGE_URL)) .build(); resultBooks.add(book); } } try (ResultSet rs = conn.createStatement().executeQuery("SELECT FOUND_ROWS()")) { int totalNumRows = 0; if (rs.next()) { totalNumRows = rs.getInt(1); } if (totalNumRows > offset + 10) { return new Result<>(resultBooks, Integer.toString(offset + 10)); } else { return new Result<>(resultBooks); } } } } // [END listbooks] } // [END example] ================================================ FILE: bookshelf-standard/2-structured-data/src/main/java/com/example/getstarted/daos/DatastoreDao.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.daos; import com.example.getstarted.objects.Book; import com.example.getstarted.objects.Result; import com.google.appengine.api.datastore.Cursor; 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.EntityNotFoundException; import com.google.appengine.api.datastore.FetchOptions; import com.google.appengine.api.datastore.Key; import com.google.appengine.api.datastore.KeyFactory; import com.google.appengine.api.datastore.PreparedQuery; import com.google.appengine.api.datastore.Query; import com.google.appengine.api.datastore.Query.SortDirection; import com.google.appengine.api.datastore.QueryResultIterator; import java.util.ArrayList; import java.util.Iterator; import java.util.List; // [START example] public class DatastoreDao implements BookDao { // [START constructor] private DatastoreService datastore; private static final String BOOK_KIND = "Book2"; public DatastoreDao() { datastore = DatastoreServiceFactory.getDatastoreService(); // Authorized Datastore service } // [END constructor] // [START entityToBook] public Book entityToBook(Entity entity) { return new Book.Builder() // Convert to Book form .author((String) entity.getProperty(Book.AUTHOR)) .description((String) entity.getProperty(Book.DESCRIPTION)) .id(entity.getKey().getId()) .publishedDate((String) entity.getProperty(Book.PUBLISHED_DATE)) .title((String) entity.getProperty(Book.TITLE)) .build(); } // [END entityToBook] // [START create] @Override public Long createBook(Book book) { Entity incBookEntity = new Entity(BOOK_KIND); // Key will be assigned once written incBookEntity.setProperty(Book.AUTHOR, book.getAuthor()); incBookEntity.setProperty(Book.DESCRIPTION, book.getDescription()); incBookEntity.setProperty(Book.PUBLISHED_DATE, book.getPublishedDate()); incBookEntity.setProperty(Book.TITLE, book.getTitle()); Key bookKey = datastore.put(incBookEntity); // Save the Entity return bookKey.getId(); // The ID of the Key } // [END create] // [START read] @Override public Book readBook(Long bookId) { try { Entity bookEntity = datastore.get(KeyFactory.createKey(BOOK_KIND, bookId)); return entityToBook(bookEntity); } catch (EntityNotFoundException e) { return null; } } // [END read] // [START update] @Override public void updateBook(Book book) { Key key = KeyFactory.createKey(BOOK_KIND, book.getId()); // From a book, create a Key Entity entity = new Entity(key); // Convert Book to an Entity entity.setProperty(Book.AUTHOR, book.getAuthor()); entity.setProperty(Book.DESCRIPTION, book.getDescription()); entity.setProperty(Book.PUBLISHED_DATE, book.getPublishedDate()); entity.setProperty(Book.TITLE, book.getTitle()); datastore.put(entity); // Update the Entity } // [END update] // [START delete] @Override public void deleteBook(Long bookId) { Key key = KeyFactory.createKey(BOOK_KIND, bookId); // Create the Key datastore.delete(key); // Delete the Entity } // [END delete] // [START entitiesToBooks] public List entitiesToBooks(Iterator results) { List resultBooks = new ArrayList<>(); while (results.hasNext()) { // We still have data resultBooks.add(entityToBook(results.next())); // Add the Book to the List } return resultBooks; } // [END entitiesToBooks] // [START listbooks] @Override public Result listBooks(String startCursorString) { FetchOptions fetchOptions = FetchOptions.Builder.withLimit(10); // Only show 10 at a time if (startCursorString != null && !startCursorString.equals("")) { fetchOptions.startCursor(Cursor.fromWebSafeString(startCursorString)); // Where we left off } Query query = new Query(BOOK_KIND) // We only care about Books .addSort(Book.TITLE, SortDirection.ASCENDING); // Use default Index "title" PreparedQuery preparedQuery = datastore.prepare(query); QueryResultIterator results = preparedQuery.asQueryResultIterator(fetchOptions); List resultBooks = entitiesToBooks(results); // Retrieve and convert Entities Cursor cursor = results.getCursor(); // Where to start next time if (cursor != null && resultBooks.size() == 10) { // Are we paging? Save Cursor String cursorString = cursor.toWebSafeString(); // Cursors are WebSafe return new Result<>(resultBooks, cursorString); } else { return new Result<>(resultBooks); } } // [END listbooks] } // [END example] ================================================ FILE: bookshelf-standard/2-structured-data/src/main/java/com/example/getstarted/objects/Book.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.objects; // [START example] public class Book { // [START book] private String title; private String author; private String createdBy; private String createdById; private String publishedDate; private String description; private Long id; private String imageUrl; // [END book] // [START keys] public static final String AUTHOR = "author"; public static final String CREATED_BY = "createdBy"; public static final String CREATED_BY_ID = "createdById"; public static final String DESCRIPTION = "description"; public static final String ID = "id"; public static final String PUBLISHED_DATE = "publishedDate"; public static final String TITLE = "title"; public static final String IMAGE_URL = "imageUrl"; // [END keys] // [START constructor] // We use a Builder pattern here to simplify and standardize construction of Book objects. private Book(Builder builder) { this.title = builder.title; this.author = builder.author; this.createdBy = builder.createdBy; this.createdById = builder.createdById; this.publishedDate = builder.publishedDate; this.description = builder.description; this.id = builder.id; this.imageUrl = builder.imageUrl; } // [END constructor] // [START builder] public static class Builder { private String title; private String author; private String createdBy; private String createdById; private String publishedDate; private String description; private Long id; private String imageUrl; public Builder title(String title) { this.title = title; return this; } public Builder author(String author) { this.author = author; return this; } public Builder createdBy(String createdBy) { this.createdBy = createdBy; return this; } public Builder createdById(String createdById) { this.createdById = createdById; return this; } public Builder publishedDate(String publishedDate) { this.publishedDate = publishedDate; return this; } public Builder description(String description) { this.description = description; return this; } public Builder id(Long id) { this.id = id; return this; } public Builder imageUrl(String imageUrl) { this.imageUrl = imageUrl; return this; } public Book build() { return new Book(this); } } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public String getCreatedBy() { return createdBy; } public void setCreatedBy(String createdBy) { this.createdBy = createdBy; } public String getCreatedById() { return createdById; } public void setCreatedById(String createdById) { this.createdById = createdById; } public String getPublishedDate() { return publishedDate; } public void setPublishedDate(String publishedDate) { this.publishedDate = publishedDate; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getImageUrl() { return imageUrl; } public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; } // [END builder] @Override public String toString() { return "Title: " + title + ", Author: " + author + ", Published date: " + publishedDate + ", Added by: " + createdBy; } } // [END example] ================================================ FILE: bookshelf-standard/2-structured-data/src/main/java/com/example/getstarted/objects/Result.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.objects; import java.util.List; // [START example] public class Result { public String cursor; public List result; public Result(List result, String cursor) { this.result = result; this.cursor = cursor; } public Result(List result) { this.result = result; this.cursor = null; } } // [END example] ================================================ FILE: bookshelf-standard/2-structured-data/src/main/java/com/example/getstarted/util/DatastoreSessionFilter.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.util; 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.EntityNotFoundException; import com.google.appengine.api.datastore.Key; import com.google.appengine.api.datastore.KeyFactory; import com.google.appengine.api.datastore.Query; import com.google.appengine.api.datastore.Query.FilterOperator; import com.google.appengine.api.datastore.Query.FilterPredicate; import com.google.appengine.api.datastore.Transaction; import com.google.common.collect.FluentIterable; import com.google.common.collect.MapDifference; import com.google.common.collect.Maps; import java.io.IOException; import java.math.BigInteger; import java.security.SecureRandom; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; // [START init] public class DatastoreSessionFilter implements Filter { private static DatastoreService datastore; private static final DateTimeFormatter DTF = DateTimeFormat.forPattern("yyyyMMddHHmmssSSS"); private static final String SESSION_KIND = "SessionVariable"; @Override public void init(FilterConfig config) throws ServletException { // initialize local copy of datastore session variables datastore = DatastoreServiceFactory.getDatastoreService(); // Delete all sessions unmodified for over two days DateTime dt = DateTime.now(DateTimeZone.UTC); Query query = new Query(SESSION_KIND).setFilter(new FilterPredicate( "lastModified", FilterOperator.LESS_THAN_OR_EQUAL, dt.minusDays(2).toString(DTF))); Iterator results = datastore.prepare(query).asIterator(); while (results.hasNext()) { Entity stateEntity = results.next(); datastore.delete(stateEntity.getKey()); } } // [END init] @Override public void doFilter(ServletRequest servletReq, ServletResponse servletResp, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) servletReq; HttpServletResponse resp = (HttpServletResponse) servletResp; // Check if the session cookie is there, if not there, make a session cookie using a unique // identifier. String sessionId = getCookieValue(req, "bookshelfSessionId"); if (sessionId.equals("")) { String sessionNum = new BigInteger(130, new SecureRandom()).toString(32); Cookie session = new Cookie("bookshelfSessionId", sessionNum); session.setPath("/"); resp.addCookie(session); } Map datastoreMap = loadSessionVariables(req); // session variables for request chain.doFilter(servletReq, servletResp); // Allow the servlet to process request and response HttpSession session = req.getSession(); // Create session map Map sessionMap = new HashMap<>(); Enumeration attrNames = session.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = attrNames.nextElement(); sessionMap.put(attrName, (String) session.getAttribute(attrName)); } // Create a diff between the new session variables and the existing session variables // to minimize datastore access MapDifference diff = Maps.difference(sessionMap, datastoreMap); Map setMap = diff.entriesOnlyOnLeft(); Map deleteMap = diff.entriesOnlyOnRight(); // Apply the diff setSessionVariables(sessionId, setMap); deleteSessionVariables( sessionId, FluentIterable.from(deleteMap.keySet()).toArray(String.class)); } @SuppressWarnings({"unused", "JdkObsolete"}) private String mapToString(Map map) { StringBuffer names = new StringBuffer(); for (String name : map.keySet()) { names.append(name + " "); } return names.toString(); } @Override public void destroy() { } protected String getCookieValue(HttpServletRequest req, String cookieName) { Cookie[] cookies = req.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { if (cookie.getName().equals(cookieName)) { return cookie.getValue(); } } } return ""; } // [START deleteSessionVariables] /** * Delete a value stored in the project's datastore. * * @param sessionId Request from which the session is extracted. */ protected void deleteSessionVariables(String sessionId, String... varNames) { if (sessionId.equals("")) { return; } Key key = KeyFactory.createKey(SESSION_KIND, sessionId); Transaction transaction = datastore.beginTransaction(); try { Entity stateEntity = datastore.get(transaction, key); for (String varName : varNames) { stateEntity.removeProperty(varName); } datastore.put(transaction, stateEntity); transaction.commit(); } catch (EntityNotFoundException e) { // Ignore - if there's no session, there's nothing to delete. } finally { if (transaction.isActive()) { transaction.rollback(); } } } // [END deleteSessionVariables] protected void deleteSessionWithValue(String varName, String varValue) { Transaction transaction = datastore.beginTransaction(); try { Query query = new Query(SESSION_KIND) .setFilter(new FilterPredicate(varName, FilterOperator.EQUAL, varValue)); Iterator results = datastore.prepare(transaction, query).asIterator(); while (results.hasNext()) { Entity stateEntity = results.next(); datastore.delete(transaction, stateEntity.getKey()); } transaction.commit(); } finally { if (transaction.isActive()) { transaction.rollback(); } } } // [START setSessionVariables] /** * Stores the state value in each key-value pair in the project's datastore. * * @param sessionId Request from which to extract session. * @param varName the name of the desired session variable * @param varValue the value of the desired session variable */ protected void setSessionVariables(String sessionId, Map setMap) { if (sessionId.equals("")) { return; } Key key = KeyFactory.createKey(SESSION_KIND, sessionId); Transaction transaction = datastore.beginTransaction(); DateTime dt = DateTime.now(DateTimeZone.UTC); dt.toString(DTF); try { Entity stateEntity; try { stateEntity = datastore.get(transaction, key); } catch (EntityNotFoundException e) { stateEntity = new Entity(key); } for (String varName : setMap.keySet()) { stateEntity.setProperty(varName, setMap.get(varName)); } stateEntity.setProperty("lastModified", dt.toString(DTF)); datastore.put(transaction, stateEntity); transaction.commit(); } finally { if (transaction.isActive()) { transaction.rollback(); } } } // [END setSessionVariables] // [START loadSessionVariables] /** * Take an HttpServletRequest, and copy all of the current session variables over to it * * @param req Request from which to extract session. * @return a map of strings containing all the session variables loaded or an empty map. */ protected Map loadSessionVariables(HttpServletRequest req) throws ServletException { Map datastoreMap = new HashMap<>(); String sessionId = getCookieValue(req, "bookshelfSessionId"); if (sessionId.equals("")) { return datastoreMap; } Key key = KeyFactory.createKey(SESSION_KIND, sessionId); Transaction transaction = datastore.beginTransaction(); try { Entity stateEntity = datastore.get(transaction, key); Map properties = stateEntity.getProperties(); for (Map.Entry property : properties.entrySet()) { req.getSession().setAttribute(property.getKey(), property.getValue()); datastoreMap.put(property.getKey(), (String) property.getValue()); } transaction.commit(); } catch (EntityNotFoundException e) { // Ignore - if there's no session, there's nothing to delete. } finally { if (transaction.isActive()) { transaction.rollback(); } } return datastoreMap; } // [END loadSessionVariables] } ================================================ FILE: bookshelf-standard/2-structured-data/src/main/webapp/WEB-INF/appengine-web.xml ================================================ true true true ================================================ FILE: bookshelf-standard/2-structured-data/src/main/webapp/WEB-INF/logging.properties ================================================ ================================================ FILE: bookshelf-standard/2-structured-data/src/main/webapp/WEB-INF/web.xml ================================================ DatastoreSessionFilter com.example.getstarted.util.DatastoreSessionFilter DatastoreSessionFilter / /books /books/mine /create /delete /login /logout /oauth2callback /read /update list com.example.getstarted.basicactions.ListBookServlet 1 list / /books create com.example.getstarted.basicactions.CreateBookServlet create /create update com.example.getstarted.basicactions.UpdateBookServlet update /update read com.example.getstarted.basicactions.ReadBookServlet read /read delete com.example.getstarted.basicactions.DeleteBookServlet delete /delete bookshelf.storageType ${bookshelf.storageType} sql.urlRemoteGAE jdbc:google:mysql://${sql.instanceName}/${sql.dbName}?user=${sql.userName}&password=${sql.password} sql.urlRemote jdbc:mysql://google/${sql.dbName}?cloudSqlInstance=${sql.instanceName}&socketFactory=com.google.cloud.sql.mysql.SocketFactory&user=${sql.userName}&password=${sql.password} sql.urlLocal jdbc:mysql://localhost/${sql.dbName}?user=${sql.userName}&password=${sql.password}&useSSL=false ================================================ FILE: bookshelf-standard/2-structured-data/src/main/webapp/base.jsp ================================================ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> Bookshelf - Java on Google Cloud Platform ================================================ FILE: bookshelf-standard/2-structured-data/src/main/webapp/form.jsp ================================================ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>

book

================================================ FILE: bookshelf-standard/2-structured-data/src/main/webapp/list.jsp ================================================ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> ================================================ FILE: bookshelf-standard/2-structured-data/src/main/webapp/view.jsp ================================================ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>

Book

${fn:escapeXml(book.title)} ${fn:escapeXml(book.publishedDate)}

By ${fn:escapeXml(not empty book.author?book.author:'Unknown')}

${fn:escapeXml(book.description)}

Added by ${fn:escapeXml(not empty book.createdBy?book.createdBy:'Anonymous')}
================================================ FILE: bookshelf-standard/3-binary-data/README.md ================================================ # Bookshelf App for Java on App Engine Standard Tutorial ## Binary Data Contains the code for using Cloud Datastore and Cloud SQL v2. This is part of a [Bookshelf tutorial](https://cloud.google.com/java/getting-started/tutorial-app). Most users can get this running by updating the parameters in `pom.xml`. You'll also need to [create a bucket][create-bucket] in Google Cloud Storage, referred to below as `MY-BUCKET`. [create-bucket]: https://cloud.google.com/storage/docs/creating-buckets ### Running Locally mvn -Plocal clean appengine:devserver -Dbookshelf.bucket=MY-BUCKET **Note**: If you run into an error about `Invalid Credentials`, you may have to run: gcloud auth application-default login ### Deploying to App Engine Standard * In the `pom.xml`, update the [App Engine Maven Plugin](https://cloud.google.com/appengine/docs/standard/java/tools/maven-reference) with your Google Cloud Project Id: ``` com.google.cloud.tools appengine-maven-plugin 2.3.0 GCLOUD_CONFIG bookshelf ``` * Deploy your App mvn package appengine:deploy \ -Dbookshelf.bucket=MY-BUCKET Visit it at http://bookshelf..appspot.com ================================================ FILE: bookshelf-standard/3-binary-data/jenkins.sh ================================================ #!/usr/bin/env bash # Copyright 2017 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. # Fail on non-zero return and print command to stdout set -xe # Jenkins provides values for GOOGLE_PROJECT_ID and GOOGLE_VERSION_ID # Make sure it works on GAE7 # Deploy and run selenium tests mvn clean appengine:update verify \ -Pselenium \ -Dappengine.appId="${GOOGLE_PROJECT_ID}" \ -Dappengine.version="${GOOGLE_VERSION_ID}" \ -Dbookshelf.bucket="${GCS_BUCKET}" \ -Dappengine.additionalParams="--service_account_json_key_file=${GOOGLE_APPLICATION_CREDENTIALS}" # Make sure it deploys on GAE8 FILE_PATH=src/main/webapp/WEB-INF/appengine-web.xml sed -i'.bak' '/java8' "${FILE_PATH}" # Restore the backup after we're done trap 'mv "${FILE_PATH}"{.bak,}' EXIT # Deploy and run selenium tests mvn clean appengine:update verify \ -Pselenium \ -Dappengine.appId="${GOOGLE_PROJECT_ID}" \ -Dappengine.version="${GOOGLE_VERSION_ID}" \ -Dbookshelf.bucket="${GCS_BUCKET}" \ -Dappengine.additionalParams="--service_account_json_key_file=${GOOGLE_APPLICATION_CREDENTIALS}" ================================================ FILE: bookshelf-standard/3-binary-data/pom.xml ================================================ 4.0.0 war com.example.standard.gettingstarted bookshelf-standard-3 1.0-SNAPSHOT com.google.cloud.samples shared-configuration 1.2.0 datastore bookshelf DATABASE-connectionName-HERE root MYSQL-ROOT-PASSWORD-HERE BUCKET-NAME-HERE UTF-8 1.8 1.8 true true true local com.google.cloud.sql mysql-socket-factory 1.6.1 mysql mysql-connector-java 8.0.30 com.google.api-client google-api-client-appengine 2.0.0 com.google.appengine appengine-api-1.0-sdk 2.0.15 javax.servlet javax.servlet-api 4.0.1 provided javax.servlet jsp-api 2.0 jstl jstl 1.2 taglibs standard 1.1.2 com.google.cloud google-cloud-storage 2.23.0 com.google.guava guava 33.1.0-jre compile joda-time joda-time 2.12.6 commons-fileupload commons-fileupload 1.5 ${project.build.directory}/${project.build.finalName}/WEB-INF/classes com.google.cloud.tools appengine-maven-plugin 2.4.4 GCLOUD_CONFIG bookshelf org.apache.maven.plugins maven-compiler-plugin 3.11.0 ================================================ FILE: bookshelf-standard/3-binary-data/src/main/java/com/example/getstarted/basicactions/CreateBookServlet.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.basicactions; import com.example.getstarted.daos.BookDao; import com.example.getstarted.objects.Book; import com.example.getstarted.util.CloudStorageHelper; import com.google.common.base.Strings; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileItemIterator; import org.apache.commons.fileupload.FileItemStream; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.fileupload.util.Streams; // [START example] @SuppressWarnings("serial") public class CreateBookServlet extends HttpServlet { // [START setup] @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setAttribute("action", "Add"); // Part of the Header in form.jsp req.setAttribute("destination", "create"); // The urlPattern to invoke (this Servlet) req.setAttribute("page", "form"); // Tells base.jsp to include form.jsp req.getRequestDispatcher("/base.jsp").forward(req, resp); } // [END setup] // [START formpost] @Override public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // [START storageHelper] assert ServletFileUpload.isMultipartContent(req); CloudStorageHelper storageHelper = (CloudStorageHelper) getServletContext().getAttribute("storageHelper"); String newImageUrl = null; Map params = new HashMap(); try { FileItemIterator iter = new ServletFileUpload().getItemIterator(req); while (iter.hasNext()) { FileItemStream item = iter.next(); if (item.isFormField()) { params.put(item.getFieldName(), Streams.asString(item.openStream())); } else if (!Strings.isNullOrEmpty(item.getName())) { newImageUrl = storageHelper.uploadFile( item, getServletContext().getInitParameter("bookshelf.bucket")); } } } catch (FileUploadException e) { throw new IOException(e); } // [START bookBuilder] Book book = new Book.Builder() .author(params.get("author")) .description(params.get("description")) .publishedDate(params.get("publishedDate")) .title(params.get("title")) .imageUrl(null == newImageUrl ? params.get("imageUrl") : newImageUrl) .build(); // [END bookBuilder] // [END storageHelper] BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); try { Long id = dao.createBook(book); resp.sendRedirect("/read?id=" + id.toString()); // read what we just wrote } catch (Exception e) { throw new ServletException("Error creating book", e); } } // [END formpost] } // [END example] ================================================ FILE: bookshelf-standard/3-binary-data/src/main/java/com/example/getstarted/basicactions/DeleteBookServlet.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.basicactions; import com.example.getstarted.daos.BookDao; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // [START example] @SuppressWarnings("serial") public class DeleteBookServlet extends HttpServlet { @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Long id = Long.decode(req.getParameter("id")); BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); try { dao.deleteBook(id); resp.sendRedirect("/books"); } catch (Exception e) { throw new ServletException("Error deleting book", e); } } } // [END example] ================================================ FILE: bookshelf-standard/3-binary-data/src/main/java/com/example/getstarted/basicactions/ListBookServlet.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.basicactions; import com.example.getstarted.daos.BookDao; import com.example.getstarted.daos.CloudSqlDao; import com.example.getstarted.daos.DatastoreDao; import com.example.getstarted.objects.Book; import com.example.getstarted.objects.Result; import com.example.getstarted.util.CloudStorageHelper; import com.google.common.base.Strings; import java.io.IOException; import java.sql.SQLException; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // [START example] @SuppressWarnings("serial") public class ListBookServlet extends HttpServlet { @Override public void init() throws ServletException { BookDao dao = null; // [START storageHelper] CloudStorageHelper storageHelper = new CloudStorageHelper(); // [END storageHelper] // Creates the DAO based on the Context Parameters String storageType = this.getServletContext().getInitParameter("bookshelf.storageType"); switch (storageType) { case "datastore": dao = new DatastoreDao(); break; case "cloudsql": try { // Use this url when using dev appserver, but connecting to Cloud SQL String connect = this.getServletContext().getInitParameter("sql.urlRemote"); if (connect.contains("localhost")) { // Use this url when using a local mysql server connect = this.getServletContext().getInitParameter("sql.urlLocal"); } else if (System.getProperty("com.google.appengine.runtime.version") .startsWith("Google App Engine/")) { // Use this url when on App Engine, connecting to Cloud SQL. // Uses a special adapter because of the App Engine sandbox. connect = this.getServletContext().getInitParameter("sql.urlRemoteGAE"); } dao = new CloudSqlDao(connect); } catch (SQLException e) { throw new ServletException("SQL error", e); } break; default: throw new IllegalStateException( "Invalid storage type. Check if bookshelf.storageType property is set."); } this.getServletContext().setAttribute("dao", dao); // [START save_storage] this.getServletContext().setAttribute("storageHelper", storageHelper); this.getServletContext().setAttribute( "isCloudStorageConfigured", // Hide upload when Cloud Storage is not configured. !Strings.isNullOrEmpty(getServletContext().getInitParameter("bookshelf.bucket"))); // [END save_storage] } @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); String startCursor = req.getParameter("cursor"); List books = null; String endCursor = null; try { Result result = dao.listBooks(startCursor); books = result.result; endCursor = result.cursor; } catch (Exception e) { throw new ServletException("Error listing books", e); } req.getSession().getServletContext().setAttribute("books", books); StringBuilder bookNames = new StringBuilder(); for (Book book : books) { bookNames.append(book.getTitle() + " "); } req.setAttribute("cursor", endCursor); req.setAttribute("page", "list"); req.getRequestDispatcher("/base.jsp").forward(req, resp); } } // [END example] ================================================ FILE: bookshelf-standard/3-binary-data/src/main/java/com/example/getstarted/basicactions/ReadBookServlet.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.basicactions; import com.example.getstarted.daos.BookDao; import com.example.getstarted.objects.Book; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // [START example] @SuppressWarnings("serial") public class ReadBookServlet extends HttpServlet { @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { Long id = Long.decode(req.getParameter("id")); BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); try { Book book = dao.readBook(id); req.setAttribute("book", book); req.setAttribute("page", "view"); req.getRequestDispatcher("/base.jsp").forward(req, resp); } catch (Exception e) { throw new ServletException("Error reading book", e); } } } // [END example] ================================================ FILE: bookshelf-standard/3-binary-data/src/main/java/com/example/getstarted/basicactions/UpdateBookServlet.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.basicactions; import com.example.getstarted.daos.BookDao; import com.example.getstarted.objects.Book; import com.example.getstarted.util.CloudStorageHelper; import com.google.common.base.Strings; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileItemIterator; import org.apache.commons.fileupload.FileItemStream; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.fileupload.util.Streams; // [START example] @SuppressWarnings("serial") public class UpdateBookServlet extends HttpServlet { @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); try { Book book = dao.readBook(Long.decode(req.getParameter("id"))); req.setAttribute("book", book); req.setAttribute("action", "Edit"); req.setAttribute("destination", "update"); req.setAttribute("page", "form"); req.getRequestDispatcher("/base.jsp").forward(req, resp); } catch (Exception e) { throw new ServletException("Error loading book for editing", e); } } @Override public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); // [START storageHelper] assert ServletFileUpload.isMultipartContent(req); CloudStorageHelper storageHelper = (CloudStorageHelper) getServletContext().getAttribute("storageHelper"); String newImageUrl = null; Map params = new HashMap(); try { FileItemIterator iter = new ServletFileUpload().getItemIterator(req); while (iter.hasNext()) { FileItemStream item = iter.next(); if (item.isFormField()) { params.put(item.getFieldName(), Streams.asString(item.openStream())); } else if (!Strings.isNullOrEmpty(item.getName())) { newImageUrl = storageHelper.uploadFile( item, getServletContext().getInitParameter("bookshelf.bucket")); } } } catch (FileUploadException e) { throw new IOException(e); } // [START bookBuilder] Book book = new Book.Builder() .author(params.get("author")) .description(params.get("description")) .publishedDate(params.get("publishedDate")) .title(params.get("title")) .imageUrl(null == newImageUrl ? params.get("imageUrl") : newImageUrl) .id(Long.decode(params.get("id"))) .build(); // [END bookBuilder] // [END storageHelper] try { dao.updateBook(book); resp.sendRedirect("/read?id=" + params.get("id")); } catch (Exception e) { throw new ServletException("Error updating book", e); } } } // [END example] ================================================ FILE: bookshelf-standard/3-binary-data/src/main/java/com/example/getstarted/daos/BookDao.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.daos; import com.example.getstarted.objects.Book; import com.example.getstarted.objects.Result; import java.sql.SQLException; // [START example] public interface BookDao { Long createBook(Book book) throws SQLException; Book readBook(Long bookId) throws SQLException; void updateBook(Book book) throws SQLException; void deleteBook(Long bookId) throws SQLException; Result listBooks(String startCursor) throws SQLException; } // [END example] ================================================ FILE: bookshelf-standard/3-binary-data/src/main/java/com/example/getstarted/daos/CloudSqlDao.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.daos; import com.example.getstarted.objects.Book; import com.example.getstarted.objects.Result; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; // [START example] public class CloudSqlDao implements BookDao { // [START constructor] private String sqlUrl; /** * A data access object for Bookshelf using a Google Cloud SQL server for storage. */ public CloudSqlDao(final String url) throws SQLException { sqlUrl = url; final String createTableSql = "CREATE TABLE IF NOT EXISTS books3 ( id INT NOT NULL " + "AUTO_INCREMENT, author VARCHAR(255), createdBy VARCHAR(255), createdById VARCHAR(255), " + "description VARCHAR(255), publishedDate VARCHAR(255), title VARCHAR(255), imageUrl " + "VARCHAR(255), PRIMARY KEY (id))"; try (Connection conn = DriverManager.getConnection(sqlUrl)) { conn.createStatement().executeUpdate(createTableSql); } } // [END constructor] // [START create] @Override public Long createBook(Book book) throws SQLException { final String createBookString = "INSERT INTO books3 " + "(author, createdBy, createdById, description, publishedDate, title, imageUrl) " + "VALUES (?, ?, ?, ?, ?, ?, ?)"; try (Connection conn = DriverManager.getConnection(sqlUrl); final PreparedStatement createBookStmt = conn.prepareStatement(createBookString, Statement.RETURN_GENERATED_KEYS)) { createBookStmt.setString(1, book.getAuthor()); createBookStmt.setString(2, book.getCreatedBy()); createBookStmt.setString(3, book.getCreatedById()); createBookStmt.setString(4, book.getDescription()); createBookStmt.setString(5, book.getPublishedDate()); createBookStmt.setString(6, book.getTitle()); createBookStmt.setString(7, book.getImageUrl()); createBookStmt.executeUpdate(); try (ResultSet keys = createBookStmt.getGeneratedKeys()) { keys.next(); return keys.getLong(1); } } } // [END create] // [START read] @Override public Book readBook(Long bookId) throws SQLException { final String readBookString = "SELECT * FROM books3 WHERE id = ?"; try (Connection conn = DriverManager.getConnection(sqlUrl); PreparedStatement readBookStmt = conn.prepareStatement(readBookString)) { readBookStmt.setLong(1, bookId); try (ResultSet keys = readBookStmt.executeQuery()) { keys.next(); return new Book.Builder() .author(keys.getString(Book.AUTHOR)) .createdBy(keys.getString(Book.CREATED_BY)) .createdById(keys.getString(Book.CREATED_BY_ID)) .description(keys.getString(Book.DESCRIPTION)) .id(keys.getLong(Book.ID)) .publishedDate(keys.getString(Book.PUBLISHED_DATE)) .title(keys.getString(Book.TITLE)) .imageUrl(keys.getString(Book.IMAGE_URL)) .build(); } } } // [END read] // [START update] @Override public void updateBook(Book book) throws SQLException { final String updateBookString = "UPDATE books3 SET author = ?, createdBy = ?, createdById = ?, " + "description = ?, publishedDate = ?, title = ?, imageUrl = ? WHERE id = ?"; try (Connection conn = DriverManager.getConnection(sqlUrl); PreparedStatement updateBookStmt = conn.prepareStatement(updateBookString)) { updateBookStmt.setString(1, book.getAuthor()); updateBookStmt.setString(2, book.getCreatedBy()); updateBookStmt.setString(3, book.getCreatedById()); updateBookStmt.setString(4, book.getDescription()); updateBookStmt.setString(5, book.getPublishedDate()); updateBookStmt.setString(6, book.getTitle()); updateBookStmt.setString(7, book.getImageUrl()); updateBookStmt.setLong(8, book.getId()); updateBookStmt.executeUpdate(); } } // [END update] // [START delete] @Override public void deleteBook(Long bookId) throws SQLException { final String deleteBookString = "DELETE FROM books3 WHERE id = ?"; try (Connection conn = DriverManager.getConnection(sqlUrl); PreparedStatement deleteBookStmt = conn.prepareStatement(deleteBookString)) { deleteBookStmt.setLong(1, bookId); deleteBookStmt.executeUpdate(); } } // [END delete] // [START listbooks] @Override public Result listBooks(String cursor) throws SQLException { int offset = 0; if (cursor != null && !cursor.equals("")) { offset = Integer.parseInt(cursor); } final String listBooksString = "SELECT SQL_CALC_FOUND_ROWS author, createdBy, createdById, " + "description, id, publishedDate, title, imageUrl FROM books3 ORDER BY title ASC " + "LIMIT 10 OFFSET ?"; try (Connection conn = DriverManager.getConnection(sqlUrl); PreparedStatement listBooksStmt = conn.prepareStatement(listBooksString)) { listBooksStmt.setInt(1, offset); List resultBooks = new ArrayList<>(); try (ResultSet rs = listBooksStmt.executeQuery()) { while (rs.next()) { Book book = new Book.Builder() .author(rs.getString(Book.AUTHOR)) .createdBy(rs.getString(Book.CREATED_BY)) .createdById(rs.getString(Book.CREATED_BY_ID)) .description(rs.getString(Book.DESCRIPTION)) .id(rs.getLong(Book.ID)) .publishedDate(rs.getString(Book.PUBLISHED_DATE)) .title(rs.getString(Book.TITLE)) .imageUrl(rs.getString(Book.IMAGE_URL)) .build(); resultBooks.add(book); } } try (ResultSet rs = conn.createStatement().executeQuery("SELECT FOUND_ROWS()")) { int totalNumRows = 0; if (rs.next()) { totalNumRows = rs.getInt(1); } if (totalNumRows > offset + 10) { return new Result<>(resultBooks, Integer.toString(offset + 10)); } else { return new Result<>(resultBooks); } } } } // [END listbooks] } // [END example] ================================================ FILE: bookshelf-standard/3-binary-data/src/main/java/com/example/getstarted/daos/DatastoreDao.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.daos; import com.example.getstarted.objects.Book; import com.example.getstarted.objects.Result; import com.google.appengine.api.datastore.Cursor; 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.EntityNotFoundException; import com.google.appengine.api.datastore.FetchOptions; import com.google.appengine.api.datastore.Key; import com.google.appengine.api.datastore.KeyFactory; import com.google.appengine.api.datastore.PreparedQuery; import com.google.appengine.api.datastore.Query; import com.google.appengine.api.datastore.Query.SortDirection; import com.google.appengine.api.datastore.QueryResultIterator; import java.util.ArrayList; import java.util.Iterator; import java.util.List; // [START example] public class DatastoreDao implements BookDao { // [START constructor] private DatastoreService datastore; private static final String BOOK_KIND = "Book3"; public DatastoreDao() { datastore = DatastoreServiceFactory.getDatastoreService(); // Authorized Datastore service } // [END constructor] // [START entityToBook] public Book entityToBook(Entity entity) { return new Book.Builder() // Convert to Book form .author((String) entity.getProperty(Book.AUTHOR)) .description((String) entity.getProperty(Book.DESCRIPTION)) .id(entity.getKey().getId()) .publishedDate((String) entity.getProperty(Book.PUBLISHED_DATE)) .imageUrl((String) entity.getProperty(Book.IMAGE_URL)) .title((String) entity.getProperty(Book.TITLE)) .build(); } // [END entityToBook] // [START create] @Override public Long createBook(Book book) { Entity incBookEntity = new Entity(BOOK_KIND); // Key will be assigned once written incBookEntity.setProperty(Book.AUTHOR, book.getAuthor()); incBookEntity.setProperty(Book.DESCRIPTION, book.getDescription()); incBookEntity.setProperty(Book.PUBLISHED_DATE, book.getPublishedDate()); incBookEntity.setProperty(Book.TITLE, book.getTitle()); incBookEntity.setProperty(Book.IMAGE_URL, book.getImageUrl()); Key bookKey = datastore.put(incBookEntity); // Save the Entity return bookKey.getId(); // The ID of the Key } // [END create] // [START read] @Override public Book readBook(Long bookId) { try { Entity bookEntity = datastore.get(KeyFactory.createKey(BOOK_KIND, bookId)); return entityToBook(bookEntity); } catch (EntityNotFoundException e) { return null; } } // [END read] // [START update] @Override public void updateBook(Book book) { Key key = KeyFactory.createKey(BOOK_KIND, book.getId()); // From a book, create a Key Entity entity = new Entity(key); // Convert Book to an Entity entity.setProperty(Book.AUTHOR, book.getAuthor()); entity.setProperty(Book.DESCRIPTION, book.getDescription()); entity.setProperty(Book.PUBLISHED_DATE, book.getPublishedDate()); entity.setProperty(Book.TITLE, book.getTitle()); entity.setProperty(Book.IMAGE_URL, book.getImageUrl()); datastore.put(entity); // Update the Entity } // [END update] // [START delete] @Override public void deleteBook(Long bookId) { Key key = KeyFactory.createKey(BOOK_KIND, bookId); // Create the Key datastore.delete(key); // Delete the Entity } // [END delete] // [START entitiesToBooks] public List entitiesToBooks(Iterator results) { List resultBooks = new ArrayList<>(); while (results.hasNext()) { // We still have data resultBooks.add(entityToBook(results.next())); // Add the Book to the List } return resultBooks; } // [END entitiesToBooks] // [START listbooks] @Override public Result listBooks(String startCursorString) { FetchOptions fetchOptions = FetchOptions.Builder.withLimit(10); // Only show 10 at a time if (startCursorString != null && !startCursorString.equals("")) { fetchOptions.startCursor(Cursor.fromWebSafeString(startCursorString)); // Where we left off } Query query = new Query(BOOK_KIND) // We only care about Books .addSort(Book.TITLE, SortDirection.ASCENDING); // Use default Index "title" PreparedQuery preparedQuery = datastore.prepare(query); QueryResultIterator results = preparedQuery.asQueryResultIterator(fetchOptions); List resultBooks = entitiesToBooks(results); // Retrieve and convert Entities Cursor cursor = results.getCursor(); // Where to start next time if (cursor != null && resultBooks.size() == 10) { // Are we paging? Save Cursor String cursorString = cursor.toWebSafeString(); // Cursors are WebSafe return new Result<>(resultBooks, cursorString); } else { return new Result<>(resultBooks); } } // [END listbooks] } // [END example] ================================================ FILE: bookshelf-standard/3-binary-data/src/main/java/com/example/getstarted/objects/Book.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.objects; // [START example] public class Book { // [START book] private String title; private String author; private String createdBy; private String createdById; private String publishedDate; private String description; private Long id; private String imageUrl; // [END book] // [START keys] public static final String AUTHOR = "author"; public static final String CREATED_BY = "createdBy"; public static final String CREATED_BY_ID = "createdById"; public static final String DESCRIPTION = "description"; public static final String ID = "id"; public static final String PUBLISHED_DATE = "publishedDate"; public static final String TITLE = "title"; public static final String IMAGE_URL = "imageUrl"; // [END keys] // [START constructor] // We use a Builder pattern here to simplify and standardize construction of Book objects. private Book(Builder builder) { this.title = builder.title; this.author = builder.author; this.createdBy = builder.createdBy; this.createdById = builder.createdById; this.publishedDate = builder.publishedDate; this.description = builder.description; this.id = builder.id; this.imageUrl = builder.imageUrl; } // [END constructor] // [START builder] public static class Builder { private String title; private String author; private String createdBy; private String createdById; private String publishedDate; private String description; private Long id; private String imageUrl; public Builder title(String title) { this.title = title; return this; } public Builder author(String author) { this.author = author; return this; } public Builder createdBy(String createdBy) { this.createdBy = createdBy; return this; } public Builder createdById(String createdById) { this.createdById = createdById; return this; } public Builder publishedDate(String publishedDate) { this.publishedDate = publishedDate; return this; } public Builder description(String description) { this.description = description; return this; } public Builder id(Long id) { this.id = id; return this; } public Builder imageUrl(String imageUrl) { this.imageUrl = imageUrl; return this; } public Book build() { return new Book(this); } } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public String getCreatedBy() { return createdBy; } public void setCreatedBy(String createdBy) { this.createdBy = createdBy; } public String getCreatedById() { return createdById; } public void setCreatedById(String createdById) { this.createdById = createdById; } public String getPublishedDate() { return publishedDate; } public void setPublishedDate(String publishedDate) { this.publishedDate = publishedDate; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getImageUrl() { return imageUrl; } public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; } // [END builder] @Override public String toString() { return "Title: " + title + ", Author: " + author + ", Published date: " + publishedDate + ", Added by: " + createdBy; } } // [END example] ================================================ FILE: bookshelf-standard/3-binary-data/src/main/java/com/example/getstarted/objects/Result.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.objects; import java.util.List; // [START example] public class Result { public String cursor; public List result; public Result(List result, String cursor) { this.result = result; this.cursor = cursor; } public Result(List result) { this.result = result; this.cursor = null; } } // [END example] ================================================ FILE: bookshelf-standard/3-binary-data/src/main/java/com/example/getstarted/util/CloudStorageHelper.java ================================================ /* * Copyright 2016 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. */ package com.example.getstarted.util; import com.google.cloud.storage.Acl; import com.google.cloud.storage.Acl.Role; import com.google.cloud.storage.Acl.User; import com.google.cloud.storage.BlobInfo; import com.google.cloud.storage.Storage; import com.google.cloud.storage.StorageOptions; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import javax.servlet.ServletException; import org.apache.commons.fileupload.FileItemStream; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; // [START example] public class CloudStorageHelper { private static Storage storage = null; // [START init] static { storage = StorageOptions.getDefaultInstance().getService(); } // [END init] // [START uploadFile] /** * Uploads a file to Google Cloud Storage to the bucket specified in the BUCKET_NAME * environment variable, appending a timestamp to end of the uploaded filename. */ @SuppressWarnings("deprecation") public String uploadFile(FileItemStream fileStream, final String bucketName) throws IOException, ServletException { checkFileExtension(fileStream.getName()); DateTimeFormatter dtf = DateTimeFormat.forPattern("-YYYY-MM-dd-HHmmssSSS"); DateTime dt = DateTime.now(DateTimeZone.UTC); String dtString = dt.toString(dtf); final String fileName = fileStream.getName() + dtString; // the inputstream is closed by default, so we don't need to close it here BlobInfo blobInfo = storage.create( BlobInfo .newBuilder(bucketName, fileName) // Modify access list to allow all users with link to read file .setAcl(new ArrayList<>(Arrays.asList(Acl.of(User.ofAllUsers(), Role.READER)))) .build(), fileStream.openStream()); // return the public download link return blobInfo.getMediaLink(); } // [END uploadFile] // [START checkFileExtension] /** * Checks that the file extension is supported. */ private void checkFileExtension(String fileName) throws ServletException { if (fileName != null && !fileName.isEmpty() && fileName.contains(".")) { String[] allowedExt = {".jpg", ".jpeg", ".png", ".gif"}; for (String ext : allowedExt) { if (fileName.endsWith(ext)) { return; } } throw new ServletException("file must be an image"); } } // [END checkFileExtension] } // [END example] ================================================ FILE: bookshelf-standard/3-binary-data/src/main/java/com/example/getstarted/util/DatastoreSessionFilter.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.util; 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.EntityNotFoundException; import com.google.appengine.api.datastore.Key; import com.google.appengine.api.datastore.KeyFactory; import com.google.appengine.api.datastore.Query; import com.google.appengine.api.datastore.Query.FilterOperator; import com.google.appengine.api.datastore.Query.FilterPredicate; import com.google.appengine.api.datastore.Transaction; import com.google.common.collect.FluentIterable; import com.google.common.collect.MapDifference; import com.google.common.collect.Maps; import java.io.IOException; import java.math.BigInteger; import java.security.SecureRandom; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; // [START init] public class DatastoreSessionFilter implements Filter { private static DatastoreService datastore; private static final DateTimeFormatter DTF = DateTimeFormat.forPattern("yyyyMMddHHmmssSSS"); private static final String SESSION_KIND = "SessionVariable"; @Override public void init(FilterConfig config) throws ServletException { // initialize local copy of datastore session variables datastore = DatastoreServiceFactory.getDatastoreService(); // Delete all sessions unmodified for over two days DateTime dt = DateTime.now(DateTimeZone.UTC); Query query = new Query(SESSION_KIND).setFilter(new FilterPredicate( "lastModified", FilterOperator.LESS_THAN_OR_EQUAL, dt.minusDays(2).toString(DTF))); Iterator results = datastore.prepare(query).asIterator(); while (results.hasNext()) { Entity stateEntity = results.next(); datastore.delete(stateEntity.getKey()); } } // [END init] @Override public void doFilter(ServletRequest servletReq, ServletResponse servletResp, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) servletReq; HttpServletResponse resp = (HttpServletResponse) servletResp; // Check if the session cookie is there, if not there, make a session cookie using a unique // identifier. String sessionId = getCookieValue(req, "bookshelfSessionId"); if (sessionId.equals("")) { String sessionNum = new BigInteger(130, new SecureRandom()).toString(32); Cookie session = new Cookie("bookshelfSessionId", sessionNum); session.setPath("/"); resp.addCookie(session); } Map datastoreMap = loadSessionVariables(req); // session variables for request chain.doFilter(servletReq, servletResp); // Allow the servlet to process request and response HttpSession session = req.getSession(); // Create session map Map sessionMap = new HashMap<>(); Enumeration attrNames = session.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = attrNames.nextElement(); sessionMap.put(attrName, (String) session.getAttribute(attrName)); } // Create a diff between the new session variables and the existing session variables // to minimize datastore access MapDifference diff = Maps.difference(sessionMap, datastoreMap); Map setMap = diff.entriesOnlyOnLeft(); Map deleteMap = diff.entriesOnlyOnRight(); // Apply the diff setSessionVariables(sessionId, setMap); deleteSessionVariables( sessionId, FluentIterable.from(deleteMap.keySet()).toArray(String.class)); } @SuppressWarnings({"unused", "JdkObsolete"}) private String mapToString(Map map) { StringBuffer names = new StringBuffer(); for (String name : map.keySet()) { names.append(name + " "); } return names.toString(); } @Override public void destroy() { } protected String getCookieValue(HttpServletRequest req, String cookieName) { Cookie[] cookies = req.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { if (cookie.getName().equals(cookieName)) { return cookie.getValue(); } } } return ""; } // [START deleteSessionVariables] /** * Delete a value stored in the project's datastore. * * @param sessionId Request from which the session is extracted. */ protected void deleteSessionVariables(String sessionId, String... varNames) { if (sessionId.equals("")) { return; } Key key = KeyFactory.createKey(SESSION_KIND, sessionId); Transaction transaction = datastore.beginTransaction(); try { Entity stateEntity = datastore.get(transaction, key); for (String varName : varNames) { stateEntity.removeProperty(varName); } datastore.put(transaction, stateEntity); transaction.commit(); } catch (EntityNotFoundException e) { // Ignore - if there's no session, there's nothing to delete. } finally { if (transaction.isActive()) { transaction.rollback(); } } } // [END deleteSessionVariables] protected void deleteSessionWithValue(String varName, String varValue) { Transaction transaction = datastore.beginTransaction(); try { Query query = new Query(SESSION_KIND) .setFilter(new FilterPredicate(varName, FilterOperator.EQUAL, varValue)); Iterator results = datastore.prepare(transaction, query).asIterator(); while (results.hasNext()) { Entity stateEntity = results.next(); datastore.delete(transaction, stateEntity.getKey()); } transaction.commit(); } finally { if (transaction.isActive()) { transaction.rollback(); } } } // [START setSessionVariables] /** * Stores the state value in each key-value pair in the project's datastore. * * @param sessionId Request from which to extract session. * @param varName the name of the desired session variable * @param varValue the value of the desired session variable */ protected void setSessionVariables(String sessionId, Map setMap) { if (sessionId.equals("")) { return; } Key key = KeyFactory.createKey(SESSION_KIND, sessionId); Transaction transaction = datastore.beginTransaction(); DateTime dt = DateTime.now(DateTimeZone.UTC); dt.toString(DTF); try { Entity stateEntity; try { stateEntity = datastore.get(transaction, key); } catch (EntityNotFoundException e) { stateEntity = new Entity(key); } for (String varName : setMap.keySet()) { stateEntity.setProperty(varName, setMap.get(varName)); } stateEntity.setProperty("lastModified", dt.toString(DTF)); datastore.put(transaction, stateEntity); transaction.commit(); } finally { if (transaction.isActive()) { transaction.rollback(); } } } // [END setSessionVariables] // [START loadSessionVariables] /** * Take an HttpServletRequest, and copy all of the current session variables over to it * * @param req Request from which to extract session. * @return a map of strings containing all the session variables loaded or an empty map. */ protected Map loadSessionVariables(HttpServletRequest req) throws ServletException { Map datastoreMap = new HashMap<>(); String sessionId = getCookieValue(req, "bookshelfSessionId"); if (sessionId.equals("")) { return datastoreMap; } Key key = KeyFactory.createKey(SESSION_KIND, sessionId); Transaction transaction = datastore.beginTransaction(); try { Entity stateEntity = datastore.get(transaction, key); Map properties = stateEntity.getProperties(); for (Map.Entry property : properties.entrySet()) { req.getSession().setAttribute(property.getKey(), property.getValue()); datastoreMap.put(property.getKey(), (String) property.getValue()); } transaction.commit(); } catch (EntityNotFoundException e) { // Ignore - if there's no session, there's nothing to delete. } finally { if (transaction.isActive()) { transaction.rollback(); } } return datastoreMap; } // [END loadSessionVariables] } ================================================ FILE: bookshelf-standard/3-binary-data/src/main/webapp/WEB-INF/appengine-web.xml ================================================ true true true ================================================ FILE: bookshelf-standard/3-binary-data/src/main/webapp/WEB-INF/logging.properties ================================================ ================================================ FILE: bookshelf-standard/3-binary-data/src/main/webapp/WEB-INF/web.xml ================================================ DatastoreSessionFilter com.example.getstarted.util.DatastoreSessionFilter DatastoreSessionFilter / /books /books/mine /create /delete /login /logout /oauth2callback /read /update list com.example.getstarted.basicactions.ListBookServlet 1 list / /books create com.example.getstarted.basicactions.CreateBookServlet create /create update com.example.getstarted.basicactions.UpdateBookServlet update /update read com.example.getstarted.basicactions.ReadBookServlet read /read delete com.example.getstarted.basicactions.DeleteBookServlet delete /delete bookshelf.storageType ${bookshelf.storageType} bookshelf.bucket ${bookshelf.bucket} sql.urlRemoteGAE jdbc:google:mysql://${sql.instanceName}/${sql.dbName}?user=${sql.userName}&password=${sql.password} sql.urlRemote jdbc:mysql://google/${sql.dbName}?cloudSqlInstance=${sql.instanceName}&socketFactory=com.google.cloud.sql.mysql.SocketFactory&user=${sql.userName}&password=${sql.password} sql.urlLocal jdbc:mysql://localhost/${sql.dbName}?user=${sql.userName}&password=${sql.password}&useSSL=false ================================================ FILE: bookshelf-standard/3-binary-data/src/main/webapp/base.jsp ================================================ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> Bookshelf - Java on Google Cloud Platform ================================================ FILE: bookshelf-standard/3-binary-data/src/main/webapp/form.jsp ================================================ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>

book

================================================ FILE: bookshelf-standard/3-binary-data/src/main/webapp/list.jsp ================================================ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> ================================================ FILE: bookshelf-standard/3-binary-data/src/main/webapp/view.jsp ================================================ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>

Book

${fn:escapeXml(book.title)} ${fn:escapeXml(book.publishedDate)}

By ${fn:escapeXml(not empty book.author?book.author:'Unknown')}

${fn:escapeXml(book.description)}

Added by ${fn:escapeXml(not empty book.createdBy?book.createdBy:'Anonymous')}
================================================ FILE: bookshelf-standard/4-auth/README.md ================================================ # Bookshelf App for Java on App Engine Standard Tutorial ## Auth Contains the code for using Cloud Datastore and Cloud SQL v2. This is part of a [Bookshelf tutorial](https://cloud.google.com/java/getting-started/tutorial-app). Most users can get this running by updating the parameters in `pom.xml`. You'll also need to [create a bucket][create-bucket] in Google Cloud Storage, referred to below as `MY-BUCKET`. [create-bucket]: https://cloud.google.com/storage/docs/creating-buckets ### Running Locally mvn -Plocal clean appengine:devserver -Dbookshelf.bucket=MY-BUCKET **Note**: If you run into an error about `Invalid Credentials`, you may have to run: gcloud auth application-default login ### Deploying to App Engine Standard * In the `pom.xml`, update the [App Engine Maven Plugin](https://cloud.google.com/appengine/docs/standard/java/tools/maven-reference) with your Google Cloud Project Id: ``` com.google.cloud.tools appengine-maven-plugin 2.3.0 GCLOUD_CONFIG bookshelf ``` * Deploy your App mvn clean appengine appengine:deploy \ -Dbookshelf.bucket=MY-BUCKET Visit it at http://bookshelf..appspot.com ================================================ FILE: bookshelf-standard/4-auth/jenkins.sh ================================================ #!/usr/bin/env bash # Copyright 2017 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. # Fail on non-zero return and print command to stdout set -xe # Jenkins provides values for GOOGLE_PROJECT_ID and GOOGLE_VERSION_ID # Make sure it works on GAE7 # Deploy and run selenium tests mvn clean appengine:update verify \ -Pselenium \ -Dappengine.appId="${GOOGLE_PROJECT_ID}" \ -Dappengine.version="${GOOGLE_VERSION_ID}" \ -Dbookshelf.bucket="${GCS_BUCKET}" \ -Dappengine.additionalParams="--service_account_json_key_file=${GOOGLE_APPLICATION_CREDENTIALS}" # Make sure it deploys on GAE8 FILE_PATH=src/main/webapp/WEB-INF/appengine-web.xml sed -i'.bak' '/java8' "${FILE_PATH}" # Restore the backup after we're done trap 'mv "${FILE_PATH}"{.bak,}' EXIT # Deploy and run selenium tests mvn clean appengine:update verify \ -Pselenium \ -Dappengine.appId="${GOOGLE_PROJECT_ID}" \ -Dappengine.version="${GOOGLE_VERSION_ID}" \ -Dbookshelf.bucket="${GCS_BUCKET}" \ -Dappengine.additionalParams="--service_account_json_key_file=${GOOGLE_APPLICATION_CREDENTIALS}" ================================================ FILE: bookshelf-standard/4-auth/pom.xml ================================================ 4.0.0 war com.example.standard.gettingstarted bookshelf-standard-4 1.0-SNAPSHOT com.google.cloud.samples shared-configuration 1.2.0 ../ datastore bookshelf DATABASE-connectionName-HERE root MYSQL-ROOT-PASSWORD-HERE BUCKET-NAME-HERE UTF-8 1.8 1.8 true true true local com.google.cloud.sql mysql-socket-factory 1.6.1 mysql mysql-connector-java 8.0.30 com.google.api-client google-api-client-appengine 2.0.0 com.google.appengine appengine-api-1.0-sdk 2.0.15 javax.servlet javax.servlet-api 4.0.1 provided javax.servlet jsp-api 2.0 jstl jstl 1.2 taglibs standard 1.1.2 com.google.cloud google-cloud-storage 2.23.0 com.google.guava guava 33.1.0-jre compile joda-time joda-time 2.12.6 commons-fileupload commons-fileupload 1.5 ${project.build.directory}/${project.build.finalName}/WEB-INF/classes com.google.cloud.tools appengine-maven-plugin 2.4.4 GCLOUD_CONFIG bookshelf org.apache.maven.plugins maven-compiler-plugin 3.11.0 ================================================ FILE: bookshelf-standard/4-auth/src/main/java/com/example/getstarted/auth/ListByUserFilter.java ================================================ /* * Copyright 2016 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. */ package com.example.getstarted.auth; import com.google.appengine.api.users.UserService; import com.google.appengine.api.users.UserServiceFactory; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ListByUserFilter implements Filter { @Override public void init(FilterConfig config) throws ServletException { } @Override public void doFilter(ServletRequest servletReq, ServletResponse servletResp, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) servletReq; HttpServletResponse resp = (HttpServletResponse) servletResp; UserService userService = UserServiceFactory.getUserService(); if (userService.isUserLoggedIn()) { chain.doFilter(servletReq, servletResp); } else { req.getSession().setAttribute("loginDestination", "/books/mine"); resp.sendRedirect(userService.createLoginURL("/login")); } } @Override public void destroy() { } } ================================================ FILE: bookshelf-standard/4-auth/src/main/java/com/example/getstarted/auth/LoginServlet.java ================================================ /* * Copyright 2016 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. */ package com.example.getstarted.auth; 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 javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // [START example] @SuppressWarnings("serial") public class LoginServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { UserService userService = UserServiceFactory.getUserService(); if (userService.isUserLoggedIn()) { // Save the relevant profile info and store it in the session. User user = userService.getCurrentUser(); req.getSession().setAttribute("userEmail", user.getEmail()); req.getSession().setAttribute("userId", user.getUserId()); String destination = (String) req.getSession().getAttribute("loginDestination"); if (destination == null) { destination = "/books"; } resp.sendRedirect(destination); } else { resp.sendRedirect(userService.createLoginURL("/login")); } } } // [END example] ================================================ FILE: bookshelf-standard/4-auth/src/main/java/com/example/getstarted/auth/LogoutFilter.java ================================================ /* * Copyright 2016 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. */ package com.example.getstarted.auth; import com.google.appengine.api.users.UserService; import com.google.appengine.api.users.UserServiceFactory; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // [START init] public class LogoutFilter implements Filter { // [END init] @Override public void init(FilterConfig config) throws ServletException { } @Override public void doFilter(ServletRequest servletReq, ServletResponse servletResp, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) servletReq; HttpServletResponse resp = (HttpServletResponse) servletResp; String path = req.getRequestURI(); chain.doFilter(servletReq, servletResp); UserService userService = UserServiceFactory.getUserService(); if (userService.isUserLoggedIn()) { resp.sendRedirect(userService.createLogoutURL("/logout")); } else if (path.startsWith("/logout")) { resp.sendRedirect("/books"); } } @Override public void destroy() { } } ================================================ FILE: bookshelf-standard/4-auth/src/main/java/com/example/getstarted/auth/LogoutServlet.java ================================================ /* * Copyright 2016 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. */ package com.example.getstarted.auth; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; // [START example] @SuppressWarnings("serial") public class LogoutServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { // you can also make an authenticated request to logout, but here we choose to // simply delete the session variables for simplicity HttpSession session = req.getSession(false); if (session != null) { session.invalidate(); } // rebuild session req.getSession(); } } // [END example] ================================================ FILE: bookshelf-standard/4-auth/src/main/java/com/example/getstarted/basicactions/CreateBookServlet.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.basicactions; import com.example.getstarted.daos.BookDao; import com.example.getstarted.objects.Book; import com.example.getstarted.util.CloudStorageHelper; import com.google.common.base.Strings; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.commons.fileupload.FileItemIterator; import org.apache.commons.fileupload.FileItemStream; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.fileupload.util.Streams; // [START example] @SuppressWarnings("serial") public class CreateBookServlet extends HttpServlet { // [START setup] @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setAttribute("action", "Add"); // Part of the Header in form.jsp req.setAttribute("destination", "create"); // The urlPattern to invoke (this Servlet) req.setAttribute("page", "form"); // Tells base.jsp to include form.jsp req.getRequestDispatcher("/base.jsp").forward(req, resp); } // [END setup] // [START formpost] @Override public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { assert ServletFileUpload.isMultipartContent(req); CloudStorageHelper storageHelper = (CloudStorageHelper) getServletContext().getAttribute("storageHelper"); String newImageUrl = null; Map params = new HashMap(); try { FileItemIterator iter = new ServletFileUpload().getItemIterator(req); while (iter.hasNext()) { FileItemStream item = iter.next(); if (item.isFormField()) { params.put(item.getFieldName(), Streams.asString(item.openStream())); } else if (!Strings.isNullOrEmpty(item.getName())) { newImageUrl = storageHelper.uploadFile( item, getServletContext().getInitParameter("bookshelf.bucket")); } } } catch (FileUploadException e) { throw new IOException(e); } // [START createdBy] String createdByString = ""; String createdByIdString = ""; HttpSession session = req.getSession(); if (session.getAttribute("userEmail") != null) { // Does the user have a logged in session? createdByString = (String) session.getAttribute("userEmail"); createdByIdString = (String) session.getAttribute("userId"); } // [END createdBy] // [START bookBuilder] Book book = new Book.Builder() .author(params.get("author")) .description(params.get("description")) .publishedDate(params.get("publishedDate")) .title(params.get("title")) .imageUrl(null == newImageUrl ? params.get("imageUrl") : newImageUrl) // [START auth] .createdBy(createdByString) .createdById(createdByIdString) // [END auth] .build(); // [END bookBuilder] BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); try { Long id = dao.createBook(book); resp.sendRedirect("/read?id=" + id.toString()); // read what we just wrote } catch (Exception e) { throw new ServletException("Error creating book", e); } } // [END formpost] } // [END example] ================================================ FILE: bookshelf-standard/4-auth/src/main/java/com/example/getstarted/basicactions/DeleteBookServlet.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.basicactions; import com.example.getstarted.daos.BookDao; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // [START example] @SuppressWarnings("serial") public class DeleteBookServlet extends HttpServlet { @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Long id = Long.decode(req.getParameter("id")); BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); try { dao.deleteBook(id); resp.sendRedirect("/books"); } catch (Exception e) { throw new ServletException("Error deleting book", e); } } } // [END example] ================================================ FILE: bookshelf-standard/4-auth/src/main/java/com/example/getstarted/basicactions/ListBookServlet.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.basicactions; import com.example.getstarted.daos.BookDao; import com.example.getstarted.daos.CloudSqlDao; import com.example.getstarted.daos.DatastoreDao; import com.example.getstarted.objects.Book; import com.example.getstarted.objects.Result; import com.example.getstarted.util.CloudStorageHelper; import com.google.common.base.Strings; import java.io.IOException; import java.sql.SQLException; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // [START example] @SuppressWarnings("serial") public class ListBookServlet extends HttpServlet { @Override public void init() throws ServletException { BookDao dao = null; CloudStorageHelper storageHelper = new CloudStorageHelper(); // Creates the DAO based on the Context Parameters String storageType = this.getServletContext().getInitParameter("bookshelf.storageType"); switch (storageType) { case "datastore": dao = new DatastoreDao(); break; case "cloudsql": try { // Use this url when using dev appserver, but connecting to Cloud SQL String connect = this.getServletContext().getInitParameter("sql.urlRemote"); if (connect.contains("localhost")) { // Use this url when using a local mysql server connect = this.getServletContext().getInitParameter("sql.urlLocal"); } else if (System.getProperty("com.google.appengine.runtime.version") .startsWith("Google App Engine/")) { // Use this url when on App Engine, connecting to Cloud SQL. // Uses a special adapter because of the App Engine sandbox. connect = this.getServletContext().getInitParameter("sql.urlRemoteGAE"); } dao = new CloudSqlDao(connect); } catch (SQLException e) { throw new ServletException("SQL error", e); } break; default: throw new IllegalStateException( "Invalid storage type. Check if bookshelf.storageType property is set."); } this.getServletContext().setAttribute("dao", dao); this.getServletContext().setAttribute("storageHelper", storageHelper); this.getServletContext().setAttribute( "isCloudStorageConfigured", // Hide upload when Cloud Storage is not configured. !Strings.isNullOrEmpty(getServletContext().getInitParameter("bookshelf.bucket"))); } @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); String startCursor = req.getParameter("cursor"); List books = null; String endCursor = null; try { Result result = dao.listBooks(startCursor); books = result.result; endCursor = result.cursor; } catch (Exception e) { throw new ServletException("Error listing books", e); } req.getSession().getServletContext().setAttribute("books", books); StringBuilder bookNames = new StringBuilder(); for (Book book : books) { bookNames.append(book.getTitle() + " "); } req.setAttribute("cursor", endCursor); req.setAttribute("page", "list"); req.getRequestDispatcher("/base.jsp").forward(req, resp); } } // [END example] ================================================ FILE: bookshelf-standard/4-auth/src/main/java/com/example/getstarted/basicactions/ListByUserServlet.java ================================================ /* * Copyright 2016 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. */ package com.example.getstarted.basicactions; import com.example.getstarted.daos.BookDao; import com.example.getstarted.objects.Book; import com.example.getstarted.objects.Result; import java.io.IOException; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // [START example] @SuppressWarnings("serial") public class ListByUserServlet extends HttpServlet { @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); String startCursor = req.getParameter("cursor"); List books = null; String endCursor = null; try { Result result = dao.listBooksByUser((String) req.getSession().getAttribute("userId"), startCursor); books = result.result; endCursor = result.cursor; } catch (Exception e) { throw new ServletException("Error listing books", e); } req.getSession().getServletContext().setAttribute("books", books); StringBuilder bookNames = new StringBuilder(); for (Book book : books) { bookNames.append(book.getTitle() + " "); } req.getSession().setAttribute("cursor", endCursor); req.getSession().setAttribute("page", "list"); req.getRequestDispatcher("/base.jsp").forward(req, resp); } } // [END example] ================================================ FILE: bookshelf-standard/4-auth/src/main/java/com/example/getstarted/basicactions/ReadBookServlet.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.basicactions; import com.example.getstarted.daos.BookDao; import com.example.getstarted.objects.Book; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // [START example] @SuppressWarnings("serial") public class ReadBookServlet extends HttpServlet { @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { Long id = Long.decode(req.getParameter("id")); BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); try { Book book = dao.readBook(id); req.setAttribute("book", book); req.setAttribute("page", "view"); req.getRequestDispatcher("/base.jsp").forward(req, resp); } catch (Exception e) { throw new ServletException("Error reading book", e); } } } // [END example] ================================================ FILE: bookshelf-standard/4-auth/src/main/java/com/example/getstarted/basicactions/UpdateBookServlet.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.basicactions; import com.example.getstarted.daos.BookDao; import com.example.getstarted.objects.Book; import com.example.getstarted.util.CloudStorageHelper; import com.google.common.base.Strings; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileItemIterator; import org.apache.commons.fileupload.FileItemStream; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.fileupload.util.Streams; // [START example] @SuppressWarnings("serial") public class UpdateBookServlet extends HttpServlet { @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); try { Book book = dao.readBook(Long.decode(req.getParameter("id"))); req.setAttribute("book", book); req.setAttribute("action", "Edit"); req.setAttribute("destination", "update"); req.setAttribute("page", "form"); req.getRequestDispatcher("/base.jsp").forward(req, resp); } catch (Exception e) { throw new ServletException("Error loading book for editing", e); } } @Override public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); assert ServletFileUpload.isMultipartContent(req); CloudStorageHelper storageHelper = (CloudStorageHelper) getServletContext().getAttribute("storageHelper"); String newImageUrl = null; Map params = new HashMap(); try { FileItemIterator iter = new ServletFileUpload().getItemIterator(req); while (iter.hasNext()) { FileItemStream item = iter.next(); if (item.isFormField()) { params.put(item.getFieldName(), Streams.asString(item.openStream())); } else if (!Strings.isNullOrEmpty(item.getName())) { newImageUrl = storageHelper.uploadFile( item, getServletContext().getInitParameter("bookshelf.bucket")); } } } catch (FileUploadException e) { throw new IOException(e); } try { Book oldBook = dao.readBook(Long.decode(params.get("id"))); // [START bookBuilder] Book book = new Book.Builder() .author(params.get("author")) .description(params.get("description")) .publishedDate(params.get("publishedDate")) .title(params.get("title")) .imageUrl(null == newImageUrl ? params.get("imageUrl") : newImageUrl) .id(Long.decode(params.get("id"))) .createdBy(oldBook.getCreatedBy()) .createdById(oldBook.getCreatedById()) .build(); // [END bookBuilder] dao.updateBook(book); resp.sendRedirect("/read?id=" + params.get("id")); } catch (Exception e) { throw new ServletException("Error updating book", e); } } } // [END example] ================================================ FILE: bookshelf-standard/4-auth/src/main/java/com/example/getstarted/daos/BookDao.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.daos; import com.example.getstarted.objects.Book; import com.example.getstarted.objects.Result; import java.sql.SQLException; // [START example] public interface BookDao { Long createBook(Book book) throws SQLException; Book readBook(Long bookId) throws SQLException; void updateBook(Book book) throws SQLException; void deleteBook(Long bookId) throws SQLException; Result listBooks(String startCursor) throws SQLException; Result listBooksByUser(String userId, String startCursor) throws SQLException; } // [END example] ================================================ FILE: bookshelf-standard/4-auth/src/main/java/com/example/getstarted/daos/CloudSqlDao.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.daos; import com.example.getstarted.objects.Book; import com.example.getstarted.objects.Result; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; // [START example] public class CloudSqlDao implements BookDao { // [START constructor] private String sqlUrl; /** * A data access object for Bookshelf using a Google Cloud SQL server for storage. */ public CloudSqlDao(final String url) throws SQLException { sqlUrl = url; final String createTableSql = "CREATE TABLE IF NOT EXISTS books4 ( id INT NOT NULL " + "AUTO_INCREMENT, author VARCHAR(255), createdBy VARCHAR(255), createdById VARCHAR(255), " + "description VARCHAR(255), publishedDate VARCHAR(255), title VARCHAR(255), imageUrl " + "VARCHAR(255), PRIMARY KEY (id))"; try (Connection conn = DriverManager.getConnection(sqlUrl)) { conn.createStatement().executeUpdate(createTableSql); } } // [END constructor] // [START create] @Override public Long createBook(Book book) throws SQLException { final String createBookString = "INSERT INTO books4 " + "(author, createdBy, createdById, description, publishedDate, title, imageUrl) " + "VALUES (?, ?, ?, ?, ?, ?, ?)"; try (Connection conn = DriverManager.getConnection(sqlUrl); final PreparedStatement createBookStmt = conn.prepareStatement(createBookString, Statement.RETURN_GENERATED_KEYS)) { createBookStmt.setString(1, book.getAuthor()); createBookStmt.setString(2, book.getCreatedBy()); createBookStmt.setString(3, book.getCreatedById()); createBookStmt.setString(4, book.getDescription()); createBookStmt.setString(5, book.getPublishedDate()); createBookStmt.setString(6, book.getTitle()); createBookStmt.setString(7, book.getImageUrl()); createBookStmt.executeUpdate(); try (ResultSet keys = createBookStmt.getGeneratedKeys()) { keys.next(); return keys.getLong(1); } } } // [END create] // [START read] @Override public Book readBook(Long bookId) throws SQLException { final String readBookString = "SELECT * FROM books4 WHERE id = ?"; try (Connection conn = DriverManager.getConnection(sqlUrl); PreparedStatement readBookStmt = conn.prepareStatement(readBookString)) { readBookStmt.setLong(1, bookId); try (ResultSet keys = readBookStmt.executeQuery()) { keys.next(); return new Book.Builder() .author(keys.getString(Book.AUTHOR)) .createdBy(keys.getString(Book.CREATED_BY)) .createdById(keys.getString(Book.CREATED_BY_ID)) .description(keys.getString(Book.DESCRIPTION)) .id(keys.getLong(Book.ID)) .publishedDate(keys.getString(Book.PUBLISHED_DATE)) .title(keys.getString(Book.TITLE)) .imageUrl(keys.getString(Book.IMAGE_URL)) .build(); } } } // [END read] // [START update] @Override public void updateBook(Book book) throws SQLException { final String updateBookString = "UPDATE books4 SET author = ?, createdBy = ?, createdById = ?, " + "description = ?, publishedDate = ?, title = ?, imageUrl = ? WHERE id = ?"; try (Connection conn = DriverManager.getConnection(sqlUrl); PreparedStatement updateBookStmt = conn.prepareStatement(updateBookString)) { updateBookStmt.setString(1, book.getAuthor()); updateBookStmt.setString(2, book.getCreatedBy()); updateBookStmt.setString(3, book.getCreatedById()); updateBookStmt.setString(4, book.getDescription()); updateBookStmt.setString(5, book.getPublishedDate()); updateBookStmt.setString(6, book.getTitle()); updateBookStmt.setString(7, book.getImageUrl()); updateBookStmt.setLong(8, book.getId()); updateBookStmt.executeUpdate(); } } // [END update] // [START delete] @Override public void deleteBook(Long bookId) throws SQLException { final String deleteBookString = "DELETE FROM books4 WHERE id = ?"; try (Connection conn = DriverManager.getConnection(sqlUrl); PreparedStatement deleteBookStmt = conn.prepareStatement(deleteBookString)) { deleteBookStmt.setLong(1, bookId); deleteBookStmt.executeUpdate(); } } // [END delete] // [START listbooks] @Override public Result listBooks(String cursor) throws SQLException { int offset = 0; if (cursor != null && !cursor.equals("")) { offset = Integer.parseInt(cursor); } final String listBooksString = "SELECT SQL_CALC_FOUND_ROWS author, createdBy, createdById, " + "description, id, publishedDate, title, imageUrl FROM books4 ORDER BY title ASC " + "LIMIT 10 OFFSET ?"; try (Connection conn = DriverManager.getConnection(sqlUrl); PreparedStatement listBooksStmt = conn.prepareStatement(listBooksString)) { listBooksStmt.setInt(1, offset); List resultBooks = new ArrayList<>(); try (ResultSet rs = listBooksStmt.executeQuery()) { while (rs.next()) { Book book = new Book.Builder() .author(rs.getString(Book.AUTHOR)) .createdBy(rs.getString(Book.CREATED_BY)) .createdById(rs.getString(Book.CREATED_BY_ID)) .description(rs.getString(Book.DESCRIPTION)) .id(rs.getLong(Book.ID)) .publishedDate(rs.getString(Book.PUBLISHED_DATE)) .title(rs.getString(Book.TITLE)) .imageUrl(rs.getString(Book.IMAGE_URL)) .build(); resultBooks.add(book); } } try (ResultSet rs = conn.createStatement().executeQuery("SELECT FOUND_ROWS()")) { int totalNumRows = 0; if (rs.next()) { totalNumRows = rs.getInt(1); } if (totalNumRows > offset + 10) { return new Result<>(resultBooks, Integer.toString(offset + 10)); } else { return new Result<>(resultBooks); } } } } // [END listbooks] // [START listbyuser] @Override public Result listBooksByUser(String userId, String startCursor) throws SQLException { int offset = 0; if (startCursor != null && !startCursor.equals("")) { offset = Integer.parseInt(startCursor); } final String listBooksString = "SELECT SQL_CALC_FOUND_ROWS author, createdBy, createdById, " + "description, id, publishedDate, title, imageUrl FROM books WHERE createdById = ? " + "ORDER BY title ASC LIMIT 10 OFFSET ?"; try (Connection conn = DriverManager.getConnection(sqlUrl); PreparedStatement listBooksStmt = conn.prepareStatement(listBooksString)) { listBooksStmt.setString(1, userId); listBooksStmt.setInt(2, offset); List resultBooks = new ArrayList<>(); try (ResultSet rs = listBooksStmt.executeQuery()) { while (rs.next()) { Book book = new Book.Builder() .author(rs.getString(Book.AUTHOR)) .createdBy(rs.getString(Book.CREATED_BY)) .createdById(rs.getString(Book.CREATED_BY_ID)) .description(rs.getString(Book.DESCRIPTION)) .id(rs.getLong(Book.ID)) .publishedDate(rs.getString(Book.PUBLISHED_DATE)) .title(rs.getString(Book.TITLE)) .imageUrl(rs.getString(Book.IMAGE_URL)) .build(); resultBooks.add(book); } } try (ResultSet rs = conn.createStatement().executeQuery("SELECT FOUND_ROWS()")) { int totalNumRows = 0; if (rs.next()) { totalNumRows = rs.getInt(1); } if (totalNumRows > offset + 10) { return new Result<>(resultBooks, Integer.toString(offset + 10)); } else { return new Result<>(resultBooks); } } } } // [END listbyuser] } // [END example] ================================================ FILE: bookshelf-standard/4-auth/src/main/java/com/example/getstarted/daos/DatastoreDao.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.daos; import com.example.getstarted.objects.Book; import com.example.getstarted.objects.Result; import com.google.appengine.api.datastore.Cursor; 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.EntityNotFoundException; import com.google.appengine.api.datastore.FetchOptions; import com.google.appengine.api.datastore.Key; import com.google.appengine.api.datastore.KeyFactory; import com.google.appengine.api.datastore.PreparedQuery; import com.google.appengine.api.datastore.Query; import com.google.appengine.api.datastore.Query.SortDirection; import com.google.appengine.api.datastore.QueryResultIterator; import java.util.ArrayList; import java.util.Iterator; import java.util.List; // [START example] public class DatastoreDao implements BookDao { // [START constructor] private DatastoreService datastore; private static final String BOOK_KIND = "Book4"; public DatastoreDao() { datastore = DatastoreServiceFactory.getDatastoreService(); // Authorized Datastore service } // [END constructor] // [START entityToBook] public Book entityToBook(Entity entity) { return new Book.Builder() // Convert to Book form .author((String) entity.getProperty(Book.AUTHOR)) .description((String) entity.getProperty(Book.DESCRIPTION)) .id(entity.getKey().getId()) .publishedDate((String) entity.getProperty(Book.PUBLISHED_DATE)) .imageUrl((String) entity.getProperty(Book.IMAGE_URL)) .createdBy((String) entity.getProperty(Book.CREATED_BY)) .createdById((String) entity.getProperty(Book.CREATED_BY_ID)) .title((String) entity.getProperty(Book.TITLE)) .build(); } // [END entityToBook] // [START create] @Override public Long createBook(Book book) { Entity incBookEntity = new Entity(BOOK_KIND); // Key will be assigned once written incBookEntity.setProperty(Book.AUTHOR, book.getAuthor()); incBookEntity.setProperty(Book.DESCRIPTION, book.getDescription()); incBookEntity.setProperty(Book.PUBLISHED_DATE, book.getPublishedDate()); incBookEntity.setProperty(Book.TITLE, book.getTitle()); incBookEntity.setProperty(Book.IMAGE_URL, book.getImageUrl()); incBookEntity.setProperty(Book.CREATED_BY, book.getCreatedBy()); incBookEntity.setProperty(Book.CREATED_BY_ID, book.getCreatedById()); Key bookKey = datastore.put(incBookEntity); // Save the Entity return bookKey.getId(); // The ID of the Key } // [END create] // [START read] @Override public Book readBook(Long bookId) { try { Entity bookEntity = datastore.get(KeyFactory.createKey(BOOK_KIND, bookId)); return entityToBook(bookEntity); } catch (EntityNotFoundException e) { return null; } } // [END read] // [START update] @Override public void updateBook(Book book) { Key key = KeyFactory.createKey(BOOK_KIND, book.getId()); // From a book, create a Key Entity entity = new Entity(key); // Convert Book to an Entity entity.setProperty(Book.AUTHOR, book.getAuthor()); entity.setProperty(Book.DESCRIPTION, book.getDescription()); entity.setProperty(Book.PUBLISHED_DATE, book.getPublishedDate()); entity.setProperty(Book.TITLE, book.getTitle()); entity.setProperty(Book.IMAGE_URL, book.getImageUrl()); entity.setProperty(Book.CREATED_BY, book.getCreatedBy()); entity.setProperty(Book.CREATED_BY_ID, book.getCreatedById()); datastore.put(entity); // Update the Entity } // [END update] // [START delete] @Override public void deleteBook(Long bookId) { Key key = KeyFactory.createKey(BOOK_KIND, bookId); // Create the Key datastore.delete(key); // Delete the Entity } // [END delete] // [START entitiesToBooks] public List entitiesToBooks(Iterator results) { List resultBooks = new ArrayList<>(); while (results.hasNext()) { // We still have data resultBooks.add(entityToBook(results.next())); // Add the Book to the List } return resultBooks; } // [END entitiesToBooks] // [START listbooks] @Override public Result listBooks(String startCursorString) { FetchOptions fetchOptions = FetchOptions.Builder.withLimit(10); // Only show 10 at a time if (startCursorString != null && !startCursorString.equals("")) { fetchOptions.startCursor(Cursor.fromWebSafeString(startCursorString)); // Where we left off } Query query = new Query(BOOK_KIND) // We only care about Books .addSort(Book.TITLE, SortDirection.ASCENDING); // Use default Index "title" PreparedQuery preparedQuery = datastore.prepare(query); QueryResultIterator results = preparedQuery.asQueryResultIterator(fetchOptions); List resultBooks = entitiesToBooks(results); // Retrieve and convert Entities Cursor cursor = results.getCursor(); // Where to start next time if (cursor != null && resultBooks.size() == 10) { // Are we paging? Save Cursor String cursorString = cursor.toWebSafeString(); // Cursors are WebSafe return new Result<>(resultBooks, cursorString); } else { return new Result<>(resultBooks); } } // [END listbooks] // [START listbyuser] @Override public Result listBooksByUser(String userId, String startCursorString) { FetchOptions fetchOptions = FetchOptions.Builder.withLimit(10); // Only show 10 at a time if (startCursorString != null && !startCursorString.equals("")) { fetchOptions.startCursor(Cursor.fromWebSafeString(startCursorString)); // Where we left off } Query query = new Query(BOOK_KIND) // We only care about Books // Only for this user .setFilter(new Query.FilterPredicate( Book.CREATED_BY_ID, Query.FilterOperator.EQUAL, userId)) // a custom datastore index is required since you are filtering by one property // but ordering by another .addSort(Book.TITLE, SortDirection.ASCENDING); PreparedQuery preparedQuery = datastore.prepare(query); QueryResultIterator results = preparedQuery.asQueryResultIterator(fetchOptions); List resultBooks = entitiesToBooks(results); // Retrieve and convert Entities Cursor cursor = results.getCursor(); // Where to start next time if (cursor != null && resultBooks.size() == 10) { // Are we paging? Save Cursor String cursorString = cursor.toWebSafeString(); // Cursors are WebSafe return new Result<>(resultBooks, cursorString); } else { return new Result<>(resultBooks); } } // [END listbyuser] } // [END example] ================================================ FILE: bookshelf-standard/4-auth/src/main/java/com/example/getstarted/objects/Book.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.objects; // [START example] public class Book { // [START book] private String title; private String author; private String createdBy; private String createdById; private String publishedDate; private String description; private Long id; private String imageUrl; // [END book] // [START keys] public static final String AUTHOR = "author"; public static final String CREATED_BY = "createdBy"; public static final String CREATED_BY_ID = "createdById"; public static final String DESCRIPTION = "description"; public static final String ID = "id"; public static final String PUBLISHED_DATE = "publishedDate"; public static final String TITLE = "title"; public static final String IMAGE_URL = "imageUrl"; // [END keys] // [START constructor] // We use a Builder pattern here to simplify and standardize construction of Book objects. private Book(Builder builder) { this.title = builder.title; this.author = builder.author; this.createdBy = builder.createdBy; this.createdById = builder.createdById; this.publishedDate = builder.publishedDate; this.description = builder.description; this.id = builder.id; this.imageUrl = builder.imageUrl; } // [END constructor] // [START builder] public static class Builder { private String title; private String author; private String createdBy; private String createdById; private String publishedDate; private String description; private Long id; private String imageUrl; public Builder title(String title) { this.title = title; return this; } public Builder author(String author) { this.author = author; return this; } public Builder createdBy(String createdBy) { this.createdBy = createdBy; return this; } public Builder createdById(String createdById) { this.createdById = createdById; return this; } public Builder publishedDate(String publishedDate) { this.publishedDate = publishedDate; return this; } public Builder description(String description) { this.description = description; return this; } public Builder id(Long id) { this.id = id; return this; } public Builder imageUrl(String imageUrl) { this.imageUrl = imageUrl; return this; } public Book build() { return new Book(this); } } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public String getCreatedBy() { return createdBy; } public void setCreatedBy(String createdBy) { this.createdBy = createdBy; } public String getCreatedById() { return createdById; } public void setCreatedById(String createdById) { this.createdById = createdById; } public String getPublishedDate() { return publishedDate; } public void setPublishedDate(String publishedDate) { this.publishedDate = publishedDate; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getImageUrl() { return imageUrl; } public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; } // [END builder] @Override public String toString() { return "Title: " + title + ", Author: " + author + ", Published date: " + publishedDate + ", Added by: " + createdBy; } } // [END example] ================================================ FILE: bookshelf-standard/4-auth/src/main/java/com/example/getstarted/objects/Result.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.objects; import java.util.List; // [START example] public class Result { public String cursor; public List result; public Result(List result, String cursor) { this.result = result; this.cursor = cursor; } public Result(List result) { this.result = result; this.cursor = null; } } // [END example] ================================================ FILE: bookshelf-standard/4-auth/src/main/java/com/example/getstarted/util/CloudStorageHelper.java ================================================ /* * Copyright 2016 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. */ package com.example.getstarted.util; import com.google.cloud.storage.Acl; import com.google.cloud.storage.Acl.Role; import com.google.cloud.storage.Acl.User; import com.google.cloud.storage.BlobInfo; import com.google.cloud.storage.Storage; import com.google.cloud.storage.StorageOptions; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import javax.servlet.ServletException; import org.apache.commons.fileupload.FileItemStream; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; // [START example] public class CloudStorageHelper { private static Storage storage = null; // [START init] static { storage = StorageOptions.getDefaultInstance().getService(); } // [END init] // [START uploadFile] /** * Uploads a file to Google Cloud Storage to the bucket specified in the BUCKET_NAME * environment variable, appending a timestamp to end of the uploaded filename. */ public String uploadFile(FileItemStream fileStream, final String bucketName) throws IOException, ServletException { checkFileExtension(fileStream.getName()); DateTimeFormatter dtf = DateTimeFormat.forPattern("-YYYY-MM-dd-HHmmssSSS"); DateTime dt = DateTime.now(DateTimeZone.UTC); String dtString = dt.toString(dtf); final String fileName = fileStream.getName() + dtString; // the inputstream is closed by default, so we don't need to close it here @SuppressWarnings("deprecation") BlobInfo blobInfo = storage.create( BlobInfo .newBuilder(bucketName, fileName) // Modify access list to allow all users with link to read file .setAcl(new ArrayList<>(Arrays.asList(Acl.of(User.ofAllUsers(), Role.READER)))) .build(), fileStream.openStream()); // return the public download link return blobInfo.getMediaLink(); } // [END uploadFile] // [START checkFileExtension] /** * Checks that the file extension is supported. */ private void checkFileExtension(String fileName) throws ServletException { if (fileName != null && !fileName.isEmpty() && fileName.contains(".")) { String[] allowedExt = { ".jpg", ".jpeg", ".png", ".gif" }; for (String ext : allowedExt) { if (fileName.endsWith(ext)) { return; } } throw new ServletException("file must be an image"); } } // [END checkFileExtension] } // [END example] ================================================ FILE: bookshelf-standard/4-auth/src/main/java/com/example/getstarted/util/DatastoreSessionFilter.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.util; 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.EntityNotFoundException; import com.google.appengine.api.datastore.Key; import com.google.appengine.api.datastore.KeyFactory; import com.google.appengine.api.datastore.Query; import com.google.appengine.api.datastore.Query.FilterOperator; import com.google.appengine.api.datastore.Query.FilterPredicate; import com.google.appengine.api.datastore.Transaction; import com.google.common.collect.FluentIterable; import com.google.common.collect.MapDifference; import com.google.common.collect.Maps; import java.io.IOException; import java.math.BigInteger; import java.security.SecureRandom; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; // [START init] public class DatastoreSessionFilter implements Filter { private static DatastoreService datastore; private static final DateTimeFormatter DTF = DateTimeFormat.forPattern("yyyyMMddHHmmssSSS"); private static final String SESSION_KIND = "SessionVariable"; @Override public void init(FilterConfig config) throws ServletException { // initialize local copy of datastore session variables datastore = DatastoreServiceFactory.getDatastoreService(); // Delete all sessions unmodified for over two days DateTime dt = DateTime.now(DateTimeZone.UTC); Query query = new Query(SESSION_KIND).setFilter(new FilterPredicate( "lastModified", FilterOperator.LESS_THAN_OR_EQUAL, dt.minusDays(2).toString(DTF))); Iterator results = datastore.prepare(query).asIterator(); while (results.hasNext()) { Entity stateEntity = results.next(); datastore.delete(stateEntity.getKey()); } } // [END init] @Override public void doFilter(ServletRequest servletReq, ServletResponse servletResp, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) servletReq; HttpServletResponse resp = (HttpServletResponse) servletResp; // Check if the session cookie is there, if not there, make a session cookie using a unique // identifier. String sessionId = getCookieValue(req, "bookshelfSessionId"); if (sessionId.equals("")) { String sessionNum = new BigInteger(130, new SecureRandom()).toString(32); Cookie session = new Cookie("bookshelfSessionId", sessionNum); session.setPath("/"); resp.addCookie(session); } Map datastoreMap = loadSessionVariables(req); // session variables for request chain.doFilter(servletReq, servletResp); // Allow the servlet to process request and response HttpSession session = req.getSession(); // Create session map Map sessionMap = new HashMap<>(); Enumeration attrNames = session.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = attrNames.nextElement(); sessionMap.put(attrName, (String) session.getAttribute(attrName)); } // Create a diff between the new session variables and the existing session variables // to minimize datastore access MapDifference diff = Maps.difference(sessionMap, datastoreMap); Map setMap = diff.entriesOnlyOnLeft(); Map deleteMap = diff.entriesOnlyOnRight(); // Apply the diff setSessionVariables(sessionId, setMap); deleteSessionVariables( sessionId, FluentIterable.from(deleteMap.keySet()).toArray(String.class)); } @SuppressWarnings({"unused", "JdkObsolete"}) private String mapToString(Map map) { StringBuffer names = new StringBuffer(); for (String name : map.keySet()) { names.append(name + " "); } return names.toString(); } @Override public void destroy() { } protected String getCookieValue(HttpServletRequest req, String cookieName) { Cookie[] cookies = req.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { if (cookie.getName().equals(cookieName)) { return cookie.getValue(); } } } return ""; } // [START deleteSessionVariables] /** * Delete a value stored in the project's datastore. * * @param sessionId Request from which the session is extracted. */ protected void deleteSessionVariables(String sessionId, String... varNames) { if (sessionId.equals("")) { return; } Key key = KeyFactory.createKey(SESSION_KIND, sessionId); Transaction transaction = datastore.beginTransaction(); try { Entity stateEntity = datastore.get(transaction, key); for (String varName : varNames) { stateEntity.removeProperty(varName); } datastore.put(transaction, stateEntity); transaction.commit(); } catch (EntityNotFoundException e) { // Ignore - if there's no session, there's nothing to delete. } finally { if (transaction.isActive()) { transaction.rollback(); } } } // [END deleteSessionVariables] protected void deleteSessionWithValue(String varName, String varValue) { Transaction transaction = datastore.beginTransaction(); try { Query query = new Query(SESSION_KIND) .setFilter(new FilterPredicate(varName, FilterOperator.EQUAL, varValue)); Iterator results = datastore.prepare(transaction, query).asIterator(); while (results.hasNext()) { Entity stateEntity = results.next(); datastore.delete(transaction, stateEntity.getKey()); } transaction.commit(); } finally { if (transaction.isActive()) { transaction.rollback(); } } } // [START setSessionVariables] /** * Stores the state value in each key-value pair in the project's datastore. * * @param sessionId Request from which to extract session. * @param varName the name of the desired session variable * @param varValue the value of the desired session variable */ protected void setSessionVariables(String sessionId, Map setMap) { if (sessionId.equals("")) { return; } Key key = KeyFactory.createKey(SESSION_KIND, sessionId); Transaction transaction = datastore.beginTransaction(); DateTime dt = DateTime.now(DateTimeZone.UTC); dt.toString(DTF); try { Entity stateEntity; try { stateEntity = datastore.get(transaction, key); } catch (EntityNotFoundException e) { stateEntity = new Entity(key); } for (String varName : setMap.keySet()) { stateEntity.setProperty(varName, setMap.get(varName)); } stateEntity.setProperty("lastModified", dt.toString(DTF)); datastore.put(transaction, stateEntity); transaction.commit(); } finally { if (transaction.isActive()) { transaction.rollback(); } } } // [END setSessionVariables] // [START loadSessionVariables] /** * Take an HttpServletRequest, and copy all of the current session variables over to it * * @param req Request from which to extract session. * @return a map of strings containing all the session variables loaded or an empty map. */ protected Map loadSessionVariables(HttpServletRequest req) throws ServletException { Map datastoreMap = new HashMap<>(); String sessionId = getCookieValue(req, "bookshelfSessionId"); if (sessionId.equals("")) { return datastoreMap; } Key key = KeyFactory.createKey(SESSION_KIND, sessionId); Transaction transaction = datastore.beginTransaction(); try { Entity stateEntity = datastore.get(transaction, key); Map properties = stateEntity.getProperties(); for (Map.Entry property : properties.entrySet()) { req.getSession().setAttribute(property.getKey(), property.getValue()); datastoreMap.put(property.getKey(), (String) property.getValue()); } transaction.commit(); } catch (EntityNotFoundException e) { // Ignore - if there's no session, there's nothing to delete. } finally { if (transaction.isActive()) { transaction.rollback(); } } return datastoreMap; } // [END loadSessionVariables] } ================================================ FILE: bookshelf-standard/4-auth/src/main/webapp/WEB-INF/appengine-web.xml ================================================ true true true ================================================ FILE: bookshelf-standard/4-auth/src/main/webapp/WEB-INF/datastore-indexes.xml ================================================ ================================================ FILE: bookshelf-standard/4-auth/src/main/webapp/WEB-INF/logging.properties ================================================ ================================================ FILE: bookshelf-standard/4-auth/src/main/webapp/WEB-INF/web.xml ================================================ DatastoreSessionFilter com.example.getstarted.util.DatastoreSessionFilter DatastoreSessionFilter / /books /books/mine /create /delete /login /logout /read /update LogoutFilter com.example.getstarted.auth.LogoutFilter LogoutFilter /logout ListByUserFilter com.example.getstarted.auth.ListByUserFilter ListByUserFilter /books/mine list com.example.getstarted.basicactions.ListBookServlet 1 list / /books create com.example.getstarted.basicactions.CreateBookServlet create /create update com.example.getstarted.basicactions.UpdateBookServlet update /update read com.example.getstarted.basicactions.ReadBookServlet read /read delete com.example.getstarted.basicactions.DeleteBookServlet delete /delete logout com.example.getstarted.auth.LogoutServlet logout /logout login com.example.getstarted.auth.LoginServlet login /login listbyuser com.example.getstarted.basicactions.ListByUserServlet listbyuser /books/mine bookshelf.storageType ${bookshelf.storageType} bookshelf.bucket ${bookshelf.bucket} sql.urlRemoteGAE jdbc:google:mysql://${sql.instanceName}/${sql.dbName}?user=${sql.userName}&password=${sql.password} sql.urlRemote jdbc:mysql://google/${sql.dbName}?cloudSqlInstance=${sql.instanceName}&socketFactory=com.google.cloud.sql.mysql.SocketFactory&user=${sql.userName}&password=${sql.password} sql.urlLocal jdbc:mysql://localhost/${sql.dbName}?user=${sql.userName}&password=${sql.password}&useSSL=false ================================================ FILE: bookshelf-standard/4-auth/src/main/webapp/base.jsp ================================================ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> Bookshelf - Java on Google Cloud Platform ================================================ FILE: bookshelf-standard/4-auth/src/main/webapp/form.jsp ================================================ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>

book

================================================ FILE: bookshelf-standard/4-auth/src/main/webapp/list.jsp ================================================ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> ================================================ FILE: bookshelf-standard/4-auth/src/main/webapp/view.jsp ================================================ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>

Book

${fn:escapeXml(book.title)} ${fn:escapeXml(book.publishedDate)}

By ${fn:escapeXml(not empty book.author?book.author:'Unknown')}

${fn:escapeXml(book.description)}

Added by ${fn:escapeXml(not empty book.createdBy?book.createdBy:'Anonymous')}
================================================ FILE: bookshelf-standard/5-logging/README.md ================================================ # Bookshelf App for Java on App Engine Standard Tutorial ## Logging Contains the code for using Cloud Datastore and Cloud SQL v2. This is part of a [Bookshelf tutorial](https://cloud.google.com/java/getting-started/tutorial-app). Most users can get this running by updating the parameters in `pom.xml`. You'll also need to [create a bucket][create-bucket] in Google Cloud Storage, referred to below as `MY-BUCKET`. [create-bucket]: https://cloud.google.com/storage/docs/creating-buckets ### Running Locally mvn -Plocal clean appengine:devserver -Dbookshelf.bucket=MY-BUCKET **Note**: If you run into an error about `Invalid Credentials`, you may have to run: gcloud auth application-default login ### Deploying to App Engine Standard * In the `pom.xml`, update the [App Engine Maven Plugin](https://cloud.google.com/appengine/docs/standard/java/tools/maven-reference) with your Google Cloud Project Id: ``` com.google.cloud.tools appengine-maven-plugin 2.3.0 GCLOUD_CONFIG bookshelf ``` * Deploy your App mvn package appengine:deploy \ -Dbookshelf.bucket=MY-BUCKET Visit it at http://bookshelf..appspot.com ================================================ FILE: bookshelf-standard/5-logging/jenkins.sh ================================================ #!/usr/bin/env bash # Copyright 2017 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. # Fail on non-zero return and print command to stdout set -xe # Jenkins provides values for GOOGLE_PROJECT_ID and GOOGLE_VERSION_ID # Make sure it works on GAE7 # Deploy and run selenium tests mvn clean appengine:update verify \ -Pselenium \ -Dappengine.appId="${GOOGLE_PROJECT_ID}" \ -Dappengine.version="${GOOGLE_VERSION_ID}" \ -Dbookshelf.bucket="${GCS_BUCKET}" \ -Dappengine.additionalParams="--service_account_json_key_file=${GOOGLE_APPLICATION_CREDENTIALS}" # Make sure it deploys on GAE8 FILE_PATH=src/main/webapp/WEB-INF/appengine-web.xml sed -i'.bak' '/java8' "${FILE_PATH}" # Restore the backup after we're done trap 'mv "${FILE_PATH}"{.bak,}' EXIT # Deploy and run selenium tests mvn clean appengine:update verify \ -Pselenium \ -Dappengine.appId="${GOOGLE_PROJECT_ID}" \ -Dappengine.version="${GOOGLE_VERSION_ID}" \ -Dbookshelf.bucket="${GCS_BUCKET}" \ -Dappengine.additionalParams="--service_account_json_key_file=${GOOGLE_APPLICATION_CREDENTIALS}" ================================================ FILE: bookshelf-standard/5-logging/pom.xml ================================================ 4.0.0 war com.example.standard.gettingstarted bookshelf-standard-5 1.0-SNAPSHOT com.google.cloud.samples shared-configuration 1.2.0 ../ datastore bookshelf DATABASE-connectionName-HERE root MYSQL-ROOT-PASSWORD-HERE BUCKET-NAME-HERE UTF-8 1.8 1.8 true true true local com.google.cloud.sql mysql-socket-factory 1.6.1 mysql mysql-connector-java 8.0.30 com.google.api-client google-api-client-appengine 2.0.0 com.google.appengine appengine-api-1.0-sdk 2.0.15 javax.servlet javax.servlet-api 4.0.1 provided javax.servlet jsp-api 2.0 jstl jstl 1.2 taglibs standard 1.1.2 com.google.cloud google-cloud-storage 2.23.0 com.google.guava guava 33.1.0-jre compile joda-time joda-time 2.12.6 commons-fileupload commons-fileupload 1.5 ${project.build.directory}/${project.build.finalName}/WEB-INF/classes com.google.cloud.tools appengine-maven-plugin 2.4.4 GCLOUD_CONFIG bookshelf org.apache.maven.plugins maven-compiler-plugin 3.11.0 ================================================ FILE: bookshelf-standard/5-logging/src/main/java/com/example/getstarted/auth/ListByUserFilter.java ================================================ /* * Copyright 2016 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. */ package com.example.getstarted.auth; import com.google.appengine.api.users.UserService; import com.google.appengine.api.users.UserServiceFactory; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ListByUserFilter implements Filter { // [START createLogger] private static final Logger logger = Logger.getLogger(ListByUserFilter.class.getName()); // [END createLogger] @Override public void init(FilterConfig config) throws ServletException { } @Override public void doFilter(ServletRequest servletReq, ServletResponse servletResp, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) servletReq; HttpServletResponse resp = (HttpServletResponse) servletResp; // [START logStuff] String instanceId = System.getenv().containsKey("GAE_MODULE_INSTANCE") ? System.getenv("GAE_MODULE_INSTANCE") : "-1"; logger.log( Level.INFO, "ListByUserFilter processing new request for path: " + req.getRequestURI() + " and instance: " + instanceId); // [END logStuff] UserService userService = UserServiceFactory.getUserService(); if (userService.isUserLoggedIn()) { chain.doFilter(servletReq, servletResp); } else { logger.log(Level.INFO, "Not logged in, setting loginDestination to /books/mine"); req.getSession().setAttribute("loginDestination", "/books/mine"); resp.sendRedirect(userService.createLoginURL("/login")); } } @Override public void destroy() { logger.log(Level.INFO, "ListByUserFilter is de-initializing"); } } ================================================ FILE: bookshelf-standard/5-logging/src/main/java/com/example/getstarted/auth/LoginServlet.java ================================================ /* * Copyright 2016 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. */ package com.example.getstarted.auth; 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.logging.Level; import java.util.logging.Logger; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // [START example] @SuppressWarnings("serial") public class LoginServlet extends HttpServlet { private Logger logger = Logger.getLogger(this.getClass().getName()); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { UserService userService = UserServiceFactory.getUserService(); if (userService.isUserLoggedIn()) { // Save the relevant profile info and store it in the session. User user = userService.getCurrentUser(); req.getSession().setAttribute("userEmail", user.getEmail()); req.getSession().setAttribute("userId", user.getUserId()); String destination = (String) req.getSession().getAttribute("loginDestination"); if (destination == null) { destination = "/books"; } logger.log(Level.INFO, "logging destination " + destination); resp.sendRedirect(destination); } else { resp.sendRedirect(userService.createLoginURL("/login")); logger.log(Level.INFO, "logging destination /login"); } } } // [END example] ================================================ FILE: bookshelf-standard/5-logging/src/main/java/com/example/getstarted/auth/LogoutFilter.java ================================================ /* * Copyright 2016 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. */ package com.example.getstarted.auth; import com.google.appengine.api.users.UserService; import com.google.appengine.api.users.UserServiceFactory; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // [START init] public class LogoutFilter implements Filter { private static final Logger logger = Logger.getLogger(ListByUserFilter.class.getName()); // [END init] @Override public void init(FilterConfig config) throws ServletException { } @Override public void doFilter(ServletRequest servletReq, ServletResponse servletResp, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) servletReq; HttpServletResponse resp = (HttpServletResponse) servletResp; String path = req.getRequestURI(); chain.doFilter(servletReq, servletResp); UserService userService = UserServiceFactory.getUserService(); if (userService.isUserLoggedIn()) { resp.sendRedirect(userService.createLogoutURL("/logout")); } else if (path.startsWith("/logout")) { resp.sendRedirect("/books"); } } @Override public void destroy() { logger.log(Level.INFO, "destroy called in LogoutFilter"); } } ================================================ FILE: bookshelf-standard/5-logging/src/main/java/com/example/getstarted/auth/LogoutServlet.java ================================================ /* * Copyright 2016 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. */ package com.example.getstarted.auth; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; // [START example] @SuppressWarnings("serial") public class LogoutServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { // you can also make an authenticated request to logout, but here we choose to // simply delete the session variables for simplicity HttpSession session = req.getSession(false); if (session != null) { session.invalidate(); } // rebuild session req.getSession(); } } // [END example] ================================================ FILE: bookshelf-standard/5-logging/src/main/java/com/example/getstarted/basicactions/CreateBookServlet.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.basicactions; import com.example.getstarted.daos.BookDao; import com.example.getstarted.objects.Book; import com.example.getstarted.util.CloudStorageHelper; import com.google.common.base.Strings; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.commons.fileupload.FileItemIterator; import org.apache.commons.fileupload.FileItemStream; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.fileupload.util.Streams; // [START example] @SuppressWarnings("serial") public class CreateBookServlet extends HttpServlet { private static final Logger logger = Logger.getLogger(CreateBookServlet.class.getName()); // [START setup] @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setAttribute("action", "Add"); // Part of the Header in form.jsp req.setAttribute("destination", "create"); // The urlPattern to invoke (this Servlet) req.setAttribute("page", "form"); // Tells base.jsp to include form.jsp req.getRequestDispatcher("/base.jsp").forward(req, resp); } // [END setup] // [START formpost] @Override public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { assert ServletFileUpload.isMultipartContent(req); CloudStorageHelper storageHelper = (CloudStorageHelper) getServletContext().getAttribute("storageHelper"); String newImageUrl = null; Map params = new HashMap(); try { FileItemIterator iter = new ServletFileUpload().getItemIterator(req); while (iter.hasNext()) { FileItemStream item = iter.next(); if (item.isFormField()) { params.put(item.getFieldName(), Streams.asString(item.openStream())); } else if (!Strings.isNullOrEmpty(item.getName())) { newImageUrl = storageHelper.uploadFile( item, getServletContext().getInitParameter("bookshelf.bucket")); } } } catch (FileUploadException e) { throw new IOException(e); } String createdByString = ""; String createdByIdString = ""; HttpSession session = req.getSession(); if (session.getAttribute("userEmail") != null) { // Does the user have a logged in session? createdByString = (String) session.getAttribute("userEmail"); createdByIdString = (String) session.getAttribute("userId"); } Book book = new Book.Builder() .author(params.get("author")) .description(params.get("description")) .publishedDate(params.get("publishedDate")) .title(params.get("title")) .imageUrl(null == newImageUrl ? params.get("imageUrl") : newImageUrl) .createdBy(createdByString) .createdById(createdByIdString) .build(); BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); try { Long id = dao.createBook(book); logger.log(Level.INFO, "Created book {0}", book); resp.sendRedirect("/read?id=" + id.toString()); // read what we just wrote } catch (Exception e) { throw new ServletException("Error creating book", e); } } // [END formpost] } // [END example] ================================================ FILE: bookshelf-standard/5-logging/src/main/java/com/example/getstarted/basicactions/DeleteBookServlet.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.basicactions; import com.example.getstarted.daos.BookDao; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // [START example] @SuppressWarnings("serial") public class DeleteBookServlet extends HttpServlet { @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Long id = Long.decode(req.getParameter("id")); BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); try { dao.deleteBook(id); resp.sendRedirect("/books"); } catch (Exception e) { throw new ServletException("Error deleting book", e); } } } // [END example] ================================================ FILE: bookshelf-standard/5-logging/src/main/java/com/example/getstarted/basicactions/ListBookServlet.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.basicactions; import com.example.getstarted.daos.BookDao; import com.example.getstarted.daos.CloudSqlDao; import com.example.getstarted.daos.DatastoreDao; import com.example.getstarted.objects.Book; import com.example.getstarted.objects.Result; import com.example.getstarted.util.CloudStorageHelper; import com.google.common.base.Strings; import java.io.IOException; import java.sql.SQLException; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // [START example] // a url pattern of "" makes this servlet the root servlet @SuppressWarnings("serial") public class ListBookServlet extends HttpServlet { private static final Logger logger = Logger.getLogger(ListBookServlet.class.getName()); @Override public void init() throws ServletException { BookDao dao = null; CloudStorageHelper storageHelper = new CloudStorageHelper(); // Creates the DAO based on the Context Parameters String storageType = this.getServletContext().getInitParameter("bookshelf.storageType"); switch (storageType) { case "datastore": dao = new DatastoreDao(); break; case "cloudsql": try { // Use this url when using dev appserver, but connecting to Cloud SQL String connect = this.getServletContext().getInitParameter("sql.urlRemote"); if (connect.contains("localhost")) { // Use this url when using a local mysql server connect = this.getServletContext().getInitParameter("sql.urlLocal"); } else if (System.getProperty("com.google.appengine.runtime.version") .startsWith("Google App Engine/")) { // Use this url when on App Engine, connecting to Cloud SQL. // Uses a special adapter because of the App Engine sandbox. connect = this.getServletContext().getInitParameter("sql.urlRemoteGAE"); } dao = new CloudSqlDao(connect); } catch (SQLException e) { throw new ServletException("SQL error", e); } break; default: throw new IllegalStateException( "Invalid storage type. Check if bookshelf.storageType property is set."); } this.getServletContext().setAttribute("dao", dao); this.getServletContext().setAttribute("storageHelper", storageHelper); this.getServletContext().setAttribute( "isCloudStorageConfigured", // Hide upload when Cloud Storage is not configured. !Strings.isNullOrEmpty(getServletContext().getInitParameter("bookshelf.bucket"))); } @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); String startCursor = req.getParameter("cursor"); List books = null; String endCursor = null; try { Result result = dao.listBooks(startCursor); logger.log(Level.INFO, "Retrieved list of all books"); books = result.result; endCursor = result.cursor; } catch (Exception e) { throw new ServletException("Error listing books", e); } req.getSession().getServletContext().setAttribute("books", books); StringBuilder bookNames = new StringBuilder(); for (Book book : books) { bookNames.append(book.getTitle() + " "); } logger.log(Level.INFO, "Loaded books: " + bookNames.toString()); req.setAttribute("cursor", endCursor); req.setAttribute("page", "list"); req.getRequestDispatcher("/base.jsp").forward(req, resp); } } // [END example] ================================================ FILE: bookshelf-standard/5-logging/src/main/java/com/example/getstarted/basicactions/ListByUserServlet.java ================================================ /* * Copyright 2016 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. */ package com.example.getstarted.basicactions; import com.example.getstarted.daos.BookDao; import com.example.getstarted.objects.Book; import com.example.getstarted.objects.Result; import java.io.IOException; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // [START example] @SuppressWarnings("serial") public class ListByUserServlet extends HttpServlet { private static final Logger logger = Logger.getLogger(ListByUserServlet.class.getName()); @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); String startCursor = req.getParameter("cursor"); List books = null; String endCursor = null; try { Result result = dao.listBooksByUser((String) req.getSession().getAttribute("userId"), startCursor); books = result.result; endCursor = result.cursor; } catch (Exception e) { throw new ServletException("Error listing books", e); } req.getSession().getServletContext().setAttribute("books", books); StringBuilder bookNames = new StringBuilder(); for (Book book : books) { bookNames.append(book.getTitle() + " "); } logger.log(Level.INFO, "Loaded books: " + bookNames.toString() + " for user " + (String) req.getSession().getAttribute("userId")); req.getSession().setAttribute("cursor", endCursor); req.getSession().setAttribute("page", "list"); req.getRequestDispatcher("/base.jsp").forward(req, resp); } } // [END example] ================================================ FILE: bookshelf-standard/5-logging/src/main/java/com/example/getstarted/basicactions/ReadBookServlet.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.basicactions; import com.example.getstarted.daos.BookDao; import com.example.getstarted.objects.Book; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // [START example] @SuppressWarnings("serial") public class ReadBookServlet extends HttpServlet { // [START init] private final Logger logger = Logger.getLogger(ReadBookServlet.class.getName()); // [END init] @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { Long id = Long.decode(req.getParameter("id")); BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); try { Book book = dao.readBook(id); // [START log] logger.log(Level.INFO, "Read book with id {0}", id); // [END log] req.setAttribute("book", book); req.setAttribute("page", "view"); req.getRequestDispatcher("/base.jsp").forward(req, resp); } catch (Exception e) { throw new ServletException("Error reading book", e); } } } // [END example] ================================================ FILE: bookshelf-standard/5-logging/src/main/java/com/example/getstarted/basicactions/UpdateBookServlet.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.basicactions; import com.example.getstarted.daos.BookDao; import com.example.getstarted.objects.Book; import com.example.getstarted.util.CloudStorageHelper; import com.google.common.base.Strings; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileItemIterator; import org.apache.commons.fileupload.FileItemStream; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.fileupload.util.Streams; // [START example] @SuppressWarnings("serial") public class UpdateBookServlet extends HttpServlet { @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); try { Book book = dao.readBook(Long.decode(req.getParameter("id"))); req.setAttribute("book", book); req.setAttribute("action", "Edit"); req.setAttribute("destination", "update"); req.setAttribute("page", "form"); req.getRequestDispatcher("/base.jsp").forward(req, resp); } catch (Exception e) { throw new ServletException("Error loading book for editing", e); } } @Override public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); assert ServletFileUpload.isMultipartContent(req); CloudStorageHelper storageHelper = (CloudStorageHelper) getServletContext().getAttribute("storageHelper"); String newImageUrl = null; Map params = new HashMap(); try { FileItemIterator iter = new ServletFileUpload().getItemIterator(req); while (iter.hasNext()) { FileItemStream item = iter.next(); if (item.isFormField()) { params.put(item.getFieldName(), Streams.asString(item.openStream())); } else if (!Strings.isNullOrEmpty(item.getName())) { newImageUrl = storageHelper.uploadFile( item, getServletContext().getInitParameter("bookshelf.bucket")); } } } catch (FileUploadException e) { throw new IOException(e); } try { Book oldBook = dao.readBook(Long.decode(params.get("id"))); Book book = new Book.Builder() .author(params.get("author")) .description(params.get("description")) .publishedDate(params.get("publishedDate")) .title(params.get("title")) .imageUrl(null == newImageUrl ? params.get("imageUrl") : newImageUrl) .id(Long.decode(params.get("id"))) .createdBy(oldBook.getCreatedBy()) .createdById(oldBook.getCreatedById()) .build(); dao.updateBook(book); resp.sendRedirect("/read?id=" + params.get("id")); } catch (Exception e) { throw new ServletException("Error updating book", e); } } } // [END example] ================================================ FILE: bookshelf-standard/5-logging/src/main/java/com/example/getstarted/daos/BookDao.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.daos; import com.example.getstarted.objects.Book; import com.example.getstarted.objects.Result; import java.sql.SQLException; // [START example] public interface BookDao { Long createBook(Book book) throws SQLException; Book readBook(Long bookId) throws SQLException; void updateBook(Book book) throws SQLException; void deleteBook(Long bookId) throws SQLException; Result listBooks(String startCursor) throws SQLException; Result listBooksByUser(String userId, String startCursor) throws SQLException; } // [END example] ================================================ FILE: bookshelf-standard/5-logging/src/main/java/com/example/getstarted/daos/CloudSqlDao.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.daos; import com.example.getstarted.objects.Book; import com.example.getstarted.objects.Result; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; // [START example] public class CloudSqlDao implements BookDao { // [START constructor] private String sqlUrl; /** * A data access object for Bookshelf using a Google Cloud SQL server for storage. */ public CloudSqlDao(final String url) throws SQLException { sqlUrl = url; final String createTableSql = "CREATE TABLE IF NOT EXISTS books5 ( id INT NOT NULL " + "AUTO_INCREMENT, author VARCHAR(255), createdBy VARCHAR(255), createdById VARCHAR(255), " + "description VARCHAR(255), publishedDate VARCHAR(255), title VARCHAR(255), imageUrl " + "VARCHAR(255), PRIMARY KEY (id))"; try (Connection conn = DriverManager.getConnection(sqlUrl)) { conn.createStatement().executeUpdate(createTableSql); } } // [END constructor] // [START create] @Override public Long createBook(Book book) throws SQLException { final String createBookString = "INSERT INTO books5 " + "(author, createdBy, createdById, description, publishedDate, title, imageUrl) " + "VALUES (?, ?, ?, ?, ?, ?, ?)"; try (Connection conn = DriverManager.getConnection(sqlUrl); final PreparedStatement createBookStmt = conn.prepareStatement(createBookString, Statement.RETURN_GENERATED_KEYS)) { createBookStmt.setString(1, book.getAuthor()); createBookStmt.setString(2, book.getCreatedBy()); createBookStmt.setString(3, book.getCreatedById()); createBookStmt.setString(4, book.getDescription()); createBookStmt.setString(5, book.getPublishedDate()); createBookStmt.setString(6, book.getTitle()); createBookStmt.setString(7, book.getImageUrl()); createBookStmt.executeUpdate(); try (ResultSet keys = createBookStmt.getGeneratedKeys()) { keys.next(); return keys.getLong(1); } } } // [END create] // [START read] @Override public Book readBook(Long bookId) throws SQLException { final String readBookString = "SELECT * FROM books5 WHERE id = ?"; try (Connection conn = DriverManager.getConnection(sqlUrl); PreparedStatement readBookStmt = conn.prepareStatement(readBookString)) { readBookStmt.setLong(1, bookId); try (ResultSet keys = readBookStmt.executeQuery()) { keys.next(); return new Book.Builder() .author(keys.getString(Book.AUTHOR)) .createdBy(keys.getString(Book.CREATED_BY)) .createdById(keys.getString(Book.CREATED_BY_ID)) .description(keys.getString(Book.DESCRIPTION)) .id(keys.getLong(Book.ID)) .publishedDate(keys.getString(Book.PUBLISHED_DATE)) .title(keys.getString(Book.TITLE)) .imageUrl(keys.getString(Book.IMAGE_URL)) .build(); } } } // [END read] // [START update] @Override public void updateBook(Book book) throws SQLException { final String updateBookString = "UPDATE books5 SET author = ?, createdBy = ?, createdById = ?, " + "description = ?, publishedDate = ?, title = ?, imageUrl = ? WHERE id = ?"; try (Connection conn = DriverManager.getConnection(sqlUrl); PreparedStatement updateBookStmt = conn.prepareStatement(updateBookString)) { updateBookStmt.setString(1, book.getAuthor()); updateBookStmt.setString(2, book.getCreatedBy()); updateBookStmt.setString(3, book.getCreatedById()); updateBookStmt.setString(4, book.getDescription()); updateBookStmt.setString(5, book.getPublishedDate()); updateBookStmt.setString(6, book.getTitle()); updateBookStmt.setString(7, book.getImageUrl()); updateBookStmt.setLong(8, book.getId()); updateBookStmt.executeUpdate(); } } // [END update] // [START delete] @Override public void deleteBook(Long bookId) throws SQLException { final String deleteBookString = "DELETE FROM books5 WHERE id = ?"; try (Connection conn = DriverManager.getConnection(sqlUrl); PreparedStatement deleteBookStmt = conn.prepareStatement(deleteBookString)) { deleteBookStmt.setLong(1, bookId); deleteBookStmt.executeUpdate(); } } // [END delete] // [START listbooks] @Override public Result listBooks(String cursor) throws SQLException { int offset = 0; if (cursor != null && !cursor.equals("")) { offset = Integer.parseInt(cursor); } final String listBooksString = "SELECT SQL_CALC_FOUND_ROWS author, createdBy, createdById, " + "description, id, publishedDate, title, imageUrl FROM books5 ORDER BY title ASC " + "LIMIT 10 OFFSET ?"; try (Connection conn = DriverManager.getConnection(sqlUrl); PreparedStatement listBooksStmt = conn.prepareStatement(listBooksString)) { listBooksStmt.setInt(1, offset); List resultBooks = new ArrayList<>(); try (ResultSet rs = listBooksStmt.executeQuery()) { while (rs.next()) { Book book = new Book.Builder() .author(rs.getString(Book.AUTHOR)) .createdBy(rs.getString(Book.CREATED_BY)) .createdById(rs.getString(Book.CREATED_BY_ID)) .description(rs.getString(Book.DESCRIPTION)) .id(rs.getLong(Book.ID)) .publishedDate(rs.getString(Book.PUBLISHED_DATE)) .title(rs.getString(Book.TITLE)) .imageUrl(rs.getString(Book.IMAGE_URL)) .build(); resultBooks.add(book); } } try (ResultSet rs = conn.createStatement().executeQuery("SELECT FOUND_ROWS()")) { int totalNumRows = 0; if (rs.next()) { totalNumRows = rs.getInt(1); } if (totalNumRows > offset + 10) { return new Result<>(resultBooks, Integer.toString(offset + 10)); } else { return new Result<>(resultBooks); } } } } // [END listbooks] // [START listbyuser] @Override public Result listBooksByUser(String userId, String startCursor) throws SQLException { int offset = 0; if (startCursor != null && !startCursor.equals("")) { offset = Integer.parseInt(startCursor); } final String listBooksString = "SELECT SQL_CALC_FOUND_ROWS author, createdBy, createdById, " + "description, id, publishedDate, title, imageUrl FROM books WHERE createdById = ? " + "ORDER BY title ASC LIMIT 10 OFFSET ?"; try (Connection conn = DriverManager.getConnection(sqlUrl); PreparedStatement listBooksStmt = conn.prepareStatement(listBooksString)) { listBooksStmt.setString(1, userId); listBooksStmt.setInt(2, offset); List resultBooks = new ArrayList<>(); try (ResultSet rs = listBooksStmt.executeQuery()) { while (rs.next()) { Book book = new Book.Builder() .author(rs.getString(Book.AUTHOR)) .createdBy(rs.getString(Book.CREATED_BY)) .createdById(rs.getString(Book.CREATED_BY_ID)) .description(rs.getString(Book.DESCRIPTION)) .id(rs.getLong(Book.ID)) .publishedDate(rs.getString(Book.PUBLISHED_DATE)) .title(rs.getString(Book.TITLE)) .imageUrl(rs.getString(Book.IMAGE_URL)) .build(); resultBooks.add(book); } } try (ResultSet rs = conn.createStatement().executeQuery("SELECT FOUND_ROWS()")) { int totalNumRows = 0; if (rs.next()) { totalNumRows = rs.getInt(1); } if (totalNumRows > offset + 10) { return new Result<>(resultBooks, Integer.toString(offset + 10)); } else { return new Result<>(resultBooks); } } } } // [END listbyuser] } // [END example] ================================================ FILE: bookshelf-standard/5-logging/src/main/java/com/example/getstarted/daos/DatastoreDao.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.daos; import com.example.getstarted.objects.Book; import com.example.getstarted.objects.Result; import com.google.appengine.api.datastore.Cursor; 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.EntityNotFoundException; import com.google.appengine.api.datastore.FetchOptions; import com.google.appengine.api.datastore.Key; import com.google.appengine.api.datastore.KeyFactory; import com.google.appengine.api.datastore.PreparedQuery; import com.google.appengine.api.datastore.Query; import com.google.appengine.api.datastore.Query.SortDirection; import com.google.appengine.api.datastore.QueryResultIterator; import java.util.ArrayList; import java.util.Iterator; import java.util.List; // [START example] public class DatastoreDao implements BookDao { // [START constructor] private DatastoreService datastore; private static final String BOOK_KIND = "Book5"; public DatastoreDao() { datastore = DatastoreServiceFactory.getDatastoreService(); // Authorized Datastore service } // [END constructor] // [START entityToBook] public Book entityToBook(Entity entity) { return new Book.Builder() // Convert to Book form .author((String) entity.getProperty(Book.AUTHOR)) .description((String) entity.getProperty(Book.DESCRIPTION)) .id(entity.getKey().getId()) .publishedDate((String) entity.getProperty(Book.PUBLISHED_DATE)) .imageUrl((String) entity.getProperty(Book.IMAGE_URL)) .createdBy((String) entity.getProperty(Book.CREATED_BY)) .createdById((String) entity.getProperty(Book.CREATED_BY_ID)) .title((String) entity.getProperty(Book.TITLE)) .build(); } // [END entityToBook] // [START create] @Override public Long createBook(Book book) { Entity incBookEntity = new Entity(BOOK_KIND); // Key will be assigned once written incBookEntity.setProperty(Book.AUTHOR, book.getAuthor()); incBookEntity.setProperty(Book.DESCRIPTION, book.getDescription()); incBookEntity.setProperty(Book.PUBLISHED_DATE, book.getPublishedDate()); incBookEntity.setProperty(Book.TITLE, book.getTitle()); incBookEntity.setProperty(Book.IMAGE_URL, book.getImageUrl()); incBookEntity.setProperty(Book.CREATED_BY, book.getCreatedBy()); incBookEntity.setProperty(Book.CREATED_BY_ID, book.getCreatedById()); Key bookKey = datastore.put(incBookEntity); // Save the Entity return bookKey.getId(); // The ID of the Key } // [END create] // [START read] @Override public Book readBook(Long bookId) { try { Entity bookEntity = datastore.get(KeyFactory.createKey(BOOK_KIND, bookId)); return entityToBook(bookEntity); } catch (EntityNotFoundException e) { return null; } } // [END read] // [START update] @Override public void updateBook(Book book) { Key key = KeyFactory.createKey(BOOK_KIND, book.getId()); // From a book, create a Key Entity entity = new Entity(key); // Convert Book to an Entity entity.setProperty(Book.AUTHOR, book.getAuthor()); entity.setProperty(Book.DESCRIPTION, book.getDescription()); entity.setProperty(Book.PUBLISHED_DATE, book.getPublishedDate()); entity.setProperty(Book.TITLE, book.getTitle()); entity.setProperty(Book.IMAGE_URL, book.getImageUrl()); entity.setProperty(Book.CREATED_BY, book.getCreatedBy()); entity.setProperty(Book.CREATED_BY_ID, book.getCreatedById()); datastore.put(entity); // Update the Entity } // [END update] // [START delete] @Override public void deleteBook(Long bookId) { Key key = KeyFactory.createKey(BOOK_KIND, bookId); // Create the Key datastore.delete(key); // Delete the Entity } // [END delete] // [START entitiesToBooks] public List entitiesToBooks(Iterator results) { List resultBooks = new ArrayList<>(); while (results.hasNext()) { // We still have data resultBooks.add(entityToBook(results.next())); // Add the Book to the List } return resultBooks; } // [END entitiesToBooks] // [START listbooks] @Override public Result listBooks(String startCursorString) { FetchOptions fetchOptions = FetchOptions.Builder.withLimit(10); // Only show 10 at a time if (startCursorString != null && !startCursorString.equals("")) { fetchOptions.startCursor(Cursor.fromWebSafeString(startCursorString)); // Where we left off } Query query = new Query(BOOK_KIND) // We only care about Books .addSort(Book.TITLE, SortDirection.ASCENDING); // Use default Index "title" PreparedQuery preparedQuery = datastore.prepare(query); QueryResultIterator results = preparedQuery.asQueryResultIterator(fetchOptions); List resultBooks = entitiesToBooks(results); // Retrieve and convert Entities Cursor cursor = results.getCursor(); // Where to start next time if (cursor != null && resultBooks.size() == 10) { // Are we paging? Save Cursor String cursorString = cursor.toWebSafeString(); // Cursors are WebSafe return new Result<>(resultBooks, cursorString); } else { return new Result<>(resultBooks); } } // [END listbooks] // [START listbyuser] @Override public Result listBooksByUser(String userId, String startCursorString) { FetchOptions fetchOptions = FetchOptions.Builder.withLimit(10); // Only show 10 at a time if (startCursorString != null && !startCursorString.equals("")) { fetchOptions.startCursor(Cursor.fromWebSafeString(startCursorString)); // Where we left off } Query query = new Query(BOOK_KIND) // We only care about Books // Only for this user .setFilter(new Query.FilterPredicate( Book.CREATED_BY_ID, Query.FilterOperator.EQUAL, userId)) // a custom datastore index is required since you are filtering by one property // but ordering by another .addSort(Book.TITLE, SortDirection.ASCENDING); PreparedQuery preparedQuery = datastore.prepare(query); QueryResultIterator results = preparedQuery.asQueryResultIterator(fetchOptions); List resultBooks = entitiesToBooks(results); // Retrieve and convert Entities Cursor cursor = results.getCursor(); // Where to start next time if (cursor != null && resultBooks.size() == 10) { // Are we paging? Save Cursor String cursorString = cursor.toWebSafeString(); // Cursors are WebSafe return new Result<>(resultBooks, cursorString); } else { return new Result<>(resultBooks); } } // [END listbyuser] } // [END example] ================================================ FILE: bookshelf-standard/5-logging/src/main/java/com/example/getstarted/objects/Book.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.objects; // [START example] public class Book { // [START book] private String title; private String author; private String createdBy; private String createdById; private String publishedDate; private String description; private Long id; private String imageUrl; // [END book] // [START keys] public static final String AUTHOR = "author"; public static final String CREATED_BY = "createdBy"; public static final String CREATED_BY_ID = "createdById"; public static final String DESCRIPTION = "description"; public static final String ID = "id"; public static final String PUBLISHED_DATE = "publishedDate"; public static final String TITLE = "title"; public static final String IMAGE_URL = "imageUrl"; // [END keys] // [START constructor] // We use a Builder pattern here to simplify and standardize construction of Book objects. private Book(Builder builder) { this.title = builder.title; this.author = builder.author; this.createdBy = builder.createdBy; this.createdById = builder.createdById; this.publishedDate = builder.publishedDate; this.description = builder.description; this.id = builder.id; this.imageUrl = builder.imageUrl; } // [END constructor] // [START builder] public static class Builder { private String title; private String author; private String createdBy; private String createdById; private String publishedDate; private String description; private Long id; private String imageUrl; public Builder title(String title) { this.title = title; return this; } public Builder author(String author) { this.author = author; return this; } public Builder createdBy(String createdBy) { this.createdBy = createdBy; return this; } public Builder createdById(String createdById) { this.createdById = createdById; return this; } public Builder publishedDate(String publishedDate) { this.publishedDate = publishedDate; return this; } public Builder description(String description) { this.description = description; return this; } public Builder id(Long id) { this.id = id; return this; } public Builder imageUrl(String imageUrl) { this.imageUrl = imageUrl; return this; } public Book build() { return new Book(this); } } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public String getCreatedBy() { return createdBy; } public void setCreatedBy(String createdBy) { this.createdBy = createdBy; } public String getCreatedById() { return createdById; } public void setCreatedById(String createdById) { this.createdById = createdById; } public String getPublishedDate() { return publishedDate; } public void setPublishedDate(String publishedDate) { this.publishedDate = publishedDate; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getImageUrl() { return imageUrl; } public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; } // [END builder] @Override public String toString() { return "Title: " + title + ", Author: " + author + ", Published date: " + publishedDate + ", Added by: " + createdBy; } } // [END example] ================================================ FILE: bookshelf-standard/5-logging/src/main/java/com/example/getstarted/objects/Result.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.objects; import java.util.List; // [START example] public class Result { public String cursor; public List result; public Result(List result, String cursor) { this.result = result; this.cursor = cursor; } public Result(List result) { this.result = result; this.cursor = null; } } // [END example] ================================================ FILE: bookshelf-standard/5-logging/src/main/java/com/example/getstarted/util/CloudStorageHelper.java ================================================ /* * Copyright 2016 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. */ package com.example.getstarted.util; import com.google.cloud.storage.Acl; import com.google.cloud.storage.Acl.Role; import com.google.cloud.storage.Acl.User; import com.google.cloud.storage.BlobInfo; import com.google.cloud.storage.Storage; import com.google.cloud.storage.StorageOptions; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.ServletException; import org.apache.commons.fileupload.FileItemStream; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; // [START example] public class CloudStorageHelper { private final Logger logger = Logger.getLogger(CloudStorageHelper.class.getName()); private static Storage storage = null; // [START init] static { storage = StorageOptions.getDefaultInstance().getService(); } // [END init] // [START uploadFile] /** * Uploads a file to Google Cloud Storage to the bucket specified in the BUCKET_NAME * environment variable, appending a timestamp to end of the uploaded filename. */ public String uploadFile(FileItemStream fileStream, final String bucketName) throws IOException, ServletException { checkFileExtension(fileStream.getName()); DateTimeFormatter dtf = DateTimeFormat.forPattern("-YYYY-MM-dd-HHmmssSSS"); DateTime dt = DateTime.now(DateTimeZone.UTC); String dtString = dt.toString(dtf); final String fileName = fileStream.getName() + dtString; // the inputstream is closed by default, so we don't need to close it here @SuppressWarnings("deprecation") BlobInfo blobInfo = storage.create( BlobInfo .newBuilder(bucketName, fileName) // Modify access list to allow all users with link to read file .setAcl(new ArrayList<>(Arrays.asList(Acl.of(User.ofAllUsers(), Role.READER)))) .build(), fileStream.openStream()); logger.log(Level.INFO, "Uploaded file {0} as {1}", new Object[]{ fileStream.getName(), fileName}); // return the public download link return blobInfo.getMediaLink(); } // [END uploadFile] // [START checkFileExtension] /** * Checks that the file extension is supported. */ private void checkFileExtension(String fileName) throws ServletException { if (fileName != null && !fileName.isEmpty() && fileName.contains(".")) { String[] allowedExt = { ".jpg", ".jpeg", ".png", ".gif" }; for (String ext : allowedExt) { if (fileName.endsWith(ext)) { return; } } throw new ServletException("file must be an image"); } } // [END checkFileExtension] } // [END example] ================================================ FILE: bookshelf-standard/5-logging/src/main/java/com/example/getstarted/util/DatastoreSessionFilter.java ================================================ /* Copyright 2016 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. */ package com.example.getstarted.util; 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.EntityNotFoundException; import com.google.appengine.api.datastore.Key; import com.google.appengine.api.datastore.KeyFactory; import com.google.appengine.api.datastore.Query; import com.google.appengine.api.datastore.Query.FilterOperator; import com.google.appengine.api.datastore.Query.FilterPredicate; import com.google.appengine.api.datastore.Transaction; import com.google.common.collect.FluentIterable; import com.google.common.collect.MapDifference; import com.google.common.collect.Maps; import java.io.IOException; import java.math.BigInteger; import java.security.SecureRandom; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; // [START init] public class DatastoreSessionFilter implements Filter { private static DatastoreService datastore; private static final DateTimeFormatter DTF = DateTimeFormat.forPattern("yyyyMMddHHmmssSSS"); private static final String SESSION_KIND = "SessionVariable"; @Override public void init(FilterConfig config) throws ServletException { // initialize local copy of datastore session variables datastore = DatastoreServiceFactory.getDatastoreService(); // Delete all sessions unmodified for over two days DateTime dt = DateTime.now(DateTimeZone.UTC); Query query = new Query(SESSION_KIND).setFilter(new FilterPredicate( "lastModified", FilterOperator.LESS_THAN_OR_EQUAL, dt.minusDays(2).toString(DTF))); Iterator results = datastore.prepare(query).asIterator(); while (results.hasNext()) { Entity stateEntity = results.next(); datastore.delete(stateEntity.getKey()); } } // [END init] @Override public void doFilter(ServletRequest servletReq, ServletResponse servletResp, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) servletReq; HttpServletResponse resp = (HttpServletResponse) servletResp; // Check if the session cookie is there, if not there, make a session cookie using a unique // identifier. String sessionId = getCookieValue(req, "bookshelfSessionId"); if (sessionId.equals("")) { String sessionNum = new BigInteger(130, new SecureRandom()).toString(32); Cookie session = new Cookie("bookshelfSessionId", sessionNum); session.setPath("/"); resp.addCookie(session); } Map datastoreMap = loadSessionVariables(req); // session variables for request chain.doFilter(servletReq, servletResp); // Allow the servlet to process request and response HttpSession session = req.getSession(); // Create session map Map sessionMap = new HashMap<>(); Enumeration attrNames = session.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = attrNames.nextElement(); sessionMap.put(attrName, (String) session.getAttribute(attrName)); } // Create a diff between the new session variables and the existing session variables // to minimize datastore access MapDifference diff = Maps.difference(sessionMap, datastoreMap); Map setMap = diff.entriesOnlyOnLeft(); Map deleteMap = diff.entriesOnlyOnRight(); // Apply the diff setSessionVariables(sessionId, setMap); deleteSessionVariables( sessionId, FluentIterable.from(deleteMap.keySet()).toArray(String.class)); } @SuppressWarnings({"unused", "JdkObsolete"}) private String mapToString(Map map) { StringBuffer names = new StringBuffer(); for (String name : map.keySet()) { names.append(name + " "); } return names.toString(); } @Override public void destroy() { } protected String getCookieValue(HttpServletRequest req, String cookieName) { Cookie[] cookies = req.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { if (cookie.getName().equals(cookieName)) { return cookie.getValue(); } } } return ""; } // [START deleteSessionVariables] /** * Delete a value stored in the project's datastore. * * @param sessionId Request from which the session is extracted. */ protected void deleteSessionVariables(String sessionId, String... varNames) { if (sessionId.equals("")) { return; } Key key = KeyFactory.createKey(SESSION_KIND, sessionId); Transaction transaction = datastore.beginTransaction(); try { Entity stateEntity = datastore.get(transaction, key); for (String varName : varNames) { stateEntity.removeProperty(varName); } datastore.put(transaction, stateEntity); transaction.commit(); } catch (EntityNotFoundException e) { // Ignore - if there's no session, there's nothing to delete. } finally { if (transaction.isActive()) { transaction.rollback(); } } } // [END deleteSessionVariables] protected void deleteSessionWithValue(String varName, String varValue) { Transaction transaction = datastore.beginTransaction(); try { Query query = new Query(SESSION_KIND) .setFilter(new FilterPredicate(varName, FilterOperator.EQUAL, varValue)); Iterator results = datastore.prepare(transaction, query).asIterator(); while (results.hasNext()) { Entity stateEntity = results.next(); datastore.delete(transaction, stateEntity.getKey()); } transaction.commit(); } finally { if (transaction.isActive()) { transaction.rollback(); } } } // [START setSessionVariables] /** * Stores the state value in each key-value pair in the project's datastore. * * @param sessionId Request from which to extract session. * @param varName the name of the desired session variable * @param varValue the value of the desired session variable */ protected void setSessionVariables(String sessionId, Map setMap) { if (sessionId.equals("")) { return; } Key key = KeyFactory.createKey(SESSION_KIND, sessionId); Transaction transaction = datastore.beginTransaction(); DateTime dt = DateTime.now(DateTimeZone.UTC); dt.toString(DTF); try { Entity stateEntity; try { stateEntity = datastore.get(transaction, key); } catch (EntityNotFoundException e) { stateEntity = new Entity(key); } for (String varName : setMap.keySet()) { stateEntity.setProperty(varName, setMap.get(varName)); } stateEntity.setProperty("lastModified", dt.toString(DTF)); datastore.put(transaction, stateEntity); transaction.commit(); } finally { if (transaction.isActive()) { transaction.rollback(); } } } // [END setSessionVariables] // [START loadSessionVariables] /** * Take an HttpServletRequest, and copy all of the current session variables over to it * * @param req Request from which to extract session. * @return a map of strings containing all the session variables loaded or an empty map. */ protected Map loadSessionVariables(HttpServletRequest req) throws ServletException { Map datastoreMap = new HashMap<>(); String sessionId = getCookieValue(req, "bookshelfSessionId"); if (sessionId.equals("")) { return datastoreMap; } Key key = KeyFactory.createKey(SESSION_KIND, sessionId); Transaction transaction = datastore.beginTransaction(); try { Entity stateEntity = datastore.get(transaction, key); Map properties = stateEntity.getProperties(); for (Map.Entry property : properties.entrySet()) { req.getSession().setAttribute(property.getKey(), property.getValue()); datastoreMap.put(property.getKey(), (String) property.getValue()); } transaction.commit(); } catch (EntityNotFoundException e) { // Ignore - if there's no session, there's nothing to delete. } finally { if (transaction.isActive()) { transaction.rollback(); } } return datastoreMap; } // [END loadSessionVariables] } ================================================ FILE: bookshelf-standard/5-logging/src/main/webapp/WEB-INF/appengine-web.xml ================================================ true true true ================================================ FILE: bookshelf-standard/5-logging/src/main/webapp/WEB-INF/datastore-indexes.xml ================================================ ================================================ FILE: bookshelf-standard/5-logging/src/main/webapp/WEB-INF/logging.properties ================================================ # A default java.util.logging configuration. # (All App Engine logging is through java.util.logging by default). # Set the default logging level for all loggers to INFO .level = INFO handlers=java.util.logging.ConsoleHandler java.util.logging.ConsoleHandler.level=FINEST java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter java.util.logging.SimpleFormatter.format = [%1$tc] %4$s: %2$s - %5$s %6$s%n ================================================ FILE: bookshelf-standard/5-logging/src/main/webapp/WEB-INF/web.xml ================================================ DatastoreSessionFilter com.example.getstarted.util.DatastoreSessionFilter DatastoreSessionFilter / /books /books/mine /create /delete /login /logout /read /update LogoutFilter com.example.getstarted.auth.LogoutFilter LogoutFilter /logout ListByUserFilter com.example.getstarted.auth.ListByUserFilter ListByUserFilter /books/mine list com.example.getstarted.basicactions.ListBookServlet 1 list / /books create com.example.getstarted.basicactions.CreateBookServlet create /create update com.example.getstarted.basicactions.UpdateBookServlet update /update read com.example.getstarted.basicactions.ReadBookServlet read /read delete com.example.getstarted.basicactions.DeleteBookServlet delete /delete logout com.example.getstarted.auth.LogoutServlet logout /logout login com.example.getstarted.auth.LoginServlet login /login listbyuser com.example.getstarted.basicactions.ListByUserServlet listbyuser /books/mine bookshelf.storageType ${bookshelf.storageType} bookshelf.bucket ${bookshelf.bucket} sql.urlRemoteGAE jdbc:google:mysql://${sql.instanceName}/${sql.dbName}?user=${sql.userName}&password=${sql.password} sql.urlRemote jdbc:mysql://google/${sql.dbName}?cloudSqlInstance=${sql.instanceName}&socketFactory=com.google.cloud.sql.mysql.SocketFactory&user=${sql.userName}&password=${sql.password} sql.urlLocal jdbc:mysql://localhost/${sql.dbName}?user=${sql.userName}&password=${sql.password}&useSSL=false ================================================ FILE: bookshelf-standard/5-logging/src/main/webapp/base.jsp ================================================ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> Bookshelf - Java on Google Cloud Platform ================================================ FILE: bookshelf-standard/5-logging/src/main/webapp/form.jsp ================================================ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>

book

================================================ FILE: bookshelf-standard/5-logging/src/main/webapp/list.jsp ================================================ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> ================================================ FILE: bookshelf-standard/5-logging/src/main/webapp/view.jsp ================================================ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>

Book

${fn:escapeXml(book.title)} ${fn:escapeXml(book.publishedDate)}

By ${fn:escapeXml(not empty book.author?book.author:'Unknown')}

${fn:escapeXml(book.description)}

Added by ${fn:escapeXml(not empty book.createdBy?book.createdBy:'Anonymous')}
================================================ FILE: codecov.yml ================================================ # Copyright 2016 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. codecov: branch: main comment: branches: - main ================================================ FILE: gce/README.md ================================================ # Getting Started with Java - Google Compute Engine See the [Bookshelf tutorial][tutorial] for help getting started with Google App Engine (Standard), Google Cloud Firestore, and more. You'll need to [create a bucket][create-bucket] in Google Cloud Storage, referred to below as `MY-BUCKET`. You'll also need to create an OAuth2 client and secret, and edit `pom.xml` with its values. ### Running Locally mvn clean jetty:run-war -DprojectID=YOUR-PROJECT-ID ### Deploying to Compute Engine * Initialize the [Google Cloud SDK][cloud_sdk] gcloud init * In the `makeProject` script update the `BUCKET` environment variable with your bucket name. * Deploy your App ./makeProject gce * To tear down the App, use ./makeProject down ### Deploying to Compute Engine with horizontal scaling * Initialize Google Cloud SDK and `makeProject` as above. * Deploy your App ./makeProject gce-many * To tear down the App, use ./makeProject down-many [tutorial]: https://cloud.google.com/java/getting-started/tutorial-app [create-bucket]: https://cloud.google.com/storage/docs/creating-buckets#storage-create-bucket-console [cloud_sdk]: https://cloud.google.com/sdk/ ================================================ FILE: gce/config/base/etc/java-util-logging.properties ================================================ # Copyright 2019 Google LLC # # 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. # A default java.util.logging configuration. # handlers=java.util.logging.ConsoleHandler,java.util.logging.FileHandler java.util.logging.FileHandler.level=INFO #java.util.logging.FileHandler.formatter= #java.util.logging.FileHandler.pattern=/opt/jetty/logs/app.%g.log.json java.util.logging.FileHandler.limit=104857600 java.util.logging.FileHandler.count=3 # Set the default logging level for all loggers to INFO .level=INFO # Override root level com.foo.level=FINE # Override parent's level com.foo.bar.level=SEVERE ================================================ FILE: gce/config/base/modules/gce.mod ================================================ # Copyright 2019 Google LLC # # 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. # # GCE Module # [depend] resources server [optional] [ini-template] ## Google Defaults jetty.httpConfig.outputAggregationSize=32768 jetty.httpConfig.headerCacheSize=512 jetty.httpConfig.sendServerVersion=true jetty.httpConfig.sendDateHeader=false #gae.httpPort=80 #gae.httpsPort=443 ================================================ FILE: gce/config/base/resources/jetty-logging.properties ================================================ # Copyright 2019 Google LLC # # 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. # Direct Jetty logging to JavaUtilLog # see etc/java-util-logging.properties org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.JavaUtilLog ================================================ FILE: gce/makeProject ================================================ #!/bin/bash # # Copyright 2016 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. set -ex # [START getting_started_useful] BUCKET= REGION=us-central1 ZONE=$REGION-f GROUP=frontend-group TEMPLATE=$GROUP-tmpl MACHINE_TYPE=g1-small IMAGE_FAMILY=debian-9 IMAGE_PROJECT=debian-cloud STARTUP_SCRIPT=scripts/startup-script.sh SCOPES="datastore,userinfo-email,logging-write,storage-full,cloud-platform" TAGS=http-server MIN_INSTANCES=1 MAX_INSTANCES=10 TARGET_UTILIZATION=0.6 # [END getting_started_useful] SERVICE=frontend-web-service WAR=getting-started-gce-1.0-SNAPSHOT.war function print_usage() { echo "Usage: ${0} gce | down | gce-many | down-many" echo "" echo "This command is useful as a place to let you easily move back and forth between running or" echo "deploying the hello-world app. You may add all your configuration here, so you don't need" echo "to change them in every version of this application." echo "" echo "gce - mvn package; gcloud storage cp; gcloud compute instances create ...; - deploys to Compute Engine" echo "down - tears down a single instance group" echo "gce-many - deploys a managed instance group" echo "down-many- tears down a managed instance group" } if [ $# = 0 ]; then print_usage exit fi COMMAND=$1 case $COMMAND in # usage flags --help|-help|-h) print_usage exit ;; run) set -v mvn -Plocal clean jetty:run-exploded ;; deploy) set -v mvn clean gcloud:deploy ;; gce) set -v # [START getting_started_gce_single] mvn clean package gcloud storage cp --recursive target/${WAR} config/base gs://${BUCKET}/gce/ gcloud compute firewall-rules create allow-http-hello-world \ --allow tcp:80 \ --source-ranges 0.0.0.0/0 \ --target-tags ${TAGS} \ --description "Allow port 80 access to instances tagged with ${TAGS}" gcloud compute instances create my-app-instance \ --machine-type=${MACHINE_TYPE} \ --scopes=${SCOPES} \ --metadata-from-file startup-script=${STARTUP_SCRIPT} \ --zone=${ZONE} \ --tags=${TAGS} \ --image-family=${IMAGE_FAMILY} \ --image-project=${IMAGE_PROJECT} \ --metadata BUCKET=${BUCKET} # [END getting_started_gce_single] ;; down) set -v gcloud compute instances delete my-app-instance --zone=${ZONE} --quiet gcloud compute firewall-rules delete allow-http-hello-world --quiet ;; gce-many) set -v +e # # Instance group setup # mvn clean package # First we have to create an instance template. # This template will be used by the instance group # to create new instances. # [START getting_started_create_template] gcloud compute instance-templates create ${TEMPLATE} \ --image-family=${IMAGE_FAMILY} \ --image-project=${IMAGE_PROJECT} \ --machine-type=${MACHINE_TYPE} \ --scopes=${SCOPES} \ --metadata-from-file startup-script=${STARTUP_SCRIPT} \ --tags ${TAGS} \ --metadata BUCKET=${BUCKET} # [END getting_started_create_template] # Add a firewall rule so that we can connect directly to # the compute instances in the group. gcloud compute firewall-rules create allow-http-hello-world \ --allow tcp:80 \ --source-ranges 0.0.0.0/0 \ --target-tags ${TAGS} \ --description "Allow port 80 access to instances tagged with ${TAGS}" # Create the managed instance group. # [START getting_started_create_group] gcloud compute instance-groups managed \ create ${GROUP} \ --base-instance-name ${GROUP} \ --size ${MIN_INSTANCES} \ --template ${TEMPLATE} \ --zone ${ZONE} # [END getting_started_create_group] # # Load Balancer Setup # # A complete HTTP load balancer is structured as follows: # # 1) A global forwarding rule directs incoming requests to a target HTTP proxy. # 2) The target HTTP proxy checks each request against a URL map to determine the # appropriate backend service for the request. # 3) The backend service directs each request to an appropriate backend based on # serving capacity, zone, and instance health of its attached backends. The # health of each backend instance is verified using either a health check. # # We'll create these resources in reverse order: # service, health check, backend service, url map, proxy. # Create a health check # The load balancer will use this check to keep track of which instances to send traffic to. # Note that health checks will not cause the load balancer to shutdown any instances. # [START getting_started_create_health_check] gcloud compute http-health-checks create ah-health-check \ --request-path /_ah/health \ --port 80 # [END getting_started_create_health_check] # Create a backend service, associate it with the health check and instance group. # The backend service serves as a target for load balancing. # [START getting_started_create_backend_service] gcloud compute backend-services create $SERVICE \ --http-health-checks ah-health-check --global # [END getting_started_create_backend_service] # [START getting_started_add_backend_service] gcloud compute backend-services add-backend $SERVICE \ --instance-group $GROUP \ --instance-group-zone $ZONE \ --global # [END getting_started_add_backend_service] # Create a URL map and web Proxy. The URL map will send all requests to the # backend service defined above. # [START getting_started_create_url_map] gcloud compute url-maps create $SERVICE-map \ --default-service $SERVICE # [END getting_started_create_url_map] # [START getting_started_create_http_proxy] gcloud compute target-http-proxies create $SERVICE-proxy \ --url-map $SERVICE-map # [END getting_started_create_http_proxy] # Create a global forwarding rule to send all traffic to our proxy # [START getting_started_create_forwarding_rule] gcloud compute forwarding-rules create $SERVICE-http-rule \ --global \ --target-http-proxy $SERVICE-proxy \ --ports 80 # [END getting_started_create_forwarding_rule] # # Autoscaler configuration # # [START getting_started_set_autoscaling] gcloud compute instance-groups managed set-autoscaling \ $GROUP \ --max-num-replicas $MAX_INSTANCES \ --target-load-balancing-utilization $TARGET_UTILIZATION \ --zone $ZONE # [END getting_started_set_autoscaling] ;; down-many) set -v +e # [START getting_started_stop_gce] gcloud compute instance-groups managed stop-autoscaling $GROUP --zone $ZONE --quiet gcloud compute forwarding-rules delete $SERVICE-http-rule --global --quiet gcloud compute target-http-proxies delete $SERVICE-proxy --quiet gcloud compute url-maps delete $SERVICE-map --quiet gcloud compute backend-services delete $SERVICE --global --quiet gcloud compute http-health-checks delete ah-health-check --quiet gcloud compute instance-groups managed delete $GROUP --zone $ZONE --quiet gcloud compute firewall-rules delete allow-http-hello-world --quiet gcloud compute instance-templates delete $TEMPLATE --quiet # [END getting_started_stop_gce] ;; esac set +v ================================================ FILE: gce/pom.xml ================================================ 4.0.0 war com.google.gce.demos getting-started-gce 1.0-SNAPSHOT com.google.cloud.samples shared-configuration 1.2.0 UTF-8 true true true 1.8 1.8 1.8 true local http localhost:8080 javax.servlet javax.servlet-api 4.0.1 javax.servlet jsp-api 2.0 junit junit 4.13.2 test com.google.guava guava 33.1.0-jre test org.seleniumhq.selenium selenium-server 4.0.0-alpha-2 test org.seleniumhq.selenium selenium-chrome-driver 4.10.0 test org.eclipse.jetty jetty-maven-plugin 9.4.47.v20220610 start-selenium pre-integration-test start stop-selenium post-integration-test stop stopPlzKThxBai 9283 60 ${project.build.directory}/${project.build.finalName}/WEB-INF/web.xml com.google.cloud.tools appengine-maven-plugin 2.4.4 GCLOUD_CONFIG GCLOUD_CONFIG ================================================ FILE: gce/scripts/startup-script.sh ================================================ #! /bin/bash # Copyright 2019 Google LLC # # 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 script] set -e set -v # Talk to the metadata server to get the project id PROJECTID=$(curl -s "http://metadata.google.internal/computeMetadata/v1/project/project-id" -H "Metadata-Flavor: Google") echo "Project ID: ${PROJECTID}" # Install dependencies from apt apt-get install -yq openjdk-11-jdk git maven mvn --version # Jetty Setup mkdir -p /opt/jetty/temp mkdir -p /var/log/jetty # Get Jetty curl -L https://repo1.maven.org/maven2/org/eclipse/jetty/jetty-distribution/9.4.13.v20181111/jetty-distribution-9.4.13.v20181111.tar.gz -o jetty9.tgz tar xf jetty9.tgz --strip-components=1 -C /opt/jetty # Add a Jetty User useradd --user-group --shell /bin/false --home-dir /opt/jetty/temp jetty cd /opt/jetty # Add running as "jetty" java -jar /opt/jetty/start.jar --add-to-startd=setuid cd / # Clone the source repository. git clone https://github.com/GoogleCloudPlatform/getting-started-java cd getting-started-java/gce # Build the .war file and rename. # very important - by renaming the war to root.war, it will run as the root servlet. mvn clean package -q mv target/getting-started-gce-1.0-SNAPSHOT.war /opt/jetty/webapps/root.war # Make sure "jetty" owns everything. chown --recursive jetty /opt/jetty # Configure the default paths for the Jetty service cp /opt/jetty/bin/jetty.sh /etc/init.d/jetty echo "JETTY_HOME=/opt/jetty" > /etc/default/jetty { echo "JETTY_BASE=/opt/jetty" echo "TMPDIR=/opt/jetty/temp" echo "JAVA_OPTIONS=-Djetty.http.port=80" echo "JETTY_LOGS=/var/log/jetty" } >> /etc/default/jetty # Reload daemon to pick up new service systemctl daemon-reload # Install logging monitor. The monitor will automatically pickup logs sent to syslog. curl -sSO https://dl.google.com/cloudagents/add-logging-agent-repo.sh sudo bash add-logging-agent-repo.sh --also-install service google-fluentd restart & service jetty start service jetty check echo "Startup Complete" # [END script] ================================================ FILE: gce/src/main/appengine/app.yaml ================================================ # Copyright 2019 Google LLC # # 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 runtime] runtime: java env: flex handlers: - url: /.* script: this field is required, but ignored # [START env_variables] env_variables: # Logging options JAVA_OPTS: >- -D.level=INFO # [END env_variables] # [END runtime] runtime_config: # Optional jdk: openjdk8 server: jetty9 ================================================ FILE: gce/src/main/java/com/example/getstarted/basicactions/HelloworldController.java ================================================ /* * Copyright 2019 Google LLC * * 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.getstarted.basicactions; import java.io.IOException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet(value = "/") public class HelloworldController extends HttpServlet { @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { resp.getWriter().write("Hello world - GCE!"); resp.setStatus(HttpServletResponse.SC_OK); } } ================================================ FILE: gce/src/main/webapp/WEB-INF/web.xml ================================================ / ================================================ FILE: gce/src/test/java/com/example/getstarted/basicactions/UserJourneyTestIT.java ================================================ /* * Copyright 2019 Google LLC * * 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.getstarted.basicactions; import static org.junit.Assert.assertTrue; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriverService; import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.remote.RemoteWebDriver; import org.openqa.selenium.remote.service.DriverService; @RunWith(JUnit4.class) @SuppressWarnings("checkstyle:AbbreviationAsWordInName") public class UserJourneyTestIT { private static DriverService service; private WebDriver driver; @BeforeClass public static void setupClass() throws Exception { service = ChromeDriverService.createDefaultService(); service.start(); } @Before public void setup() { driver = new RemoteWebDriver(service.getUrl(), new ChromeOptions()); } @After public void tearDown() { driver.quit(); } @Test @Ignore("b/138123046") public void userJourney() { driver.get("http://localhost:8080"); try { assertTrue(driver.getPageSource().contains("Hello world - GCE!")); } catch (Exception e) { System.err.println(driver.getPageSource()); throw e; } } } ================================================ FILE: helloworld-jsp/README.md ================================================ # Java Server Pages based Hello World app ## Requirements * [Apache Maven](http://maven.apache.org) 3.3.9 or greater * [Google Cloud SDK](https://cloud.google.com/sdk/) * `gcloud components install app-engine-java` * `gcloud components update` ## Setup Use either: * `gcloud init` * `gcloud beta auth application-default login` Set your project, the plugins in this example are configured to use this value from gcloud * `gcloud config set project ` We support building with [Maven](http://maven.apache.org/), [Gradle](https://gradle.org), and [IntelliJ IDEA](https://cloud.google.com/tools/intellij/docs/). The samples have files to support both Maven and Gradle. To use the IDE plugins, see the documentation pages above. ## Maven [Using Maven and the App Engine Plugin](https://cloud.google.com/appengine/docs/flexible/java/using-maven) & [Maven Plugin Goals and Parameters](https://cloud.google.com/appengine/docs/flexible/java/maven-reference) ### Running locally $ mvn jetty:run-exploded ### Deploying * In the `pom.xml`, update the [App Engine Maven Plugin](https://cloud.google.com/appengine/docs/standard/java/tools/maven-reference) with your Google Cloud Project Id: ``` com.google.cloud.tools appengine-maven-plugin 2.3.0 GCLOUD_CONFIG GCLOUD_CONFIG ``` **Note:** `GCLOUD_CONFIG` is a special version for autogenerating an App Engine version. Change this field to specify a specific version name. * Deploy your App ``` $ mvn package appengine:deploy ``` ## Gradle [Using Gradle and the App Engine Plugin](https://cloud.google.com/appengine/docs/flexible/java/using-gradle) & [Gradle Tasks and Parameters](https://cloud.google.com/appengine/docs/flexible/java/gradle-reference) ### Running locally $ gradle jettyRun ### Deploying $ gradle appengineDeploy ================================================ FILE: helloworld-jsp/build.gradle ================================================ // Copyright 2016 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. // [START gradle] buildscript { // Configuration for building repositories { jcenter() // Bintray's repository - a fast Maven Central mirror & more mavenCentral() } dependencies { classpath 'com.google.cloud.tools:appengine-gradle-plugin:2.5.0' classpath 'org.akhikhl.gretty:gretty:+' } } repositories { // repositories for JARs you access in your code maven { url 'https://maven-central.storage.googleapis.com' // Google's mirror of Maven Central } //maven { // url 'https://oss.sonatype.org/content/repositories/snapshots' // SNAPSHOT repository if needed //} jcenter() mavenCentral() } apply plugin: 'java' apply plugin: 'war' apply plugin: 'org.akhikhl.gretty' apply plugin: 'com.google.cloud.tools.appengine' dependencies { providedCompile 'javax.servlet:javax.servlet-api:4.0.1' providedCompile 'com.google.appengine:appengine:+' // Add your dependencies here. } // [START gretty] gretty { httpPort = 8080 contextPath = '/' servletContainer = 'jetty9' // What App Engine Flexible uses } // [END gretty] // [START model] appengine { deploy { // deploy configuration stopPreviousVersion = true // default - stop the current version promote = true // default - & make this the current version projectId = 'GCLOUD_CONFIG' // delegate to project in gcloud config version = 'GCLOUD_CONFIG' // delegate to gcloud to generate a version } } // [END model] group = 'com.example.appengine.helloworld-jsp' // Generated output GroupId version = '1.0-SNAPSHOT' // Version in generated output sourceCompatibility = 1.8 targetCompatibility = 1.8 // [END gradle] ================================================ FILE: helloworld-jsp/eclipse-launch-profiles/AppEngineDeploy.launch ================================================ ================================================ FILE: helloworld-jsp/eclipse-launch-profiles/AppEngineRun.launch ================================================ ================================================ FILE: helloworld-jsp/gradle/wrapper/gradle-wrapper.properties ================================================ #Wed Sep 07 11:48:19 PDT 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip ================================================ FILE: helloworld-jsp/gradlew ================================================ #!/usr/bin/env bash ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn ( ) { echo "$*" } die ( ) { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin, switch paths to Windows format before running java if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=$((i+1)) done case $i in (0) set -- ;; (1) set -- "$args0" ;; (2) set -- "$args0" "$args1" ;; (3) set -- "$args0" "$args1" "$args2" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules function splitJvmOpts() { JVM_OPTS=("$@") } eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong if [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]]; then cd "$(dirname "$0")" fi exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" ================================================ FILE: helloworld-jsp/gradlew.bat ================================================ @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS= @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: helloworld-jsp/nbactions.xml ================================================ CUSTOM-appengine:gcloud appengine:run jetty:run-exploded CUSTOM-appengine:gcloud appengine:deploy appengine:deploy ================================================ FILE: helloworld-jsp/pom.xml ================================================ 4.0.0 war 1.0-SNAPSHOT com.google.flex.demos helloworld-jsp 1.8 1.8 UTF-8 false 2.3.0 9.4.51.v20230217 com.google.cloud.samples shared-configuration 1.2.0 javax.servlet javax.servlet-api 4.0.1 jar provided target/${project.artifactId}-${project.version}/WEB-INF/classes com.google.cloud.tools appengine-maven-plugin 2.4.4 GCLOUD_CONFIG GCLOUD_CONFIG org.eclipse.jetty jetty-maven-plugin ${jetty.maven.plugin} ================================================ FILE: helloworld-jsp/src/main/appengine/app.yaml ================================================ # [START_EXCLUDE] #Copyright 2015 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. # [END_EXCLUDE] runtime: java env: flex runtime_config: # Optional jdk: openjdk8 server: jetty9 handlers: - url: /.* script: this field is required, but ignored manual_scaling: instances: 1 beta_settings: java_quickstart: true ================================================ FILE: helloworld-jsp/src/main/java/org/example/appengine/hello/HelloInfo.java ================================================ /* Copyright 2016 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. */ package org.example.appengine.hello; // [START example] /** * Generate some simple information. */ public class HelloInfo { public static String getInfo() { return "Version: " + System.getProperty("java.version") + " OS: " + System.getProperty("os.name") + " User: " + System.getProperty("user.name"); } } // [END example] ================================================ FILE: helloworld-jsp/src/main/webapp/WEB-INF/logging.properties ================================================ # 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 ================================================ FILE: helloworld-jsp/src/main/webapp/WEB-INF/web.xml ================================================ hello.jsp ================================================ FILE: helloworld-jsp/src/main/webapp/hello.jsp ================================================ <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page import="org.example.appengine.hello.HelloInfo" %> <%-- ~ Copyright (c) 2016 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. --%>

Hello!

This is <%= HelloInfo.getInfo() %>.

================================================ FILE: mvnw.cmd ================================================ @REM ---------------------------------------------------------------------------- @REM Licensed to the Apache Software Foundation (ASF) under one @REM or more contributor license agreements. See the NOTICE file @REM distributed with this work for additional information @REM regarding copyright ownership. The ASF licenses this file @REM to you under the Apache License, Version 2.0 (the @REM "License"); you may not use this file except in compliance @REM with the License. You may obtain a copy of the License at @REM @REM http://www.apache.org/licenses/LICENSE-2.0 @REM @REM Unless required by applicable law or agreed to in writing, @REM software distributed under the License is distributed on an @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @REM KIND, either express or implied. See the License for the @REM specific language governing permissions and limitations @REM under the License. @REM ---------------------------------------------------------------------------- @REM ---------------------------------------------------------------------------- @REM Maven2 Start Up Batch script @REM @REM Required ENV vars: @REM JAVA_HOME - location of a JDK home dir @REM @REM Optional ENV vars @REM M2_HOME - location of maven2's installed home dir @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven @REM e.g. to debug Maven itself, use @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files @REM ---------------------------------------------------------------------------- @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' @echo off @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% @REM set %HOME% to equivalent of $HOME if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") @REM Execute a user defined script before this one if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre @REM check for pre script, once with legacy .bat ending and once with .cmd ending if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" :skipRcPre @setlocal set ERROR_CODE=0 @REM To isolate internal variables from possible post scripts, we use another setlocal @setlocal @REM ==== START VALIDATION ==== if not "%JAVA_HOME%" == "" goto OkJHome echo. echo Error: JAVA_HOME not found in your environment. >&2 echo Please set the JAVA_HOME variable in your environment to match the >&2 echo location of your Java installation. >&2 echo. goto error :OkJHome if exist "%JAVA_HOME%\bin\java.exe" goto init echo. echo Error: JAVA_HOME is set to an invalid directory. >&2 echo JAVA_HOME = "%JAVA_HOME%" >&2 echo Please set the JAVA_HOME variable in your environment to match the >&2 echo location of your Java installation. >&2 echo. goto error @REM ==== END VALIDATION ==== :init set MAVEN_CMD_LINE_ARGS=%MAVEN_CONFIG% %* @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". @REM Fallback to current working directory if not found. set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir set EXEC_DIR=%CD% set WDIR=%EXEC_DIR% :findBaseDir IF EXIST "%WDIR%"\.mvn goto baseDirFound cd .. IF "%WDIR%"=="%CD%" goto baseDirNotFound set WDIR=%CD% goto findBaseDir :baseDirFound set MAVEN_PROJECTBASEDIR=%WDIR% cd "%EXEC_DIR%" goto endDetectBaseDir :baseDirNotFound set MAVEN_PROJECTBASEDIR=%EXEC_DIR% cd "%EXEC_DIR%" :endDetectBaseDir IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig @setlocal EnableExtensions EnableDelayedExpansion for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% :endReadAdditionalConfig SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" set WRAPPER_JAR=""%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"" set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain # avoid using MAVEN_CMD_LINE_ARGS below since that would loose parameter escaping in %* %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* if ERRORLEVEL 1 goto error goto end :error set ERROR_CODE=1 :end @endlocal & set ERROR_CODE=%ERROR_CODE% if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost @REM check for post script, once with legacy .bat ending and once with .cmd ending if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" :skipRcPost @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' if "%MAVEN_BATCH_PAUSE%" == "on" pause if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% exit /B %ERROR_CODE% ================================================ FILE: renovate.json ================================================ { "extends": [ "config:base" ], "packageRules": [ { "packagePatterns": [ "^com.google.appengine:" ], "groupName": "AppEngine packages" }, { "packagePatterns": [ "^io.grpc:grpc-" ], "groupName": "gRPC packages" }, { "packagePatterns": [ "^io.vertx:vertex-" ], "groupName": "Vertx packages" }, { "packagePatterns": [ "^org.jetbrains.kotlin:" ], "groupName": "Kotlin packages" }, { "packagePatterns": [ "^org.springframework.boot:" ], "groupName": "Spring packages" }, { "packagePatterns": [ "^io.opencensus:" ], "groupName": "Opencensus packages" } ], "labels": [ "automerge" ], "prConcurrentLimit": 0, "rebaseWhen": "never", "dependencyDashboard": true, "dependencyDashboardLabels": [ "type: process" ], "constraints": { "java": "1.8" } }