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®
[](https://circleci.com/gh/GoogleCloudPlatform/getting-started-java)
[](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.toolsappengine-maven-plugin2.3.0GCLOUD_CONFIGGCLOUD_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.0war1.0-SNAPSHOTcom.example.appengine_standard_java8helloworld_gae_toolingcom.google.cloud.samplesshared-configuration1.2.01.81.8UTF-8UTF-8truetruefalsecom.google.appengineappengine-api-1.0-sdk2.0.15javax.servletjavax.servlet-api4.0.1jarprovidedjstljstl1.2com.google.appengineappengine-testing2.0.15testcom.google.appengineappengine-api-stubs2.0.15testcom.google.appengineappengine-tools-sdk2.0.15testcom.google.truthtruth1.2.0testjunitjunit4.13.2testorg.mockitomockito-core4.5.0test${project.build.directory}/${project.build.finalName}/WEB-INF/classescom.google.cloud.toolsappengine-maven-plugin2.4.4GCLOUD_CONFIGGCLOUD_CONFIG1maven-compiler-plugin3.11.01.81.8org.codehaus.mojoversions-maven-plugin2.11.0compiledisplay-dependency-updatesdisplay-plugin-updatesmaven-war-plugin3.4.0maven-clean-plugin3.3.2maven-install-plugin3.1.1maven-surefire-plugin3.1.2maven-site-plugin3.12.1maven-resources-plugin3.3.0maven-deploy-plugin3.0.0maven-enforcer-plugin3.1.0enforce-mavenenforce3.5Best Practice is to always define plugin versions!truetrueclean,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-gaejava8true
================================================
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
================================================
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.toolsappengine-maven-plugin2.3.0GCLOUD_CONFIGGCLOUD_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:devserverappengine:devserverappengine:devserverCUSTOM-appengine:updateappengine:updateappengine:updateCUSTOM-appengine:rollbackappengine:rollbackappengine:rollbackCUSTOM-appengine:update_cronappengine:update_cronappengine:update_cronCUSTOM-appengine:update_dosappengine:update_dosappengine:update_dosCUSTOM-appengine:update_indexesappengine:update_indexesappengine:update_indexesCUSTOM-appengine:update_queuesappengine:update_queuesappengine:update_queuesrunwarearejbappengine:devservertrue
================================================
FILE: appengine-standard-java8/kotlin-appengine-standard/pom.xml
================================================
4.0.0war1.0-SNAPSHOTcom.google.appengine.demoskotlin-appengine-standardcom.google.cloud.samplesshared-configuration1.2.01.81.3.721.81.8com.google.appengineappengine-api-1.0-sdk2.0.15jstljstl1.2javax.servletjavax.servlet-api4.0.1jarorg.jetbrains.kotlinkotlin-stdlib1.6.0target/${project.artifactId}-${project.version}/WEB-INF/classes${project.basedir}/src/main/kotlin${project.basedir}/src/test/kotlinkotlin-maven-pluginorg.jetbrains.kotlin1.5.31compilecompilecompiletest-compiletest-compiletest-compileorg.apache.maven.pluginsmaven-war-plugin3.3.2truefalsecom.google.cloud.toolsappengine-maven-plugin2.4.4GCLOUD_CONFIGGCLOUD_CONFIGmaven-compiler-plugin3.11.01.81.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
================================================
truejava8
================================================
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.toolsappengine-maven-plugin2.3.0GCLOUD_CONFIGGCLOUD_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:devserverappengine:devserverappengine:devserverCUSTOM-appengine:updateappengine:updateappengine:updateCUSTOM-appengine:rollbackappengine:rollbackappengine:rollbackCUSTOM-appengine:update_cronappengine:update_cronappengine:update_cronCUSTOM-appengine:update_dosappengine:update_dosappengine:update_dosCUSTOM-appengine:update_indexesappengine:update_indexesappengine:update_indexesCUSTOM-appengine:update_queuesappengine:update_queuesappengine:update_queuesrunwarearejbappengine:devservertrue
================================================
FILE: appengine-standard-java8/kotlin-sb-appengine-standard/pom.xml
================================================
4.0.0war1.0-SNAPSHOTcom.google.appengine.demoskotlin-sb-appengine-standardcom.google.cloud.samplesshared-configuration1.2.01.81.6.01.81.8com.google.appengineappengine-api-1.0-sdk2.0.15jstljstl1.2org.springframework.bootspring-boot-starter-web3.1.1org.slf4jjul-to-slf4jorg.springframework.bootspring-boot-starter-tomcatjavax.servletjavax.servlet-api4.0.1jarorg.jetbrains.kotlinkotlin-stdlib-jdk8${kotlin.version}org.jetbrains.kotlinkotlin-reflect${kotlin.version}org.jetbrains.kotlinkotlin-maven-allopen${kotlin.version}target/${project.artifactId}-${project.version}/WEB-INF/classes${project.basedir}/src/main/kotlin${project.basedir}/src/test/kotlinkotlin-maven-pluginorg.jetbrains.kotlin${kotlin.version}spring${java.version}compilecompilecompiletest-compiletest-compiletest-compileorg.jetbrains.kotlinkotlin-maven-allopen${kotlin.version}org.apache.maven.pluginsmaven-war-plugin3.3.2truefalsecom.google.cloud.toolsappengine-maven-plugin2.4.4GCLOUD_CONFIGGCLOUD_CONFIGmaven-compiler-plugin3.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
================================================
truejava8
================================================
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.toolsappengine-maven-plugin2.3.0GCLOUD_CONFIGGCLOUD_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.0war1.0-SNAPSHOTcom.google.appengine.demoskotlin-spark-appengine-standardcom.google.cloud.samplesshared-configuration1.2.01.81.8com.google.appengineappengine-api-1.0-sdk2.0.15jstljstl1.2javax.servletjavax.servlet-api4.0.1jarorg.jetbrains.kotlinkotlin-stdlib-jre81.2.71com.sparkjavaspark-kotlin1.0.0-alphaorg.eclipse.jettyjetty-serverorg.eclipse.jettyjetty-ioorg.eclipse.jettyjetty-securityorg.eclipse.jettyjetty-webapporg.eclipse.jettyjetty-utilorg.eclipse.jettyjetty-clientorg.eclipse.jetty.websocketwebsocket-apiorg.eclipse.jetty.websocketwebsocket-clientorg.eclipse.jetty.websocketwebsocket-commonorg.eclipse.jetty.websocketwebsocket-serverorg.slf4jslf4j-apiorg.eclipse.jetty.websocketwebsocket-servletorg.slf4jslf4j-api2.0.11target/${project.artifactId}-${project.version}/WEB-INF/classes${project.basedir}/src/main/kotlin${project.basedir}/src/test/kotlinkotlin-maven-pluginorg.jetbrains.kotlin1.5.31compilecompilecompiletest-compiletest-compiletest-compileorg.apache.maven.pluginsmaven-war-plugin3.3.2truecom.google.cloud.toolsappengine-maven-plugin2.4.4GCLOUD_CONFIGGCLOUD_CONFIGmaven-compiler-plugin3.11.01.81.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
================================================
truejava8
================================================
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.0warcom.example.getstartedbackground-processing1.0-SNAPSHOTcom.google.cloud.samplesshared-configuration1.2.0MY_PROJECTfalseUTF-81.81.8truetruefalsefalse9.4.51.v20230217com.google.cloudgoogle-cloud-firestore3.13.2com.google.cloudgoogle-cloud-translate2.20.0com.google.cloudgoogle-cloud-pubsub1.123.17javax.servletjavax.servlet-api4.0.1com.google.guavaguava33.1.0-jrecompileio.opencensusopencensus-contrib-http-util0.31.1jstljstl1.2junitjunit4.13.2testorg.seleniumhq.seleniumselenium-server4.0.0-alpha-2org.seleniumhq.seleniumselenium-chrome-driver4.10.0background-processing${project.build.directory}/${project.build.finalName}/WEB-INF/classes
org.eclipse.jettyjetty-maven-plugin${jetty.version}com.google.cloud.toolsjib-maven-plugin3.4.0gcr.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/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.0warcom.example.getstartedbookshelf-cloud-run1.0-SNAPSHOTcom.google.cloud.samplesshared-configuration1.2.0MY_PROJECTfalseUTF-81.81.8truetruefalse9.4.51.v20230217javax.servletjavax.servlet-api4.0.1com.google.cloudgoogle-cloud-firestore3.13.2joda-timejoda-time2.12.6com.google.guavaguava33.1.0-jrecompileio.opencensusopencensus-contrib-http-util0.31.1jstljstl1.2com.google.cloudgoogle-cloud-storage2.23.0commons-fileuploadcommons-fileupload1.5commons-iocommons-io2.13.0bookshelf-cloud-run${project.build.directory}/${project.build.finalName}/WEB-INF/classes
org.eclipse.jettyjetty-maven-plugin${jetty.version}com.google.cloud.toolsjib-maven-plugin3.4.0jetty:10.0.9-jre11java,-jar,/usr/local/jetty/start.jargcr.io/${gcloud.appId}/bookshelforg.apache.maven.pluginsmaven-war-plugin3.3.2false
================================================
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
================================================
listcom.example.getstarted.basicactions.ListBookServlet1list//bookscreatecom.example.getstarted.basicactions.CreateBookServletcreate/createupdatecom.example.getstarted.basicactions.UpdateBookServletupdate/updatereadcom.example.getstarted.basicactions.ReadBookServletread/readdeletecom.example.getstarted.basicactions.DeleteBookServletdelete/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
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.toolsappengine-maven-plugin2.3.0GCLOUD_CONFIGbookshelf
```
* 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.0warcom.example.standard.gettingstartedbookshelf-standard-21.0-SNAPSHOTcom.google.cloud.samplesshared-configuration1.2.0datastorebookshelfDATABASE-connectionName-HERE
rootMYSQL-ROOT-PASSWORD-HEREUTF-81.81.8truetruetruelocalcom.google.cloud.sqlmysql-socket-factory1.6.1mysqlmysql-connector-java8.0.30com.google.api-clientgoogle-api-client-appengine2.0.0com.google.appengineappengine-api-1.0-sdk2.0.15javax.servletjavax.servlet-api4.0.1providedjavax.servletjsp-api2.0jstljstl1.2taglibsstandard1.1.2com.google.guavaguava33.1.0-jrecompilejoda-timejoda-time2.12.6${project.build.directory}/${project.build.finalName}/WEB-INF/classes
com.google.cloud.toolsappengine-maven-plugin2.4.4GCLOUD_CONFIGbookshelforg.apache.maven.pluginsmaven-compiler-plugin3.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
================================================
truetruetrue
================================================
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
================================================
DatastoreSessionFiltercom.example.getstarted.util.DatastoreSessionFilterDatastoreSessionFilter//books/books/mine/create/delete/login/logout/oauth2callback/read/updatelistcom.example.getstarted.basicactions.ListBookServlet1list//bookscreatecom.example.getstarted.basicactions.CreateBookServletcreate/createupdatecom.example.getstarted.basicactions.UpdateBookServletupdate/updatereadcom.example.getstarted.basicactions.ReadBookServletread/readdeletecom.example.getstarted.basicactions.DeleteBookServletdelete/deletebookshelf.storageType${bookshelf.storageType}sql.urlRemoteGAEjdbc:google:mysql://${sql.instanceName}/${sql.dbName}?user=${sql.userName}&password=${sql.password}sql.urlRemotejdbc:mysql://google/${sql.dbName}?cloudSqlInstance=${sql.instanceName}&socketFactory=com.google.cloud.sql.mysql.SocketFactory&user=${sql.userName}&password=${sql.password}sql.urlLocaljdbc: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
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.toolsappengine-maven-plugin2.3.0GCLOUD_CONFIGbookshelf
```
* 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.0warcom.example.standard.gettingstartedbookshelf-standard-31.0-SNAPSHOTcom.google.cloud.samplesshared-configuration1.2.0datastorebookshelfDATABASE-connectionName-HERE
rootMYSQL-ROOT-PASSWORD-HEREBUCKET-NAME-HEREUTF-81.81.8truetruetruelocalcom.google.cloud.sqlmysql-socket-factory1.6.1mysqlmysql-connector-java8.0.30com.google.api-clientgoogle-api-client-appengine2.0.0com.google.appengineappengine-api-1.0-sdk2.0.15javax.servletjavax.servlet-api4.0.1providedjavax.servletjsp-api2.0jstljstl1.2taglibsstandard1.1.2com.google.cloudgoogle-cloud-storage2.23.0com.google.guavaguava33.1.0-jrecompilejoda-timejoda-time2.12.6commons-fileuploadcommons-fileupload1.5${project.build.directory}/${project.build.finalName}/WEB-INF/classes
com.google.cloud.toolsappengine-maven-plugin2.4.4GCLOUD_CONFIGbookshelforg.apache.maven.pluginsmaven-compiler-plugin3.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
================================================
truetruetrue
================================================
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
================================================
DatastoreSessionFiltercom.example.getstarted.util.DatastoreSessionFilterDatastoreSessionFilter//books/books/mine/create/delete/login/logout/oauth2callback/read/updatelistcom.example.getstarted.basicactions.ListBookServlet1list//bookscreatecom.example.getstarted.basicactions.CreateBookServletcreate/createupdatecom.example.getstarted.basicactions.UpdateBookServletupdate/updatereadcom.example.getstarted.basicactions.ReadBookServletread/readdeletecom.example.getstarted.basicactions.DeleteBookServletdelete/deletebookshelf.storageType${bookshelf.storageType}bookshelf.bucket${bookshelf.bucket}sql.urlRemoteGAEjdbc:google:mysql://${sql.instanceName}/${sql.dbName}?user=${sql.userName}&password=${sql.password}sql.urlRemotejdbc:mysql://google/${sql.dbName}?cloudSqlInstance=${sql.instanceName}&socketFactory=com.google.cloud.sql.mysql.SocketFactory&user=${sql.userName}&password=${sql.password}sql.urlLocaljdbc: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
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.toolsappengine-maven-plugin2.3.0GCLOUD_CONFIGbookshelf
```
* 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.0warcom.example.standard.gettingstartedbookshelf-standard-41.0-SNAPSHOTcom.google.cloud.samplesshared-configuration1.2.0../datastorebookshelfDATABASE-connectionName-HERE
rootMYSQL-ROOT-PASSWORD-HEREBUCKET-NAME-HEREUTF-81.81.8truetruetruelocalcom.google.cloud.sqlmysql-socket-factory1.6.1mysqlmysql-connector-java8.0.30com.google.api-clientgoogle-api-client-appengine2.0.0com.google.appengineappengine-api-1.0-sdk2.0.15javax.servletjavax.servlet-api4.0.1providedjavax.servletjsp-api2.0jstljstl1.2taglibsstandard1.1.2com.google.cloudgoogle-cloud-storage2.23.0com.google.guavaguava33.1.0-jrecompilejoda-timejoda-time2.12.6commons-fileuploadcommons-fileupload1.5${project.build.directory}/${project.build.finalName}/WEB-INF/classes
com.google.cloud.toolsappengine-maven-plugin2.4.4GCLOUD_CONFIGbookshelforg.apache.maven.pluginsmaven-compiler-plugin3.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
================================================
truetruetrue
================================================
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
================================================
DatastoreSessionFiltercom.example.getstarted.util.DatastoreSessionFilterDatastoreSessionFilter//books/books/mine/create/delete/login/logout/read/updateLogoutFiltercom.example.getstarted.auth.LogoutFilterLogoutFilter/logoutListByUserFiltercom.example.getstarted.auth.ListByUserFilterListByUserFilter/books/minelistcom.example.getstarted.basicactions.ListBookServlet1list//bookscreatecom.example.getstarted.basicactions.CreateBookServletcreate/createupdatecom.example.getstarted.basicactions.UpdateBookServletupdate/updatereadcom.example.getstarted.basicactions.ReadBookServletread/readdeletecom.example.getstarted.basicactions.DeleteBookServletdelete/deletelogoutcom.example.getstarted.auth.LogoutServletlogout/logoutlogincom.example.getstarted.auth.LoginServletlogin/loginlistbyusercom.example.getstarted.basicactions.ListByUserServletlistbyuser/books/minebookshelf.storageType${bookshelf.storageType}bookshelf.bucket${bookshelf.bucket}sql.urlRemoteGAEjdbc:google:mysql://${sql.instanceName}/${sql.dbName}?user=${sql.userName}&password=${sql.password}sql.urlRemotejdbc:mysql://google/${sql.dbName}?cloudSqlInstance=${sql.instanceName}&socketFactory=com.google.cloud.sql.mysql.SocketFactory&user=${sql.userName}&password=${sql.password}sql.urlLocaljdbc: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
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.toolsappengine-maven-plugin2.3.0GCLOUD_CONFIGbookshelf
```
* 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.0warcom.example.standard.gettingstartedbookshelf-standard-51.0-SNAPSHOTcom.google.cloud.samplesshared-configuration1.2.0../datastorebookshelfDATABASE-connectionName-HERE
rootMYSQL-ROOT-PASSWORD-HEREBUCKET-NAME-HEREUTF-81.81.8truetruetruelocalcom.google.cloud.sqlmysql-socket-factory1.6.1mysqlmysql-connector-java8.0.30com.google.api-clientgoogle-api-client-appengine2.0.0com.google.appengineappengine-api-1.0-sdk2.0.15javax.servletjavax.servlet-api4.0.1providedjavax.servletjsp-api2.0jstljstl1.2taglibsstandard1.1.2com.google.cloudgoogle-cloud-storage2.23.0com.google.guavaguava33.1.0-jrecompilejoda-timejoda-time2.12.6commons-fileuploadcommons-fileupload1.5${project.build.directory}/${project.build.finalName}/WEB-INF/classes
com.google.cloud.toolsappengine-maven-plugin2.4.4GCLOUD_CONFIGbookshelforg.apache.maven.pluginsmaven-compiler-plugin3.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
================================================
truetruetrue
================================================
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
================================================
DatastoreSessionFiltercom.example.getstarted.util.DatastoreSessionFilterDatastoreSessionFilter//books/books/mine/create/delete/login/logout/read/updateLogoutFiltercom.example.getstarted.auth.LogoutFilterLogoutFilter/logoutListByUserFiltercom.example.getstarted.auth.ListByUserFilterListByUserFilter/books/minelistcom.example.getstarted.basicactions.ListBookServlet1list//bookscreatecom.example.getstarted.basicactions.CreateBookServletcreate/createupdatecom.example.getstarted.basicactions.UpdateBookServletupdate/updatereadcom.example.getstarted.basicactions.ReadBookServletread/readdeletecom.example.getstarted.basicactions.DeleteBookServletdelete/deletelogoutcom.example.getstarted.auth.LogoutServletlogout/logoutlogincom.example.getstarted.auth.LoginServletlogin/loginlistbyusercom.example.getstarted.basicactions.ListByUserServletlistbyuser/books/minebookshelf.storageType${bookshelf.storageType}bookshelf.bucket${bookshelf.bucket}sql.urlRemoteGAEjdbc:google:mysql://${sql.instanceName}/${sql.dbName}?user=${sql.userName}&password=${sql.password}sql.urlRemotejdbc:mysql://google/${sql.dbName}?cloudSqlInstance=${sql.instanceName}&socketFactory=com.google.cloud.sql.mysql.SocketFactory&user=${sql.userName}&password=${sql.password}sql.urlLocaljdbc: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
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.0warcom.google.gce.demosgetting-started-gce1.0-SNAPSHOTcom.google.cloud.samplesshared-configuration1.2.0UTF-8truetruetrue1.81.81.8truelocalhttplocalhost:8080javax.servletjavax.servlet-api4.0.1javax.servletjsp-api2.0junitjunit4.13.2testcom.google.guavaguava33.1.0-jretestorg.seleniumhq.seleniumselenium-server4.0.0-alpha-2testorg.seleniumhq.seleniumselenium-chrome-driver4.10.0testorg.eclipse.jettyjetty-maven-plugin9.4.47.v20220610start-seleniumpre-integration-teststartstop-seleniumpost-integration-teststopstopPlzKThxBai928360${project.build.directory}/${project.build.finalName}/WEB-INF/web.xmlcom.google.cloud.toolsappengine-maven-plugin2.4.4GCLOUD_CONFIGGCLOUD_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.toolsappengine-maven-plugin2.3.0GCLOUD_CONFIGGCLOUD_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:gcloudappengine:runjetty:run-explodedCUSTOM-appengine:gcloudappengine:deployappengine:deploy
================================================
FILE: helloworld-jsp/pom.xml
================================================
4.0.0war1.0-SNAPSHOTcom.google.flex.demoshelloworld-jsp1.81.8UTF-8false2.3.09.4.51.v20230217com.google.cloud.samplesshared-configuration1.2.0javax.servletjavax.servlet-api4.0.1jarprovidedtarget/${project.artifactId}-${project.version}/WEB-INF/classescom.google.cloud.toolsappengine-maven-plugin2.4.4GCLOUD_CONFIGGCLOUD_CONFIGorg.eclipse.jettyjetty-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"
}
}