diff --git a/.bazeliskrc b/.bazeliskrc
index 04cd2fe7ca..6b386cf868 100644
--- a/.bazeliskrc
+++ b/.bazeliskrc
@@ -1,2 +1,3 @@
# See https://github.com/bazelbuild/bazelisk
-USE_BAZEL_VERSION=6.0.0
+# As per b/302171264#comment5, 6.4.0 supports Java 21.
+USE_BAZEL_VERSION=6.4.0
diff --git a/.cloudbuild/cloudbuild-test-a.yaml b/.cloudbuild/cloudbuild-test-a.yaml
new file mode 100644
index 0000000000..5da2f41078
--- /dev/null
+++ b/.cloudbuild/cloudbuild-test-a.yaml
@@ -0,0 +1,33 @@
+# Copyright 2023 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.
+
+timeout: 7200s # 2 hours
+substitutions:
+ _SHARED_DEPENDENCIES_VERSION: '3.24.0' # {x-version-update:google-cloud-shared-dependencies:current}
+ _JAVA_SHARED_CONFIG_VERSION: '1.7.1'
+
+steps:
+ # GraalVM A build
+ - name: gcr.io/cloud-builders/docker
+ args: ["build", "-t", "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_a:${_SHARED_DEPENDENCIES_VERSION}", "--file", "graalvm_a.Dockerfile", "--build-arg", "JAVA_SHARED_CONFIG_VERSION=$_JAVA_SHARED_CONFIG_VERSION", "."]
+ dir: .cloudbuild
+ id: graalvm-a-build
+ waitFor: ["-"]
+ - name: gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_a:${_SHARED_DEPENDENCIES_VERSION}
+ entrypoint: bash
+ args: [ './.kokoro/presubmit/downstream-build.sh' ]
+ waitFor: [ "graalvm-a-build" ]
+ env:
+ - 'MODULES_UNDER_TEST=java-kms'
+ - 'GOOGLE_CLOUD_PROJECT=cloud-java-ci-test'
\ No newline at end of file
diff --git a/.cloudbuild/cloudbuild-test-b.yaml b/.cloudbuild/cloudbuild-test-b.yaml
new file mode 100644
index 0000000000..5fa0b16766
--- /dev/null
+++ b/.cloudbuild/cloudbuild-test-b.yaml
@@ -0,0 +1,33 @@
+# Copyright 2023 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.
+
+timeout: 7200s # 2 hours
+substitutions:
+ _SHARED_DEPENDENCIES_VERSION: '3.24.0' # {x-version-update:google-cloud-shared-dependencies:current}
+ _JAVA_SHARED_CONFIG_VERSION: '1.7.1'
+
+steps:
+ # GraalVM A build
+ - name: gcr.io/cloud-builders/docker
+ args: ["build", "-t", "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_b:${_SHARED_DEPENDENCIES_VERSION}", "--file", "graalvm_b.Dockerfile", "--build-arg", "JAVA_SHARED_CONFIG_VERSION=$_JAVA_SHARED_CONFIG_VERSION", "."]
+ dir: .cloudbuild
+ id: graalvm-b-build
+ waitFor: ["-"]
+ - name: gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_b:${_SHARED_DEPENDENCIES_VERSION}
+ entrypoint: bash
+ args: [ './.kokoro/presubmit/downstream-build.sh' ]
+ waitFor: [ "graalvm-b-build" ]
+ env:
+ - 'MODULES_UNDER_TEST=java-kms'
+ - 'GOOGLE_CLOUD_PROJECT=cloud-java-ci-test'
\ No newline at end of file
diff --git a/.cloudbuild/cloudbuild.yaml b/.cloudbuild/cloudbuild.yaml
new file mode 100644
index 0000000000..ae3d152d9f
--- /dev/null
+++ b/.cloudbuild/cloudbuild.yaml
@@ -0,0 +1,37 @@
+# Copyright 2023 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.
+
+timeout: 7200s # 2 hours
+substitutions:
+ _SHARED_DEPENDENCIES_VERSION: '3.24.0' # {x-version-update:google-cloud-shared-dependencies:current}
+ _JAVA_SHARED_CONFIG_VERSION: '1.7.1'
+steps:
+ # GraalVM A build
+ - name: gcr.io/cloud-builders/docker
+ args: ["build", "-t", "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_a:${_SHARED_DEPENDENCIES_VERSION}", "--file", "graalvm_a.Dockerfile", "--build-arg", "JAVA_SHARED_CONFIG_VERSION=$_JAVA_SHARED_CONFIG_VERSION", "."]
+ dir: .cloudbuild
+ id: graalvm-a-build
+ waitFor: ["-"]
+
+ # GraalVM B build
+ - name: gcr.io/cloud-builders/docker
+ args: [ "build", "-t", "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_b:${_SHARED_DEPENDENCIES_VERSION}", "--file", "graalvm_b.Dockerfile", "--build-arg", "JAVA_SHARED_CONFIG_VERSION=$_JAVA_SHARED_CONFIG_VERSION", "." ]
+ dir: .cloudbuild
+ id: graalvm-b-build
+ waitFor: [ "-" ]
+
+
+images:
+ - gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_a:${_SHARED_DEPENDENCIES_VERSION}
+ - gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_b:${_SHARED_DEPENDENCIES_VERSION}
diff --git a/.cloudbuild/google-cloud-sdk.repo b/.cloudbuild/google-cloud-sdk.repo
new file mode 100644
index 0000000000..8c654dc317
--- /dev/null
+++ b/.cloudbuild/google-cloud-sdk.repo
@@ -0,0 +1,8 @@
+[google-cloud-sdk]
+name=Google Cloud SDK
+baseurl=https://packages.cloud.google.com/yum/repos/cloud-sdk-el7-x86_64
+enabled=1
+gpgcheck=1
+repo_gpgcheck=0
+gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg
+ https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
diff --git a/.cloudbuild/graalvm_a.Dockerfile b/.cloudbuild/graalvm_a.Dockerfile
new file mode 100644
index 0000000000..40fa809aa8
--- /dev/null
+++ b/.cloudbuild/graalvm_a.Dockerfile
@@ -0,0 +1,19 @@
+# Copyright 2023 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.
+
+
+ARG JAVA_SHARED_CONFIG_VERSION
+
+FROM gcr.io/cloud-devrel-public-resources/graalvm_a:$JAVA_SHARED_CONFIG_VERSION
+
diff --git a/java-common-protos/.github/auto-label.yaml b/.cloudbuild/graalvm_b.Dockerfile
similarity index 79%
rename from java-common-protos/.github/auto-label.yaml
rename to .cloudbuild/graalvm_b.Dockerfile
index 4caef688b7..8bd84e53ed 100644
--- a/java-common-protos/.github/auto-label.yaml
+++ b/.cloudbuild/graalvm_b.Dockerfile
@@ -1,4 +1,4 @@
-# Copyright 2021 Google LLC
+# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -11,5 +11,8 @@
# 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.
-requestsize:
- enabled: true
+
+
+ARG JAVA_SHARED_CONFIG_VERSION
+
+FROM gcr.io/cloud-devrel-public-resources/graalvm_b:$JAVA_SHARED_CONFIG_VERSION
\ No newline at end of file
diff --git a/.github/release-please.yml b/.github/release-please.yml
index aebf5d41f0..c964bc6236 100644
--- a/.github/release-please.yml
+++ b/.github/release-please.yml
@@ -2,4 +2,5 @@ releaseType: java-yoshi
bumpMinorPreMajor: true
handleGHRelease: true
primaryBranch: main
-extraFiles: ["WORKSPACE"]
+manifest: true
+extraFiles: ["WORKSPACE", ".cloudbuild/cloudbuild.yaml", ".cloudbuild/cloudbuild-test-a.yaml", ".cloudbuild/cloudbuild-test-b.yaml" ]
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index d1d0ae6bf4..64b648607d 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-22.04
strategy:
matrix:
- java: [ 11, 17 ]
+ java: [ 11, 17]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
@@ -48,13 +48,35 @@ jobs:
pushd /tmp/java-compute/google-cloud-compute-small-v1-java
./gradlew clean build publishToMavenLocal sourcesJar allJars
popd
-
+ build-java-21:
+ name: "build(21) except self-service clients"
+ # Support for Java 21 is available for all use cases except self-service clients.
+ runs-on: ubuntu-22.04
+ steps:
+ - uses: actions/checkout@v3
+ - uses: actions/setup-java@v3
+ with:
+ java-version: 21
+ distribution: temurin
+ cache: maven
+ - run: java -version
+ - name: Unit Tests
+ run: |
+ mvn test --batch-mode --no-transfer-progress -Dcheckstyle.skip \
+ -Dfmt.skip -DenableTestCoverage
+ - run: bazelisk version
+ - name: Install Maven modules
+ run: |
+ mvn install -B -ntp -DskipTests -Dclirr.skip -Dcheckstyle.skip
+ - name: Integration Tests
+ run: |
+ bazelisk --batch test //test/integration/...
build-java8-except-gapic-generator-java:
name: "build(8) except for gapic-generator-java"
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- # Java 8 tests uses JDK 11 to compile and JDK 8 to run tests.
+ # Java 8 tests uses JDK 17 to compile and JDK 8 to run tests.
- uses: actions/setup-java@v3
with:
java-version: 8
@@ -69,7 +91,7 @@ jobs:
shell: bash
run: |
set -x
- export JAVA_HOME=$JAVA11_HOME
+ export JAVA_HOME=$JAVA_HOME
export PATH=${JAVA_HOME}/bin:$PATH
# Maven surefire plugin lets us to specify the JVM when running tests via
# the "jvm" system property.
@@ -153,15 +175,6 @@ jobs:
# testlib modules of gax
run: mvn package clirr:check -DskipTests
- requirements-kokoro:
- runs-on: ubuntu-latest
- container: gcr.io/cloud-devrel-public-resources/java11
- steps:
- - uses: actions/checkout@v3
- - run: python3 --version
- - run: python3 -m pip install --require-hashes -r .kokoro/requirements.txt
- - run: python3 -m releasetool publish-reporter-script > /dev/null
-
showcase:
runs-on: ubuntu-22.04
strategy:
@@ -282,7 +295,29 @@ jobs:
run: |
mvn install -B -ntp -DskipTests -Dclirr.skip -Dcheckstyle.skip
- name: Validate gapic-generator-java-bom
- uses: googleapis/java-cloud-bom/tests/validate-bom@a0bfee488b0d74f9b68918ae01d4bf130520d2c5
+ uses: googleapis/java-cloud-bom/tests/validate-bom@8bc17e9ae3c6354f04df2fdf2d57aeafa63add66
with:
bom-path: gapic-generator-java-bom/pom.xml
+ unmanaged_dependency_check:
+ runs-on: ubuntu-22.04
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.pull_request.head.sha }}
+ - uses: actions/setup-java@v3
+ with:
+ java-version: 11
+ distribution: temurin
+ - run: mvn -version
+ - name: Unit Tests
+ run: |
+ mvn test --batch-mode --no-transfer-progress
+ working-directory: java-shared-dependencies/unmanaged-dependency-check
+ - name: Install Maven modules
+ run: |
+ mvn install -B -ntp -DskipTests -Dclirr.skip -Dcheckstyle.skip
+ - name: Unmanaged dependency check
+ uses: ./java-shared-dependencies/unmanaged-dependency-check
+ with:
+ bom-path: gapic-generator-java-bom/pom.xml
diff --git a/.github/workflows/create_additional_release_tag.yaml b/.github/workflows/create_additional_release_tag.yaml
index 419cab0ac6..892a821788 100644
--- a/.github/workflows/create_additional_release_tag.yaml
+++ b/.github/workflows/create_additional_release_tag.yaml
@@ -14,16 +14,14 @@ jobs:
uses: actions/checkout@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
-
- name: Set up Git
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
-
- name: Fetch all tags
run: git fetch --tags
-
- name: Create additional tags
+ shell: bash
run: |
ARTIFACT_IDS=('google-cloud-shared-dependencies' 'api-common' 'gax')
for ARTIFACT_ID in "${ARTIFACT_IDS[@]}"; do
@@ -36,3 +34,12 @@ jobs:
git tag $TAG_NAME
git push origin $TAG_NAME
done
+ # Generate a tag for unmanaged dependencies check.
+ # Use fixed tag so that checks in handwritten libraries do not need to
+ # update the version.
+ CHECK_LATEST_TAG="unmanaged-dependencies-check-latest"
+ git tag ${CHECK_LATEST_TAG}
+ # delete the tag in remote repo and push again.
+ git push --delete origin ${CHECK_LATEST_TAG}
+ git push origin ${CHECK_LATEST_TAG}
+
diff --git a/.github/workflows/downstream_unmanaged_dependency_check.yaml b/.github/workflows/downstream_unmanaged_dependency_check.yaml
new file mode 100644
index 0000000000..a3886cc81c
--- /dev/null
+++ b/.github/workflows/downstream_unmanaged_dependency_check.yaml
@@ -0,0 +1,80 @@
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ paths:
+ - .github/workflows/downstream_unmanaged_dependency_check.yaml
+ - java-shared-dependencies/**
+
+name: Downstream Unmanaged Dependency Check
+jobs:
+ validate:
+ runs-on: ubuntu-22.04
+ strategy:
+ fail-fast: false
+ matrix:
+ repo:
+ - java-bigtable
+ - java-firestore
+ - java-logging
+ steps:
+ - name: Checkout sdk-platform-java
+ uses: actions/checkout@v3
+ with:
+ path: sdk-platform-java
+ - name: Checkout the downstream repo
+ uses: actions/checkout@v4
+ with:
+ repository: googleapis/${{ matrix.repo }}
+ path: ${{ matrix.repo }}
+ - name: Check the environment
+ shell: bash
+ run: |
+ set -euxo pipefail
+ pwd
+ ls -alt
+ - uses: actions/setup-java@v3
+ with:
+ java-version: 11
+ distribution: temurin
+ cache: maven
+ - name: Install the modules of sdk-platform-java
+ shell: bash
+ working-directory: sdk-platform-java
+ run: |
+ set -euo pipefail
+ # gapic-generator-java is irrelevant
+ mvn -q -B -ntp install --projects '!gapic-generator-java' \
+ -Dcheckstyle.skip -Dfmt.skip -DskipTests -T 1C
+ - name: Build unmanaged dependency check
+ shell: bash
+ working-directory: sdk-platform-java/java-shared-dependencies/unmanaged-dependency-check
+ run: |
+ set -euo pipefail
+ echo "Install Unmanaged Dependency Check in $(pwd)"
+ mvn clean install -V --batch-mode --no-transfer-progress -DskipTests
+ - name: Install the modules of the downstream repository
+ shell: bash
+ working-directory: ${{ matrix.repo }}
+ run: |
+ # No argument to build.sh installs the modules in local Maven repository
+ .kokoro/build.sh
+ - name: Run unmanaged dependency check
+ shell: bash
+ run: |
+ set -euo pipefail
+ set -x
+ # java-bigtable has "-deps-bom" that declares its dependencies. It's not a good
+ # BOM to list the artifacts generated by that repository.
+ bom_dir=$(find ${{ matrix.repo }} -type d -name 'google-*-bom' ! -name '*-deps-bom')
+ bom_absolute_path=$(realpath "${bom_dir}/pom.xml")
+ cd sdk-platform-java/java-shared-dependencies/unmanaged-dependency-check
+ echo "Running Unmanaged Dependency Check against ${bom_absolute_path}"
+ unmanaged_dependencies=$(mvn exec:java -Dexec.args="../pom.xml ${bom_absolute_path}" -q)
+ if [[ "${unmanaged_dependencies}" != "[]" ]]; then
+ echo "With this change, the unmanaged dependencies check installed in ${{ matrix.repo }} will start to"
+ echo "fail due to ${unmanaged_dependencies}, among the artifacts listed in ${bom_absolute_path}."
+ exit 1
+ fi
+ echo "Unmanaged dependency check passed"
diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml
index ceee1b0ac3..355a651f55 100644
--- a/.github/workflows/nightly.yaml
+++ b/.github/workflows/nightly.yaml
@@ -55,7 +55,6 @@ jobs:
with:
java-version: 8
distribution: temurin
- cache: maven
- run: mvn -version
- name: Test with Java 8
# Direct goal invocation ("surefire:test") prevents recompiling tests
diff --git a/.github/workflows/renovate_config_check.yaml b/.github/workflows/renovate_config_check.yaml
new file mode 100644
index 0000000000..236beaf99d
--- /dev/null
+++ b/.github/workflows/renovate_config_check.yaml
@@ -0,0 +1,25 @@
+name: Renovate Bot Config Validation
+
+on:
+ pull_request:
+ paths:
+ - 'renovate.json'
+
+jobs:
+ config_validation:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Set up Node.js
+ uses: actions/setup-node@v3
+ with:
+ node-version: '20'
+
+ - name: Install Renovate and Config Validator
+ run: |
+ npm install -g npm@latest
+ npm install --global renovate
+ renovate-config-validator
\ No newline at end of file
diff --git a/.github/workflows/verify_library_generation.yaml b/.github/workflows/verify_library_generation.yaml
index 5a361828d7..0b4ae1b8ed 100644
--- a/.github/workflows/verify_library_generation.yaml
+++ b/.github/workflows/verify_library_generation.yaml
@@ -12,8 +12,9 @@ jobs:
integration_tests:
strategy:
matrix:
- java: [ 8 ]
+ java: [ 11 ]
os: [ ubuntu-22.04, macos-12 ]
+ post_processing: [ 'true', 'false' ]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
@@ -22,11 +23,57 @@ jobs:
java-version: ${{ matrix.java }}
distribution: temurin
cache: maven
- - name: Run integration tests
+ - uses: actions/setup-python@v4
+ with:
+ python-version: 3.11
+ - name: install pyenv
+ shell: bash
+ run: |
+ set -ex
+ curl https://pyenv.run | bash
+ # setup environment
+ export PYENV_ROOT="$HOME/.pyenv"
+ export PATH="$PYENV_ROOT/bin:$PATH"
+ echo "PYENV_ROOT=${PYENV_ROOT}" >> $GITHUB_ENV
+ echo "PATH=${PATH}" >> $GITHUB_ENV
+
+ set +ex
+ - name: install python dependencies
+ shell: bash
+ run: |
+ set -ex
+ pushd library_generation
+ pip install -r requirements.in
+ popd
+
+ - name: install utils (macos)
+ if: matrix.os == 'macos-12'
+ shell: bash
+ run: |
+ brew update --preinstall
+ # we need the `realpath` command to be available
+ brew install coreutils
+ - name: install docker (ubuntu)
+ if: matrix.os == 'ubuntu-22.04'
+ shell: bash
run: |
set -x
+ # install docker
+ sudo apt install containerd -y
+ sudo apt install -y docker.io docker-compose
+
+ # launch docker
+ sudo systemctl start docker
+ - name: Run integration tests
+ # we don't run ITs with postprocessing on macos because one of its dependencies "synthtool" is designed to run on linux only
+ if: matrix.os == 'ubuntu-22.04' || matrix.post_processing == 'false'
+ shell: bash
+ run: |
+ git config --global user.email "github-workflow@github.com"
+ git config --global user.name "Github Workflow"
library_generation/test/generate_library_integration_test.sh \
- --googleapis_gen_url https://cloud-java-bot:${{ secrets.CLOUD_JAVA_BOT_GITHUB_TOKEN }}@github.com/googleapis/googleapis-gen.git
+ --googleapis_gen_url https://cloud-java-bot:${{ secrets.CLOUD_JAVA_BOT_GITHUB_TOKEN }}@github.com/googleapis/googleapis-gen.git \
+ --enable_postprocessing "${{ matrix.post_processing }}"
unit_tests:
strategy:
matrix:
@@ -35,10 +82,30 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- - name: Run unit tests
+ - name: install utils (macos)
+ if: matrix.os == 'macos-12'
+ shell: bash
+ run: |
+ brew update --preinstall
+ brew install coreutils
+ - uses: actions/setup-python@v4
+ with:
+ python-version: 3.11
+ - name: install python dependencies
+ shell: bash
+ run: |
+ set -ex
+ pushd library_generation
+ pip install -r requirements.in
+ popd
+ - name: Run shell unit tests
run: |
set -x
library_generation/test/generate_library_unit_tests.sh
+ - name: Run python unit tests
+ run: |
+ set -x
+ python -m unittest library_generation/test/unit_tests.py
lint:
runs-on: ubuntu-22.04
steps:
diff --git a/.gitignore b/.gitignore
index f98cac050a..3da2d8a7d2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,6 +17,9 @@ target/
*.iml
+# Python
+**/__pycache__/
+
# library generation
output/
library_generation/output/
diff --git a/.kokoro/presubmit/graalvm-native-17-downstream.cfg b/.kokoro/presubmit/graalvm-native-17-downstream.cfg
index d1f84b4eec..ac46d4b89f 100644
--- a/.kokoro/presubmit/graalvm-native-17-downstream.cfg
+++ b/.kokoro/presubmit/graalvm-native-17-downstream.cfg
@@ -3,5 +3,5 @@
# Configure the docker image for kokoro-trampoline.
env_vars: {
key: "TRAMPOLINE_IMAGE"
- value: "gcr.io/cloud-devrel-kokoro-resources/graalvm17:22.3.2"
+ value: "gcr.io/cloud-devrel-kokoro-resources/graalvm17:22.3.3"
}
diff --git a/.kokoro/presubmit/graalvm-native-downstream.cfg b/.kokoro/presubmit/graalvm-native-downstream.cfg
index 73a3bdfb44..4535885f5e 100644
--- a/.kokoro/presubmit/graalvm-native-downstream.cfg
+++ b/.kokoro/presubmit/graalvm-native-downstream.cfg
@@ -3,5 +3,5 @@
# Configure the docker image for kokoro-trampoline.
env_vars: {
key: "TRAMPOLINE_IMAGE"
- value: "gcr.io/cloud-devrel-kokoro-resources/graalvm:22.3.2"
+ value: "gcr.io/cloud-devrel-kokoro-resources/graalvm:22.3.3"
}
diff --git a/.kokoro/release/common.cfg b/.kokoro/release/common.cfg
deleted file mode 100644
index de40730e6d..0000000000
--- a/.kokoro/release/common.cfg
+++ /dev/null
@@ -1,49 +0,0 @@
-# Format: //devtools/kokoro/config/proto/build.proto
-
-# Download trampoline resources.
-gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline"
-
-# Use the trampoline script to run in docker.
-build_file: "gapic-generator-java/.kokoro/trampoline.sh"
-
-# Configure the docker image for kokoro-trampoline.
-env_vars: {
- key: "TRAMPOLINE_IMAGE"
- value: "gcr.io/cloud-devrel-kokoro-resources/java17"
-}
-
-before_action {
- fetch_keystore {
- keystore_resource {
- keystore_config_id: 70247
- keyname: "maven-gpg-keyring"
- }
- }
-}
-
-before_action {
- fetch_keystore {
- keystore_resource {
- keystore_config_id: 70247
- keyname: "maven-gpg-passphrase"
- }
- }
-}
-
-before_action {
- fetch_keystore {
- keystore_resource {
- keystore_config_id: 70247
- keyname: "maven-gpg-pubkeyring"
- }
- }
-}
-
-before_action {
- fetch_keystore {
- keystore_resource {
- keystore_config_id: 70247
- keyname: "sonatype-credentials"
- }
- }
-}
diff --git a/.kokoro/release/common.sh b/.kokoro/release/common.sh
deleted file mode 100755
index 693bae8bfb..0000000000
--- a/.kokoro/release/common.sh
+++ /dev/null
@@ -1,52 +0,0 @@
-#!/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.
-
-set -eo pipefail
-
-# Get secrets from keystore and set and environment variables
-setup_environment_secrets() {
- export GPG_PASSPHRASE=$(cat ${KOKORO_KEYSTORE_DIR}/70247_maven-gpg-passphrase)
- export GPG_TTY=$(tty)
- export GPG_HOMEDIR=/gpg
- mkdir $GPG_HOMEDIR
- mv ${KOKORO_KEYSTORE_DIR}/70247_maven-gpg-pubkeyring $GPG_HOMEDIR/pubring.gpg
- mv ${KOKORO_KEYSTORE_DIR}/70247_maven-gpg-keyring $GPG_HOMEDIR/secring.gpg
- export GPG_KEY_ID=$(echo -n $(gpg --with-colons ${GPG_HOMEDIR}/pubring.gpg | awk -F':' '/pub/{ print $5 }'))
- export SONATYPE_USERNAME=$(cat ${KOKORO_KEYSTORE_DIR}/70247_sonatype-credentials | cut -f1 -d'|')
- export SONATYPE_PASSWORD=$(cat ${KOKORO_KEYSTORE_DIR}/70247_sonatype-credentials | cut -f2 -d'|')
-}
-
-create_settings_xml_file() {
- echo "
-
-
-
- ossrh
- ${SONATYPE_USERNAME}
- ${SONATYPE_PASSWORD}
-
-
- sonatype-nexus-staging
- ${SONATYPE_USERNAME}
- ${SONATYPE_PASSWORD}
-
-
- sonatype-nexus-snapshots
- ${SONATYPE_USERNAME}
- ${SONATYPE_PASSWORD}
-
-
-" > $1
-}
\ No newline at end of file
diff --git a/.kokoro/release/publish_javadoc11.cfg b/.kokoro/release/publish_javadoc11.cfg
deleted file mode 100644
index a6c2a7595a..0000000000
--- a/.kokoro/release/publish_javadoc11.cfg
+++ /dev/null
@@ -1,30 +0,0 @@
-# Format: //devtools/kokoro/config/proto/build.proto
-
-# cloud-rad production
-env_vars: {
- key: "STAGING_BUCKET_V2"
- value: "docs-staging-v2"
-}
-
-# Configure the docker image for kokoro-trampoline
-env_vars: {
- key: "TRAMPOLINE_IMAGE"
- value: "gcr.io/cloud-devrel-kokoro-resources/java11"
-}
-
-env_vars: {
- key: "TRAMPOLINE_BUILD_FILE"
- value: "github/gapic-generator-java/.kokoro/release/publish_javadoc11.sh"
-}
-
-before_action {
- fetch_keystore {
- keystore_resource {
- keystore_config_id: 73713
- keyname: "docuploader_service_account"
- }
- }
-}
-
-# Downloads docfx doclet resource. This will be in ${KOKORO_GFILE_DIR}/
-gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/docfx"
diff --git a/.kokoro/release/publish_javadoc11.sh b/.kokoro/release/publish_javadoc11.sh
deleted file mode 100755
index 7bff173139..0000000000
--- a/.kokoro/release/publish_javadoc11.sh
+++ /dev/null
@@ -1,144 +0,0 @@
-#!/bin/bash
-# Copyright 2022 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.
-
-set -eo pipefail
-
-if [[ -z "${CREDENTIALS}" ]]; then
- CREDENTIALS=${KOKORO_KEYSTORE_DIR}/73713_docuploader_service_account
-fi
-
-if [[ -z "${STAGING_BUCKET_V2}" ]]; then
- echo "Need to set STAGING_BUCKET_V2 environment variable"
- exit 1
-fi
-
-#work from the git root directory
-pushd $(dirname "$0")/../../
-
-root_dir=$(pwd)
-
-python3 --version
-
-# install docuploader package
-python3 -m pip install --require-hashes -r .kokoro/requirements.txt
-
-# If DOCLET_VERSION is passed in (overriding version in shared-config)
-if [ -n "${DOCLET_VERSION}" ]; then
- doclet_name="java-docfx-doclet-${DOCLET_VERSION}.jar"
-fi
-
-mvn -B -ntp \
- -DtrimStackTrace=false \
- -Dclirr.skip=true \
- -Denforcer.skip=true \
- -Dcheckstyle.skip=true \
- -Dflatten.skip=true \
- -Danimal.sniffer.skip=true \
- -DskipTests=true \
- -Djacoco.skip=true \
- -T 1C \
- install
-
-if [[ -z "${MODULE_LIST}" ]]; then
- # Retrieve list of modules from aggregator pom
- modules=($(mvn help:evaluate -Dexpression=project.modules | grep '<.*>.*' | sed -e 's/<.*>\(.*\)<\/.*>/\1/g'))
-else
- modules=($(echo "${MODULE_LIST}" | tr ',' ' '))
-fi
-
-excluded_modules=('gapic-generator-java-pom-parent' 'gapic-generator-java' 'gapic-generator-java-bom' )
-failed_modules=()
-
-declare -A cloud_rad_module_name
-cloud_rad_module_name+=( ["gax-java"]=gax ["api-common-java"]=api-common ["java-common-protos"]=proto-google-common-protos ["java-iam"]=proto-google-iam-v1 )
-
-for module in "${modules[@]}"; do
- # Proceed if module is not excluded
- # Spaces are intentionally added -- Query is regex and array elements are space separated
- # It tries to match the *exact* `module` text
- if [[ ! " ${excluded_modules[*]} " =~ " ${module} " ]]; then
- pushd $module
-
- NAME=${cloud_rad_module_name[${module}]}
- # Extract (current) version from root `versions.txt` file and remove `-SNAPSHOT`
- VERSION=$(grep "^${NAME}:" "${root_dir}/versions.txt" | cut -d: -f3 | sed -e 's/-SNAPSHOT//g')
- echo "Running for ${NAME}-${VERSION}"
-
- # Cloud RAD generation
- if [ -z "${doclet_name}" ]; then
- mvn clean -B -ntp \
- -P docFX \
- -Dclirr.skip=true \
- -Denforcer.skip=true \
- -Dcheckstyle.skip=true \
- -Dflatten.skip=true \
- -Danimal.sniffer.skip=true \
- javadoc:aggregate
- else
- mvn clean -B -ntp \
- -P docFX \
- -DdocletPath=${KOKORO_GFILE_DIR}/${doclet_name} \
- -Dclirr.skip=true \
- -Denforcer.skip=true \
- -Dcheckstyle.skip=true \
- -Dflatten.skip=true \
- -Danimal.sniffer.skip=true \
- javadoc:aggregate
- fi
-
- if [ "$?" -ne "0" ]; then
- failed_modules+=("${module}")
- continue
- fi
- # include CHANGELOG if exists
- if [ -e CHANGELOG.md ]; then
- cp CHANGELOG.md target/docfx-yml/history.md
- fi
- pushd target/docfx-yml
-
- echo "Creating metadata for ${module}..."
- # create metadata
- python3 -m docuploader create-metadata \
- --name ${NAME} \
- --version ${VERSION} \
- --xrefs devsite://java/gax \
- --xrefs devsite://java/google-cloud-core \
- --xrefs devsite://java/api-common \
- --xrefs devsite://java/proto-google-common-protos \
- --xrefs devsite://java/google-api-client \
- --xrefs devsite://java/google-http-client \
- --xrefs devsite://java/protobuf \
- --language java
-
- echo "Uploading tarball for ${module}..."
- # upload yml to production bucket
- python3 -m docuploader upload . \
- --credentials ${CREDENTIALS} \
- --staging-bucket ${STAGING_BUCKET_V2} \
- --destination-prefix docfx
-
- echo "Uploaded tarball for ${module}"
-
- popd # out of target/docfx-yml
- popd # out of $module
- fi
-done
-
-if [ ${#failed_modules[@]} -eq 0 ]; then
- echo "All modules uploaded to CloudRAD"
-else
- echo "These modules failed: ${failed_modules[*]}"
- exit 1
-fi
\ No newline at end of file
diff --git a/.kokoro/release/stage.cfg b/.kokoro/release/stage.cfg
deleted file mode 100644
index ddd3ab71dc..0000000000
--- a/.kokoro/release/stage.cfg
+++ /dev/null
@@ -1,17 +0,0 @@
-# Format: //devtools/kokoro/config/proto/build.proto
-env_vars: {
- key: "TRAMPOLINE_BUILD_FILE"
- value: "github/gapic-generator-java/.kokoro/release/stage.sh"
-}
-
-action {
- define_artifacts {
- regex: "github/gapic-generator-java/target/nexus-staging/staging/*.properties"
- strip_prefix: "github/gapic-generator-java"
- }
-}
-
-env_vars: {
- key: "SECRET_MANAGER_KEYS"
- value: "releasetool-publish-reporter-app,releasetool-publish-reporter-googleapis-installation,releasetool-publish-reporter-pem"
-}
diff --git a/.kokoro/release/stage.sh b/.kokoro/release/stage.sh
deleted file mode 100755
index c4afa39810..0000000000
--- a/.kokoro/release/stage.sh
+++ /dev/null
@@ -1,55 +0,0 @@
-#!/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.
-
-set -eo pipefail
-
-if [[ -n "${AUTORELEASE_PR}" ]]
-then
- # Start the releasetool reporter
- requirementsFile=$(realpath $(dirname "${BASH_SOURCE[0]}")/../requirements.txt)
- python3 -m pip install --require-hashes -r $requirementsFile
- python3 -m releasetool publish-reporter-script > /tmp/publisher-script; source /tmp/publisher-script
-fi
-
-source $(dirname "$0")/common.sh
-MAVEN_SETTINGS_FILE=$(realpath $(dirname "$0")/../../)/settings.xml
-pushd $(dirname "$0")/../../
-
-setup_environment_secrets
-create_settings_xml_file "settings.xml"
-
-# workaround for nexus maven plugin issue with Java 16+: https://issues.sonatype.org/browse/OSSRH-66257
-export MAVEN_OPTS="--add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.text=ALL-UNNAMED --add-opens=java.desktop/java.awt.font=ALL-UNNAMED"
-
-mvn clean deploy -B \
- -DskipTests=true \
- -Dclirr.skip=true \
- --settings ${MAVEN_SETTINGS_FILE} \
- -Dgpg.executable=gpg \
- -Dgpg.passphrase=${GPG_PASSPHRASE} \
- -Dgpg.homedir=${GPG_HOMEDIR} \
- -P release
-
-# The job triggered by Release Please (release-trigger) has this AUTORELEASE_PR
-# environment variable. Fusion also lets us to specify this variable.
-if [[ -n "${AUTORELEASE_PR}" ]]
-then
- mvn nexus-staging:release -B \
- -P release-staging-repository \
- -DperformRelease=true \
- --settings=${MAVEN_SETTINGS_FILE}
-else
- echo "AUTORELEASE_PR is not set. Not releasing."
-fi
diff --git a/.kokoro/requirements.in b/.kokoro/requirements.in
deleted file mode 100644
index a15f606847..0000000000
--- a/.kokoro/requirements.in
+++ /dev/null
@@ -1,9 +0,0 @@
-gcp-docuploader
-gcp-releasetool>=1.10.5 # required for compatibility with cryptography>=39.x
-typing-extensions
-click<8.1.0
-jinja2
-keyring
-secretstorage
-cryptography>=39.0.1
-certifi>=2022.12.07
\ No newline at end of file
diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt
deleted file mode 100644
index 56bdc9e94a..0000000000
--- a/.kokoro/requirements.txt
+++ /dev/null
@@ -1,410 +0,0 @@
-#
-# This file is autogenerated by pip-compile with Python 3.8
-# by the following command:
-#
-# pip-compile --generate-hashes .kokoro/requirements.in
-#
-attrs==22.2.0 \
- --hash=sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836 \
- --hash=sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99
- # via gcp-releasetool
-cachetools==4.2.4 \
- --hash=sha256:89ea6f1b638d5a73a4f9226be57ac5e4f399d22770b92355f92dcb0f7f001693 \
- --hash=sha256:92971d3cb7d2a97efff7c7bb1657f21a8f5fb309a37530537c71b1774189f2d1
- # via google-auth
-certifi==2023.7.22 \
- --hash=sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082 \
- --hash=sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9
- # via
- # -r requirements.in
- # requests
-cffi==1.16.0 \
- --hash=sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc \
- --hash=sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a \
- --hash=sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417 \
- --hash=sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab \
- --hash=sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520 \
- --hash=sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36 \
- --hash=sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743 \
- --hash=sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8 \
- --hash=sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed \
- --hash=sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684 \
- --hash=sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56 \
- --hash=sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324 \
- --hash=sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d \
- --hash=sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235 \
- --hash=sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e \
- --hash=sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088 \
- --hash=sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000 \
- --hash=sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7 \
- --hash=sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e \
- --hash=sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673 \
- --hash=sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c \
- --hash=sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe \
- --hash=sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2 \
- --hash=sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098 \
- --hash=sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8 \
- --hash=sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a \
- --hash=sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0 \
- --hash=sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b \
- --hash=sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896 \
- --hash=sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e \
- --hash=sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9 \
- --hash=sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2 \
- --hash=sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b \
- --hash=sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6 \
- --hash=sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404 \
- --hash=sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f \
- --hash=sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0 \
- --hash=sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4 \
- --hash=sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc \
- --hash=sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936 \
- --hash=sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba \
- --hash=sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872 \
- --hash=sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb \
- --hash=sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614 \
- --hash=sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1 \
- --hash=sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d \
- --hash=sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969 \
- --hash=sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b \
- --hash=sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4 \
- --hash=sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627 \
- --hash=sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956 \
- --hash=sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357
- # via cryptography
-charset-normalizer==2.1.1 \
- --hash=sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845 \
- --hash=sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f
- # via requests
-click==8.0.4 \
- --hash=sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1 \
- --hash=sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb
- # via
- # -r requirements.in
- # gcp-docuploader
- # gcp-releasetool
-colorlog==6.7.0 \
- --hash=sha256:0d33ca236784a1ba3ff9c532d4964126d8a2c44f1f0cb1d2b0728196f512f662 \
- --hash=sha256:bd94bd21c1e13fac7bd3153f4bc3a7dc0eb0974b8bc2fdf1a989e474f6e582e5
- # via gcp-docuploader
-cryptography==41.0.4 \
- --hash=sha256:004b6ccc95943f6a9ad3142cfabcc769d7ee38a3f60fb0dddbfb431f818c3a67 \
- --hash=sha256:047c4603aeb4bbd8db2756e38f5b8bd7e94318c047cfe4efeb5d715e08b49311 \
- --hash=sha256:0d9409894f495d465fe6fda92cb70e8323e9648af912d5b9141d616df40a87b8 \
- --hash=sha256:23a25c09dfd0d9f28da2352503b23e086f8e78096b9fd585d1d14eca01613e13 \
- --hash=sha256:2ed09183922d66c4ec5fdaa59b4d14e105c084dd0febd27452de8f6f74704143 \
- --hash=sha256:35c00f637cd0b9d5b6c6bd11b6c3359194a8eba9c46d4e875a3660e3b400005f \
- --hash=sha256:37480760ae08065437e6573d14be973112c9e6dcaf5f11d00147ee74f37a3829 \
- --hash=sha256:3b224890962a2d7b57cf5eeb16ccaafba6083f7b811829f00476309bce2fe0fd \
- --hash=sha256:5a0f09cefded00e648a127048119f77bc2b2ec61e736660b5789e638f43cc397 \
- --hash=sha256:5b72205a360f3b6176485a333256b9bcd48700fc755fef51c8e7e67c4b63e3ac \
- --hash=sha256:7e53db173370dea832190870e975a1e09c86a879b613948f09eb49324218c14d \
- --hash=sha256:7febc3094125fc126a7f6fb1f420d0da639f3f32cb15c8ff0dc3997c4549f51a \
- --hash=sha256:80907d3faa55dc5434a16579952ac6da800935cd98d14dbd62f6f042c7f5e839 \
- --hash=sha256:86defa8d248c3fa029da68ce61fe735432b047e32179883bdb1e79ed9bb8195e \
- --hash=sha256:8ac4f9ead4bbd0bc8ab2d318f97d85147167a488be0e08814a37eb2f439d5cf6 \
- --hash=sha256:93530900d14c37a46ce3d6c9e6fd35dbe5f5601bf6b3a5c325c7bffc030344d9 \
- --hash=sha256:9eeb77214afae972a00dee47382d2591abe77bdae166bda672fb1e24702a3860 \
- --hash=sha256:b5f4dfe950ff0479f1f00eda09c18798d4f49b98f4e2006d644b3301682ebdca \
- --hash=sha256:c3391bd8e6de35f6f1140e50aaeb3e2b3d6a9012536ca23ab0d9c35ec18c8a91 \
- --hash=sha256:c880eba5175f4307129784eca96f4e70b88e57aa3f680aeba3bab0e980b0f37d \
- --hash=sha256:cecfefa17042941f94ab54f769c8ce0fe14beff2694e9ac684176a2535bf9714 \
- --hash=sha256:e40211b4923ba5a6dc9769eab704bdb3fbb58d56c5b336d30996c24fcf12aadb \
- --hash=sha256:efc8ad4e6fc4f1752ebfb58aefece8b4e3c4cae940b0994d43649bdfce8d0d4f
- # via
- # -r requirements.in
- # gcp-releasetool
- # secretstorage
-gcp-docuploader==0.6.5 \
- --hash=sha256:30221d4ac3e5a2b9c69aa52fdbef68cc3f27d0e6d0d90e220fc024584b8d2318 \
- --hash=sha256:b7458ef93f605b9d46a4bf3a8dc1755dad1f31d030c8679edf304e343b347eea
- # via -r requirements.in
-gcp-releasetool==1.16.0 \
- --hash=sha256:27bf19d2e87aaa884096ff941aa3c592c482be3d6a2bfe6f06afafa6af2353e3 \
- --hash=sha256:a316b197a543fd036209d0caba7a8eb4d236d8e65381c80cbc6d7efaa7606d63
- # via -r requirements.in
-google-api-core==2.12.0 \
- --hash=sha256:c22e01b1e3c4dcd90998494879612c38d0a3411d1f7b679eb89e2abe3ce1f553 \
- --hash=sha256:ec6054f7d64ad13b41e43d96f735acbd763b0f3b695dabaa2d579673f6a6e160
- # via
- # google-cloud-core
- # google-cloud-storage
-google-auth==2.23.3 \
- --hash=sha256:6864247895eea5d13b9c57c9e03abb49cb94ce2dc7c58e91cba3248c7477c9e3 \
- --hash=sha256:a8f4608e65c244ead9e0538f181a96c6e11199ec114d41f1d7b1bffa96937bda
- # via
- # gcp-releasetool
- # google-api-core
- # google-cloud-core
- # google-cloud-storage
-google-cloud-core==2.3.3 \
- --hash=sha256:37b80273c8d7eee1ae816b3a20ae43585ea50506cb0e60f3cf5be5f87f1373cb \
- --hash=sha256:fbd11cad3e98a7e5b0343dc07cb1039a5ffd7a5bb96e1f1e27cee4bda4a90863
- # via google-cloud-storage
-google-cloud-storage==2.12.0 \
- --hash=sha256:57c0bcda2f5e11f008a155d8636d8381d5abab46b58e0cae0e46dd5e595e6b46 \
- --hash=sha256:bc52563439d42981b6e21b071a76da2791672776eda3ba99d13a8061ebbd6e5e
- # via gcp-docuploader
-google-crc32c==1.5.0 \
- --hash=sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a \
- --hash=sha256:02c65b9817512edc6a4ae7c7e987fea799d2e0ee40c53ec573a692bee24de876 \
- --hash=sha256:02ebb8bf46c13e36998aeaad1de9b48f4caf545e91d14041270d9dca767b780c \
- --hash=sha256:07eb3c611ce363c51a933bf6bd7f8e3878a51d124acfc89452a75120bc436289 \
- --hash=sha256:1034d91442ead5a95b5aaef90dbfaca8633b0247d1e41621d1e9f9db88c36298 \
- --hash=sha256:116a7c3c616dd14a3de8c64a965828b197e5f2d121fedd2f8c5585c547e87b02 \
- --hash=sha256:19e0a019d2c4dcc5e598cd4a4bc7b008546b0358bd322537c74ad47a5386884f \
- --hash=sha256:1c7abdac90433b09bad6c43a43af253e688c9cfc1c86d332aed13f9a7c7f65e2 \
- --hash=sha256:1e986b206dae4476f41bcec1faa057851f3889503a70e1bdb2378d406223994a \
- --hash=sha256:272d3892a1e1a2dbc39cc5cde96834c236d5327e2122d3aaa19f6614531bb6eb \
- --hash=sha256:278d2ed7c16cfc075c91378c4f47924c0625f5fc84b2d50d921b18b7975bd210 \
- --hash=sha256:2ad40e31093a4af319dadf503b2467ccdc8f67c72e4bcba97f8c10cb078207b5 \
- --hash=sha256:2e920d506ec85eb4ba50cd4228c2bec05642894d4c73c59b3a2fe20346bd00ee \
- --hash=sha256:3359fc442a743e870f4588fcf5dcbc1bf929df1fad8fb9905cd94e5edb02e84c \
- --hash=sha256:37933ec6e693e51a5b07505bd05de57eee12f3e8c32b07da7e73669398e6630a \
- --hash=sha256:398af5e3ba9cf768787eef45c803ff9614cc3e22a5b2f7d7ae116df8b11e3314 \
- --hash=sha256:3b747a674c20a67343cb61d43fdd9207ce5da6a99f629c6e2541aa0e89215bcd \
- --hash=sha256:461665ff58895f508e2866824a47bdee72497b091c730071f2b7575d5762ab65 \
- --hash=sha256:4c6fdd4fccbec90cc8a01fc00773fcd5fa28db683c116ee3cb35cd5da9ef6c37 \
- --hash=sha256:5829b792bf5822fd0a6f6eb34c5f81dd074f01d570ed7f36aa101d6fc7a0a6e4 \
- --hash=sha256:596d1f98fc70232fcb6590c439f43b350cb762fb5d61ce7b0e9db4539654cc13 \
- --hash=sha256:5ae44e10a8e3407dbe138984f21e536583f2bba1be9491239f942c2464ac0894 \
- --hash=sha256:635f5d4dd18758a1fbd1049a8e8d2fee4ffed124462d837d1a02a0e009c3ab31 \
- --hash=sha256:64e52e2b3970bd891309c113b54cf0e4384762c934d5ae56e283f9a0afcd953e \
- --hash=sha256:66741ef4ee08ea0b2cc3c86916ab66b6aef03768525627fd6a1b34968b4e3709 \
- --hash=sha256:67b741654b851abafb7bc625b6d1cdd520a379074e64b6a128e3b688c3c04740 \
- --hash=sha256:6ac08d24c1f16bd2bf5eca8eaf8304812f44af5cfe5062006ec676e7e1d50afc \
- --hash=sha256:6f998db4e71b645350b9ac28a2167e6632c239963ca9da411523bb439c5c514d \
- --hash=sha256:72218785ce41b9cfd2fc1d6a017dc1ff7acfc4c17d01053265c41a2c0cc39b8c \
- --hash=sha256:74dea7751d98034887dbd821b7aae3e1d36eda111d6ca36c206c44478035709c \
- --hash=sha256:759ce4851a4bb15ecabae28f4d2e18983c244eddd767f560165563bf9aefbc8d \
- --hash=sha256:77e2fd3057c9d78e225fa0a2160f96b64a824de17840351b26825b0848022906 \
- --hash=sha256:7c074fece789b5034b9b1404a1f8208fc2d4c6ce9decdd16e8220c5a793e6f61 \
- --hash=sha256:7c42c70cd1d362284289c6273adda4c6af8039a8ae12dc451dcd61cdabb8ab57 \
- --hash=sha256:7f57f14606cd1dd0f0de396e1e53824c371e9544a822648cd76c034d209b559c \
- --hash=sha256:83c681c526a3439b5cf94f7420471705bbf96262f49a6fe546a6db5f687a3d4a \
- --hash=sha256:8485b340a6a9e76c62a7dce3c98e5f102c9219f4cfbf896a00cf48caf078d438 \
- --hash=sha256:84e6e8cd997930fc66d5bb4fde61e2b62ba19d62b7abd7a69920406f9ecca946 \
- --hash=sha256:89284716bc6a5a415d4eaa11b1726d2d60a0cd12aadf5439828353662ede9dd7 \
- --hash=sha256:8b87e1a59c38f275c0e3676fc2ab6d59eccecfd460be267ac360cc31f7bcde96 \
- --hash=sha256:8f24ed114432de109aa9fd317278518a5af2d31ac2ea6b952b2f7782b43da091 \
- --hash=sha256:98cb4d057f285bd80d8778ebc4fde6b4d509ac3f331758fb1528b733215443ae \
- --hash=sha256:998679bf62b7fb599d2878aa3ed06b9ce688b8974893e7223c60db155f26bd8d \
- --hash=sha256:9ba053c5f50430a3fcfd36f75aff9caeba0440b2d076afdb79a318d6ca245f88 \
- --hash=sha256:9c99616c853bb585301df6de07ca2cadad344fd1ada6d62bb30aec05219c45d2 \
- --hash=sha256:a1fd716e7a01f8e717490fbe2e431d2905ab8aa598b9b12f8d10abebb36b04dd \
- --hash=sha256:a2355cba1f4ad8b6988a4ca3feed5bff33f6af2d7f134852cf279c2aebfde541 \
- --hash=sha256:b1f8133c9a275df5613a451e73f36c2aea4fe13c5c8997e22cf355ebd7bd0728 \
- --hash=sha256:b8667b48e7a7ef66afba2c81e1094ef526388d35b873966d8a9a447974ed9178 \
- --hash=sha256:ba1eb1843304b1e5537e1fca632fa894d6f6deca8d6389636ee5b4797affb968 \
- --hash=sha256:be82c3c8cfb15b30f36768797a640e800513793d6ae1724aaaafe5bf86f8f346 \
- --hash=sha256:c02ec1c5856179f171e032a31d6f8bf84e5a75c45c33b2e20a3de353b266ebd8 \
- --hash=sha256:c672d99a345849301784604bfeaeba4db0c7aae50b95be04dd651fd2a7310b93 \
- --hash=sha256:c6c777a480337ac14f38564ac88ae82d4cd238bf293f0a22295b66eb89ffced7 \
- --hash=sha256:cae0274952c079886567f3f4f685bcaf5708f0a23a5f5216fdab71f81a6c0273 \
- --hash=sha256:cd67cf24a553339d5062eff51013780a00d6f97a39ca062781d06b3a73b15462 \
- --hash=sha256:d3515f198eaa2f0ed49f8819d5732d70698c3fa37384146079b3799b97667a94 \
- --hash=sha256:d5280312b9af0976231f9e317c20e4a61cd2f9629b7bfea6a693d1878a264ebd \
- --hash=sha256:de06adc872bcd8c2a4e0dc51250e9e65ef2ca91be023b9d13ebd67c2ba552e1e \
- --hash=sha256:e1674e4307fa3024fc897ca774e9c7562c957af85df55efe2988ed9056dc4e57 \
- --hash=sha256:e2096eddb4e7c7bdae4bd69ad364e55e07b8316653234a56552d9c988bd2d61b \
- --hash=sha256:e560628513ed34759456a416bf86b54b2476c59144a9138165c9a1575801d0d9 \
- --hash=sha256:edfedb64740750e1a3b16152620220f51d58ff1b4abceb339ca92e934775c27a \
- --hash=sha256:f13cae8cc389a440def0c8c52057f37359014ccbc9dc1f0827936bcd367c6100 \
- --hash=sha256:f314013e7dcd5cf45ab1945d92e713eec788166262ae8deb2cfacd53def27325 \
- --hash=sha256:f583edb943cf2e09c60441b910d6a20b4d9d626c75a36c8fcac01a6c96c01183 \
- --hash=sha256:fd8536e902db7e365f49e7d9029283403974ccf29b13fc7028b97e2295b33556 \
- --hash=sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4
- # via google-resumable-media
-google-resumable-media==2.6.0 \
- --hash=sha256:972852f6c65f933e15a4a210c2b96930763b47197cdf4aa5f5bea435efb626e7 \
- --hash=sha256:fc03d344381970f79eebb632a3c18bb1828593a2dc5572b5f90115ef7d11e81b
- # via google-cloud-storage
-googleapis-common-protos==1.61.0 \
- --hash=sha256:22f1915393bb3245343f6efe87f6fe868532efc12aa26b391b15132e1279f1c0 \
- --hash=sha256:8a64866a97f6304a7179873a465d6eee97b7a24ec6cfd78e0f575e96b821240b
- # via google-api-core
-idna==3.4 \
- --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \
- --hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2
- # via requests
-importlib-metadata==4.13.0 \
- --hash=sha256:8a8a81bcf996e74fee46f0d16bd3eaa382a7eb20fd82445c3ad11f4090334116 \
- --hash=sha256:dd0173e8f150d6815e098fd354f6414b0f079af4644ddfe90c71e2fc6174346d
- # via keyring
-jeepney==0.8.0 \
- --hash=sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806 \
- --hash=sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755
- # via
- # keyring
- # secretstorage
-Jinja2==3.1.2 \
- --hash=sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852 \
- --hash=sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61
- # via
- # -r requirements.in
- # gcp-releasetool
-keyring==23.4.1 \
- --hash=sha256:17e49fb0d6883c2b4445359434dba95aad84aabb29bbff044ad0ed7100232eca \
- --hash=sha256:89cbd74d4683ed164c8082fb38619341097741323b3786905c6dac04d6915a55
- # via
- # -r requirements.in
- # gcp-releasetool
-MarkupSafe==2.1.3 \
- --hash=sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e \
- --hash=sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e \
- --hash=sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431 \
- --hash=sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686 \
- --hash=sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c \
- --hash=sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559 \
- --hash=sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc \
- --hash=sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb \
- --hash=sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939 \
- --hash=sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c \
- --hash=sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0 \
- --hash=sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4 \
- --hash=sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9 \
- --hash=sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575 \
- --hash=sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba \
- --hash=sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d \
- --hash=sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd \
- --hash=sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3 \
- --hash=sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00 \
- --hash=sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155 \
- --hash=sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac \
- --hash=sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52 \
- --hash=sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f \
- --hash=sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8 \
- --hash=sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b \
- --hash=sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007 \
- --hash=sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24 \
- --hash=sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea \
- --hash=sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198 \
- --hash=sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0 \
- --hash=sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee \
- --hash=sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be \
- --hash=sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2 \
- --hash=sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1 \
- --hash=sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707 \
- --hash=sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6 \
- --hash=sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c \
- --hash=sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58 \
- --hash=sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823 \
- --hash=sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779 \
- --hash=sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636 \
- --hash=sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c \
- --hash=sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad \
- --hash=sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee \
- --hash=sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc \
- --hash=sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2 \
- --hash=sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48 \
- --hash=sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7 \
- --hash=sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e \
- --hash=sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b \
- --hash=sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa \
- --hash=sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5 \
- --hash=sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e \
- --hash=sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb \
- --hash=sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9 \
- --hash=sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57 \
- --hash=sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc \
- --hash=sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc \
- --hash=sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2 \
- --hash=sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11
- # via jinja2
-packaging==21.3 \
- --hash=sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb \
- --hash=sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522
- # via gcp-releasetool
-protobuf==3.20.3 \
- --hash=sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7 \
- --hash=sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c \
- --hash=sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2 \
- --hash=sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b \
- --hash=sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050 \
- --hash=sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9 \
- --hash=sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7 \
- --hash=sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454 \
- --hash=sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480 \
- --hash=sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469 \
- --hash=sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c \
- --hash=sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e \
- --hash=sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db \
- --hash=sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905 \
- --hash=sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b \
- --hash=sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86 \
- --hash=sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4 \
- --hash=sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402 \
- --hash=sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7 \
- --hash=sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4 \
- --hash=sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99 \
- --hash=sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee
- # via
- # gcp-docuploader
- # gcp-releasetool
- # google-api-core
- # google-cloud-storage
-pyasn1==0.5.0 \
- --hash=sha256:87a2121042a1ac9358cabcaf1d07680ff97ee6404333bacca15f76aa8ad01a57 \
- --hash=sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde
- # via
- # pyasn1-modules
- # rsa
-pyasn1-modules==0.3.0 \
- --hash=sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c \
- --hash=sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d
- # via google-auth
-pycparser==2.21 \
- --hash=sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9 \
- --hash=sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206
- # via cffi
-PyJWT==2.8.0 \
- --hash=sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de \
- --hash=sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320
- # via gcp-releasetool
-pyparsing==3.1.1 \
- --hash=sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb \
- --hash=sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db
- # via packaging
-pyperclip==1.8.2 \
- --hash=sha256:105254a8b04934f0bc84e9c24eb360a591aaf6535c9def5f29d92af107a9bf57
- # via gcp-releasetool
-python-dateutil==2.8.2 \
- --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \
- --hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9
- # via gcp-releasetool
-requests==2.31.0 \
- --hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \
- --hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
- # via
- # gcp-releasetool
- # google-api-core
- # google-cloud-storage
-rsa==4.9 \
- --hash=sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7 \
- --hash=sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21
- # via google-auth
-secretstorage==3.3.3 \
- --hash=sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77 \
- --hash=sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99
- # via
- # -r requirements.in
- # keyring
-six==1.16.0 \
- --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
- --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254
- # via
- # gcp-docuploader
- # google-auth
- # python-dateutil
-typing-extensions==4.8.0 \
- --hash=sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0 \
- --hash=sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef
- # via -r requirements.in
-urllib3==1.26.18 \
- --hash=sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07 \
- --hash=sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0
- # via requests
-zipp==3.17.0 \
- --hash=sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31 \
- --hash=sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0
- # via importlib-metadata
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
new file mode 100644
index 0000000000..7a1c4674e4
--- /dev/null
+++ b/.release-please-manifest.json
@@ -0,0 +1,3 @@
+{
+ ".": "2.34.0"
+}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ee0300cf54..96c80a2b00 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,159 @@
# Changelog
+## [2.34.0](https://github.com/googleapis/sdk-platform-java/compare/v2.33.0...v2.34.0) (2024-01-31)
+
+
+### Features
+
+* autopopulate fields in the request ([#2353](https://github.com/googleapis/sdk-platform-java/issues/2353)) ([b28235a](https://github.com/googleapis/sdk-platform-java/commit/b28235ab20fd174deddafc0426b8d20352af6e85))
+* enable generation with postprocessing of multiple service versions ([#2342](https://github.com/googleapis/sdk-platform-java/issues/2342)) ([363e35e](https://github.com/googleapis/sdk-platform-java/commit/363e35e46e41c88b810e4b0672906f73cb7c38b6))
+* MetricsTracer implementation ([#2421](https://github.com/googleapis/sdk-platform-java/issues/2421)) ([5c291e8](https://github.com/googleapis/sdk-platform-java/commit/5c291e8786b8e976979ec2e26b13f0327333bb02))
+* move new client script ([#2333](https://github.com/googleapis/sdk-platform-java/issues/2333)) ([acdde47](https://github.com/googleapis/sdk-platform-java/commit/acdde47445916dd306ce8b91489fab45c9c2ef50))
+
+
+### Bug Fixes
+
+* Endpoint resolution uses user set endpoint from ClientSettings ([#2429](https://github.com/googleapis/sdk-platform-java/issues/2429)) ([46b0a85](https://github.com/googleapis/sdk-platform-java/commit/46b0a857eaa4484c5f1ebe1170338fc90a994375))
+* Move direct path misconfiguration log to before creating the first channel ([#2430](https://github.com/googleapis/sdk-platform-java/issues/2430)) ([9916540](https://github.com/googleapis/sdk-platform-java/commit/99165403902ff91ecb0b14b858333855e7a10c60))
+
+## [2.33.0](https://github.com/googleapis/sdk-platform-java/compare/v2.32.0...v2.33.0) (2024-01-24)
+
+
+### Features
+
+* Introduce interfaces for metrics instrumentation ([#2403](https://github.com/googleapis/sdk-platform-java/issues/2403)) ([3c61b14](https://github.com/googleapis/sdk-platform-java/commit/3c61b14fef87c735ea2ed382f8510b29176a4279))
+
+
+### Bug Fixes
+
+* Verify Universe Domain's DirectPath Compatibility after Endpoint Resolution ([#2412](https://github.com/googleapis/sdk-platform-java/issues/2412)) ([e2de93b](https://github.com/googleapis/sdk-platform-java/commit/e2de93bb7051039e8a96128b9eacc0f2ea3a1205))
+
+## [2.32.0](https://github.com/googleapis/sdk-platform-java/compare/v2.31.0...v2.32.0) (2024-01-19)
+
+
+### Features
+
+* Do not set the default endpoint in StubSettings ([97ae228](https://github.com/googleapis/sdk-platform-java/commit/97ae228a262738e09ddf7b4dab95bb81a4f8860a))
+* Numeric enums in routing headers ([#2328](https://github.com/googleapis/sdk-platform-java/issues/2328)) ([4d043de](https://github.com/googleapis/sdk-platform-java/commit/4d043deeaf096a093e54c5c7b07e408f38647296))
+* StubSettings' `getEndpoint()` will return the service's pre-configured endpoint if there are no user configurations ([97ae228](https://github.com/googleapis/sdk-platform-java/commit/97ae228a262738e09ddf7b4dab95bb81a4f8860a))
+* Validate the Universe Domain ([#2330](https://github.com/googleapis/sdk-platform-java/issues/2330)) ([097bc93](https://github.com/googleapis/sdk-platform-java/commit/097bc93c39d10d8ff08eb8fb3a378bbe6d96c1ac))
+
+
+### Bug Fixes
+
+* adjust release-please configs for cloudbuild yaml updates ([#2351](https://github.com/googleapis/sdk-platform-java/issues/2351)) ([ed16261](https://github.com/googleapis/sdk-platform-java/commit/ed16261ac8c90fe588026e30d62d948e7ca1af13))
+* DirectPath non-default SA requires creds ([#2281](https://github.com/googleapis/sdk-platform-java/issues/2281)) ([c7d614a](https://github.com/googleapis/sdk-platform-java/commit/c7d614acd9be75b0aa3d365eed9ef4db41419906))
+* format method types and table in Client Overview ([#2361](https://github.com/googleapis/sdk-platform-java/issues/2361)) ([7436995](https://github.com/googleapis/sdk-platform-java/commit/743699504039c110a1490276ddef809f57716e24))
+
+
+### Dependencies
+
+* update dependency com.fasterxml.jackson:jackson-bom to v2.16.1 ([#2386](https://github.com/googleapis/sdk-platform-java/issues/2386)) ([1160f95](https://github.com/googleapis/sdk-platform-java/commit/1160f95d16b1dd0627772a9e8f9c7c8cae7e9c55))
+* update dependency com.google.errorprone:error_prone_annotations to v2.24.1 ([#2390](https://github.com/googleapis/sdk-platform-java/issues/2390)) ([d533760](https://github.com/googleapis/sdk-platform-java/commit/d5337600d95f226bbb3f328ebdb7eb41cd2cb43a))
+* update dependency com.google.errorprone:error_prone_annotations to v2.24.1 ([#2391](https://github.com/googleapis/sdk-platform-java/issues/2391)) ([98b7f3e](https://github.com/googleapis/sdk-platform-java/commit/98b7f3ebcb46430fda53d0d656fcc090ed73654e))
+* update dependency com.google.oauth-client:google-oauth-client-bom to v1.35.0 ([#2392](https://github.com/googleapis/sdk-platform-java/issues/2392)) ([4b78ac7](https://github.com/googleapis/sdk-platform-java/commit/4b78ac7a8e7a8e6844516c30ac8b112a44cfcc9c))
+* update dependency io.perfmark:perfmark-api to v0.27.0 ([#2388](https://github.com/googleapis/sdk-platform-java/issues/2388)) ([42808ba](https://github.com/googleapis/sdk-platform-java/commit/42808baab2f949bfda2abdb109560033f9e34da3))
+* update dependency io.perfmark:perfmark-api to v0.27.0 ([#2389](https://github.com/googleapis/sdk-platform-java/issues/2389)) ([51241f7](https://github.com/googleapis/sdk-platform-java/commit/51241f77bc71769c482d12c47cf82c2f32d0be30))
+* update dependency net.bytebuddy:byte-buddy to v1.14.11 ([#2387](https://github.com/googleapis/sdk-platform-java/issues/2387)) ([07b8ee6](https://github.com/googleapis/sdk-platform-java/commit/07b8ee6d93c42299617de768946ba48e2049dbf9))
+* update dependency org.checkerframework:checker-qual to v3.42.0 ([#2287](https://github.com/googleapis/sdk-platform-java/issues/2287)) ([7c4eb80](https://github.com/googleapis/sdk-platform-java/commit/7c4eb80c2faf1b2d9e0a1bc10acffe4a8d2d7d28))
+* update gapic-showcase to v0.30.0 ([#2354](https://github.com/googleapis/sdk-platform-java/issues/2354)) ([762c125](https://github.com/googleapis/sdk-platform-java/commit/762c125abadb5e56682224c9f1587c71e5c6e653))
+* update google api dependencies ([#2382](https://github.com/googleapis/sdk-platform-java/issues/2382)) ([92bbe61](https://github.com/googleapis/sdk-platform-java/commit/92bbe6123090a9c66a0d6a688484612a069fd9de))
+* update googleapis/java-cloud-bom digest to 8bc17e9 ([#2376](https://github.com/googleapis/sdk-platform-java/issues/2376)) ([bddd4ea](https://github.com/googleapis/sdk-platform-java/commit/bddd4ea81c0ce2d53c7968fbe55eb0755a3dfee8))
+* update grpc dependencies to v1.61.0 ([#2383](https://github.com/googleapis/sdk-platform-java/issues/2383)) ([af15bd1](https://github.com/googleapis/sdk-platform-java/commit/af15bd1ef06456fea983a8e44b3dc78d30751125))
+* update netty dependencies to v4.1.105.final ([#2302](https://github.com/googleapis/sdk-platform-java/issues/2302)) ([1563a55](https://github.com/googleapis/sdk-platform-java/commit/1563a550f820063881c1e1ab87aa79fb47ca667c))
+* update protobuf dependencies to v3.25.2 ([#2378](https://github.com/googleapis/sdk-platform-java/issues/2378)) ([836e7b8](https://github.com/googleapis/sdk-platform-java/commit/836e7b86eecf61552f203e26dff04359ed27bde2))
+* update slf4j monorepo to v2.0.11 ([#2381](https://github.com/googleapis/sdk-platform-java/issues/2381)) ([9e758b7](https://github.com/googleapis/sdk-platform-java/commit/9e758b792615c0bc4d784e8c33d4f90fcc1566ee))
+
+## [2.31.0](https://github.com/googleapis/sdk-platform-java/compare/v2.30.0...v2.31.0) (2024-01-04)
+
+
+### Features
+
+* [common-protos,common-protos] add auto_populated_fields to google.api.MethodSettings ([#2273](https://github.com/googleapis/sdk-platform-java/issues/2273)) ([d9be11c](https://github.com/googleapis/sdk-platform-java/commit/d9be11c7a127452c5e5ef871854a4a65c68b1b34))
+* add auto_populated_fields to google.api.MethodSettings ([d9be11c](https://github.com/googleapis/sdk-platform-java/commit/d9be11c7a127452c5e5ef871854a4a65c68b1b34))
+* add parsing of autopopulated fields from serviceyaml ([#2312](https://github.com/googleapis/sdk-platform-java/issues/2312)) ([4f535a7](https://github.com/googleapis/sdk-platform-java/commit/4f535a7829f98fe79053e62e22deaf91a97ab917))
+* Add Universe Domain to ClientSettings ([#2331](https://github.com/googleapis/sdk-platform-java/issues/2331)) ([1bddac5](https://github.com/googleapis/sdk-platform-java/commit/1bddac5f91b5b7742c6082da2ae0c6667523aaa4))
+* Add Universe Domain to Java-Core ([#2329](https://github.com/googleapis/sdk-platform-java/issues/2329)) ([586ac9f](https://github.com/googleapis/sdk-platform-java/commit/586ac9f668e0ae647e3ef233458d2c754959f6e5))
+* Full Endpoint Resolution from EndpointContext ([#2313](https://github.com/googleapis/sdk-platform-java/issues/2313)) ([f499ced](https://github.com/googleapis/sdk-platform-java/commit/f499ced28a562cbb3ea49f14a4fa16eb6a8173cc))
+* move Java Owlbot into this repository for postprocessing ([#2282](https://github.com/googleapis/sdk-platform-java/issues/2282)) ([f8969d2](https://github.com/googleapis/sdk-platform-java/commit/f8969d2b5b50b338967802436bac8d21c3656e07))
+* new artifact for sdk-platform-java configs. ([#2315](https://github.com/googleapis/sdk-platform-java/issues/2315)) ([99e5195](https://github.com/googleapis/sdk-platform-java/commit/99e51953f319c97b225a0209b093367fa440f9d3))
+* Parse Host Service Name ([#2300](https://github.com/googleapis/sdk-platform-java/issues/2300)) ([8822f3b](https://github.com/googleapis/sdk-platform-java/commit/8822f3b514bdddf028c12db7a773e80fa4f4f3a1))
+* Structs mapper utility ([#2278](https://github.com/googleapis/sdk-platform-java/issues/2278)) ([da6607b](https://github.com/googleapis/sdk-platform-java/commit/da6607b17130ab045640618d505fda915ddb8e49))
+* unmanaged dependency check ([#2223](https://github.com/googleapis/sdk-platform-java/issues/2223)) ([3439691](https://github.com/googleapis/sdk-platform-java/commit/34396919b6c58f85af26650a7d3d429d9b4e9008))
+
+
+### Bug Fixes
+
+* format proto comments in Client Overview ([#2280](https://github.com/googleapis/sdk-platform-java/issues/2280)) ([4029fbd](https://github.com/googleapis/sdk-platform-java/commit/4029fbdd9ab060ef55382b1890f323f85fe3ceef))
+* re-enable checkstyle in sdk-platform-java-config ([#2335](https://github.com/googleapis/sdk-platform-java/issues/2335)) ([285bdb1](https://github.com/googleapis/sdk-platform-java/commit/285bdb101194740a7a9618565679b7be01c70f43))
+
+
+### Dependencies
+
+* update google api dependencies ([#2277](https://github.com/googleapis/sdk-platform-java/issues/2277)) ([4bc45bd](https://github.com/googleapis/sdk-platform-java/commit/4bc45bd3c525714acc3866ce47943f91d860f8d2))
+* update googleapis/java-cloud-bom digest to 3fe0c17 ([#2286](https://github.com/googleapis/sdk-platform-java/issues/2286)) ([c7de93e](https://github.com/googleapis/sdk-platform-java/commit/c7de93e6a6e08b663041e98eaab9cb5449d51b9f))
+* update grpc dependencies to v1.60.0 ([#2288](https://github.com/googleapis/sdk-platform-java/issues/2288)) ([c8bf058](https://github.com/googleapis/sdk-platform-java/commit/c8bf05813cb8968447572df5622cf497510bca34))
+
+## [2.30.0](https://github.com/googleapis/sdk-platform-java/compare/v2.29.0...v2.30.0) (2023-11-29)
+
+
+### Features
+
+* update javadocs for Client classes to include table of methods ([#2114](https://github.com/googleapis/sdk-platform-java/issues/2114)) ([c81cd0f](https://github.com/googleapis/sdk-platform-java/commit/c81cd0ffb95df7fc6d52629c770254aea60ff4fd))
+
+
+### Bug Fixes
+
+* confirm owlbot-copy succeeeded to transfer java files ([#2235](https://github.com/googleapis/sdk-platform-java/issues/2235)) ([94d1dd2](https://github.com/googleapis/sdk-platform-java/commit/94d1dd24e38b348a9727f231d6d9aa4e8635144e))
+* improve information on CancellationException for LROs ([#2236](https://github.com/googleapis/sdk-platform-java/issues/2236)) ([741e40c](https://github.com/googleapis/sdk-platform-java/commit/741e40ceb0b7d5e0eceb8a90a52acf57648c0066))
+* owlbot-cli image sha to be locked (hermetic) ([#2252](https://github.com/googleapis/sdk-platform-java/issues/2252)) ([6c4c236](https://github.com/googleapis/sdk-platform-java/commit/6c4c2364c1798642a19c8208c09c9becd562012d))
+
+
+### Dependencies
+
+* update dependency com.fasterxml.jackson:jackson-bom to v2.16.0 ([#2259](https://github.com/googleapis/sdk-platform-java/issues/2259)) ([4eef7ec](https://github.com/googleapis/sdk-platform-java/commit/4eef7ec8311cf196253143fdaa46ccdb6c8fc153))
+* update dependency com.google.cloud:grpc-gcp to v1.5.0 ([#2265](https://github.com/googleapis/sdk-platform-java/issues/2265)) ([964a649](https://github.com/googleapis/sdk-platform-java/commit/964a649838b9b58c2ae40e6d7da205a9ffd17b90))
+* update dependency com.google.errorprone:error_prone_annotations to v2.23.0 ([#2182](https://github.com/googleapis/sdk-platform-java/issues/2182)) ([5116f3d](https://github.com/googleapis/sdk-platform-java/commit/5116f3dcd93759220b450ebfd0a1535cb56e6e30))
+* update dependency com.googlecode.maven-download-plugin:download-maven-plugin to v1.7.1 ([#2151](https://github.com/googleapis/sdk-platform-java/issues/2151)) ([cbe1bb1](https://github.com/googleapis/sdk-platform-java/commit/cbe1bb11548c791cde32d3c5b237977fa486f11a))
+* update dependency cryptography to v41.0.7 ([#2255](https://github.com/googleapis/sdk-platform-java/issues/2255)) ([a98ea5c](https://github.com/googleapis/sdk-platform-java/commit/a98ea5cccc8dc8e4015473a9529f3cd066ad7913))
+* update dependency net.bytebuddy:byte-buddy to v1.14.10 ([#2256](https://github.com/googleapis/sdk-platform-java/issues/2256)) ([094f263](https://github.com/googleapis/sdk-platform-java/commit/094f2639e74e483658b567ceb7125079caed7e34))
+* update dependency org.apache.commons:commons-lang3 to v3.14.0 ([#2260](https://github.com/googleapis/sdk-platform-java/issues/2260)) ([03dc96b](https://github.com/googleapis/sdk-platform-java/commit/03dc96bb5f48e38436812eb882255695d256d69f))
+* update dependency org.checkerframework:checker-qual to v3.40.0 ([#2221](https://github.com/googleapis/sdk-platform-java/issues/2221)) ([c3fcfbd](https://github.com/googleapis/sdk-platform-java/commit/c3fcfbd44656f7589527642056f46c21be8019a5))
+* update dependency pyasn1 to v0.5.1 ([#2262](https://github.com/googleapis/sdk-platform-java/issues/2262)) ([4bfa6cf](https://github.com/googleapis/sdk-platform-java/commit/4bfa6cf7c347d8068b6cbf94788cd057686afb12))
+* update google api dependencies ([#2219](https://github.com/googleapis/sdk-platform-java/issues/2219)) ([877cb2a](https://github.com/googleapis/sdk-platform-java/commit/877cb2a14a3dd9abe26b1c3a4652811c1770ccdc))
+* update googleapis/java-cloud-bom digest to ab98a49 ([#2231](https://github.com/googleapis/sdk-platform-java/issues/2231)) ([67a2c94](https://github.com/googleapis/sdk-platform-java/commit/67a2c947fe6ee4bb3478b873a92d5e7c66eaa3b7))
+* update grpc dependencies to v1.59.1 ([#2263](https://github.com/googleapis/sdk-platform-java/issues/2263)) ([fdb1559](https://github.com/googleapis/sdk-platform-java/commit/fdb1559013e6c2f6c6638c8ed6a078a92314f803))
+* update guava monorepo ([#2139](https://github.com/googleapis/sdk-platform-java/issues/2139)) ([b861f19](https://github.com/googleapis/sdk-platform-java/commit/b861f19a824353da8f585151924813d9197b3c9b))
+* update netty dependencies to v4.1.101.final ([#2257](https://github.com/googleapis/sdk-platform-java/issues/2257)) ([2733fcf](https://github.com/googleapis/sdk-platform-java/commit/2733fcf8baf33f6129cccec3f6b3a305e68ad947))
+* update protobuf dependencies to v3.25.0 ([#2222](https://github.com/googleapis/sdk-platform-java/issues/2222)) ([445477f](https://github.com/googleapis/sdk-platform-java/commit/445477f7fec3a6d4be9df4c9cf3f01c007307645))
+* update protobuf dependencies to v3.25.1 ([#2242](https://github.com/googleapis/sdk-platform-java/issues/2242)) ([faea6c4](https://github.com/googleapis/sdk-platform-java/commit/faea6c4414977fb5e5d7d2abc3604e01d27731c1))
+
+
+### Documentation
+
+* Update RetrySettings javadocs to include polling ([#1863](https://github.com/googleapis/sdk-platform-java/issues/1863)) ([3c9cb06](https://github.com/googleapis/sdk-platform-java/commit/3c9cb061c8cee59dd1072bd415c32110ecf10517))
+
+## [2.29.0](https://github.com/googleapis/sdk-platform-java/compare/v2.28.0...v2.29.0) (2023-10-31)
+
+
+### Features
+
+* `generate_library.sh` with postprocessing ([#1951](https://github.com/googleapis/sdk-platform-java/issues/1951)) ([39b9f0e](https://github.com/googleapis/sdk-platform-java/commit/39b9f0e956b7967d118873ee2e365fe6a02a029b))
+
+
+### Dependencies
+
+* update dependency cryptography to v41.0.5 ([#2206](https://github.com/googleapis/sdk-platform-java/issues/2206)) ([6d1f84a](https://github.com/googleapis/sdk-platform-java/commit/6d1f84a7923573346fbfbfa3107a3da4c0a19bfe))
+* update dependency google-auth to v2.23.4 ([#2217](https://github.com/googleapis/sdk-platform-java/issues/2217)) ([f1ee04d](https://github.com/googleapis/sdk-platform-java/commit/f1ee04d902000c5f8dd6a9c51dea57c9de01a25e))
+* update dependency google-cloud-storage to v2.13.0 ([#2216](https://github.com/googleapis/sdk-platform-java/issues/2216)) ([1af12a8](https://github.com/googleapis/sdk-platform-java/commit/1af12a8881c2036d4ddb844c061b5f6b17e991d9))
+* update google api dependencies ([#2187](https://github.com/googleapis/sdk-platform-java/issues/2187)) ([448b0d1](https://github.com/googleapis/sdk-platform-java/commit/448b0d1eea5c4bd5f89176315c21cf7d49bc1af5))
+* update googleapis/java-cloud-bom digest to 41d86db ([#2205](https://github.com/googleapis/sdk-platform-java/issues/2205)) ([9152f24](https://github.com/googleapis/sdk-platform-java/commit/9152f24e7aafa165326205b12d3709c61c842a3f))
+* update googleapis/java-cloud-bom digest to b8394a1 ([#2201](https://github.com/googleapis/sdk-platform-java/issues/2201)) ([f9957df](https://github.com/googleapis/sdk-platform-java/commit/f9957df04bc00d72e1a26dfd5c4c4805172d58d7))
+* update googleapis/java-cloud-bom digest to d06156f ([#2200](https://github.com/googleapis/sdk-platform-java/issues/2200)) ([097e37e](https://github.com/googleapis/sdk-platform-java/commit/097e37e560646ed47925e3620c5a490a78889ec7))
+* update googleapis/java-cloud-bom digest to e896c4e ([#2198](https://github.com/googleapis/sdk-platform-java/issues/2198)) ([15a796f](https://github.com/googleapis/sdk-platform-java/commit/15a796f718e7e27991d27a337223314addb0375a))
+* update graal-sdk to 22.3.3 in bazel dependencies file ([#2209](https://github.com/googleapis/sdk-platform-java/issues/2209)) ([25957d3](https://github.com/googleapis/sdk-platform-java/commit/25957d3b8cc0424d5b1ac293e771a15f0fc54721))
+* update grpc dependencies to v1.59.0 ([#2211](https://github.com/googleapis/sdk-platform-java/issues/2211)) ([7dafa8d](https://github.com/googleapis/sdk-platform-java/commit/7dafa8d452615e5ac5dd5fbb95e645a1ce4a9226))
+
## [2.28.0](https://github.com/googleapis/sdk-platform-java/compare/v2.27.0...v2.28.0) (2023-10-19)
diff --git a/WORKSPACE b/WORKSPACE
index d86d550b49..2a36da16fd 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -60,7 +60,7 @@ maven_install(
repositories = ["https://repo.maven.apache.org/maven2/"],
)
-_gapic_generator_java_version = "2.28.0" # {x-version-update:gapic-generator-java:current}
+_gapic_generator_java_version = "2.34.0" # {x-version-update:gapic-generator-java:current}
maven_install(
artifacts = [
diff --git a/api-common-java/pom.xml b/api-common-java/pom.xml
index a2e7f86659..634ce87ad4 100644
--- a/api-common-java/pom.xml
+++ b/api-common-java/pom.xml
@@ -5,14 +5,14 @@
com.google.apiapi-commonjar
- 2.19.0
+ 2.25.0API CommonCommon utilities for Google APIs in Javacom.google.apigapic-generator-java-pom-parent
- 2.28.0
+ 2.34.0../gapic-generator-java-pom-parent
@@ -63,7 +63,7 @@
com.google.errorproneerror_prone_annotations
- 2.22.0
+ 2.24.1compile
@@ -77,7 +77,7 @@
com.google.truthtruth
- 1.1.5
+ 1.2.0test
diff --git a/coverage-report/pom.xml b/coverage-report/pom.xml
index b20e5d1f9e..39fa549992 100644
--- a/coverage-report/pom.xml
+++ b/coverage-report/pom.xml
@@ -31,22 +31,22 @@
com.google.apigax
- 2.36.0
+ 2.42.0com.google.apigax-grpc
- 2.36.0
+ 2.42.0com.google.apigax-httpjson
- 2.36.0
+ 2.42.0com.google.apiapi-common
- 2.19.0
+ 2.25.0
diff --git a/gapic-generator-java-bom/pom.xml b/gapic-generator-java-bom/pom.xml
index bf0756438f..6a7cb04b1b 100644
--- a/gapic-generator-java-bom/pom.xml
+++ b/gapic-generator-java-bom/pom.xml
@@ -4,7 +4,7 @@
com.google.apigapic-generator-java-bompom
- 2.28.0
+ 2.34.0GAPIC Generator Java BOM
BOM for the libraries in gapic-generator-java repository. Users should not
@@ -15,7 +15,7 @@
com.google.apigapic-generator-java-pom-parent
- 2.28.0
+ 2.34.0../gapic-generator-java-pom-parent
@@ -75,61 +75,61 @@
com.google.apiapi-common
- 2.19.0
+ 2.25.0com.google.apigax-bom
- 2.36.0
+ 2.42.0pomimportcom.google.apigapic-generator-java
- 2.28.0
+ 2.34.0com.google.api.grpcgrpc-google-common-protos
- 2.27.0
+ 2.33.0com.google.api.grpcproto-google-common-protos
- 2.27.0
+ 2.33.0com.google.api.grpcproto-google-iam-v1
- 1.22.0
+ 1.28.0com.google.api.grpcproto-google-iam-v2
- 1.22.0
+ 1.28.0com.google.api.grpcproto-google-iam-v2beta
- 1.22.0
+ 1.28.0com.google.api.grpcgrpc-google-iam-v1
- 1.22.0
+ 1.28.0com.google.api.grpcgrpc-google-iam-v2
- 1.22.0
+ 1.28.0com.google.api.grpcgrpc-google-iam-v2beta
- 1.22.0
+ 1.28.0
diff --git a/gapic-generator-java-pom-parent/pom.xml b/gapic-generator-java-pom-parent/pom.xml
index fb52c48cdd..9bf02cdf3c 100644
--- a/gapic-generator-java-pom-parent/pom.xml
+++ b/gapic-generator-java-pom-parent/pom.xml
@@ -5,7 +5,7 @@
4.0.0com.google.apigapic-generator-java-pom-parent
- 2.28.0
+ 2.34.0pomGAPIC Generator Java POM Parenthttps://github.com/googleapis/sdk-platform-java
@@ -15,7 +15,7 @@
com.google.cloudgoogle-cloud-shared-config
- 1.6.0
+ 1.7.1
@@ -26,12 +26,12 @@
1.3.2
- 1.58.0
- 1.20.0
+ 1.61.0
+ 1.22.01.43.32.10.1
- 32.1.2-jre
- 3.24.4
+ 32.1.3-jre
+ 3.25.28
@@ -110,7 +110,7 @@
org.apache.maven.pluginsmaven-surefire-plugin
- 3.1.2
+ 3.2.5
@@ -126,7 +126,7 @@
org.apache.maven.pluginsmaven-failsafe-plugin
- 3.1.2
+ 3.2.5
diff --git a/gapic-generator-java/DEVELOPMENT.md b/gapic-generator-java/DEVELOPMENT.md
index 7052d0d0ac..d097e491fc 100644
--- a/gapic-generator-java/DEVELOPMENT.md
+++ b/gapic-generator-java/DEVELOPMENT.md
@@ -50,6 +50,8 @@ than the "test" phase.
To run integration test for gapic-generator-java, run this Bazel command in the
root of the repository (where you have WORKSPACE file for Bazel.)
+*Note* Make sure you run `mvn clean install` to gather any changes you have made before updating the integration tests.
+
```sh
# In the repository root directory
bazelisk test //... # integration tests
@@ -73,6 +75,13 @@ bazelisk test //... # integration tests
bazelisk run //test/integration:update_redis
```
+- To update all integration tests you can use this command:
+
+ ```sh
+ # In the repository root directory
+ bazelisk run //test/integration:update_asset && bazelisk run //test/integration:update_credentials && bazelisk run //test/integration:update_iam && bazelisk run //test/integration:update_kms && bazelisk run //test/integration:update_pubsub && bazelisk run //test/integration:update_logging && bazelisk run //test/integration:update_redis && bazelisk run //test/integration:update_storage && bazelisk run //test/integration:update_library && bazelisk run //test/integration:update_compute && bazelisk run //test/integration:update_bigtable && bazelisk run //test/integration:update_apigeeconnect
+ ```
+
## Running the Plugin under googleapis with local gapic-generator-java
For running the Plugin with showcase protos and local gapic-generator-java, see
diff --git a/gapic-generator-java/pom.xml b/gapic-generator-java/pom.xml
index 6960296a2a..b0016c80c8 100644
--- a/gapic-generator-java/pom.xml
+++ b/gapic-generator-java/pom.xml
@@ -4,7 +4,7 @@
4.0.0com.google.apigapic-generator-java
- 2.28.0
+ 2.34.0GAPIC Generator JavaGAPIC generator Java
@@ -22,7 +22,7 @@
com.google.apigapic-generator-java-pom-parent
- 2.28.0
+ 2.34.0../gapic-generator-java-pom-parent
@@ -31,7 +31,7 @@
com.google.apigapic-generator-java-bom
- 2.28.0
+ 2.34.0pomimport
@@ -106,7 +106,7 @@
maven-compiler-plugin
- 3.11.0
+ 3.12.188
@@ -317,7 +317,7 @@
org.apache.maven.pluginsmaven-surefire-plugin
- 3.1.2
+ 3.2.5
@@ -451,7 +451,7 @@
com.google.errorproneerror_prone_annotations
- 2.22.0
+ 2.24.1
+ 2.42.0pomGAX (Google Api eXtensions) for Java (BOM)Google Api eXtensions for Java (BOM)
@@ -11,7 +11,7 @@
com.google.cloudgoogle-cloud-shared-config
- 1.6.0
+ 1.7.1
@@ -43,55 +43,55 @@
com.google.apigax
- 2.36.0
+ 2.42.0com.google.apigax
- 2.36.0
+ 2.42.0test-jartestlibcom.google.apigax
- 2.36.0
+ 2.42.0testlibcom.google.apigax-grpc
- 2.36.0
+ 2.42.0com.google.apigax-grpc
- 2.36.0
+ 2.42.0test-jartestlibcom.google.apigax-grpc
- 2.36.0
+ 2.42.0testlibcom.google.apigax-httpjson
- 2.36.0
+ 2.42.0com.google.apigax-httpjson
- 2.36.0
+ 2.42.0test-jartestlibcom.google.apigax-httpjson
- 2.36.0
+ 2.42.0testlib
diff --git a/gax-java/gax-grpc/pom.xml b/gax-java/gax-grpc/pom.xml
index a2f803cc98..cad83c87cd 100644
--- a/gax-java/gax-grpc/pom.xml
+++ b/gax-java/gax-grpc/pom.xml
@@ -3,7 +3,7 @@
4.0.0gax-grpc
- 2.36.0
+ 2.42.0jarGAX (Google Api eXtensions) for Java (gRPC)Google Api eXtensions for Java (gRPC)
@@ -11,7 +11,7 @@
com.google.apigax-parent
- 2.36.0
+ 2.42.0
diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcCallContext.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcCallContext.java
index 5f33c36448..886beda4ec 100644
--- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcCallContext.java
+++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcCallContext.java
@@ -30,15 +30,21 @@
package com.google.api.gax.grpc;
import com.google.api.core.BetaApi;
+import com.google.api.core.InternalApi;
import com.google.api.gax.retrying.RetrySettings;
import com.google.api.gax.rpc.ApiCallContext;
+import com.google.api.gax.rpc.ApiExceptionFactory;
+import com.google.api.gax.rpc.EndpointContext;
import com.google.api.gax.rpc.StatusCode;
import com.google.api.gax.rpc.TransportChannel;
+import com.google.api.gax.rpc.UnauthenticatedException;
+import com.google.api.gax.rpc.UnavailableException;
import com.google.api.gax.rpc.internal.ApiCallContextOptions;
import com.google.api.gax.rpc.internal.Headers;
import com.google.api.gax.tracing.ApiTracer;
import com.google.api.gax.tracing.BaseApiTracer;
import com.google.auth.Credentials;
+import com.google.auth.Retryable;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@@ -47,7 +53,9 @@
import io.grpc.Channel;
import io.grpc.Deadline;
import io.grpc.Metadata;
+import io.grpc.Status;
import io.grpc.auth.MoreCallCredentials;
+import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -66,9 +74,15 @@
*/
@BetaApi("Reference ApiCallContext instead - this class is likely to experience breaking changes")
public final class GrpcCallContext implements ApiCallContext {
- static final CallOptions.Key TRACER_KEY = CallOptions.Key.create("gax.tracer");
+ private static final GrpcStatusCode UNAUTHENTICATED_STATUS_CODE =
+ GrpcStatusCode.of(Status.Code.UNAUTHENTICATED);
+
+ // This field is made public for handwritten libraries to easily access the tracer from
+ // CallOptions
+ public static final CallOptions.Key TRACER_KEY = CallOptions.Key.create("gax.tracer");
private final Channel channel;
+ @Nullable private final Credentials credentials;
private final CallOptions callOptions;
@Nullable private final Duration timeout;
@Nullable private final Duration streamWaitTimeout;
@@ -78,10 +92,12 @@ public final class GrpcCallContext implements ApiCallContext {
@Nullable private final ImmutableSet retryableCodes;
private final ImmutableMap> extraHeaders;
private final ApiCallContextOptions options;
+ private final EndpointContext endpointContext;
/** Returns an empty instance with a null channel and default {@link CallOptions}. */
public static GrpcCallContext createDefault() {
return new GrpcCallContext(
+ null,
null,
CallOptions.DEFAULT,
null,
@@ -91,6 +107,7 @@ public static GrpcCallContext createDefault() {
ImmutableMap.>of(),
ApiCallContextOptions.getDefaultOptions(),
null,
+ null,
null);
}
@@ -98,6 +115,7 @@ public static GrpcCallContext createDefault() {
public static GrpcCallContext of(Channel channel, CallOptions callOptions) {
return new GrpcCallContext(
channel,
+ null,
callOptions,
null,
null,
@@ -106,11 +124,13 @@ public static GrpcCallContext of(Channel channel, CallOptions callOptions) {
ImmutableMap.>of(),
ApiCallContextOptions.getDefaultOptions(),
null,
+ null,
null);
}
private GrpcCallContext(
Channel channel,
+ @Nullable Credentials credentials,
CallOptions callOptions,
@Nullable Duration timeout,
@Nullable Duration streamWaitTimeout,
@@ -119,8 +139,10 @@ private GrpcCallContext(
ImmutableMap> extraHeaders,
ApiCallContextOptions options,
@Nullable RetrySettings retrySettings,
- @Nullable Set retryableCodes) {
+ @Nullable Set retryableCodes,
+ EndpointContext endpointContext) {
this.channel = channel;
+ this.credentials = credentials;
this.callOptions = Preconditions.checkNotNull(callOptions);
this.timeout = timeout;
this.streamWaitTimeout = streamWaitTimeout;
@@ -130,6 +152,7 @@ private GrpcCallContext(
this.options = Preconditions.checkNotNull(options);
this.retrySettings = retrySettings;
this.retryableCodes = retryableCodes == null ? null : ImmutableSet.copyOf(retryableCodes);
+ this.endpointContext = endpointContext;
}
/**
@@ -158,7 +181,19 @@ public GrpcCallContext nullToSelf(ApiCallContext inputContext) {
public GrpcCallContext withCredentials(Credentials newCredentials) {
Preconditions.checkNotNull(newCredentials);
CallCredentials callCredentials = MoreCallCredentials.from(newCredentials);
- return withCallOptions(callOptions.withCallCredentials(callCredentials));
+ return new GrpcCallContext(
+ channel,
+ newCredentials,
+ callOptions.withCallCredentials(callCredentials),
+ timeout,
+ streamWaitTimeout,
+ streamIdleTimeout,
+ channelAffinity,
+ extraHeaders,
+ options,
+ retrySettings,
+ retryableCodes,
+ endpointContext);
}
@Override
@@ -172,6 +207,24 @@ public GrpcCallContext withTransportChannel(TransportChannel inputChannel) {
return withChannel(transportChannel.getChannel());
}
+ @Override
+ public GrpcCallContext withEndpointContext(EndpointContext endpointContext) {
+ Preconditions.checkNotNull(endpointContext);
+ return new GrpcCallContext(
+ channel,
+ credentials,
+ callOptions,
+ timeout,
+ streamWaitTimeout,
+ streamIdleTimeout,
+ channelAffinity,
+ extraHeaders,
+ options,
+ retrySettings,
+ retryableCodes,
+ endpointContext);
+ }
+
@Override
public GrpcCallContext withTimeout(@Nullable Duration timeout) {
// Default RetrySettings use 0 for RPC timeout. Treat that as disabled timeouts.
@@ -186,6 +239,7 @@ public GrpcCallContext withTimeout(@Nullable Duration timeout) {
return new GrpcCallContext(
channel,
+ credentials,
callOptions,
timeout,
streamWaitTimeout,
@@ -194,7 +248,8 @@ public GrpcCallContext withTimeout(@Nullable Duration timeout) {
extraHeaders,
options,
retrySettings,
- retryableCodes);
+ retryableCodes,
+ endpointContext);
}
@Nullable
@@ -212,6 +267,7 @@ public GrpcCallContext withStreamWaitTimeout(@Nullable Duration streamWaitTimeou
return new GrpcCallContext(
channel,
+ credentials,
callOptions,
timeout,
streamWaitTimeout,
@@ -220,7 +276,8 @@ public GrpcCallContext withStreamWaitTimeout(@Nullable Duration streamWaitTimeou
extraHeaders,
options,
retrySettings,
- retryableCodes);
+ retryableCodes,
+ endpointContext);
}
@Override
@@ -232,6 +289,7 @@ public GrpcCallContext withStreamIdleTimeout(@Nullable Duration streamIdleTimeou
return new GrpcCallContext(
channel,
+ credentials,
callOptions,
timeout,
streamWaitTimeout,
@@ -240,13 +298,15 @@ public GrpcCallContext withStreamIdleTimeout(@Nullable Duration streamIdleTimeou
extraHeaders,
options,
retrySettings,
- retryableCodes);
+ retryableCodes,
+ endpointContext);
}
@BetaApi("The surface for channel affinity is not stable yet and may change in the future.")
public GrpcCallContext withChannelAffinity(@Nullable Integer affinity) {
return new GrpcCallContext(
channel,
+ credentials,
callOptions,
timeout,
streamWaitTimeout,
@@ -255,7 +315,8 @@ public GrpcCallContext withChannelAffinity(@Nullable Integer affinity) {
extraHeaders,
options,
retrySettings,
- retryableCodes);
+ retryableCodes,
+ endpointContext);
}
@BetaApi("The surface for extra headers is not stable yet and may change in the future.")
@@ -266,6 +327,7 @@ public GrpcCallContext withExtraHeaders(Map> extraHeaders)
Headers.mergeHeaders(this.extraHeaders, extraHeaders);
return new GrpcCallContext(
channel,
+ credentials,
callOptions,
timeout,
streamWaitTimeout,
@@ -274,7 +336,8 @@ public GrpcCallContext withExtraHeaders(Map> extraHeaders)
newExtraHeaders,
options,
retrySettings,
- retryableCodes);
+ retryableCodes,
+ endpointContext);
}
@Override
@@ -286,6 +349,7 @@ public RetrySettings getRetrySettings() {
public GrpcCallContext withRetrySettings(RetrySettings retrySettings) {
return new GrpcCallContext(
channel,
+ credentials,
callOptions,
timeout,
streamWaitTimeout,
@@ -294,7 +358,8 @@ public GrpcCallContext withRetrySettings(RetrySettings retrySettings) {
extraHeaders,
options,
retrySettings,
- retryableCodes);
+ retryableCodes,
+ endpointContext);
}
@Override
@@ -306,6 +371,7 @@ public Set getRetryableCodes() {
public GrpcCallContext withRetryableCodes(Set retryableCodes) {
return new GrpcCallContext(
channel,
+ credentials,
callOptions,
timeout,
streamWaitTimeout,
@@ -314,7 +380,8 @@ public GrpcCallContext withRetryableCodes(Set retryableCodes) {
extraHeaders,
options,
retrySettings,
- retryableCodes);
+ retryableCodes,
+ endpointContext);
}
@Override
@@ -329,6 +396,11 @@ public ApiCallContext merge(ApiCallContext inputCallContext) {
}
GrpcCallContext grpcCallContext = (GrpcCallContext) inputCallContext;
+ Credentials newCredentials = grpcCallContext.credentials;
+ if (newCredentials == null) {
+ newCredentials = credentials;
+ }
+
Channel newChannel = grpcCallContext.channel;
if (newChannel == null) {
newChannel = channel;
@@ -394,8 +466,11 @@ public ApiCallContext merge(ApiCallContext inputCallContext) {
newCallOptions = newCallOptions.withOption(TRACER_KEY, newTracer);
}
+ // The EndpointContext is not updated as there should be no reason for a user
+ // to update this.
return new GrpcCallContext(
newChannel,
+ newCredentials,
newCallOptions,
newTimeout,
newStreamWaitTimeout,
@@ -404,7 +479,8 @@ public ApiCallContext merge(ApiCallContext inputCallContext) {
newExtraHeaders,
newOptions,
newRetrySettings,
- newRetryableCodes);
+ newRetryableCodes,
+ endpointContext);
}
/** The {@link Channel} set on this context. */
@@ -456,6 +532,7 @@ public Map> getExtraHeaders() {
public GrpcCallContext withChannel(Channel newChannel) {
return new GrpcCallContext(
newChannel,
+ credentials,
callOptions,
timeout,
streamWaitTimeout,
@@ -464,13 +541,15 @@ public GrpcCallContext withChannel(Channel newChannel) {
extraHeaders,
options,
retrySettings,
- retryableCodes);
+ retryableCodes,
+ endpointContext);
}
/** Returns a new instance with the call options set to the given call options. */
public GrpcCallContext withCallOptions(CallOptions newCallOptions) {
return new GrpcCallContext(
channel,
+ credentials,
newCallOptions,
timeout,
streamWaitTimeout,
@@ -479,7 +558,8 @@ public GrpcCallContext withCallOptions(CallOptions newCallOptions) {
extraHeaders,
options,
retrySettings,
- retryableCodes);
+ retryableCodes,
+ endpointContext);
}
public GrpcCallContext withRequestParamsDynamicHeaderOption(String requestParams) {
@@ -513,6 +593,7 @@ public GrpcCallContext withOption(Key key, T value) {
ApiCallContextOptions newOptions = options.withOption(key, value);
return new GrpcCallContext(
channel,
+ credentials,
callOptions,
timeout,
streamWaitTimeout,
@@ -521,7 +602,8 @@ public GrpcCallContext withOption(Key key, T value) {
extraHeaders,
newOptions,
retrySettings,
- retryableCodes);
+ retryableCodes,
+ endpointContext);
}
/** {@inheritDoc} */
@@ -530,10 +612,49 @@ public T getOption(Key key) {
return options.getOption(key);
}
+ /**
+ * Validate the Universe Domain to ensure that the user configured Universe Domain and the
+ * Credentials' Universe Domain match. An exception will be raised if there are any issues when
+ * trying to validate (i.e. unable to access the universe domain).
+ *
+ * @throws UnauthenticatedException Thrown if the universe domain that the user configured does
+ * not match the Credential's universe domain.
+ * @throws UnavailableException If client library is unable to retrieve the universe domain from
+ * the Credentials and the RPC is configured to retry Unavailable exceptions, the client
+ * library will attempt to retry with the RPC's defined retry bounds. If the retry bounds have
+ * been exceeded and the library is still unable to retrieve the universe domain, the
+ * exception will be thrown back to the user.
+ */
+ @InternalApi
+ public void validateUniverseDomain() {
+ try {
+ endpointContext.validateUniverseDomain(credentials, UNAUTHENTICATED_STATUS_CODE);
+ } catch (IOException e) {
+ // Check if it is an Auth Exception (All instances of IOException from endpointContext's
+ // `validateUniverseDomain()` call should be an Auth Exception).
+ if (e instanceof Retryable) {
+ Retryable retryable = (Retryable) e;
+ // Keep the behavior the same as gRPC-Java. Mark as Auth Exceptions as Unavailable
+ throw ApiExceptionFactory.createException(
+ EndpointContext.UNABLE_TO_RETRIEVE_CREDENTIALS_ERROR_MESSAGE,
+ e,
+ GrpcStatusCode.of(Status.Code.UNAVAILABLE),
+ retryable.isRetryable());
+ }
+ // This exception below should never be raised as all IOExceptions should be caught above.
+ throw ApiExceptionFactory.createException(
+ EndpointContext.UNABLE_TO_RETRIEVE_CREDENTIALS_ERROR_MESSAGE,
+ e,
+ UNAUTHENTICATED_STATUS_CODE,
+ false);
+ }
+ }
+
@Override
public int hashCode() {
return Objects.hash(
channel,
+ credentials,
callOptions,
timeout,
streamWaitTimeout,
@@ -542,7 +663,8 @@ public int hashCode() {
extraHeaders,
options,
retrySettings,
- retryableCodes);
+ retryableCodes,
+ endpointContext);
}
@Override
@@ -556,6 +678,7 @@ public boolean equals(Object o) {
GrpcCallContext that = (GrpcCallContext) o;
return Objects.equals(channel, that.channel)
+ && Objects.equals(credentials, that.credentials)
&& Objects.equals(callOptions, that.callOptions)
&& Objects.equals(timeout, that.timeout)
&& Objects.equals(streamWaitTimeout, that.streamWaitTimeout)
@@ -564,7 +687,8 @@ public boolean equals(Object o) {
&& Objects.equals(extraHeaders, that.extraHeaders)
&& Objects.equals(options, that.options)
&& Objects.equals(retrySettings, that.retrySettings)
- && Objects.equals(retryableCodes, that.retryableCodes);
+ && Objects.equals(retryableCodes, that.retryableCodes)
+ && Objects.equals(endpointContext, that.endpointContext);
}
Metadata getMetadata() {
diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcCallSettings.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcCallSettings.java
index a5aef3d69f..fae4ae9d25 100644
--- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcCallSettings.java
+++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcCallSettings.java
@@ -30,6 +30,7 @@
package com.google.api.gax.grpc;
import com.google.api.core.BetaApi;
+import com.google.api.gax.rpc.RequestMutator;
import com.google.api.gax.rpc.RequestParamsExtractor;
import io.grpc.MethodDescriptor;
@@ -37,11 +38,13 @@
public class GrpcCallSettings {
private final MethodDescriptor methodDescriptor;
private final RequestParamsExtractor paramsExtractor;
+ private final RequestMutator requestMutator;
private final boolean alwaysAwaitTrailers;
private GrpcCallSettings(Builder builder) {
this.methodDescriptor = builder.methodDescriptor;
this.paramsExtractor = builder.paramsExtractor;
+ this.requestMutator = builder.requestMutator;
this.alwaysAwaitTrailers = builder.shouldAwaitTrailers;
}
@@ -53,6 +56,10 @@ public RequestParamsExtractor getParamsExtractor() {
return paramsExtractor;
}
+ public RequestMutator getRequestMutator() {
+ return requestMutator;
+ }
+
@BetaApi
public boolean shouldAwaitTrailers() {
return alwaysAwaitTrailers;
@@ -76,6 +83,8 @@ public Builder toBuilder() {
public static class Builder {
private MethodDescriptor methodDescriptor;
private RequestParamsExtractor paramsExtractor;
+
+ private RequestMutator requestMutator;
private boolean shouldAwaitTrailers;
private Builder() {}
@@ -83,6 +92,7 @@ private Builder() {}
private Builder(GrpcCallSettings settings) {
this.methodDescriptor = settings.methodDescriptor;
this.paramsExtractor = settings.paramsExtractor;
+ this.requestMutator = settings.requestMutator;
this.shouldAwaitTrailers = settings.alwaysAwaitTrailers;
}
@@ -98,6 +108,11 @@ public Builder setParamsExtractor(
return this;
}
+ public Builder setRequestMutator(RequestMutator requestMutator) {
+ this.requestMutator = requestMutator;
+ return this;
+ }
+
@BetaApi
public Builder setShouldAwaitTrailers(boolean b) {
this.shouldAwaitTrailers = b;
diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcCallableFactory.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcCallableFactory.java
index 8a0c4e0b37..974feb0c43 100644
--- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcCallableFactory.java
+++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcCallableFactory.java
@@ -85,7 +85,13 @@ public static UnaryCallable createBas
GrpcRawCallableFactory.createUnaryCallable(
grpcCallSettings, callSettings.getRetryableCodes());
- callable = Callables.retrying(callable, callSettings, clientContext);
+ if (grpcCallSettings.getRequestMutator() != null) {
+ callable =
+ Callables.retrying(
+ callable, callSettings, clientContext, grpcCallSettings.getRequestMutator());
+ } else {
+ callable = Callables.retrying(callable, callSettings, clientContext);
+ }
return callable;
}
diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcClientCalls.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcClientCalls.java
index bc72f6f1f1..80e8797f01 100644
--- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcClientCalls.java
+++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcClientCalls.java
@@ -95,6 +95,10 @@ public static ClientCall newCall(
channel = ClientInterceptors.intercept(channel, interceptor);
}
+ // Validate the Universe Domain prior to the call. Only allow the call to go through
+ // if the Universe Domain is valid.
+ grpcContext.validateUniverseDomain();
+
try (Scope ignored = grpcContext.getTracer().inScope()) {
return channel.newCall(descriptor, callOptions);
}
diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcDirectCallable.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcDirectCallable.java
index 5b6a5f1bad..33041145dd 100644
--- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcDirectCallable.java
+++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcDirectCallable.java
@@ -30,6 +30,7 @@
package com.google.api.gax.grpc;
import com.google.api.core.ApiFuture;
+import com.google.api.core.InternalApi;
import com.google.api.core.ListenableFutureToApiFuture;
import com.google.api.gax.rpc.ApiCallContext;
import com.google.api.gax.rpc.UnaryCallable;
@@ -43,6 +44,7 @@
*
*
Package-private for internal use.
*/
+@InternalApi
class GrpcDirectCallable extends UnaryCallable {
private final MethodDescriptor descriptor;
private final boolean awaitTrailers;
diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java
index 4e7a654f7e..bf8ffc81da 100644
--- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java
+++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java
@@ -145,7 +145,6 @@ private InstantiatingGrpcChannelProvider(Builder builder) {
builder.directPathServiceConfig == null
? getDefaultDirectPathServiceConfig()
: builder.directPathServiceConfig;
- logDirectPathMisconfig();
}
/**
@@ -234,6 +233,7 @@ public TransportChannel getTransportChannel() throws IOException {
} else if (needsEndpoint()) {
throw new IllegalStateException("getTransportChannel() called when needsEndpoint() is true");
} else {
+ logDirectPathMisconfig();
return createChannel();
}
}
@@ -272,6 +272,9 @@ boolean isDirectPathXdsEnabled() {
return false;
}
+ // This method should be called once per client initialization, hence can not be called in the
+ // builder or createSingleChannel, only in getTransportChannel which creates the first channel
+ // for a client.
private void logDirectPathMisconfig() {
if (isDirectPathXdsEnabled()) {
// Case 1: does not enable DirectPath
@@ -282,7 +285,7 @@ private void logDirectPathMisconfig() {
+ " attemptDirectPathXds option.");
} else {
// Case 2: credential is not correctly set
- if (!isNonDefaultServiceAccountAllowed()) {
+ if (!isCredentialDirectPathCompatible()) {
LOG.log(
Level.WARNING,
"DirectPath is misconfigured. Please make sure the credential is an instance of "
@@ -299,7 +302,12 @@ private void logDirectPathMisconfig() {
}
}
- private boolean isNonDefaultServiceAccountAllowed() {
+ @VisibleForTesting
+ boolean isCredentialDirectPathCompatible() {
+ // DirectPath requires a call credential during gRPC channel construction.
+ if (needsCredentials()) {
+ return false;
+ }
if (allowNonDefaultServiceAccount != null && allowNonDefaultServiceAccount) {
return true;
}
@@ -325,6 +333,12 @@ static boolean isOnComputeEngine() {
return false;
}
+ // Universe Domain configuration is currently only supported in the GDU
+ @VisibleForTesting
+ boolean canUseDirectPathWithUniverseDomain() {
+ return endpoint.contains(Credentials.GOOGLE_DEFAULT_UNIVERSE);
+ }
+
@VisibleForTesting
ChannelCredentials createMtlsChannelCredentials() throws IOException, GeneralSecurityException {
if (mtlsProvider.useMtlsClientCertificate()) {
@@ -356,7 +370,10 @@ private ManagedChannel createSingleChannel() throws IOException {
// Check DirectPath traffic.
boolean useDirectPathXds = false;
- if (isDirectPathEnabled() && isNonDefaultServiceAccountAllowed() && isOnComputeEngine()) {
+ if (isDirectPathEnabled()
+ && isCredentialDirectPathCompatible()
+ && isOnComputeEngine()
+ && canUseDirectPathWithUniverseDomain()) {
CallCredentials callCreds = MoreCallCredentials.from(credentials);
ChannelCredentials channelCreds =
GoogleDefaultChannelCredentials.newBuilder().callCredentials(callCreds).build();
@@ -430,6 +447,7 @@ private ManagedChannel createSingleChannel() throws IOException {
}
/** The endpoint to be used for the channel. */
+ @Override
public String getEndpoint() {
return endpoint;
}
diff --git a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/ChannelPoolTest.java b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/ChannelPoolTest.java
index 173528d6e2..ebc941ec0a 100644
--- a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/ChannelPoolTest.java
+++ b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/ChannelPoolTest.java
@@ -37,12 +37,15 @@
import com.google.api.gax.grpc.testing.FakeChannelFactory;
import com.google.api.gax.grpc.testing.FakeMethodDescriptor;
import com.google.api.gax.rpc.ClientContext;
+import com.google.api.gax.rpc.EndpointContext;
import com.google.api.gax.rpc.ResponseObserver;
import com.google.api.gax.rpc.ServerStreamingCallSettings;
import com.google.api.gax.rpc.ServerStreamingCallable;
import com.google.api.gax.rpc.StreamController;
import com.google.api.gax.rpc.UnaryCallSettings;
import com.google.api.gax.rpc.UnaryCallable;
+import com.google.api.gax.util.FakeLogHandler;
+import com.google.auth.Credentials;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
@@ -66,9 +69,6 @@
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.logging.Handler;
-import java.util.logging.LogRecord;
-import java.util.stream.Collectors;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
@@ -630,10 +630,17 @@ public void testReleasingClientCallCancelEarly() throws IOException {
ChannelPoolSettings channelPoolSettings = ChannelPoolSettings.staticallySized(1);
ChannelFactory factory = new FakeChannelFactory(ImmutableList.of(fakeChannel));
pool = ChannelPool.create(channelPoolSettings, factory);
+
+ EndpointContext endpointContext = Mockito.mock(EndpointContext.class);
+ Mockito.doNothing()
+ .when(endpointContext)
+ .validateUniverseDomain(Mockito.any(Credentials.class), Mockito.any(GrpcStatusCode.class));
+
ClientContext context =
ClientContext.newBuilder()
.setTransportChannel(GrpcTransportChannel.create(pool))
- .setDefaultCallContext(GrpcCallContext.of(pool, CallOptions.DEFAULT))
+ .setDefaultCallContext(
+ GrpcCallContext.of(pool, CallOptions.DEFAULT).withEndpointContext(endpointContext))
.build();
ServerStreamingCallSettings settings =
ServerStreamingCallSettings.newBuilder().build();
@@ -682,11 +689,19 @@ public void testDoubleRelease() throws Exception {
pool = ChannelPool.create(channelPoolSettings, factory);
+ EndpointContext endpointContext = Mockito.mock(EndpointContext.class);
+ Mockito.doNothing()
+ .when(endpointContext)
+ .validateUniverseDomain(
+ Mockito.any(Credentials.class), Mockito.any(GrpcStatusCode.class));
+
// Construct a fake callable to use the channel pool
ClientContext context =
ClientContext.newBuilder()
.setTransportChannel(GrpcTransportChannel.create(pool))
- .setDefaultCallContext(GrpcCallContext.of(pool, CallOptions.DEFAULT))
+ .setDefaultCallContext(
+ GrpcCallContext.of(pool, CallOptions.DEFAULT)
+ .withEndpointContext(endpointContext))
.build();
UnaryCallSettings settings =
@@ -717,23 +732,4 @@ public void testDoubleRelease() throws Exception {
ChannelPool.LOG.removeHandler(logHandler);
}
}
-
- private static class FakeLogHandler extends Handler {
- List records = new ArrayList<>();
-
- @Override
- public void publish(LogRecord record) {
- records.add(record);
- }
-
- @Override
- public void flush() {}
-
- @Override
- public void close() throws SecurityException {}
-
- List getAllMessages() {
- return records.stream().map(LogRecord::getMessage).collect(Collectors.toList());
- }
- }
}
diff --git a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcCallContextTest.java b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcCallContextTest.java
index 4e563225e5..e67c4c13c2 100644
--- a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcCallContextTest.java
+++ b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcCallContextTest.java
@@ -46,6 +46,7 @@
import io.grpc.CallOptions;
import io.grpc.ManagedChannel;
import io.grpc.Metadata.Key;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -373,7 +374,7 @@ public void testWithOptions() {
}
@Test
- public void testMergeOptions() {
+ public void testMergeOptions() throws IOException {
GrpcCallContext emptyCallContext = GrpcCallContext.createDefault();
ApiCallContext.Key contextKey1 = ApiCallContext.Key.create("testKey1");
ApiCallContext.Key contextKey2 = ApiCallContext.Key.create("testKey2");
diff --git a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcCallableFactoryTest.java b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcCallableFactoryTest.java
index 2ebe93b7f7..a274512e14 100644
--- a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcCallableFactoryTest.java
+++ b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcCallableFactoryTest.java
@@ -35,12 +35,15 @@
import com.google.api.gax.grpc.testing.FakeServiceImpl;
import com.google.api.gax.grpc.testing.InProcessServer;
import com.google.api.gax.retrying.RetrySettings;
+import com.google.api.gax.rpc.ApiCallContext;
import com.google.api.gax.rpc.ClientContext;
+import com.google.api.gax.rpc.EndpointContext;
import com.google.api.gax.rpc.InvalidArgumentException;
import com.google.api.gax.rpc.ServerStreamingCallSettings;
import com.google.api.gax.rpc.ServerStreamingCallable;
import com.google.api.gax.rpc.StatusCode.Code;
import com.google.api.gax.tracing.SpanName;
+import com.google.auth.Credentials;
import com.google.common.collect.ImmutableList;
import com.google.common.truth.Truth;
import com.google.type.Color;
@@ -74,10 +77,16 @@ public void setUp() throws Exception {
inprocessServer.start();
channel = InProcessChannelBuilder.forName(serverName).directExecutor().usePlaintext().build();
+ EndpointContext endpointContext = Mockito.mock(EndpointContext.class);
+ Mockito.doNothing()
+ .when(endpointContext)
+ .validateUniverseDomain(Mockito.any(Credentials.class), Mockito.any(GrpcStatusCode.class));
clientContext =
ClientContext.newBuilder()
.setTransportChannel(GrpcTransportChannel.create(channel))
- .setDefaultCallContext(GrpcCallContext.of(channel, CallOptions.DEFAULT))
+ .setDefaultCallContext(
+ GrpcCallContext.of(channel, CallOptions.DEFAULT)
+ .withEndpointContext(endpointContext))
.build();
}
@@ -106,11 +115,10 @@ public void createServerStreamingCallableRetryableExceptions() {
GrpcCallableFactory.createServerStreamingCallable(
grpcCallSettings, nonRetryableSettings, clientContext);
+ ApiCallContext defaultCallContext = clientContext.getDefaultCallContext();
Throwable actualError = null;
try {
- nonRetryableCallable
- .first()
- .call(Color.getDefaultInstance(), clientContext.getDefaultCallContext());
+ nonRetryableCallable.first().call(Color.getDefaultInstance(), defaultCallContext);
} catch (Throwable e) {
actualError = e;
}
@@ -134,9 +142,7 @@ public void createServerStreamingCallableRetryableExceptions() {
Throwable actualError2 = null;
try {
- retryableCallable
- .first()
- .call(Color.getDefaultInstance(), clientContext.getDefaultCallContext());
+ retryableCallable.first().call(Color.getDefaultInstance(), defaultCallContext);
} catch (Throwable e) {
actualError2 = e;
}
diff --git a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcClientCallsTest.java b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcClientCallsTest.java
index fcdea5afe9..eb9277b2e1 100644
--- a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcClientCallsTest.java
+++ b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcClientCallsTest.java
@@ -30,10 +30,16 @@
package com.google.api.gax.grpc;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
import static org.mockito.Mockito.verify;
import com.google.api.gax.grpc.testing.FakeChannelFactory;
import com.google.api.gax.grpc.testing.FakeServiceGrpc;
+import com.google.api.gax.rpc.EndpointContext;
+import com.google.api.gax.rpc.UnauthenticatedException;
+import com.google.api.gax.rpc.UnavailableException;
+import com.google.auth.Credentials;
+import com.google.auth.Retryable;
import com.google.common.collect.ImmutableList;
import com.google.common.truth.Truth;
import com.google.type.Color;
@@ -45,18 +51,58 @@
import io.grpc.ManagedChannel;
import io.grpc.Metadata;
import io.grpc.MethodDescriptor;
+import io.grpc.Status;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
+import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.threeten.bp.Duration;
public class GrpcClientCallsTest {
+
+ // Auth Library's GoogleAuthException is package-private. Copy basic functionality for tests
+ private static class GoogleAuthException extends IOException implements Retryable {
+
+ private final boolean isRetryable;
+
+ private GoogleAuthException(boolean isRetryable) {
+ this.isRetryable = isRetryable;
+ }
+
+ @Override
+ public boolean isRetryable() {
+ return isRetryable;
+ }
+
+ @Override
+ public int getRetryCount() {
+ return 0;
+ }
+ }
+
+ private GrpcCallContext defaultCallContext;
+ private EndpointContext endpointContext;
+ private Credentials credentials;
+ private Channel mockChannel;
+
+ @Before
+ public void setUp() throws IOException {
+ credentials = Mockito.mock(Credentials.class);
+ endpointContext = Mockito.mock(EndpointContext.class);
+ mockChannel = Mockito.mock(Channel.class);
+
+ defaultCallContext = GrpcCallContext.createDefault().withEndpointContext(endpointContext);
+ Mockito.doNothing()
+ .when(endpointContext)
+ .validateUniverseDomain(Mockito.any(Credentials.class), Mockito.any(GrpcStatusCode.class));
+ }
+
@Test
public void testAffinity() throws IOException {
MethodDescriptor descriptor = FakeServiceGrpc.METHOD_RECOGNIZE;
@@ -78,7 +124,7 @@ public void testAffinity() throws IOException {
ChannelPool.create(
ChannelPoolSettings.staticallySized(2),
new FakeChannelFactory(Arrays.asList(channel0, channel1)));
- GrpcCallContext context = GrpcCallContext.createDefault().withChannel(pool);
+ GrpcCallContext context = defaultCallContext.withChannel(pool);
ClientCall gotCallA =
GrpcClientCalls.newCall(descriptor, context.withChannelAffinity(0));
@@ -92,7 +138,7 @@ public void testAffinity() throws IOException {
}
@Test
- public void testExtraHeaders() {
+ public void testExtraHeaders() throws IOException {
Metadata emptyHeaders = new Metadata();
final Map> extraHeaders = new HashMap<>();
extraHeaders.put(
@@ -128,12 +174,12 @@ public void testExtraHeaders() {
.thenReturn(mockClientCall);
GrpcCallContext context =
- GrpcCallContext.createDefault().withChannel(mockChannel).withExtraHeaders(extraHeaders);
+ defaultCallContext.withChannel(mockChannel).withExtraHeaders(extraHeaders);
GrpcClientCalls.newCall(descriptor, context).start(mockListener, emptyHeaders);
}
@Test
- public void testTimeoutToDeadlineConversion() {
+ public void testTimeoutToDeadlineConversion() throws IOException {
MethodDescriptor descriptor = FakeServiceGrpc.METHOD_RECOGNIZE;
@SuppressWarnings("unchecked")
@@ -152,8 +198,7 @@ public void testTimeoutToDeadlineConversion() {
Duration timeout = Duration.ofSeconds(10);
Deadline minExpectedDeadline = Deadline.after(timeout.getSeconds(), TimeUnit.SECONDS);
- GrpcCallContext context =
- GrpcCallContext.createDefault().withChannel(mockChannel).withTimeout(timeout);
+ GrpcCallContext context = defaultCallContext.withChannel(mockChannel).withTimeout(timeout);
GrpcClientCalls.newCall(descriptor, context).start(mockListener, new Metadata());
@@ -164,7 +209,7 @@ public void testTimeoutToDeadlineConversion() {
}
@Test
- public void testTimeoutAfterDeadline() {
+ public void testTimeoutAfterDeadline() throws IOException {
MethodDescriptor descriptor = FakeServiceGrpc.METHOD_RECOGNIZE;
@SuppressWarnings("unchecked")
@@ -185,7 +230,7 @@ public void testTimeoutAfterDeadline() {
Duration timeout = Duration.ofSeconds(10);
GrpcCallContext context =
- GrpcCallContext.createDefault()
+ defaultCallContext
.withChannel(mockChannel)
.withCallOptions(CallOptions.DEFAULT.withDeadline(priorDeadline))
.withTimeout(timeout);
@@ -197,7 +242,7 @@ public void testTimeoutAfterDeadline() {
}
@Test
- public void testTimeoutBeforeDeadline() {
+ public void testTimeoutBeforeDeadline() throws IOException {
MethodDescriptor descriptor = FakeServiceGrpc.METHOD_RECOGNIZE;
@SuppressWarnings("unchecked")
@@ -219,7 +264,7 @@ public void testTimeoutBeforeDeadline() {
Deadline minExpectedDeadline = Deadline.after(timeout.getSeconds(), TimeUnit.SECONDS);
GrpcCallContext context =
- GrpcCallContext.createDefault()
+ defaultCallContext
.withChannel(mockChannel)
.withCallOptions(CallOptions.DEFAULT.withDeadline(subsequentDeadline))
.withTimeout(timeout);
@@ -232,4 +277,66 @@ public void testTimeoutBeforeDeadline() {
Truth.assertThat(capturedCallOptions.getValue().getDeadline()).isAtLeast(minExpectedDeadline);
Truth.assertThat(capturedCallOptions.getValue().getDeadline()).isAtMost(maxExpectedDeadline);
}
+
+ @Test
+ public void testValidUniverseDomain() throws IOException {
+ GrpcCallContext context =
+ GrpcCallContext.createDefault()
+ .withChannel(mockChannel)
+ .withCredentials(credentials)
+ .withEndpointContext(endpointContext);
+
+ CallOptions callOptions = context.getCallOptions();
+
+ MethodDescriptor descriptor = FakeServiceGrpc.METHOD_RECOGNIZE;
+ GrpcClientCalls.newCall(descriptor, context);
+ Mockito.verify(mockChannel, Mockito.times(1)).newCall(descriptor, callOptions);
+ }
+
+ // This test is when the universe domain does not match
+ @Test
+ public void testInvalidUniverseDomain() throws IOException {
+ Mockito.doThrow(
+ new UnauthenticatedException(
+ null, GrpcStatusCode.of(Status.Code.UNAUTHENTICATED), false))
+ .when(endpointContext)
+ .validateUniverseDomain(Mockito.any(Credentials.class), Mockito.any(GrpcStatusCode.class));
+ GrpcCallContext context =
+ GrpcCallContext.createDefault()
+ .withChannel(mockChannel)
+ .withCredentials(credentials)
+ .withEndpointContext(endpointContext);
+
+ CallOptions callOptions = context.getCallOptions();
+
+ MethodDescriptor descriptor = FakeServiceGrpc.METHOD_RECOGNIZE;
+ UnauthenticatedException exception =
+ assertThrows(
+ UnauthenticatedException.class, () -> GrpcClientCalls.newCall(descriptor, context));
+ assertThat(exception.getStatusCode().getCode()).isEqualTo(GrpcStatusCode.Code.UNAUTHENTICATED);
+ Mockito.verify(mockChannel, Mockito.never()).newCall(descriptor, callOptions);
+ }
+
+ // This test is when the MDS is unable to return a valid universe domain
+ @Test
+ public void testUniverseDomainNotReady_shouldRetry() throws IOException {
+ Mockito.doThrow(new GoogleAuthException(true))
+ .when(endpointContext)
+ .validateUniverseDomain(Mockito.any(Credentials.class), Mockito.any(GrpcStatusCode.class));
+ GrpcCallContext context =
+ GrpcCallContext.createDefault()
+ .withChannel(mockChannel)
+ .withCredentials(credentials)
+ .withEndpointContext(endpointContext);
+
+ CallOptions callOptions = context.getCallOptions();
+
+ MethodDescriptor descriptor = FakeServiceGrpc.METHOD_RECOGNIZE;
+ UnavailableException exception =
+ assertThrows(
+ UnavailableException.class, () -> GrpcClientCalls.newCall(descriptor, context));
+ assertThat(exception.getStatusCode().getCode()).isEqualTo(GrpcStatusCode.Code.UNAVAILABLE);
+ Truth.assertThat(exception.isRetryable()).isTrue();
+ Mockito.verify(mockChannel, Mockito.never()).newCall(descriptor, callOptions);
+ }
}
diff --git a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcDirectServerStreamingCallableTest.java b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcDirectServerStreamingCallableTest.java
index e5084b753b..5935d1d786 100644
--- a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcDirectServerStreamingCallableTest.java
+++ b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcDirectServerStreamingCallableTest.java
@@ -36,6 +36,7 @@
import com.google.api.gax.grpc.testing.InProcessServer;
import com.google.api.gax.rpc.ApiException;
import com.google.api.gax.rpc.ClientContext;
+import com.google.api.gax.rpc.EndpointContext;
import com.google.api.gax.rpc.ResponseObserver;
import com.google.api.gax.rpc.ServerStream;
import com.google.api.gax.rpc.ServerStreamingCallSettings;
@@ -44,6 +45,7 @@
import com.google.api.gax.rpc.StatusCode;
import com.google.api.gax.rpc.StreamController;
import com.google.api.gax.rpc.testing.FakeCallContext;
+import com.google.auth.Credentials;
import com.google.common.collect.Lists;
import com.google.common.truth.Truth;
import com.google.type.Color;
@@ -63,6 +65,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.mockito.Mockito;
@RunWith(JUnit4.class)
public class GrpcDirectServerStreamingCallableTest {
@@ -85,11 +88,18 @@ public void setUp() throws InstantiationException, IllegalAccessException, IOExc
inprocessServer = new InProcessServer<>(serviceImpl, serverName);
inprocessServer.start();
+ EndpointContext endpointContext = Mockito.mock(EndpointContext.class);
+ Mockito.doNothing()
+ .when(endpointContext)
+ .validateUniverseDomain(Mockito.any(Credentials.class), Mockito.any(GrpcStatusCode.class));
+
channel = InProcessChannelBuilder.forName(serverName).directExecutor().usePlaintext().build();
clientContext =
ClientContext.newBuilder()
.setTransportChannel(GrpcTransportChannel.create(channel))
- .setDefaultCallContext(GrpcCallContext.of(channel, CallOptions.DEFAULT))
+ .setDefaultCallContext(
+ GrpcCallContext.of(channel, CallOptions.DEFAULT)
+ .withEndpointContext(endpointContext))
.build();
streamingCallSettings = ServerStreamingCallSettings.newBuilder().build();
streamingCallable =
diff --git a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcDirectStreamingCallableTest.java b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcDirectStreamingCallableTest.java
index 95d4550b03..0d39f8704d 100644
--- a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcDirectStreamingCallableTest.java
+++ b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcDirectStreamingCallableTest.java
@@ -42,7 +42,9 @@
import com.google.api.gax.rpc.ClientContext;
import com.google.api.gax.rpc.ClientStream;
import com.google.api.gax.rpc.ClientStreamingCallable;
+import com.google.api.gax.rpc.EndpointContext;
import com.google.api.gax.rpc.StatusCode.Code;
+import com.google.auth.Credentials;
import com.google.type.Color;
import com.google.type.Money;
import io.grpc.CallOptions;
@@ -58,6 +60,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.mockito.Mockito;
@RunWith(JUnit4.class)
public class GrpcDirectStreamingCallableTest {
@@ -73,10 +76,16 @@ public void setUp() throws InstantiationException, IllegalAccessException, IOExc
inprocessServer = new InProcessServer<>(serviceImpl, serverName);
inprocessServer.start();
channel = InProcessChannelBuilder.forName(serverName).directExecutor().usePlaintext().build();
+ EndpointContext endpointContext = Mockito.mock(EndpointContext.class);
+ Mockito.doNothing()
+ .when(endpointContext)
+ .validateUniverseDomain(Mockito.any(Credentials.class), Mockito.any(GrpcStatusCode.class));
clientContext =
ClientContext.newBuilder()
.setTransportChannel(GrpcTransportChannel.create(channel))
- .setDefaultCallContext(GrpcCallContext.of(channel, CallOptions.DEFAULT))
+ .setDefaultCallContext(
+ GrpcCallContext.of(channel, CallOptions.DEFAULT)
+ .withEndpointContext(endpointContext))
.build();
}
diff --git a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLongRunningTest.java b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLongRunningTest.java
index 7149f23ba7..20bceeae2c 100644
--- a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLongRunningTest.java
+++ b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLongRunningTest.java
@@ -42,11 +42,13 @@
import com.google.api.gax.longrunning.OperationTimedPollAlgorithm;
import com.google.api.gax.retrying.RetrySettings;
import com.google.api.gax.rpc.ClientContext;
+import com.google.api.gax.rpc.EndpointContext;
import com.google.api.gax.rpc.OperationCallSettings;
import com.google.api.gax.rpc.OperationCallable;
import com.google.api.gax.rpc.TransportChannel;
import com.google.api.gax.rpc.TransportChannelProvider;
import com.google.api.gax.rpc.UnaryCallSettings;
+import com.google.auth.Credentials;
import com.google.longrunning.Operation;
import com.google.longrunning.OperationsSettings;
import com.google.longrunning.stub.GrpcOperationsStub;
@@ -68,6 +70,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.mockito.Mockito;
import org.threeten.bp.Duration;
@RunWith(JUnit4.class)
@@ -133,18 +136,25 @@ public void setUp() throws IOException {
.setPollingAlgorithm(pollingAlgorithm)
.build();
+ EndpointContext endpointContext = Mockito.mock(EndpointContext.class);
+ Mockito.doNothing()
+ .when(endpointContext)
+ .validateUniverseDomain(Mockito.any(Credentials.class), Mockito.any(GrpcStatusCode.class));
+
initialContext =
ClientContext.newBuilder()
.setTransportChannel(
GrpcTransportChannel.newBuilder().setManagedChannel(channel).build())
.setExecutor(executor)
- .setDefaultCallContext(GrpcCallContext.of(channel, CallOptions.DEFAULT))
+ .setDefaultCallContext(
+ GrpcCallContext.of(channel, CallOptions.DEFAULT)
+ .withEndpointContext(endpointContext))
.setClock(clock)
.build();
}
@Test
- public void testCall() {
+ public void testCall() throws IOException {
Color resp = getColor(1.0f);
Money meta = getMoney("UAH");
Operation resultOperation = getOperation("testCall", resp, meta, true);
@@ -154,7 +164,13 @@ public void testCall() {
GrpcCallableFactory.createOperationCallable(
createGrpcSettings(), callSettings, initialContext, operationsStub);
- Color response = callable.call(2, GrpcCallContext.createDefault());
+ EndpointContext endpointContext = Mockito.mock(EndpointContext.class);
+ Mockito.doNothing()
+ .when(endpointContext)
+ .validateUniverseDomain(Mockito.any(Credentials.class), Mockito.any(GrpcStatusCode.class));
+
+ Color response =
+ callable.call(2, GrpcCallContext.createDefault().withEndpointContext(endpointContext));
assertThat(response).isEqualTo(resp);
assertThat(executor.getIterationsCount()).isEqualTo(0);
}
diff --git a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcResponseMetadataTest.java b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcResponseMetadataTest.java
index 80b041a376..fba3f3f61e 100644
--- a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcResponseMetadataTest.java
+++ b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcResponseMetadataTest.java
@@ -33,8 +33,10 @@
import com.google.api.gax.grpc.testing.FakeServiceGrpc.FakeServiceImplBase;
import com.google.api.gax.grpc.testing.InProcessServer;
import com.google.api.gax.rpc.ClientContext;
+import com.google.api.gax.rpc.EndpointContext;
import com.google.api.gax.rpc.UnaryCallSettings;
import com.google.api.gax.rpc.UnaryCallable;
+import com.google.auth.Credentials;
import com.google.type.Color;
import com.google.type.Money;
import io.grpc.CallOptions;
@@ -133,10 +135,18 @@ public void close(Status status, Metadata trailers) {
.usePlaintext()
.intercept(new GrpcMetadataHandlerInterceptor())
.build();
+
+ EndpointContext endpointContext = Mockito.mock(EndpointContext.class);
+ Mockito.doNothing()
+ .when(endpointContext)
+ .validateUniverseDomain(Mockito.any(Credentials.class), Mockito.any(GrpcStatusCode.class));
+
clientContext =
ClientContext.newBuilder()
.setTransportChannel(GrpcTransportChannel.create(channel))
- .setDefaultCallContext(GrpcCallContext.of(channel, CallOptions.DEFAULT))
+ .setDefaultCallContext(
+ GrpcCallContext.of(channel, CallOptions.DEFAULT)
+ .withEndpointContext(endpointContext))
.build();
}
diff --git a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java
index edd7a73768..a58d10ffc6 100644
--- a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java
+++ b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java
@@ -284,12 +284,43 @@ public void testDirectPathXdsDisableByDefault() throws IOException {
assertThat(provider.isDirectPathXdsEnabled()).isFalse();
}
+ @Test
+ public void testDirectPathDisallowNullCredentials() throws IOException {
+ InstantiatingGrpcChannelProvider provider =
+ InstantiatingGrpcChannelProvider.newBuilder().build();
+
+ assertThat(provider.isCredentialDirectPathCompatible()).isFalse();
+ }
+
+ @Test
+ public void testDirectPathWithGDUEndpoint() {
+ InstantiatingGrpcChannelProvider provider =
+ InstantiatingGrpcChannelProvider.newBuilder()
+ .setAttemptDirectPath(true)
+ .setAttemptDirectPathXds()
+ .setEndpoint("test.googleapis.com:443")
+ .build();
+ assertThat(provider.canUseDirectPathWithUniverseDomain()).isTrue();
+ }
+
+ @Test
+ public void testDirectPathWithNonGDUEndpoint() {
+ InstantiatingGrpcChannelProvider provider =
+ InstantiatingGrpcChannelProvider.newBuilder()
+ .setAttemptDirectPath(true)
+ .setAttemptDirectPathXds()
+ .setEndpoint("test.random.com:443")
+ .build();
+ assertThat(provider.canUseDirectPathWithUniverseDomain()).isFalse();
+ }
+
@Test
public void testDirectPathXdsEnabled() throws IOException {
InstantiatingGrpcChannelProvider provider =
InstantiatingGrpcChannelProvider.newBuilder()
.setAttemptDirectPath(true)
.setAttemptDirectPathXds()
+ .setEndpoint("test.googleapis.com:443")
.build();
assertThat(provider.isDirectPathXdsEnabled()).isTrue();
@@ -508,11 +539,19 @@ protected Object getMtlsObjectFromTransportChannel(MtlsProvider provider)
}
@Test
- public void testLogDirectPathMisconfigAttrempDirectPathNotSet() {
+ public void testLogDirectPathMisconfigAttrempDirectPathNotSet() throws Exception {
FakeLogHandler logHandler = new FakeLogHandler();
InstantiatingGrpcChannelProvider.LOG.addHandler(logHandler);
InstantiatingGrpcChannelProvider provider =
- InstantiatingGrpcChannelProvider.newBuilder().setAttemptDirectPathXds().build();
+ InstantiatingGrpcChannelProvider.newBuilder()
+ .setAttemptDirectPathXds()
+ .setHeaderProvider(Mockito.mock(HeaderProvider.class))
+ .setExecutor(Mockito.mock(Executor.class))
+ .setEndpoint("localhost:8080")
+ .build();
+
+ provider.getTransportChannel();
+
assertThat(logHandler.getAllMessages())
.contains(
"DirectPath is misconfigured. Please set the attemptDirectPath option along with the"
@@ -521,14 +560,33 @@ public void testLogDirectPathMisconfigAttrempDirectPathNotSet() {
}
@Test
- public void testLogDirectPathMisconfigWrongCredential() {
+ public void testLogDirectPathMisconfig_shouldNotLogInTheBuilder() {
+ FakeLogHandler logHandler = new FakeLogHandler();
+ InstantiatingGrpcChannelProvider.LOG.addHandler(logHandler);
+ InstantiatingGrpcChannelProvider.newBuilder()
+ .setAttemptDirectPathXds()
+ .setAttemptDirectPath(true)
+ .build();
+
+ assertThat(logHandler.getAllMessages()).isEmpty();
+ InstantiatingGrpcChannelProvider.LOG.removeHandler(logHandler);
+ }
+
+ @Test
+ public void testLogDirectPathMisconfigWrongCredential() throws Exception {
FakeLogHandler logHandler = new FakeLogHandler();
InstantiatingGrpcChannelProvider.LOG.addHandler(logHandler);
InstantiatingGrpcChannelProvider provider =
InstantiatingGrpcChannelProvider.newBuilder()
.setAttemptDirectPathXds()
.setAttemptDirectPath(true)
+ .setHeaderProvider(Mockito.mock(HeaderProvider.class))
+ .setExecutor(Mockito.mock(Executor.class))
+ .setEndpoint("test.googleapis.com:443")
.build();
+
+ provider.getTransportChannel();
+
assertThat(logHandler.getAllMessages())
.contains(
"DirectPath is misconfigured. Please make sure the credential is an instance of"
@@ -537,7 +595,7 @@ public void testLogDirectPathMisconfigWrongCredential() {
}
@Test
- public void testLogDirectPathMisconfigNotOnGCE() {
+ public void testLogDirectPathMisconfigNotOnGCE() throws Exception {
FakeLogHandler logHandler = new FakeLogHandler();
InstantiatingGrpcChannelProvider.LOG.addHandler(logHandler);
InstantiatingGrpcChannelProvider provider =
@@ -545,7 +603,13 @@ public void testLogDirectPathMisconfigNotOnGCE() {
.setAttemptDirectPathXds()
.setAttemptDirectPath(true)
.setAllowNonDefaultServiceAccount(true)
+ .setHeaderProvider(Mockito.mock(HeaderProvider.class))
+ .setExecutor(Mockito.mock(Executor.class))
+ .setEndpoint("test.googleapis.com:443")
.build();
+
+ provider.getTransportChannel();
+
if (!InstantiatingGrpcChannelProvider.isOnComputeEngine()) {
assertThat(logHandler.getAllMessages())
.contains(
diff --git a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/TimeoutTest.java b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/TimeoutTest.java
index 9de95c1752..d40153eff8 100644
--- a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/TimeoutTest.java
+++ b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/TimeoutTest.java
@@ -35,6 +35,7 @@
import com.google.api.gax.retrying.RetrySettings;
import com.google.api.gax.rpc.ApiException;
import com.google.api.gax.rpc.ClientContext;
+import com.google.api.gax.rpc.EndpointContext;
import com.google.api.gax.rpc.RequestParamsExtractor;
import com.google.api.gax.rpc.ServerStreamingCallSettings;
import com.google.api.gax.rpc.ServerStreamingCallable;
@@ -43,6 +44,7 @@
import com.google.api.gax.rpc.UnaryCallSettings;
import com.google.api.gax.rpc.UnaryCallable;
import com.google.api.gax.rpc.testing.FakeStatusCode;
+import com.google.auth.Credentials;
import com.google.common.collect.ImmutableSet;
import io.grpc.CallOptions;
import io.grpc.ClientCall;
@@ -51,7 +53,9 @@
import io.grpc.MethodDescriptor;
import io.grpc.MethodDescriptor.Marshaller;
import io.grpc.MethodDescriptor.MethodType;
+import java.io.IOException;
import java.util.concurrent.TimeUnit;
+import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -77,13 +81,22 @@ public class TimeoutTest {
private static final Duration totalTimeout = Duration.ofDays(DEADLINE_IN_DAYS);
private static final Duration maxRpcTimeout = Duration.ofMinutes(DEADLINE_IN_MINUTES);
private static final Duration initialRpcTimeout = Duration.ofSeconds(DEADLINE_IN_SECONDS);
- private static final GrpcCallContext defaultCallContext = GrpcCallContext.createDefault();
+ private static GrpcCallContext defaultCallContext;
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
@Mock private Marshaller stringMarshaller;
@Mock private RequestParamsExtractor paramsExtractor;
@Mock private ManagedChannel managedChannel;
+ @BeforeClass
+ public static void setUp() throws IOException {
+ EndpointContext endpointContext = Mockito.mock(EndpointContext.class);
+ Mockito.doNothing()
+ .when(endpointContext)
+ .validateUniverseDomain(Mockito.any(Credentials.class), Mockito.any(GrpcStatusCode.class));
+ defaultCallContext = GrpcCallContext.createDefault().withEndpointContext(endpointContext);
+ }
+
@Test
public void testNonRetryUnarySettings() {
RetrySettings retrySettings =
diff --git a/gax-java/gax-httpjson/pom.xml b/gax-java/gax-httpjson/pom.xml
index bf3b7bb0ff..91ba2be7dc 100644
--- a/gax-java/gax-httpjson/pom.xml
+++ b/gax-java/gax-httpjson/pom.xml
@@ -3,7 +3,7 @@
4.0.0gax-httpjson
- 2.36.0
+ 2.42.0jarGAX (Google Api eXtensions) for Java (HTTP JSON)Google Api eXtensions for Java (HTTP JSON)
@@ -11,7 +11,7 @@
com.google.apigax-parent
- 2.36.0
+ 2.42.0
diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonCallContext.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonCallContext.java
index 2d06244bf1..890c205a61 100644
--- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonCallContext.java
+++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonCallContext.java
@@ -30,10 +30,14 @@
package com.google.api.gax.httpjson;
import com.google.api.core.BetaApi;
+import com.google.api.core.InternalApi;
import com.google.api.gax.retrying.RetrySettings;
import com.google.api.gax.rpc.ApiCallContext;
+import com.google.api.gax.rpc.ApiExceptionFactory;
+import com.google.api.gax.rpc.EndpointContext;
import com.google.api.gax.rpc.StatusCode;
import com.google.api.gax.rpc.TransportChannel;
+import com.google.api.gax.rpc.UnauthenticatedException;
import com.google.api.gax.rpc.internal.ApiCallContextOptions;
import com.google.api.gax.rpc.internal.Headers;
import com.google.api.gax.tracing.ApiTracer;
@@ -42,6 +46,7 @@
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -60,6 +65,8 @@
* arguments solely depends on the arguments themselves.
*/
public final class HttpJsonCallContext implements ApiCallContext {
+ private static final HttpJsonStatusCode UNAUTHENTICATED_STATUS_CODE =
+ HttpJsonStatusCode.of(StatusCode.Code.UNAUTHENTICATED);
private final HttpJsonChannel channel;
private final HttpJsonCallOptions callOptions;
@Nullable private final Duration timeout;
@@ -70,6 +77,7 @@ public final class HttpJsonCallContext implements ApiCallContext {
private final ApiTracer tracer;
@Nullable private final RetrySettings retrySettings;
@Nullable private final ImmutableSet retryableCodes;
+ private final EndpointContext endpointContext;
/** Returns an empty instance. */
public static HttpJsonCallContext createDefault() {
@@ -83,6 +91,7 @@ public static HttpJsonCallContext createDefault() {
ApiCallContextOptions.getDefaultOptions(),
null,
null,
+ null,
null);
}
@@ -97,6 +106,7 @@ public static HttpJsonCallContext of(HttpJsonChannel channel, HttpJsonCallOption
ApiCallContextOptions.getDefaultOptions(),
null,
null,
+ null,
null);
}
@@ -110,7 +120,8 @@ private HttpJsonCallContext(
ApiCallContextOptions options,
ApiTracer tracer,
RetrySettings defaultRetrySettings,
- Set defaultRetryableCodes) {
+ Set defaultRetryableCodes,
+ EndpointContext endpointContext) {
this.channel = channel;
this.callOptions = callOptions;
this.timeout = timeout;
@@ -122,6 +133,7 @@ private HttpJsonCallContext(
this.retrySettings = defaultRetrySettings;
this.retryableCodes =
defaultRetryableCodes == null ? null : ImmutableSet.copyOf(defaultRetryableCodes);
+ this.endpointContext = endpointContext;
}
/**
@@ -201,6 +213,8 @@ public HttpJsonCallContext merge(ApiCallContext inputCallContext) {
newRetryableCodes = this.retryableCodes;
}
+ // The EndpointContext is not updated as there should be no reason for a user
+ // to update this.
return new HttpJsonCallContext(
newChannel,
newCallOptions,
@@ -211,7 +225,8 @@ public HttpJsonCallContext merge(ApiCallContext inputCallContext) {
newOptions,
newTracer,
newRetrySettings,
- newRetryableCodes);
+ newRetryableCodes,
+ endpointContext);
}
@Override
@@ -232,6 +247,23 @@ public HttpJsonCallContext withTransportChannel(TransportChannel inputChannel) {
return withChannel(transportChannel.getChannel());
}
+ @Override
+ public HttpJsonCallContext withEndpointContext(EndpointContext endpointContext) {
+ Preconditions.checkNotNull(endpointContext);
+ return new HttpJsonCallContext(
+ this.channel,
+ this.callOptions,
+ this.timeout,
+ this.streamWaitTimeout,
+ this.streamIdleTimeout,
+ this.extraHeaders,
+ this.options,
+ this.tracer,
+ this.retrySettings,
+ this.retryableCodes,
+ endpointContext);
+ }
+
@Override
public HttpJsonCallContext withTimeout(Duration timeout) {
// Default RetrySettings use 0 for RPC timeout. Treat that as disabled timeouts.
@@ -254,7 +286,8 @@ public HttpJsonCallContext withTimeout(Duration timeout) {
this.options,
this.tracer,
this.retrySettings,
- this.retryableCodes);
+ this.retryableCodes,
+ this.endpointContext);
}
@Nullable
@@ -280,7 +313,8 @@ public HttpJsonCallContext withStreamWaitTimeout(@Nullable Duration streamWaitTi
this.options,
this.tracer,
this.retrySettings,
- this.retryableCodes);
+ this.retryableCodes,
+ this.endpointContext);
}
/**
@@ -311,7 +345,8 @@ public HttpJsonCallContext withStreamIdleTimeout(@Nullable Duration streamIdleTi
this.options,
this.tracer,
this.retrySettings,
- this.retryableCodes);
+ this.retryableCodes,
+ this.endpointContext);
}
/**
@@ -341,7 +376,8 @@ public ApiCallContext withExtraHeaders(Map> extraHeaders) {
this.options,
this.tracer,
this.retrySettings,
- this.retryableCodes);
+ this.retryableCodes,
+ this.endpointContext);
}
@BetaApi("The surface for extra headers is not stable yet and may change in the future.")
@@ -364,7 +400,8 @@ public ApiCallContext withOption(Key key, T value) {
newOptions,
this.tracer,
this.retrySettings,
- this.retryableCodes);
+ this.retryableCodes,
+ this.endpointContext);
}
/** {@inheritDoc} */
@@ -373,6 +410,31 @@ public T getOption(Key key) {
return options.getOption(key);
}
+ /**
+ * Validate the Universe Domain to ensure that the user configured Universe Domain and the
+ * Credentials' Universe Domain match. An exception will be raised if there are any issues when
+ * trying to validate (i.e. unable to access the universe domain).
+ *
+ * @throws UnauthenticatedException Thrown if the universe domain that the user configured does
+ * not match the Credential's universe domain or if the client library is unable to retrieve
+ * the Universe Domain from the Credentials.
+ */
+ @InternalApi
+ public void validateUniverseDomain() {
+ try {
+ endpointContext.validateUniverseDomain(
+ getCallOptions().getCredentials(), UNAUTHENTICATED_STATUS_CODE);
+ } catch (IOException e) {
+ // All instances of IOException from endpointContext's `validateUniverseDomain()`
+ // call should be an Auth Exception
+ throw ApiExceptionFactory.createException(
+ EndpointContext.UNABLE_TO_RETRIEVE_CREDENTIALS_ERROR_MESSAGE,
+ e,
+ UNAUTHENTICATED_STATUS_CODE,
+ false);
+ }
+ }
+
public HttpJsonChannel getChannel() {
return channel;
}
@@ -410,7 +472,8 @@ public HttpJsonCallContext withRetrySettings(RetrySettings retrySettings) {
this.options,
this.tracer,
retrySettings,
- this.retryableCodes);
+ this.retryableCodes,
+ this.endpointContext);
}
@Override
@@ -430,7 +493,8 @@ public HttpJsonCallContext withRetryableCodes(Set retryableCode
this.options,
this.tracer,
this.retrySettings,
- retryableCodes);
+ retryableCodes,
+ this.endpointContext);
}
public HttpJsonCallContext withChannel(HttpJsonChannel newChannel) {
@@ -444,7 +508,8 @@ public HttpJsonCallContext withChannel(HttpJsonChannel newChannel) {
this.options,
this.tracer,
this.retrySettings,
- this.retryableCodes);
+ this.retryableCodes,
+ this.endpointContext);
}
public HttpJsonCallContext withCallOptions(HttpJsonCallOptions newCallOptions) {
@@ -458,7 +523,8 @@ public HttpJsonCallContext withCallOptions(HttpJsonCallOptions newCallOptions) {
this.options,
this.tracer,
this.retrySettings,
- this.retryableCodes);
+ this.retryableCodes,
+ this.endpointContext);
}
@Deprecated
@@ -492,7 +558,8 @@ public HttpJsonCallContext withTracer(@Nonnull ApiTracer newTracer) {
this.options,
newTracer,
this.retrySettings,
- this.retryableCodes);
+ this.retryableCodes,
+ this.endpointContext);
}
@Override
@@ -511,7 +578,8 @@ public boolean equals(Object o) {
&& Objects.equals(this.options, that.options)
&& Objects.equals(this.tracer, that.tracer)
&& Objects.equals(this.retrySettings, that.retrySettings)
- && Objects.equals(this.retryableCodes, that.retryableCodes);
+ && Objects.equals(this.retryableCodes, that.retryableCodes)
+ && Objects.equals(this.endpointContext, that.endpointContext);
}
@Override
@@ -524,6 +592,7 @@ public int hashCode() {
options,
tracer,
retrySettings,
- retryableCodes);
+ retryableCodes,
+ endpointContext);
}
}
diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonCallSettings.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonCallSettings.java
index 7dd7732175..04411fc3d7 100644
--- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonCallSettings.java
+++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonCallSettings.java
@@ -29,6 +29,7 @@
*/
package com.google.api.gax.httpjson;
+import com.google.api.gax.rpc.RequestMutator;
import com.google.api.gax.rpc.RequestParamsExtractor;
import com.google.protobuf.TypeRegistry;
@@ -36,11 +37,14 @@
public class HttpJsonCallSettings {
private final ApiMethodDescriptor methodDescriptor;
private final RequestParamsExtractor paramsExtractor;
+
+ private final RequestMutator requestMutator;
private final TypeRegistry typeRegistry;
private HttpJsonCallSettings(Builder builder) {
this.methodDescriptor = builder.methodDescriptor;
this.paramsExtractor = builder.paramsExtractor;
+ this.requestMutator = builder.requestMutator;
this.typeRegistry = builder.typeRegistry;
}
@@ -52,6 +56,10 @@ public RequestParamsExtractor getParamsExtractor() {
return paramsExtractor;
}
+ public RequestMutator getRequestMutator() {
+ return requestMutator;
+ }
+
public TypeRegistry getTypeRegistry() {
return typeRegistry;
}
@@ -72,6 +80,8 @@ public Builder toBuilder() {
}
public static class Builder {
+
+ private RequestMutator requestMutator;
private ApiMethodDescriptor methodDescriptor;
private RequestParamsExtractor paramsExtractor;
private TypeRegistry typeRegistry;
@@ -94,6 +104,11 @@ public Builder setParamsExtractor(
return this;
}
+ public Builder setRequestMutator(RequestMutator requestMutator) {
+ this.requestMutator = requestMutator;
+ return this;
+ }
+
public Builder setTypeRegistry(TypeRegistry typeRegistry) {
this.typeRegistry = typeRegistry;
return this;
diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonCallableFactory.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonCallableFactory.java
index d95751e3b0..33e2ff886e 100644
--- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonCallableFactory.java
+++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonCallableFactory.java
@@ -30,6 +30,7 @@
package com.google.api.gax.httpjson;
import com.google.api.core.InternalApi;
+import com.google.api.core.ObsoleteApi;
import com.google.api.gax.longrunning.OperationSnapshot;
import com.google.api.gax.rpc.BatchingCallSettings;
import com.google.api.gax.rpc.Callables;
@@ -72,6 +73,22 @@ private static UnaryCallable createDi
return callable;
}
+ /** Create httpJson UnaryCallable with request mutator. */
+ static UnaryCallable createUnaryCallable(
+ UnaryCallable innerCallable,
+ UnaryCallSettings, ?> callSettings,
+ HttpJsonCallSettings httpJsonCallSettings,
+ ClientContext clientContext) {
+ UnaryCallable callable =
+ new HttpJsonExceptionCallable<>(innerCallable, callSettings.getRetryableCodes());
+ callable =
+ Callables.retrying(
+ callable, callSettings, clientContext, httpJsonCallSettings.getRequestMutator());
+ return callable.withDefaultCallContext(clientContext.getDefaultCallContext());
+ }
+
+ /** Use {@link #createUnaryCallable createUnaryCallable} method instead. */
+ @ObsoleteApi("Please use other httpJson UnaryCallable method instead")
static UnaryCallable createUnaryCallable(
UnaryCallable innerCallable,
UnaryCallSettings, ?> callSettings,
@@ -96,7 +113,9 @@ public static UnaryCallable createBas
ClientContext clientContext) {
UnaryCallable callable = createDirectUnaryCallable(httpJsonCallSettings);
callable = new HttpJsonExceptionCallable<>(callable, callSettings.getRetryableCodes());
- callable = Callables.retrying(callable, callSettings, clientContext);
+ callable =
+ Callables.retrying(
+ callable, callSettings, clientContext, httpJsonCallSettings.getRequestMutator());
return callable;
}
@@ -123,7 +142,7 @@ public static UnaryCallable createUna
clientContext.getTracerFactory(),
getSpanName(httpJsonCallSettings.getMethodDescriptor()));
- return createUnaryCallable(innerCallable, callSettings, clientContext);
+ return createUnaryCallable(innerCallable, callSettings, httpJsonCallSettings, clientContext);
}
/**
@@ -141,7 +160,8 @@ UnaryCallable createPagedCallable(
PagedCallSettings pagedCallSettings,
ClientContext clientContext) {
UnaryCallable callable = createDirectUnaryCallable(httpJsonCallSettings);
- callable = createUnaryCallable(callable, pagedCallSettings, clientContext);
+ callable =
+ createUnaryCallable(callable, pagedCallSettings, httpJsonCallSettings, clientContext);
UnaryCallable pagedCallable =
Callables.paged(callable, pagedCallSettings);
return pagedCallable.withDefaultCallContext(clientContext.getDefaultCallContext());
@@ -162,7 +182,8 @@ public static UnaryCallable createBat
BatchingCallSettings batchingCallSettings,
ClientContext clientContext) {
UnaryCallable callable = createDirectUnaryCallable(httpJsonCallSettings);
- callable = createUnaryCallable(callable, batchingCallSettings, clientContext);
+ callable =
+ createUnaryCallable(callable, batchingCallSettings, httpJsonCallSettings, clientContext);
callable = Callables.batching(callable, batchingCallSettings, clientContext);
return callable.withDefaultCallContext(clientContext.getDefaultCallContext());
}
diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonClientCalls.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonClientCalls.java
index ae1ae3ca84..880a38d56a 100644
--- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonClientCalls.java
+++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonClientCalls.java
@@ -72,6 +72,10 @@ public static HttpJsonClientCall newC
httpJsonContext = httpJsonContext.withCallOptions(callOptions);
}
+ // Validate the Universe Domain prior to the call. Only allow the call to go through
+ // if the Universe Domain is valid.
+ httpJsonContext.validateUniverseDomain();
+
// TODO: add headers interceptor logic
return httpJsonContext.getChannel().newCall(methodDescriptor, httpJsonContext.getCallOptions());
}
diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/InstantiatingHttpJsonChannelProvider.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/InstantiatingHttpJsonChannelProvider.java
index 6d464bf992..f92bdf299c 100644
--- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/InstantiatingHttpJsonChannelProvider.java
+++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/InstantiatingHttpJsonChannelProvider.java
@@ -207,6 +207,7 @@ private HttpJsonTransportChannel createChannel() throws IOException, GeneralSecu
}
/** The endpoint to be used for the channel. */
+ @Override
public String getEndpoint() {
return endpoint;
}
diff --git a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonClientCallsTest.java b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonClientCallsTest.java
new file mode 100644
index 0000000000..c4cdcf1390
--- /dev/null
+++ b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonClientCallsTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.google.api.gax.httpjson;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+
+import com.google.api.gax.rpc.EndpointContext;
+import com.google.api.gax.rpc.StatusCode;
+import com.google.api.gax.rpc.UnauthenticatedException;
+import com.google.auth.Credentials;
+import com.google.auth.Retryable;
+import java.io.IOException;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mockito;
+
+@RunWith(JUnit4.class)
+public class HttpJsonClientCallsTest {
+
+ // Auth Library's GoogleAuthException is package-private. Copy basic functionality for tests
+ private static class GoogleAuthException extends IOException implements Retryable {
+
+ private final boolean isRetryable;
+
+ private GoogleAuthException(boolean isRetryable) {
+ this.isRetryable = isRetryable;
+ }
+
+ @Override
+ public boolean isRetryable() {
+ return isRetryable;
+ }
+
+ @Override
+ public int getRetryCount() {
+ return 0;
+ }
+ }
+
+ private Credentials credentials;
+ private EndpointContext endpointContext;
+ private HttpJsonChannel mockChannel;
+ private ApiMethodDescriptor descriptor;
+ private HttpJsonCallOptions callOptions;
+ private HttpJsonCallContext callContext;
+
+ @Before
+ public void setUp() throws IOException {
+ credentials = Mockito.mock(Credentials.class);
+ endpointContext = Mockito.mock(EndpointContext.class);
+ mockChannel = Mockito.mock(HttpJsonChannel.class);
+ descriptor = Mockito.mock(ApiMethodDescriptor.class);
+ callOptions = Mockito.mock(HttpJsonCallOptions.class);
+
+ callContext =
+ HttpJsonCallContext.of(mockChannel, callOptions)
+ .withEndpointContext(endpointContext)
+ .withChannel(mockChannel);
+
+ Mockito.when(callOptions.getCredentials()).thenReturn(credentials);
+ Mockito.doNothing()
+ .when(endpointContext)
+ .validateUniverseDomain(
+ Mockito.any(Credentials.class), Mockito.any(HttpJsonStatusCode.class));
+ }
+
+ @Test
+ public void testValidUniverseDomain() {
+ HttpJsonClientCalls.newCall(descriptor, callContext);
+ Mockito.verify(mockChannel, Mockito.times(1)).newCall(descriptor, callOptions);
+ }
+
+ // This test is when the universe domain does not match
+ @Test
+ public void testInvalidUniverseDomain() throws IOException {
+ Mockito.doThrow(
+ new UnauthenticatedException(
+ null, HttpJsonStatusCode.of(StatusCode.Code.UNAUTHENTICATED), false))
+ .when(endpointContext)
+ .validateUniverseDomain(
+ Mockito.any(Credentials.class), Mockito.any(HttpJsonStatusCode.class));
+
+ UnauthenticatedException exception =
+ assertThrows(
+ UnauthenticatedException.class,
+ () -> HttpJsonClientCalls.newCall(descriptor, callContext));
+ assertThat(exception.getStatusCode().getCode())
+ .isEqualTo(HttpJsonStatusCode.Code.UNAUTHENTICATED);
+ Mockito.verify(mockChannel, Mockito.never()).newCall(descriptor, callOptions);
+ }
+
+ // This test is when the MDS is unable to return a valid universe domain
+ @Test
+ public void testUniverseDomainNotReady_shouldRetry() throws IOException {
+ Mockito.doThrow(new GoogleAuthException(true))
+ .when(endpointContext)
+ .validateUniverseDomain(
+ Mockito.any(Credentials.class), Mockito.any(HttpJsonStatusCode.class));
+ HttpJsonCallContext context =
+ HttpJsonCallContext.createDefault()
+ .withChannel(mockChannel)
+ .withCredentials(credentials)
+ .withEndpointContext(endpointContext);
+
+ HttpJsonCallOptions callOptions = context.getCallOptions();
+
+ UnauthenticatedException exception =
+ assertThrows(
+ UnauthenticatedException.class,
+ () -> HttpJsonClientCalls.newCall(descriptor, callContext));
+ assertThat(exception.getStatusCode().getCode())
+ .isEqualTo(HttpJsonStatusCode.Code.UNAUTHENTICATED);
+ Mockito.verify(mockChannel, Mockito.never()).newCall(descriptor, callOptions);
+ }
+}
diff --git a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonClientInterceptorTest.java b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonClientInterceptorTest.java
index 6e081fb75f..463b76112b 100644
--- a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonClientInterceptorTest.java
+++ b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonClientInterceptorTest.java
@@ -34,6 +34,8 @@
import com.google.api.gax.httpjson.ForwardingHttpJsonClientCall.SimpleForwardingHttpJsonClientCall;
import com.google.api.gax.httpjson.ForwardingHttpJsonClientCallListener.SimpleForwardingHttpJsonClientCallListener;
import com.google.api.gax.httpjson.testing.MockHttpService;
+import com.google.api.gax.rpc.EndpointContext;
+import com.google.auth.Credentials;
import com.google.protobuf.Field;
import com.google.protobuf.Field.Cardinality;
import java.io.IOException;
@@ -51,6 +53,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.mockito.Mockito;
import org.threeten.bp.Duration;
@RunWith(JUnit4.class)
@@ -178,14 +181,21 @@ public void tearDown() {
}
@Test
- public void testCustomInterceptor() throws ExecutionException, InterruptedException {
+ public void testCustomInterceptor() throws ExecutionException, InterruptedException, IOException {
HttpJsonDirectCallable callable =
new HttpJsonDirectCallable<>(FAKE_METHOD_DESCRIPTOR);
+ EndpointContext endpointContext = Mockito.mock(EndpointContext.class);
+ Mockito.doNothing()
+ .when(endpointContext)
+ .validateUniverseDomain(
+ Mockito.any(Credentials.class), Mockito.any(HttpJsonStatusCode.class));
+
HttpJsonCallContext callContext =
HttpJsonCallContext.createDefault()
.withChannel(channel)
- .withTimeout(Duration.ofSeconds(30));
+ .withTimeout(Duration.ofSeconds(30))
+ .withEndpointContext(endpointContext);
Field request;
Field expectedResponse;
diff --git a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonDirectCallableTest.java b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonDirectCallableTest.java
index fa666dc69c..619052744a 100644
--- a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonDirectCallableTest.java
+++ b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonDirectCallableTest.java
@@ -35,10 +35,13 @@
import com.google.api.gax.httpjson.testing.MockHttpService;
import com.google.api.gax.rpc.ApiException;
import com.google.api.gax.rpc.ApiExceptionFactory;
+import com.google.api.gax.rpc.EndpointContext;
import com.google.api.gax.rpc.StatusCode.Code;
import com.google.api.gax.rpc.testing.FakeStatusCode;
+import com.google.auth.Credentials;
import com.google.protobuf.Field;
import com.google.protobuf.Field.Cardinality;
+import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -53,6 +56,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.mockito.Mockito;
import org.threeten.bp.Duration;
@RunWith(JUnit4.class)
@@ -94,7 +98,9 @@ public class HttpJsonDirectCallableTest {
private static final MockHttpService MOCK_SERVICE =
new MockHttpService(Collections.singletonList(FAKE_METHOD_DESCRIPTOR), "google.com:443");
- private final ManagedHttpJsonChannel channel =
+ private static ExecutorService executorService;
+
+ private static final ManagedHttpJsonChannel channel =
new ManagedHttpJsonInterceptorChannel(
ManagedHttpJsonChannel.newBuilder()
.setEndpoint("google.com:443")
@@ -103,10 +109,10 @@ public class HttpJsonDirectCallableTest {
.build(),
new HttpJsonHeaderInterceptor(Collections.singletonMap("header-key", "headerValue")));
- private static ExecutorService executorService;
+ private static HttpJsonCallContext defaultCallContext;
@BeforeClass
- public static void initialize() {
+ public static void initialize() throws IOException {
executorService =
Executors.newFixedThreadPool(
2,
@@ -115,6 +121,16 @@ public static void initialize() {
t.setDaemon(true);
return t;
});
+ EndpointContext endpointContext = Mockito.mock(EndpointContext.class);
+ Mockito.doNothing()
+ .when(endpointContext)
+ .validateUniverseDomain(
+ Mockito.any(Credentials.class), Mockito.any(HttpJsonStatusCode.class));
+ defaultCallContext =
+ HttpJsonCallContext.createDefault()
+ .withChannel(channel)
+ .withTimeout(Duration.ofSeconds(30))
+ .withEndpointContext(endpointContext);
}
@AfterClass
@@ -132,18 +148,13 @@ public void testSuccessfulUnaryResponse() throws ExecutionException, Interrupted
HttpJsonDirectCallable callable =
new HttpJsonDirectCallable<>(FAKE_METHOD_DESCRIPTOR);
- HttpJsonCallContext callContext =
- HttpJsonCallContext.createDefault()
- .withChannel(channel)
- .withTimeout(Duration.ofSeconds(30));
-
Field request;
Field expectedResponse;
request = expectedResponse = createTestMessage(2);
MOCK_SERVICE.addResponse(expectedResponse);
- Field actualResponse = callable.futureCall(request, callContext).get();
+ Field actualResponse = callable.futureCall(request, defaultCallContext).get();
assertThat(actualResponse).isEqualTo(expectedResponse);
assertThat(MOCK_SERVICE.getRequestPaths().size()).isEqualTo(1);
@@ -167,11 +178,6 @@ public void testSuccessfulMultipleResponsesForUnaryCall()
HttpJsonDirectCallable callable =
new HttpJsonDirectCallable<>(FAKE_METHOD_DESCRIPTOR);
- HttpJsonCallContext callContext =
- HttpJsonCallContext.createDefault()
- .withChannel(channel)
- .withTimeout(Duration.ofSeconds(30));
-
Field request = createTestMessage(2);
Field expectedResponse = createTestMessage(2);
Field otherResponse = createTestMessage(10);
@@ -179,7 +185,7 @@ public void testSuccessfulMultipleResponsesForUnaryCall()
MOCK_SERVICE.addResponse(otherResponse);
MOCK_SERVICE.addResponse(otherResponse);
- Field actualResponse = callable.futureCall(request, callContext).get();
+ Field actualResponse = callable.futureCall(request, defaultCallContext).get();
assertThat(actualResponse).isEqualTo(expectedResponse);
assertThat(MOCK_SERVICE.getRequestPaths().size()).isEqualTo(1);
String headerValue = MOCK_SERVICE.getRequestHeaders().get("header-key").iterator().next();
@@ -202,11 +208,6 @@ public void testErrorMultipleResponsesForUnaryCall()
HttpJsonDirectCallable callable =
new HttpJsonDirectCallable<>(FAKE_METHOD_DESCRIPTOR);
- HttpJsonCallContext callContext =
- HttpJsonCallContext.createDefault()
- .withChannel(channel)
- .withTimeout(Duration.ofSeconds(30));
-
Field request = createTestMessage(2);
Field expectedResponse = createTestMessage(2);
Field randomResponse1 = createTestMessage(10);
@@ -215,7 +216,7 @@ public void testErrorMultipleResponsesForUnaryCall()
MOCK_SERVICE.addResponse(expectedResponse);
MOCK_SERVICE.addResponse(randomResponse2);
- Field actualResponse = callable.futureCall(request, callContext).get();
+ Field actualResponse = callable.futureCall(request, defaultCallContext).get();
// Gax returns the first response for Unary Call
assertThat(actualResponse).isEqualTo(randomResponse1);
assertThat(actualResponse).isNotEqualTo(expectedResponse);
@@ -234,18 +235,13 @@ public void testErrorUnaryResponse() throws InterruptedException {
HttpJsonDirectCallable callable =
new HttpJsonDirectCallable<>(FAKE_METHOD_DESCRIPTOR);
- HttpJsonCallContext callContext =
- HttpJsonCallContext.createDefault()
- .withChannel(channel)
- .withTimeout(Duration.ofSeconds(30));
-
ApiException exception =
ApiExceptionFactory.createException(
new Exception(), FakeStatusCode.of(Code.NOT_FOUND), false);
MOCK_SERVICE.addException(exception);
try {
- callable.futureCall(createTestMessage(2), callContext).get();
+ callable.futureCall(createTestMessage(2), defaultCallContext).get();
Assert.fail("No exception raised");
} catch (ExecutionException e) {
HttpResponseException respExp = (HttpResponseException) e.getCause();
@@ -266,15 +262,10 @@ public void testErrorNullContentSuccessfulResponse() throws InterruptedException
HttpJsonDirectCallable callable =
new HttpJsonDirectCallable<>(FAKE_METHOD_DESCRIPTOR);
- HttpJsonCallContext callContext =
- HttpJsonCallContext.createDefault()
- .withChannel(channel)
- .withTimeout(Duration.ofSeconds(30));
-
MOCK_SERVICE.addNullResponse();
try {
- callable.futureCall(createTestMessage(2), callContext).get();
+ callable.futureCall(createTestMessage(2), defaultCallContext).get();
Assert.fail("No exception raised");
} catch (ExecutionException e) {
HttpJsonStatusRuntimeException respExp = (HttpJsonStatusRuntimeException) e.getCause();
@@ -295,14 +286,10 @@ public void testErrorNullContentFailedResponse() throws InterruptedException {
HttpJsonDirectCallable callable =
new HttpJsonDirectCallable<>(FAKE_METHOD_DESCRIPTOR);
- HttpJsonCallContext callContext =
- HttpJsonCallContext.createDefault()
- .withChannel(channel)
- .withTimeout(Duration.ofSeconds(30));
MOCK_SERVICE.addNullResponse(400);
try {
- callable.futureCall(createTestMessage(2), callContext).get();
+ callable.futureCall(createTestMessage(2), defaultCallContext).get();
Assert.fail("No exception raised");
} catch (ExecutionException e) {
HttpResponseException respExp = (HttpResponseException) e.getCause();
@@ -321,18 +308,13 @@ public void testErrorNon2xxOr4xxResponse() throws InterruptedException {
HttpJsonDirectCallable callable =
new HttpJsonDirectCallable<>(FAKE_METHOD_DESCRIPTOR);
- HttpJsonCallContext callContext =
- HttpJsonCallContext.createDefault()
- .withChannel(channel)
- .withTimeout(Duration.ofSeconds(30));
-
ApiException exception =
ApiExceptionFactory.createException(
new Exception(), FakeStatusCode.of(Code.INTERNAL), false);
MOCK_SERVICE.addException(500, exception);
try {
- callable.futureCall(createTestMessage(2), callContext).get();
+ callable.futureCall(createTestMessage(2), defaultCallContext).get();
Assert.fail("No exception raised");
} catch (ExecutionException e) {
HttpResponseException respExp = (HttpResponseException) e.getCause();
@@ -353,8 +335,7 @@ public void testDeadlineExceededResponse() throws InterruptedException {
HttpJsonDirectCallable callable =
new HttpJsonDirectCallable<>(FAKE_METHOD_DESCRIPTOR);
- HttpJsonCallContext callContext =
- HttpJsonCallContext.createDefault().withChannel(channel).withTimeout(Duration.ofSeconds(3));
+ HttpJsonCallContext callContext = defaultCallContext.withTimeout(Duration.ofSeconds(3));
Field response = createTestMessage(10);
MOCK_SERVICE.addResponse(response, java.time.Duration.ofSeconds(5));
diff --git a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonDirectServerStreamingCallableTest.java b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonDirectServerStreamingCallableTest.java
index 709220be7d..3898b8e908 100644
--- a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonDirectServerStreamingCallableTest.java
+++ b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonDirectServerStreamingCallableTest.java
@@ -35,6 +35,7 @@
import com.google.api.gax.rpc.ApiException;
import com.google.api.gax.rpc.ClientContext;
import com.google.api.gax.rpc.DeadlineExceededException;
+import com.google.api.gax.rpc.EndpointContext;
import com.google.api.gax.rpc.ResponseObserver;
import com.google.api.gax.rpc.ServerStream;
import com.google.api.gax.rpc.ServerStreamingCallSettings;
@@ -44,11 +45,13 @@
import com.google.api.gax.rpc.StatusCode.Code;
import com.google.api.gax.rpc.StreamController;
import com.google.api.gax.rpc.testing.FakeCallContext;
+import com.google.auth.Credentials;
import com.google.common.collect.Lists;
import com.google.common.truth.Truth;
import com.google.protobuf.Field;
import com.google.type.Color;
import com.google.type.Money;
+import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -65,6 +68,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.mockito.Mockito;
import org.threeten.bp.Duration;
@RunWith(JUnit4.class)
@@ -124,7 +128,7 @@ public class HttpJsonDirectServerStreamingCallableTest {
private static ExecutorService executorService;
@BeforeClass
- public static void initialize() {
+ public static void initialize() throws IOException {
executorService = Executors.newFixedThreadPool(2);
channel =
new ManagedHttpJsonInterceptorChannel(
@@ -134,12 +138,18 @@ public static void initialize() {
.setHttpTransport(MOCK_SERVICE)
.build(),
new HttpJsonHeaderInterceptor(Collections.singletonMap("header-key", "headerValue")));
+ EndpointContext endpointContext = Mockito.mock(EndpointContext.class);
+ Mockito.doNothing()
+ .when(endpointContext)
+ .validateUniverseDomain(
+ Mockito.any(Credentials.class), Mockito.any(HttpJsonStatusCode.class));
clientContext =
ClientContext.newBuilder()
.setTransportChannel(HttpJsonTransportChannel.create(channel))
.setDefaultCallContext(
HttpJsonCallContext.of(channel, HttpJsonCallOptions.DEFAULT)
- .withTimeout(Duration.ofSeconds(3)))
+ .withTimeout(Duration.ofSeconds(3))
+ .withEndpointContext(endpointContext))
.build();
streamingCallSettings = ServerStreamingCallSettings.newBuilder().build();
@@ -200,8 +210,8 @@ public void testServerStreamingStart() throws InterruptedException {
Truth.assertThat(moneyObserver.controller).isNotNull();
// wait for the task to complete, otherwise it may interfere with other tests, since they share
- // the same MockService and unfinished request in this tes may start readind messages designated
- // for other tests.
+ // the same MockService and unfinished request in this test may start reading messages
+ // designated for other tests.
Truth.assertThat(latch.await(2, TimeUnit.SECONDS)).isTrue();
}
@@ -263,7 +273,7 @@ public void testOnResponseError() throws Throwable {
MoneyObserver moneyObserver = new MoneyObserver(true, latch);
streamingCallable.call(ERROR_REQUEST, moneyObserver);
- Truth.assertThat(latch.await(2000, TimeUnit.MILLISECONDS)).isTrue();
+ Truth.assertThat(latch.await(60, TimeUnit.SECONDS)).isTrue();
Truth.assertThat(moneyObserver.error).isInstanceOf(ApiException.class);
Truth.assertThat(((ApiException) moneyObserver.error).getStatusCode().getCode())
diff --git a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/RetryingTest.java b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/RetryingTest.java
index d03d7e57f0..f7b9935d31 100644
--- a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/RetryingTest.java
+++ b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/RetryingTest.java
@@ -29,9 +29,14 @@
*/
package com.google.api.gax.httpjson;
+import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.verify;
import com.google.api.client.http.HttpHeaders;
+import com.google.api.client.http.HttpMethods;
import com.google.api.client.http.HttpResponseException;
import com.google.api.client.http.HttpStatusCodes;
import com.google.api.core.ApiFuture;
@@ -50,15 +55,16 @@
import com.google.api.gax.rpc.UnaryCallable;
import com.google.api.gax.rpc.UnknownException;
import com.google.common.collect.ImmutableSet;
-import com.google.common.truth.Truth;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.UncheckedExecutionException;
+import com.google.protobuf.TypeRegistry;
import java.util.Set;
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.ArgumentCaptor;
import org.mockito.Mockito;
import org.threeten.bp.Duration;
@@ -68,10 +74,27 @@ public class RetryingTest {
@SuppressWarnings("unchecked")
private final UnaryCallable callInt = Mockito.mock(UnaryCallable.class);
+ private final ApiMethodDescriptor FAKE_METHOD_DESCRIPTOR_FOR_REQUEST_MUTATOR =
+ ApiMethodDescriptor.newBuilder()
+ .setFullMethodName("google.cloud.v1.Fake/FakeMethodForRequestMutator")
+ .setHttpMethod(HttpMethods.POST)
+ .setRequestFormatter(Mockito.mock(HttpRequestFormatter.class))
+ .setResponseParser(Mockito.mock(HttpResponseParser.class))
+ .build();
+
+ private final Integer initialRequest = 1;
+ private final Integer modifiedRequest = 0;
+
+ private final HttpJsonCallSettings httpJsonCallSettings =
+ HttpJsonCallSettings.newBuilder()
+ .setRequestMutator(request -> modifiedRequest)
+ .setMethodDescriptor(FAKE_METHOD_DESCRIPTOR_FOR_REQUEST_MUTATOR)
+ .setTypeRegistry(TypeRegistry.newBuilder().build())
+ .build();
+
private RecordingScheduler executor;
private FakeApiClock fakeClock;
private ClientContext clientContext;
-
private static final int HTTP_CODE_PRECONDITION_FAILED = 412;
private HttpResponseException HTTP_SERVICE_UNAVAILABLE_EXCEPTION =
@@ -112,7 +135,7 @@ public void teardown() {
@Test
public void retry() {
ImmutableSet retryable = ImmutableSet.of(Code.UNAVAILABLE);
- Mockito.when(callInt.futureCall((Integer) Mockito.any(), (ApiCallContext) Mockito.any()))
+ Mockito.when(callInt.futureCall((Integer) any(), (ApiCallContext) any()))
.thenReturn(ApiFutures.immediateFailedFuture(HTTP_SERVICE_UNAVAILABLE_EXCEPTION))
.thenReturn(ApiFutures.immediateFailedFuture(HTTP_SERVICE_UNAVAILABLE_EXCEPTION))
.thenReturn(ApiFutures.immediateFailedFuture(HTTP_SERVICE_UNAVAILABLE_EXCEPTION))
@@ -121,8 +144,14 @@ public void retry() {
UnaryCallSettings callSettings =
createSettings(retryable, FAST_RETRY_SETTINGS);
UnaryCallable callable =
- HttpJsonCallableFactory.createUnaryCallable(callInt, callSettings, clientContext);
- Truth.assertThat(callable.call(1)).isEqualTo(2);
+ HttpJsonCallableFactory.createUnaryCallable(
+ callInt, callSettings, httpJsonCallSettings, clientContext);
+ assertThat(callable.call(initialRequest)).isEqualTo(2);
+
+ // Capture the argument passed to futureCall
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(callInt, atLeastOnce()).futureCall(argumentCaptor.capture(), any(ApiCallContext.class));
+ assertThat(argumentCaptor.getAllValues()).containsExactly(0, 0, 0, 0).inOrder();
}
@Test
@@ -140,7 +169,7 @@ public void retryTotalTimeoutExceeded() {
httpResponseException,
HttpJsonStatusCode.of(Code.FAILED_PRECONDITION),
false);
- Mockito.when(callInt.futureCall((Integer) Mockito.any(), (ApiCallContext) Mockito.any()))
+ Mockito.when(callInt.futureCall((Integer) any(), (ApiCallContext) any()))
.thenReturn(ApiFutures.immediateFailedFuture(apiException))
.thenReturn(ApiFutures.immediateFuture(2));
@@ -152,14 +181,19 @@ public void retryTotalTimeoutExceeded() {
.build();
UnaryCallSettings callSettings = createSettings(retryable, retrySettings);
UnaryCallable callable =
- HttpJsonCallableFactory.createUnaryCallable(callInt, callSettings, clientContext);
- assertThrows(ApiException.class, () -> callable.call(1));
+ HttpJsonCallableFactory.createUnaryCallable(
+ callInt, callSettings, httpJsonCallSettings, clientContext);
+ assertThrows(ApiException.class, () -> callable.call(initialRequest));
+ // Capture the argument passed to futureCall
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(callInt, atLeastOnce()).futureCall(argumentCaptor.capture(), any(ApiCallContext.class));
+ assertThat(argumentCaptor.getAllValues()).containsExactly(0);
}
@Test
public void retryMaxAttemptsExceeded() {
ImmutableSet retryable = ImmutableSet.of(Code.UNAVAILABLE);
- Mockito.when(callInt.futureCall((Integer) Mockito.any(), (ApiCallContext) Mockito.any()))
+ Mockito.when(callInt.futureCall((Integer) any(), (ApiCallContext) any()))
.thenReturn(ApiFutures.immediateFailedFuture(HTTP_SERVICE_UNAVAILABLE_EXCEPTION))
.thenReturn(ApiFutures.immediateFailedFuture(HTTP_SERVICE_UNAVAILABLE_EXCEPTION))
.thenReturn(ApiFutures.immediateFuture(2));
@@ -167,14 +201,19 @@ public void retryMaxAttemptsExceeded() {
RetrySettings retrySettings = FAST_RETRY_SETTINGS.toBuilder().setMaxAttempts(2).build();
UnaryCallSettings callSettings = createSettings(retryable, retrySettings);
UnaryCallable callable =
- HttpJsonCallableFactory.createUnaryCallable(callInt, callSettings, clientContext);
- assertThrows(ApiException.class, () -> callable.call(1));
+ HttpJsonCallableFactory.createUnaryCallable(
+ callInt, callSettings, httpJsonCallSettings, clientContext);
+ assertThrows(ApiException.class, () -> callable.call(initialRequest));
+ // Capture the argument passed to futureCall
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(callInt, atLeastOnce()).futureCall(argumentCaptor.capture(), any(ApiCallContext.class));
+ assertThat(argumentCaptor.getAllValues()).containsExactly(0, 0).inOrder();
}
@Test
public void retryWithinMaxAttempts() {
ImmutableSet retryable = ImmutableSet.of(Code.UNAVAILABLE);
- Mockito.when(callInt.futureCall((Integer) Mockito.any(), (ApiCallContext) Mockito.any()))
+ Mockito.when(callInt.futureCall((Integer) any(), (ApiCallContext) any()))
.thenReturn(ApiFutures.immediateFailedFuture(HTTP_SERVICE_UNAVAILABLE_EXCEPTION))
.thenReturn(ApiFutures.immediateFailedFuture(HTTP_SERVICE_UNAVAILABLE_EXCEPTION))
.thenReturn(ApiFutures.immediateFuture(2));
@@ -182,9 +221,13 @@ public void retryWithinMaxAttempts() {
RetrySettings retrySettings = FAST_RETRY_SETTINGS.toBuilder().setMaxAttempts(3).build();
UnaryCallSettings callSettings = createSettings(retryable, retrySettings);
UnaryCallable callable =
- HttpJsonCallableFactory.createUnaryCallable(callInt, callSettings, clientContext);
- callable.call(1);
- Truth.assertThat(callable.call(1)).isEqualTo(2);
+ HttpJsonCallableFactory.createUnaryCallable(
+ callInt, callSettings, httpJsonCallSettings, clientContext);
+ assertThat(callable.call(initialRequest)).isEqualTo(2);
+ // Capture the argument passed to futureCall
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(callInt, atLeastOnce()).futureCall(argumentCaptor.capture(), any(ApiCallContext.class));
+ assertThat(argumentCaptor.getAllValues()).containsExactly(0, 0, 0).inOrder();
}
@Test
@@ -196,7 +239,7 @@ public void retryOnStatusUnknown() {
"temporary redirect",
new HttpHeaders())
.build();
- Mockito.when(callInt.futureCall((Integer) Mockito.any(), (ApiCallContext) Mockito.any()))
+ Mockito.when(callInt.futureCall((Integer) any(), (ApiCallContext) any()))
.thenReturn(ApiFutures.immediateFailedFuture(throwable))
.thenReturn(ApiFutures.immediateFailedFuture(throwable))
.thenReturn(ApiFutures.immediateFailedFuture(throwable))
@@ -204,22 +247,32 @@ public void retryOnStatusUnknown() {
UnaryCallSettings callSettings =
createSettings(retryable, FAST_RETRY_SETTINGS);
UnaryCallable callable =
- HttpJsonCallableFactory.createUnaryCallable(callInt, callSettings, clientContext);
- Truth.assertThat(callable.call(1)).isEqualTo(2);
+ HttpJsonCallableFactory.createUnaryCallable(
+ callInt, callSettings, httpJsonCallSettings, clientContext);
+ assertThat(callable.call(initialRequest)).isEqualTo(2);
+ // Capture the argument passed to futureCall
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(callInt, atLeastOnce()).futureCall(argumentCaptor.capture(), any(ApiCallContext.class));
+ assertThat(argumentCaptor.getAllValues()).containsExactly(0, 0, 0, 0).inOrder();
}
@Test
public void retryOnUnexpectedException() {
ImmutableSet retryable = ImmutableSet.of(Code.UNKNOWN);
Throwable throwable = new RuntimeException("foobar");
- Mockito.when(callInt.futureCall((Integer) Mockito.any(), (ApiCallContext) Mockito.any()))
+ Mockito.when(callInt.futureCall((Integer) any(), (ApiCallContext) any()))
.thenReturn(ApiFutures.immediateFailedFuture(throwable));
UnaryCallSettings callSettings =
createSettings(retryable, FAST_RETRY_SETTINGS);
UnaryCallable callable =
- HttpJsonCallableFactory.createUnaryCallable(callInt, callSettings, clientContext);
- ApiException exception = assertThrows(ApiException.class, () -> callable.call(1));
- Truth.assertThat(exception).hasCauseThat().isSameInstanceAs(throwable);
+ HttpJsonCallableFactory.createUnaryCallable(
+ callInt, callSettings, httpJsonCallSettings, clientContext);
+ ApiException exception = assertThrows(ApiException.class, () -> callable.call(initialRequest));
+ assertThat(exception).hasCauseThat().isSameInstanceAs(throwable);
+ // Capture the argument passed to futureCall
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(callInt, atLeastOnce()).futureCall(argumentCaptor.capture(), any(ApiCallContext.class));
+ assertThat(argumentCaptor.getAllValues()).containsExactly(0).inOrder();
}
@Test
@@ -235,15 +288,20 @@ public void retryNoRecover() {
httpResponseException,
HttpJsonStatusCode.of(Code.FAILED_PRECONDITION),
false);
- Mockito.when(callInt.futureCall((Integer) Mockito.any(), (ApiCallContext) Mockito.any()))
+ Mockito.when(callInt.futureCall((Integer) any(), (ApiCallContext) any()))
.thenReturn(ApiFutures.immediateFailedFuture(apiException))
.thenReturn(ApiFutures.immediateFuture(2));
UnaryCallSettings callSettings =
createSettings(retryable, FAST_RETRY_SETTINGS);
UnaryCallable callable =
- HttpJsonCallableFactory.createUnaryCallable(callInt, callSettings, clientContext);
- ApiException exception = assertThrows(ApiException.class, () -> callable.call(1));
- Truth.assertThat(exception).isSameInstanceAs(apiException);
+ HttpJsonCallableFactory.createUnaryCallable(
+ callInt, callSettings, httpJsonCallSettings, clientContext);
+ ApiException exception = assertThrows(ApiException.class, () -> callable.call(initialRequest));
+ assertThat(exception).isSameInstanceAs(apiException);
+ // Capture the argument passed to futureCall
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(callInt, atLeastOnce()).futureCall(argumentCaptor.capture(), any(ApiCallContext.class));
+ assertThat(argumentCaptor.getAllValues()).containsExactly(0);
}
@Test
@@ -253,19 +311,24 @@ public void retryKeepFailing() {
new HttpResponseException.Builder(
HttpStatusCodes.STATUS_CODE_SERVICE_UNAVAILABLE, "Unavailable", new HttpHeaders())
.build();
- Mockito.when(callInt.futureCall((Integer) Mockito.any(), (ApiCallContext) Mockito.any()))
+ Mockito.when(callInt.futureCall((Integer) any(), (ApiCallContext) any()))
.thenReturn(ApiFutures.immediateFailedFuture(throwable));
UnaryCallSettings callSettings =
createSettings(retryable, FAST_RETRY_SETTINGS);
UnaryCallable callable =
- HttpJsonCallableFactory.createUnaryCallable(callInt, callSettings, clientContext);
+ HttpJsonCallableFactory.createUnaryCallable(
+ callInt, callSettings, httpJsonCallSettings, clientContext);
// Need to advance time inside the call.
- ApiFuture future = callable.futureCall(1);
+ ApiFuture future = callable.futureCall(initialRequest);
UncheckedExecutionException exception =
assertThrows(UncheckedExecutionException.class, () -> Futures.getUnchecked(future));
- Truth.assertThat(exception).hasCauseThat().isInstanceOf(ApiException.class);
- Truth.assertThat(exception).hasCauseThat().hasMessageThat().contains("Unavailable");
+ assertThat(exception).hasCauseThat().isInstanceOf(ApiException.class);
+ assertThat(exception).hasCauseThat().hasMessageThat().contains("Unavailable");
+ // Capture the argument passed to futureCall
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(callInt, atLeastOnce()).futureCall(argumentCaptor.capture(), any(ApiCallContext.class));
+ assertThat(argumentCaptor.getValue()).isEqualTo(0);
}
@Test
@@ -289,34 +352,45 @@ public void testKnownStatusCode() {
HTTP_CODE_PRECONDITION_FAILED, "precondition failed", new HttpHeaders())
.setMessage(throwableMessage)
.build();
- Mockito.when(callInt.futureCall((Integer) Mockito.any(), (ApiCallContext) Mockito.any()))
+ Mockito.when(callInt.futureCall((Integer) any(), (ApiCallContext) any()))
.thenReturn(ApiFutures.immediateFailedFuture(throwable));
UnaryCallSettings callSettings =
UnaryCallSettings.newUnaryCallSettingsBuilder()
.setRetryableCodes(retryable)
.build();
UnaryCallable callable =
- HttpJsonCallableFactory.createUnaryCallable(callInt, callSettings, clientContext);
+ HttpJsonCallableFactory.createUnaryCallable(
+ callInt, callSettings, httpJsonCallSettings, clientContext);
ApiException exception =
- assertThrows(FailedPreconditionException.class, () -> callable.call(1));
- Truth.assertThat(exception.getStatusCode().getTransportCode())
+ assertThrows(FailedPreconditionException.class, () -> callable.call(initialRequest));
+ assertThat(exception.getStatusCode().getTransportCode())
.isEqualTo(HTTP_CODE_PRECONDITION_FAILED);
- Truth.assertThat(exception).hasMessageThat().contains("precondition failed");
+ assertThat(exception).hasMessageThat().contains("precondition failed");
+ // Capture the argument passed to futureCall
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(callInt, atLeastOnce()).futureCall(argumentCaptor.capture(), any(ApiCallContext.class));
+ assertThat(argumentCaptor.getAllValues()).containsExactly(0).inOrder();
}
@Test
public void testUnknownStatusCode() {
ImmutableSet retryable = ImmutableSet.of();
- Mockito.when(callInt.futureCall((Integer) Mockito.any(), (ApiCallContext) Mockito.any()))
+ Mockito.when(callInt.futureCall((Integer) any(), (ApiCallContext) any()))
.thenReturn(ApiFutures.immediateFailedFuture(new RuntimeException("unknown")));
UnaryCallSettings callSettings =
UnaryCallSettings.newUnaryCallSettingsBuilder()
.setRetryableCodes(retryable)
.build();
UnaryCallable callable =
- HttpJsonCallableFactory.createUnaryCallable(callInt, callSettings, clientContext);
- UnknownException exception = assertThrows(UnknownException.class, () -> callable.call(1));
- Truth.assertThat(exception).hasMessageThat().isEqualTo("java.lang.RuntimeException: unknown");
+ HttpJsonCallableFactory.createUnaryCallable(
+ callInt, callSettings, httpJsonCallSettings, clientContext);
+ UnknownException exception =
+ assertThrows(UnknownException.class, () -> callable.call(initialRequest));
+ assertThat(exception).hasMessageThat().isEqualTo("java.lang.RuntimeException: unknown");
+ // Capture the argument passed to futureCall
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(callInt, atLeastOnce()).futureCall(argumentCaptor.capture(), any(ApiCallContext.class));
+ assertThat(argumentCaptor.getAllValues()).containsExactly(0).inOrder();
}
public static UnaryCallSettings createSettings(
diff --git a/gax-java/gax/clirr-ignored-differences.xml b/gax-java/gax/clirr-ignored-differences.xml
index 15b4a76c8a..c8e68444ff 100644
--- a/gax-java/gax/clirr-ignored-differences.xml
+++ b/gax-java/gax/clirr-ignored-differences.xml
@@ -19,4 +19,27 @@
*setWaitTimeout*com.google.api.gax.rpc.ServerStreamingCallSettings$Builder
+
+
+ 7012
+ com/google/api/gax/rpc/TransportChannelProvider
+ * getEndpoint()
+
+
+
+ 7013
+ com/google/api/gax/rpc/ClientContext*
+ * *UniverseDomain*(*)
+
+
+
+ 7012
+ com/google/api/gax/rpc/ApiCallContext
+ * withEndpointContext(*)
+
+
+ 7012
+ com/google/api/gax/rpc/ApiCallContext
+ * validateUniverseDomain()
+
diff --git a/gax-java/gax/pom.xml b/gax-java/gax/pom.xml
index 899ad0c6e9..dc8b7ae1a0 100644
--- a/gax-java/gax/pom.xml
+++ b/gax-java/gax/pom.xml
@@ -3,7 +3,7 @@
4.0.0gax
- 2.36.0
+ 2.42.0jarGAX (Google Api eXtensions) for Java (Core)Google Api eXtensions for Java (Core)
@@ -11,7 +11,7 @@
com.google.apigax-parent
- 2.36.0
+ 2.42.0
@@ -76,6 +76,7 @@
com/google/api/gax/core/**com/google/api/gax/rpc/testing/**com/google/api/gax/rpc/mtls/**
+ com/google/api/gax/util/**
@@ -92,6 +93,14 @@
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ -Djava.util.logging.SimpleFormatter.format="%1$tY %1$tl:%1$tM:%1$tS.%1$tL %2$s %4$s: %5$s%6$s%n"
+
+
\ No newline at end of file
diff --git a/gax-java/gax/src/main/java/com/google/api/gax/longrunning/OperationTimedPollAlgorithm.java b/gax-java/gax/src/main/java/com/google/api/gax/longrunning/OperationTimedPollAlgorithm.java
index ec7e842e3d..0b2f225dfd 100644
--- a/gax-java/gax/src/main/java/com/google/api/gax/longrunning/OperationTimedPollAlgorithm.java
+++ b/gax-java/gax/src/main/java/com/google/api/gax/longrunning/OperationTimedPollAlgorithm.java
@@ -35,7 +35,10 @@
import com.google.api.gax.retrying.ExponentialRetryAlgorithm;
import com.google.api.gax.retrying.RetrySettings;
import com.google.api.gax.retrying.TimedAttemptSettings;
+import com.google.common.annotations.VisibleForTesting;
import java.util.concurrent.CancellationException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
/**
* Operation timed polling algorithm, which uses exponential backoff factor for determining when the
@@ -43,6 +46,14 @@
* algorithm cancels polling.
*/
public class OperationTimedPollAlgorithm extends ExponentialRetryAlgorithm {
+
+ @VisibleForTesting
+ static final Logger LOGGER = Logger.getLogger(OperationTimedPollAlgorithm.class.getName());
+
+ @VisibleForTesting
+ static final String LRO_TROUBLESHOOTING_LINK =
+ "https://github.com/googleapis/google-cloud-java#lro-timeouts";
+
/**
* Creates the polling algorithm, using the default {@code NanoClock} for time computations.
*
@@ -77,6 +88,13 @@ public boolean shouldRetry(TimedAttemptSettings nextAttemptSettings)
if (super.shouldRetry(nextAttemptSettings)) {
return true;
}
+ if (LOGGER.isLoggable(Level.WARNING)) {
+ LOGGER.log(
+ Level.WARNING,
+ "The task has been cancelled. Please refer to "
+ + LRO_TROUBLESHOOTING_LINK
+ + " for more information");
+ }
throw new CancellationException();
}
diff --git a/gax-java/gax/src/main/java/com/google/api/gax/retrying/RetrySettings.java b/gax-java/gax/src/main/java/com/google/api/gax/retrying/RetrySettings.java
index f5722f1ae9..caf25725dc 100644
--- a/gax-java/gax/src/main/java/com/google/api/gax/retrying/RetrySettings.java
+++ b/gax-java/gax/src/main/java/com/google/api/gax/retrying/RetrySettings.java
@@ -66,6 +66,12 @@
*
Server streaming RPCs interpret RPC timeouts a bit differently. For server streaming RPCs, the
* RPC timeout gets converted into a wait timeout {@link
* com.google.api.gax.rpc.ApiCallContext#withStreamWaitTimeout(Duration)}.
+ *
+ *
In Cloud Client Libraries, Retry and LRO Retry Settings may be configured for each RPC in a
+ * service. These values are chosen by the service teams and may be found by looking at the
+ * {Service}StubSettings.java file in each library. The default values listed below for each
+ * configuration are the default values for the RetrySettings class if there are no RPC specific
+ * configurations from the Service Team.
*/
@AutoValue
public abstract class RetrySettings implements Serializable {
@@ -74,36 +80,61 @@ public abstract class RetrySettings implements Serializable {
/**
* TotalTimeout has ultimate control over how long the logic should keep trying the remote call
- * until it gives up completely. The higher the total timeout, the more retries can be attempted.
- * The default value is {@code Duration.ZERO}.
+ * until it gives up completely. The higher the total timeout, the more retries and polls can be
+ * attempted. If this value is {@code Duration.ZERO}, then the logic will instead use the number
+ * of attempts to determine retries. In the event that both maxAttempts and totalTimeout values
+ * are both 0, the logic will not retry. If this value is non-{@code Duration.ZERO}, and the retry
+ * duration has reaches the timeout value, the logic will give up retrying even the number of
+ * attempts is lower than the maxAttempts value.
+ *
+ *
If there are no configurations, Retries have the default timeout value of {@code
+ * Duration.ZERO} and LROs have a default total timeout value of {@code Duration.ofMillis(300000)}
+ * (5 minutes).
*/
public abstract Duration getTotalTimeout();
/**
- * InitialRetryDelay controls the delay before the first retry. Subsequent retries will use this
- * value adjusted according to the RetryDelayMultiplier. The default value is {@code
- * Duration.ZERO}.
+ * InitialRetryDelay controls the delay before the first retry/ poll. Subsequent retries and polls
+ * will use this value adjusted according to the RetryDelayMultiplier.
+ *
+ *
If there are no configurations, Retries have the default initial retry delay value of {@code
+ * Duration.ZERO} and LROs have a default initial poll delay value of {@code
+ * Duration.ofMillis(5000)} (5 seconds).
*/
public abstract Duration getInitialRetryDelay();
/**
- * RetryDelayMultiplier controls the change in retry delay. The retry delay of the previous call
- * is multiplied by the RetryDelayMultiplier to calculate the retry delay for the next call. The
- * default value is {@code 1.0}.
+ * RetryDelayMultiplier controls the change in delay before the next retry or poll. The retry
+ * delay of the previous call is multiplied by the RetryDelayMultiplier to calculate the retry
+ * delay for the next call.
+ *
+ *
If there are no configurations, Retries have the default retry delay multiplier value of
+ * {@code 1.0} and LROs have a default retry delay multiplier of {@code 1.5}.
*/
public abstract double getRetryDelayMultiplier();
/**
* MaxRetryDelay puts a limit on the value of the retry delay, so that the RetryDelayMultiplier
- * can't increase the retry delay higher than this amount. The default value is {@code
- * Duration.ZERO}.
+ * can't increase the retry delay higher than this amount.
+ *
+ *
If there are no configurations, Retries have the default max retry delay value of {@code
+ * Duration.ZERO} and LROs have a default max poll retry delay value of {@code
+ * Duration.ofMillis(45000)} (45 seconds).
*/
public abstract Duration getMaxRetryDelay();
/**
- * MaxAttempts defines the maximum number of attempts to perform. The default value is {@code 0}.
- * If this value is greater than 0, and the number of attempts reaches this limit, the logic will
- * give up retrying even if the total retry time is still lower than TotalTimeout.
+ * MaxAttempts defines the maximum number of retry attempts to perform. If this value is set to 0,
+ * the logic will instead use the totalTimeout value to determine retries. In the event that both
+ * the maxAttempts and totalTimeout values are both 0, the logic will not retry. If this value is
+ * greater than 0, and the number of attempts exceeds this limit, the logic will give up retrying
+ * even if the total retry time is still lower than totalTimeout.
+ *
+ *
If there are no configurations, Retries and LROs have the default max attempt value of
+ * {@code 0}. LRO polling does not use this value by default.
+ *
+ *
The first RPC invocation will be considered attempt #0. Subsequent calls (retries) will
+ * increment the number of attempts and the number of attempts will not exceed this value.
*/
public abstract int getMaxAttempts();
@@ -123,22 +154,30 @@ public abstract class RetrySettings implements Serializable {
/**
* InitialRpcTimeout controls the timeout for the initial RPC. Subsequent calls will use this
- * value adjusted according to the RpcTimeoutMultiplier. The default value is {@code
- * Duration.ZERO}.
+ * value adjusted according to the RpcTimeoutMultiplier. RPC Timeout value of {@code
+ * Duration.ZERO} allows the RPC to continue indefinitely (until it hits a Connect Timeout or the
+ * connection has been terminated).
+ *
+ *
If there are no configurations, Retries have the default initial RPC timeout value of {@code
+ * Duration.ZERO}. LRO polling does not use the Initial RPC Timeout value.
*/
public abstract Duration getInitialRpcTimeout();
/**
* RpcTimeoutMultiplier controls the change in RPC timeout. The timeout of the previous call is
- * multiplied by the RpcTimeoutMultiplier to calculate the timeout for the next call. The default
- * value is {@code 1.0}.
+ * multiplied by the RpcTimeoutMultiplier to calculate the timeout for the next call.
+ *
+ *
If there are no configurations, Retries have the default RPC Timeout Multiplier value of
+ * {@code 1.0}. LRO polling does not use the RPC Timeout Multiplier value.
*/
public abstract double getRpcTimeoutMultiplier();
/**
* MaxRpcTimeout puts a limit on the value of the RPC timeout, so that the RpcTimeoutMultiplier
- * can't increase the RPC timeout higher than this amount. The default value is {@code
- * Duration.ZERO}.
+ * can't increase the RPC timeout higher than this amount.
+ *
+ *
If there are no configurations, Retries have the default Max RPC Timeout value of {@code
+ * Duration.ZERO}. LRO polling does not use the Max RPC Timeout value.
*/
public abstract Duration getMaxRpcTimeout();
@@ -166,36 +205,61 @@ public abstract static class Builder {
/**
* TotalTimeout has ultimate control over how long the logic should keep trying the remote call
- * until it gives up completely. The higher the total timeout, the more retries can be
- * attempted. The default value is {@code Duration.ZERO}.
+ * until it gives up completely. The higher the total timeout, the more retries and polls can be
+ * attempted. If this value is {@code Duration.ZERO}, then the logic will instead use the number
+ * of attempts to determine retries. In the event that both maxAttempts and totalTimeout values
+ * are both 0, the logic will not retry. If this value is non-{@code Duration.ZERO}, and the
+ * retry duration has reaches the timeout value, the logic will give up retrying even the number
+ * of attempts is lower than the maxAttempts value.
+ *
+ *
If there are no configurations, Retries have the default timeout value of {@code
+ * Duration.ZERO} and LROs have a default total timeout value of {@code
+ * Duration.ofMillis(300000)} (5 minutes).
*/
public abstract Builder setTotalTimeout(Duration totalTimeout);
/**
- * InitialRetryDelay controls the delay before the first retry. Subsequent retries will use this
- * value adjusted according to the RetryDelayMultiplier. The default value is {@code
- * Duration.ZERO}.
+ * InitialRetryDelay controls the delay before the first retry/ poll. Subsequent retries and
+ * polls will use this value adjusted according to the RetryDelayMultiplier.
+ *
+ *
If there are no configurations, Retries have the default initial retry delay value of
+ * {@code Duration.ZERO} and LROs have a default initial poll delay value of {@code
+ * Duration.ofMillis(5000)} (5 seconds).
*/
public abstract Builder setInitialRetryDelay(Duration initialDelay);
/**
- * RetryDelayMultiplier controls the change in retry delay. The retry delay of the previous call
- * is multiplied by the RetryDelayMultiplier to calculate the retry delay for the next call. The
- * default value is {@code 1.0}.
+ * RetryDelayMultiplier controls the change in delay before the next retry or poll. The retry
+ * delay of the previous call is multiplied by the RetryDelayMultiplier to calculate the retry
+ * delay for the next call.
+ *
+ *
If there are no configurations, Retries have the default retry delay multiplier value of
+ * {@code 1.0} and LROs have a default retry delay multiplier of {@code 1.5}.
*/
public abstract Builder setRetryDelayMultiplier(double multiplier);
/**
* MaxRetryDelay puts a limit on the value of the retry delay, so that the RetryDelayMultiplier
- * can't increase the retry delay higher than this amount. The default value is {@code
- * Duration.ZERO}.
+ * can't increase the retry delay higher than this amount.
+ *
+ *
If there are no configurations, Retries have the default max retry delay value of {@code
+ * Duration.ZERO} and LROs have a default max poll retry delay value of {@code
+ * Duration.ofMillis(45000)} (45 seconds).
*/
public abstract Builder setMaxRetryDelay(Duration maxDelay);
/**
- * MaxAttempts defines the maximum number of attempts to perform. The default value is {@code
- * 0}. If this value is greater than 0, and the number of attempts reaches this limit, the logic
- * will give up retrying even if the total retry time is still lower than TotalTimeout.
+ * MaxAttempts defines the maximum number of retry attempts to perform. If this value is set to
+ * 0, the logic will instead use the totalTimeout value to determine retries. In the event that
+ * both the maxAttempts and totalTimeout values are both 0, the logic will not retry. If this
+ * value is greater than 0, and the number of attempts exceeds this limit, the logic will give
+ * up retrying even if the total retry time is still lower than totalTimeout.
+ *
+ *
If there are no configurations, Retries and LROs have the default max attempt value of
+ * {@code 0}. LRO polling does not use this value by default.
+ *
+ *
The first RPC invocation will be considered attempt #0. Subsequent calls (retries) will
+ * increment the number of attempts and the number of attempts will not exceed this value.
*/
public abstract Builder setMaxAttempts(int maxAttempts);
@@ -215,49 +279,80 @@ public abstract static class Builder {
/**
* InitialRpcTimeout controls the timeout for the initial RPC. Subsequent calls will use this
- * value adjusted according to the RpcTimeoutMultiplier. The default value is {@code
- * Duration.ZERO}.
+ * value adjusted according to the RpcTimeoutMultiplier. RPC Timeout value of {@code
+ * Duration.ZERO} allows the RPC to continue indefinitely (until it hits a Connect Timeout or
+ * the connection has been terminated).
+ *
+ *
If there are no configurations, Retries have the default initial RPC timeout value of
+ * {@code Duration.ZERO}. LRO polling does not use the Initial RPC Timeout value.
*/
public abstract Builder setInitialRpcTimeout(Duration initialTimeout);
/**
- * See the class documentation of {@link RetrySettings} for a description of what this value
- * does. The default value is {@code 1.0}.
+ * RpcTimeoutMultiplier controls the change in RPC timeout. The timeout of the previous call is
+ * multiplied by the RpcTimeoutMultiplier to calculate the timeout for the next call.
+ *
+ *
If there are no configurations, Retries have the default RPC Timeout Multiplier value of
+ * {@code 1.0}. LRO polling does not use the RPC Timeout Multiplier value.
*/
public abstract Builder setRpcTimeoutMultiplier(double multiplier);
/**
* MaxRpcTimeout puts a limit on the value of the RPC timeout, so that the RpcTimeoutMultiplier
- * can't increase the RPC timeout higher than this amount. The default value is {@code
- * Duration.ZERO}.
+ * can't increase the RPC timeout higher than this amount.
+ *
+ *
If there are no configurations, Retries have the default Max RPC Timeout value of {@code
+ * Duration.ZERO}. LRO polling does not use the Max RPC Timeout value.
*/
public abstract Builder setMaxRpcTimeout(Duration maxTimeout);
/**
* TotalTimeout has ultimate control over how long the logic should keep trying the remote call
- * until it gives up completely. The higher the total timeout, the more retries can be
- * attempted. The default value is {@code Duration.ZERO}.
+ * until it gives up completely. The higher the total timeout, the more retries and polls can be
+ * attempted. If this value is {@code Duration.ZERO}, then the logic will instead use the number
+ * of attempts to determine retries. In the event that both maxAttempts and totalTimeout values
+ * are both 0, the logic will not retry. If this value is non-{@code Duration.ZERO}, and the
+ * retry duration has reaches the timeout value, the logic will give up retrying even the number
+ * of attempts is lower than the maxAttempts value.
+ *
+ *
If there are no configurations, Retries have the default timeout value of {@code
+ * Duration.ZERO} and LROs have a default total timeout value of {@code
+ * Duration.ofMillis(300000)} (5 minutes).
*/
public abstract Duration getTotalTimeout();
/**
- * InitialRetryDelay controls the delay before the first retry. Subsequent retries will use this
- * value adjusted according to the RetryDelayMultiplier. The default value is {@code
- * Duration.ZERO}.
+ * InitialRetryDelay controls the delay before the first retry/ poll. Subsequent retries and
+ * polls will use this value adjusted according to the RetryDelayMultiplier.
+ *
+ *
If there are no configurations, Retries have the default initial retry delay value of
+ * {@code Duration.ZERO} and LROs have a default initial poll delay value of {@code
+ * Duration.ofMillis(5000)} (5 seconds).
*/
public abstract Duration getInitialRetryDelay();
/**
- * RetryDelayMultiplier controls the change in retry delay. The retry delay of the previous call
- * is multiplied by the RetryDelayMultiplier to calculate the retry delay for the next call. The
- * default value is {@code 1.0}.
+ * RetryDelayMultiplier controls the change in delay before the next retry or poll. The retry
+ * delay of the previous call is multiplied by the RetryDelayMultiplier to calculate the retry
+ * delay for the next call.
+ *
+ *
If there are no configurations, Retries have the default retry delay multiplier value of
+ * {@code 1.0} and LROs have a default retry delay multiplier of {@code 1.5}.
*/
public abstract double getRetryDelayMultiplier();
/**
- * MaxAttempts defines the maximum number of attempts to perform. The default value is {@code
- * 0}. If this value is greater than 0, and the number of attempts reaches this limit, the logic
- * will give up retrying even if the total retry time is still lower than TotalTimeout.
+ * MaxAttempts defines the maximum number of retry attempts to perform. If this value is set to
+ * 0, the logic will instead use the totalTimeout value to determine retries. In the event that
+ * both the maxAttempts and totalTimeout values are both 0, the logic will not retry. If this
+ * value is greater than 0, and the number of attempts exceeds this limit, the logic will give
+ * up retrying even if the total retry time is still lower than totalTimeout.
+ *
+ *
If there are no configurations, Retries and LROs have the default max attempt value of
+ * {@code 0}. LRO polling does not use this value by default.
+ *
+ *
The first RPC invocation will be considered attempt #0. Subsequent calls (retries) will
+ * increment the number of attempts and the number of attempts will not exceed this value.
*/
public abstract int getMaxAttempts();
@@ -273,27 +368,40 @@ public abstract static class Builder {
/**
* MaxRetryDelay puts a limit on the value of the retry delay, so that the RetryDelayMultiplier
- * can't increase the retry delay higher than this amount. The default value is {@code
- * Duration.ZERO}.
+ * can't increase the retry delay higher than this amount.
+ *
+ *
If there are no configurations, Retries have the default max retry delay value of {@code
+ * Duration.ZERO} and LROs have a default max poll retry delay value of {@code
+ * Duration.ofMillis(45000)} (45 seconds).
*/
public abstract Duration getMaxRetryDelay();
/**
* InitialRpcTimeout controls the timeout for the initial RPC. Subsequent calls will use this
- * value adjusted according to the RpcTimeoutMultiplier. The default value is {@code
- * Duration.ZERO}.
+ * value adjusted according to the RpcTimeoutMultiplier. RPC Timeout value of {@code
+ * Duration.ZERO} allows the RPC to continue indefinitely (until it hits a Connect Timeout or
+ * the connection has been terminated).
+ *
+ *
If there are no configurations, Retries have the default initial RPC timeout value of
+ * {@code Duration.ZERO}. LRO polling does not use the Initial RPC Timeout value.
*/
public abstract Duration getInitialRpcTimeout();
/**
- * See the class documentation of {@link RetrySettings} for a description of what this value
- * does. The default value is {@code 1.0}.
+ * RpcTimeoutMultiplier controls the change in RPC timeout. The timeout of the previous call is
+ * multiplied by the RpcTimeoutMultiplier to calculate the timeout for the next call.
+ *
+ *
If there are no configurations, Retries have the default RPC Timeout Multiplier value of
+ * {@code 1.0}. LRO polling does not use the RPC Timeout Multiplier value.
*/
public abstract double getRpcTimeoutMultiplier();
/**
* MaxRpcTimeout puts a limit on the value of the RPC timeout, so that the RpcTimeoutMultiplier
* can't increase the RPC timeout higher than this amount.
+ *
+ *
If there are no configurations, Retries have the default Max RPC Timeout value of {@code
+ * Duration.ZERO}. LRO polling does not use the Max RPC Timeout value.
*/
public abstract Duration getMaxRpcTimeout();
diff --git a/gax-java/gax/src/main/java/com/google/api/gax/rpc/ApiCallContext.java b/gax-java/gax/src/main/java/com/google/api/gax/rpc/ApiCallContext.java
index 5a34b58e39..e650564826 100644
--- a/gax-java/gax/src/main/java/com/google/api/gax/rpc/ApiCallContext.java
+++ b/gax-java/gax/src/main/java/com/google/api/gax/rpc/ApiCallContext.java
@@ -63,6 +63,9 @@ public interface ApiCallContext extends RetryingContext {
/** Returns a new ApiCallContext with the given channel set. */
ApiCallContext withTransportChannel(TransportChannel channel);
+ /** Returns a new ApiCallContext with the given Endpoint Context. */
+ ApiCallContext withEndpointContext(EndpointContext endpointContext);
+
/**
* Returns a new ApiCallContext with the given timeout set.
*
diff --git a/gax-java/gax/src/main/java/com/google/api/gax/rpc/Callables.java b/gax-java/gax/src/main/java/com/google/api/gax/rpc/Callables.java
index 28a76fe721..b1f4b51d6a 100644
--- a/gax-java/gax/src/main/java/com/google/api/gax/rpc/Callables.java
+++ b/gax-java/gax/src/main/java/com/google/api/gax/rpc/Callables.java
@@ -51,11 +51,53 @@ public class Callables {
private Callables() {}
+ /**
+ * Create a callable object that represents a Unary API method. Designed for use by generated
+ * code.
+ *
+ * @param innerCallable the callable to issue calls
+ * @param callSettings {@link UnaryCallSettings} to configure the unary call-related settings
+ * with.
+ * @param clientContext {@link ClientContext} to use to connect to the service.
+ * @return {@link UnaryCallable} callable object.
+ */
public static UnaryCallable retrying(
UnaryCallable innerCallable,
UnaryCallSettings, ?> callSettings,
ClientContext clientContext) {
+ ScheduledRetryingExecutor retryingExecutor =
+ getRetryingExecutor(callSettings, clientContext);
+ return new RetryingCallable<>(
+ clientContext.getDefaultCallContext(), innerCallable, retryingExecutor);
+ }
+
+ /**
+ * Create a callable object that represents a Unary API method that contains a Request Mutator.
+ * Designed for use by generated code.
+ *
+ * @param innerCallable the callable to issue calls
+ * @param callSettings {@link UnaryCallSettings} to configure the unary call-related settings
+ * with.
+ * @param clientContext {@link ClientContext} to use to connect to the service.
+ * @param requestMutator {@link RequestMutator} to modify the request. Currently only used for
+ * autopopulated fields.
+ * @return {@link UnaryCallable} callable object.
+ */
+ public static UnaryCallable retrying(
+ UnaryCallable innerCallable,
+ UnaryCallSettings, ?> callSettings,
+ ClientContext clientContext,
+ RequestMutator requestMutator) {
+
+ ScheduledRetryingExecutor retryingExecutor =
+ getRetryingExecutor(callSettings, clientContext);
+ return new RetryingCallable<>(
+ clientContext.getDefaultCallContext(), innerCallable, retryingExecutor, requestMutator);
+ }
+
+ private static ScheduledRetryingExecutor getRetryingExecutor(
+ UnaryCallSettings, ?> callSettings, ClientContext clientContext) {
UnaryCallSettings, ?> settings = callSettings;
if (areRetriesDisabled(settings.getRetryableCodes(), settings.getRetrySettings())) {
@@ -73,8 +115,7 @@ public static UnaryCallable retrying(
new ExponentialRetryAlgorithm(settings.getRetrySettings(), clientContext.getClock()));
ScheduledRetryingExecutor retryingExecutor =
new ScheduledRetryingExecutor<>(retryAlgorithm, clientContext.getExecutor());
- return new RetryingCallable<>(
- clientContext.getDefaultCallContext(), innerCallable, retryingExecutor);
+ return retryingExecutor;
}
public static ServerStreamingCallable retrying(
diff --git a/gax-java/gax/src/main/java/com/google/api/gax/rpc/ClientContext.java b/gax-java/gax/src/main/java/com/google/api/gax/rpc/ClientContext.java
index c26d4a0f4d..409dbbada1 100644
--- a/gax-java/gax/src/main/java/com/google/api/gax/rpc/ClientContext.java
+++ b/gax-java/gax/src/main/java/com/google/api/gax/rpc/ClientContext.java
@@ -37,7 +37,6 @@
import com.google.api.gax.core.ExecutorAsBackgroundResource;
import com.google.api.gax.core.ExecutorProvider;
import com.google.api.gax.rpc.internal.QuotaProjectIdHidingCredentials;
-import com.google.api.gax.rpc.mtls.MtlsProvider;
import com.google.api.gax.tracing.ApiTracerFactory;
import com.google.api.gax.tracing.BaseApiTracerFactory;
import com.google.auth.Credentials;
@@ -101,6 +100,13 @@ public abstract class ClientContext {
@Nonnull
public abstract Duration getStreamWatchdogCheckInterval();
+ // Package-Private scope for internal use only. Shared between StubSettings and ClientContext
+ @Nullable
+ abstract String getServiceName();
+
+ @Nullable
+ public abstract String getUniverseDomain();
+
@Nullable
public abstract String getEndpoint();
@@ -143,29 +149,6 @@ public static ClientContext create(ClientSettings settings) throws IOException {
return create(settings.getStubSettings());
}
- /** Returns the endpoint that should be used. See https://google.aip.dev/auth/4114. */
- static String getEndpoint(
- String endpoint,
- String mtlsEndpoint,
- boolean switchToMtlsEndpointAllowed,
- MtlsProvider mtlsProvider)
- throws IOException {
- if (switchToMtlsEndpointAllowed) {
- switch (mtlsProvider.getMtlsEndpointUsagePolicy()) {
- case ALWAYS:
- return mtlsEndpoint;
- case NEVER:
- return endpoint;
- default:
- if (mtlsProvider.useMtlsClientCertificate() && mtlsProvider.getKeyStore() != null) {
- return mtlsEndpoint;
- }
- return endpoint;
- }
- }
- return endpoint;
- }
-
/**
* Instantiates the executor, credentials, and transport context based on the given client
* settings.
@@ -177,15 +160,28 @@ public static ClientContext create(StubSettings settings) throws IOException {
final ScheduledExecutorService backgroundExecutor = backgroundExecutorProvider.getExecutor();
Credentials credentials = settings.getCredentialsProvider().getCredentials();
+ boolean usingGDCH = credentials instanceof GdchCredentials;
+ EndpointContext endpointContext =
+ EndpointContext.newBuilder()
+ .setServiceName(settings.getServiceName())
+ .setUniverseDomain(settings.getUniverseDomain())
+ .setClientSettingsEndpoint(settings.getUserSetEndpoint())
+ .setTransportChannelProviderEndpoint(
+ settings.getTransportChannelProvider().getEndpoint())
+ .setMtlsEndpoint(settings.getMtlsEndpoint())
+ .setSwitchToMtlsEndpointAllowed(settings.getSwitchToMtlsEndpointAllowed())
+ .setUsingGDCH(usingGDCH)
+ .build();
+ String endpoint = endpointContext.resolvedEndpoint();
String settingsGdchApiAudience = settings.getGdchApiAudience();
- if (credentials instanceof GdchCredentials) {
+ if (usingGDCH) {
// We recompute the GdchCredentials with the audience
String audienceString;
if (!Strings.isNullOrEmpty(settingsGdchApiAudience)) {
audienceString = settingsGdchApiAudience;
- } else if (!Strings.isNullOrEmpty(settings.getEndpoint())) {
- audienceString = settings.getEndpoint();
+ } else if (!Strings.isNullOrEmpty(endpoint)) {
+ audienceString = endpoint;
} else {
throw new IllegalArgumentException("Could not infer GDCH api audience from settings");
}
@@ -224,12 +220,6 @@ public static ClientContext create(StubSettings settings) throws IOException {
if (transportChannelProvider.needsCredentials() && credentials != null) {
transportChannelProvider = transportChannelProvider.withCredentials(credentials);
}
- String endpoint =
- getEndpoint(
- settings.getEndpoint(),
- settings.getMtlsEndpoint(),
- settings.getSwitchToMtlsEndpointAllowed(),
- new MtlsProvider());
if (transportChannelProvider.needsEndpoint()) {
transportChannelProvider = transportChannelProvider.withEndpoint(endpoint);
}
@@ -240,6 +230,7 @@ public static ClientContext create(StubSettings settings) throws IOException {
if (credentials != null) {
defaultCallContext = defaultCallContext.withCredentials(credentials);
}
+ defaultCallContext = defaultCallContext.withEndpointContext(endpointContext);
WatchdogProvider watchdogProvider = settings.getStreamWatchdogProvider();
@Nullable Watchdog watchdog = null;
@@ -279,6 +270,8 @@ public static ClientContext create(StubSettings settings) throws IOException {
.setInternalHeaders(ImmutableMap.copyOf(settings.getInternalHeaderProvider().getHeaders()))
.setClock(clock)
.setDefaultCallContext(defaultCallContext)
+ .setServiceName(settings.getServiceName())
+ .setUniverseDomain(settings.getUniverseDomain())
.setEndpoint(settings.getEndpoint())
.setQuotaProjectId(settings.getQuotaProjectId())
.setStreamWatchdog(watchdog)
@@ -344,6 +337,11 @@ public abstract static class Builder {
public abstract Builder setDefaultCallContext(ApiCallContext defaultCallContext);
+ // Package-Private scope for internal use only. Shared between StubSettings and ClientContext
+ abstract Builder setServiceName(String serviceName);
+
+ public abstract Builder setUniverseDomain(String universeDomain);
+
public abstract Builder setEndpoint(String endpoint);
public abstract Builder setQuotaProjectId(String QuotaProjectId);
diff --git a/gax-java/gax/src/main/java/com/google/api/gax/rpc/ClientSettings.java b/gax-java/gax/src/main/java/com/google/api/gax/rpc/ClientSettings.java
index 04b2c9f55b..25929756f5 100644
--- a/gax-java/gax/src/main/java/com/google/api/gax/rpc/ClientSettings.java
+++ b/gax-java/gax/src/main/java/com/google/api/gax/rpc/ClientSettings.java
@@ -93,6 +93,10 @@ public final ApiClock getClock() {
return stubSettings.getClock();
}
+ public final String getUniverseDomain() {
+ return stubSettings.getUniverseDomain();
+ }
+
public final String getEndpoint() {
return stubSettings.getEndpoint();
}
@@ -125,6 +129,7 @@ public String toString() {
.add("headerProvider", getHeaderProvider())
.add("internalHeaderProvider", getInternalHeaderProvider())
.add("clock", getClock())
+ .add("universeDomain", getUniverseDomain())
.add("endpoint", getEndpoint())
.add("quotaProjectId", getQuotaProjectId())
.add("watchdogProvider", getWatchdogProvider())
@@ -241,6 +246,12 @@ public B setClock(ApiClock clock) {
return self();
}
+ /** Sets the Universe Domain to configure the resolved endpoint */
+ public B setUniverseDomain(String universeDomain) {
+ stubSettings.setUniverseDomain(universeDomain);
+ return self();
+ }
+
public B setEndpoint(String endpoint) {
stubSettings.setEndpoint(endpoint);
return self();
diff --git a/gax-java/gax/src/main/java/com/google/api/gax/rpc/EndpointContext.java b/gax-java/gax/src/main/java/com/google/api/gax/rpc/EndpointContext.java
new file mode 100644
index 0000000000..b33a97cd0a
--- /dev/null
+++ b/gax-java/gax/src/main/java/com/google/api/gax/rpc/EndpointContext.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.google.api.gax.rpc;
+
+import com.google.api.core.InternalApi;
+import com.google.api.gax.rpc.mtls.MtlsProvider;
+import com.google.auth.Credentials;
+import com.google.auto.value.AutoValue;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Strings;
+import java.io.IOException;
+import javax.annotation.Nullable;
+
+/**
+ * EndpointContext is an internal class used by the client library to resolve the endpoint. It is
+ * created once the library is initialized should not be updated manually.
+ *
+ *
Contains the fields required to resolve the endpoint and Universe Domain
+ */
+@InternalApi
+@AutoValue
+public abstract class EndpointContext {
+ private static final String INVALID_UNIVERSE_DOMAIN_ERROR_TEMPLATE =
+ "The configured universe domain (%s) does not match the universe domain found in the credentials (%s). If you haven't configured the universe domain explicitly, `googleapis.com` is the default.";
+ public static final String UNABLE_TO_RETRIEVE_CREDENTIALS_ERROR_MESSAGE =
+ "Unable to retrieve the Universe Domain from the Credentials.";
+
+ /**
+ * ServiceName is host URI for Google Cloud Services. It follows the format of
+ * `{ServiceName}.googleapis.com`. For example, speech.googleapis.com would have a ServiceName of
+ * speech and cloudasset.googleapis.com would have a ServiceName of cloudasset.
+ */
+ @Nullable
+ public abstract String serviceName();
+
+ /**
+ * Universe Domain is the domain for Google Cloud Services. It follows the format of
+ * `{ServiceName}.{UniverseDomain}`. For example, speech.googleapis.com would have a Universe
+ * Domain value of `googleapis.com` and cloudasset.test.com would have a Universe Domain of
+ * `test.com`. If this value is not set, this will default to `googleapis.com`.
+ */
+ @Nullable
+ public abstract String universeDomain();
+
+ /**
+ * ClientSettingsEndpoint is the endpoint value set via the ClientSettings/StubSettings classes.
+ */
+ @Nullable
+ public abstract String clientSettingsEndpoint();
+
+ /**
+ * TransportChannelProviderEndpoint is the endpoint value set via the TransportChannelProvider
+ * class.
+ */
+ @Nullable
+ public abstract String transportChannelProviderEndpoint();
+
+ @Nullable
+ public abstract String mtlsEndpoint();
+
+ public abstract boolean switchToMtlsEndpointAllowed();
+
+ @Nullable
+ public abstract MtlsProvider mtlsProvider();
+
+ public abstract boolean usingGDCH();
+
+ abstract String resolvedUniverseDomain();
+
+ public abstract String resolvedEndpoint();
+
+ public abstract Builder toBuilder();
+
+ public static Builder newBuilder() {
+ return new AutoValue_EndpointContext.Builder()
+ .setSwitchToMtlsEndpointAllowed(false)
+ .setUsingGDCH(false);
+ }
+
+ /**
+ * Check that the User configured universe domain matches the Credentials' universe domain. The
+ * status code parameter is passed in to this method as it's a limitation of Gax's modules. The
+ * transport-neutral module does have access the transport-specific modules (which contain the
+ * implementation of the StatusCode). This method is scoped to be internal and should be not be
+ * accessed by users.
+ *
+ * @param credentials Auth Library Credentials
+ * @param invalidUniverseDomainStatusCode Transport-specific Status Code to be returned if the
+ * Universe Domain is invalid. For both transports, this is defined to be Unauthorized.
+ * @throws IOException Implementation of Auth's Retryable interface which tells the client library
+ * whether the RPC should be retried or not.
+ */
+ public void validateUniverseDomain(
+ Credentials credentials, StatusCode invalidUniverseDomainStatusCode) throws IOException {
+ if (usingGDCH()) {
+ // GDC-H has no universe domain, return
+ return;
+ }
+ String credentialsUniverseDomain = Credentials.GOOGLE_DEFAULT_UNIVERSE;
+ // If credentials is not NoCredentialsProvider, use the Universe Domain inside Credentials
+ if (credentials != null) {
+ credentialsUniverseDomain = credentials.getUniverseDomain();
+ }
+ if (!resolvedUniverseDomain().equals(credentialsUniverseDomain)) {
+ throw ApiExceptionFactory.createException(
+ new Throwable(
+ String.format(
+ EndpointContext.INVALID_UNIVERSE_DOMAIN_ERROR_TEMPLATE,
+ resolvedUniverseDomain(),
+ credentialsUniverseDomain)),
+ invalidUniverseDomainStatusCode,
+ false);
+ }
+ }
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ /**
+ * ServiceName is host URI for Google Cloud Services. It follows the format of
+ * `{ServiceName}.googleapis.com`. For example, speech.googleapis.com would have a ServiceName
+ * of speech and cloudasset.googleapis.com would have a ServiceName of cloudasset.
+ */
+ public abstract Builder setServiceName(String serviceName);
+
+ /**
+ * Universe Domain is the domain for Google Cloud Services. It follows the format of
+ * `{ServiceName}.{UniverseDomain}`. For example, speech.googleapis.com would have a Universe
+ * Domain value of `googleapis.com` and cloudasset.test.com would have a Universe Domain of
+ * `test.com`. If this value is not set, this will default to `googleapis.com`.
+ */
+ public abstract Builder setUniverseDomain(String universeDomain);
+
+ /**
+ * ClientSettingsEndpoint is the endpoint value set via the ClientSettings/StubSettings classes.
+ */
+ public abstract Builder setClientSettingsEndpoint(String clientSettingsEndpoint);
+
+ /**
+ * TransportChannelProviderEndpoint is the endpoint value set via the TransportChannelProvider
+ * class.
+ */
+ public abstract Builder setTransportChannelProviderEndpoint(String transportChannelEndpoint);
+
+ public abstract Builder setMtlsEndpoint(String mtlsEndpoint);
+
+ public abstract Builder setSwitchToMtlsEndpointAllowed(boolean switchToMtlsEndpointAllowed);
+
+ public abstract Builder setMtlsProvider(MtlsProvider mtlsProvider);
+
+ public abstract Builder setUsingGDCH(boolean usingGDCH);
+
+ public abstract Builder setResolvedEndpoint(String resolvedEndpoint);
+
+ public abstract Builder setResolvedUniverseDomain(String resolvedUniverseDomain);
+
+ abstract String serviceName();
+
+ abstract String universeDomain();
+
+ abstract String clientSettingsEndpoint();
+
+ abstract String transportChannelProviderEndpoint();
+
+ abstract String mtlsEndpoint();
+
+ abstract boolean switchToMtlsEndpointAllowed();
+
+ abstract MtlsProvider mtlsProvider();
+
+ abstract boolean usingGDCH();
+
+ abstract String resolvedUniverseDomain();
+
+ abstract EndpointContext autoBuild();
+
+ private String determineUniverseDomain() {
+ if (usingGDCH()) {
+ // GDC-H has no concept of Universe Domain. User should not set a custom value
+ if (universeDomain() != null) {
+ throw new IllegalArgumentException(
+ "Universe domain configuration is incompatible with GDC-H");
+ }
+ return Credentials.GOOGLE_DEFAULT_UNIVERSE;
+ }
+ // Check for "" (empty string)
+ if (universeDomain() != null && universeDomain().isEmpty()) {
+ throw new IllegalArgumentException("The universe domain value cannot be empty.");
+ }
+ // Override with user set universe domain if provided
+ return universeDomain() != null ? universeDomain() : Credentials.GOOGLE_DEFAULT_UNIVERSE;
+ }
+
+ /** Determines the fully resolved endpoint and universe domain values */
+ private String determineEndpoint() throws IOException {
+ MtlsProvider mtlsProvider = mtlsProvider() == null ? new MtlsProvider() : mtlsProvider();
+ // TransportChannelProvider's endpoint will override the ClientSettings' endpoint
+ String customEndpoint =
+ transportChannelProviderEndpoint() == null
+ ? clientSettingsEndpoint()
+ : transportChannelProviderEndpoint();
+
+ // GDC-H has a separate flow
+ if (usingGDCH()) {
+ if (customEndpoint == null) {
+ return buildEndpointTemplate(serviceName(), resolvedUniverseDomain());
+ }
+ return customEndpoint;
+ }
+
+ // If user does not provide a custom endpoint, build one with the universe domain
+ if (Strings.isNullOrEmpty(customEndpoint)) {
+ customEndpoint = buildEndpointTemplate(serviceName(), resolvedUniverseDomain());
+ }
+
+ String endpoint =
+ mtlsEndpointResolver(
+ customEndpoint, mtlsEndpoint(), switchToMtlsEndpointAllowed(), mtlsProvider);
+
+ // Check if mTLS is configured with non-GDU
+ if (endpoint.equals(mtlsEndpoint())
+ && !resolvedUniverseDomain().equals(Credentials.GOOGLE_DEFAULT_UNIVERSE)) {
+ throw new IllegalArgumentException(
+ "mTLS is not supported in any universe other than googleapis.com");
+ }
+
+ return endpoint;
+ }
+
+ // Default to port 443 for HTTPS. Using HTTP requires explicitly setting the endpoint
+ private String buildEndpointTemplate(String serviceName, String resolvedUniverseDomain) {
+ return serviceName + "." + resolvedUniverseDomain + ":443";
+ }
+
+ // Follows https://google.aip.dev/auth/4114 for resolving the endpoint
+ @VisibleForTesting
+ String mtlsEndpointResolver(
+ String endpoint,
+ String mtlsEndpoint,
+ boolean switchToMtlsEndpointAllowed,
+ MtlsProvider mtlsProvider)
+ throws IOException {
+ if (switchToMtlsEndpointAllowed && mtlsProvider != null) {
+ switch (mtlsProvider.getMtlsEndpointUsagePolicy()) {
+ case ALWAYS:
+ return mtlsEndpoint;
+ case NEVER:
+ return endpoint;
+ default:
+ if (mtlsProvider.useMtlsClientCertificate() && mtlsProvider.getKeyStore() != null) {
+ return mtlsEndpoint;
+ }
+ return endpoint;
+ }
+ }
+ return endpoint;
+ }
+
+ public EndpointContext build() throws IOException {
+ // The Universe Domain is used to resolve the Endpoint. It should be resolved first
+ setResolvedUniverseDomain(determineUniverseDomain());
+ setResolvedEndpoint(determineEndpoint());
+ return autoBuild();
+ }
+ }
+}
diff --git a/gax-java/gax/src/main/java/com/google/api/gax/rpc/RequestMutator.java b/gax-java/gax/src/main/java/com/google/api/gax/rpc/RequestMutator.java
new file mode 100644
index 0000000000..d26949d87d
--- /dev/null
+++ b/gax-java/gax/src/main/java/com/google/api/gax/rpc/RequestMutator.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.google.api.gax.rpc;
+
+import com.google.api.core.InternalApi;
+
+/**
+ * A request mutator takes a {@code request} message, applies some Function to it, and then returns
+ * the modified {@code request} message. This is currently only used for autopopulation of the
+ * request ID.
+ *
+ *
Implementations of this interface are expected to be autogenerated.
+ *
+ * @param request message type
+ */
+@InternalApi("For use by transport-specific implementations")
+@FunctionalInterface
+public interface RequestMutator {
+ /**
+ * Applies a Function to {@code request} message
+ *
+ * @param request request message
+ */
+ RequestT apply(RequestT request);
+}
diff --git a/gax-java/gax/src/main/java/com/google/api/gax/rpc/RetryingCallable.java b/gax-java/gax/src/main/java/com/google/api/gax/rpc/RetryingCallable.java
index 0a92794a20..e4fe13295a 100644
--- a/gax-java/gax/src/main/java/com/google/api/gax/rpc/RetryingCallable.java
+++ b/gax-java/gax/src/main/java/com/google/api/gax/rpc/RetryingCallable.java
@@ -43,20 +43,35 @@ class RetryingCallable extends UnaryCallable callable;
private final RetryingExecutorWithContext executor;
+ private final RequestMutator requestMutator;
+
RetryingCallable(
ApiCallContext callContextPrototype,
UnaryCallable callable,
RetryingExecutorWithContext executor) {
+ this(callContextPrototype, callable, executor, null);
+ }
+
+ RetryingCallable(
+ ApiCallContext callContextPrototype,
+ UnaryCallable callable,
+ RetryingExecutorWithContext executor,
+ RequestMutator requestMutator) {
this.callContextPrototype = Preconditions.checkNotNull(callContextPrototype);
this.callable = Preconditions.checkNotNull(callable);
this.executor = Preconditions.checkNotNull(executor);
+ this.requestMutator = requestMutator;
}
@Override
public RetryingFuture futureCall(RequestT request, ApiCallContext inputContext) {
ApiCallContext context = callContextPrototype.nullToSelf(inputContext);
+ RequestT modifiedRequest = request;
+ if (this.requestMutator != null) {
+ modifiedRequest = requestMutator.apply(request);
+ }
AttemptCallable retryCallable =
- new AttemptCallable<>(callable, request, context);
+ new AttemptCallable<>(callable, modifiedRequest, context);
RetryingFuture retryingFuture = executor.createFuture(retryCallable, inputContext);
retryCallable.setExternalFuture(retryingFuture);
diff --git a/gax-java/gax/src/main/java/com/google/api/gax/rpc/StubSettings.java b/gax-java/gax/src/main/java/com/google/api/gax/rpc/StubSettings.java
index 7ebbe327c8..962443e5d2 100644
--- a/gax-java/gax/src/main/java/com/google/api/gax/rpc/StubSettings.java
+++ b/gax-java/gax/src/main/java/com/google/api/gax/rpc/StubSettings.java
@@ -32,6 +32,7 @@
import com.google.api.core.ApiClock;
import com.google.api.core.ApiFunction;
import com.google.api.core.BetaApi;
+import com.google.api.core.InternalApi;
import com.google.api.core.NanoClock;
import com.google.api.gax.core.CredentialsProvider;
import com.google.api.gax.core.ExecutorProvider;
@@ -70,6 +71,7 @@ public abstract class StubSettings> {
private final HeaderProvider internalHeaderProvider;
private final TransportChannelProvider transportChannelProvider;
private final ApiClock clock;
+ private final String serviceName;
private final String endpoint;
private final String mtlsEndpoint;
private final String quotaProjectId;
@@ -79,6 +81,7 @@ public abstract class StubSettings> {
@Nonnull private final ApiTracerFactory tracerFactory;
// Track if deprecated setExecutorProvider is called
private boolean deprecatedExecutorProviderSet;
+ private final String universeDomain;
/**
* Indicate when creating transport whether it is allowed to use mTLS endpoint instead of the
@@ -96,6 +99,7 @@ protected StubSettings(Builder builder) {
this.headerProvider = builder.headerProvider;
this.internalHeaderProvider = builder.internalHeaderProvider;
this.clock = builder.clock;
+ this.serviceName = builder.serviceName;
this.endpoint = builder.endpoint;
this.mtlsEndpoint = builder.mtlsEndpoint;
this.switchToMtlsEndpointAllowed = builder.switchToMtlsEndpointAllowed;
@@ -105,6 +109,7 @@ protected StubSettings(Builder builder) {
this.tracerFactory = builder.tracerFactory;
this.deprecatedExecutorProviderSet = builder.deprecatedExecutorProviderSet;
this.gdchApiAudience = builder.gdchApiAudience;
+ this.universeDomain = builder.universeDomain;
}
/** @deprecated Please use {@link #getBackgroundExecutorProvider()}. */
@@ -137,7 +142,29 @@ public final ApiClock getClock() {
return clock;
}
- public final String getEndpoint() {
+ // Intended for Internal Use and Overriden by generated ServiceStubSettings classes.
+ // Meant to be shared between StubSettings and ClientContext.
+ @InternalApi
+ public String getServiceName() {
+ return "";
+ }
+
+ public final String getUniverseDomain() {
+ return universeDomain;
+ }
+
+ public String getEndpoint() {
+ return endpoint;
+ }
+
+ /**
+ * This is an internal api meant to either return the user set endpoint or null. The difference
+ * between this method and {@link #getEndpoint()}} is that {@link #getEndpoint()} is reimplemented
+ * by the child class and will return the default service endpoint if the user did not set an
+ * endpoint (does not return null).
+ */
+ @InternalApi
+ String getUserSetEndpoint() {
return endpoint;
}
@@ -189,6 +216,7 @@ public String toString() {
.add("headerProvider", headerProvider)
.add("internalHeaderProvider", internalHeaderProvider)
.add("clock", clock)
+ .add("universeDomain", universeDomain)
.add("endpoint", endpoint)
.add("mtlsEndpoint", mtlsEndpoint)
.add("switchToMtlsEndpointAllowed", switchToMtlsEndpointAllowed)
@@ -211,6 +239,7 @@ public abstract static class Builder<
private HeaderProvider internalHeaderProvider;
private TransportChannelProvider transportChannelProvider;
private ApiClock clock;
+ private String serviceName;
private String endpoint;
private String mtlsEndpoint;
private String quotaProjectId;
@@ -219,6 +248,7 @@ public abstract static class Builder<
@Nonnull private Duration streamWatchdogCheckInterval;
@Nonnull private ApiTracerFactory tracerFactory;
private boolean deprecatedExecutorProviderSet;
+ private String universeDomain;
/**
* Indicate when creating transport whether it is allowed to use mTLS endpoint instead of the
@@ -236,6 +266,7 @@ protected Builder(StubSettings settings) {
this.headerProvider = settings.headerProvider;
this.internalHeaderProvider = settings.internalHeaderProvider;
this.clock = settings.clock;
+ this.serviceName = settings.serviceName;
this.endpoint = settings.endpoint;
this.mtlsEndpoint = settings.mtlsEndpoint;
this.switchToMtlsEndpointAllowed = settings.switchToMtlsEndpointAllowed;
@@ -245,6 +276,7 @@ protected Builder(StubSettings settings) {
this.tracerFactory = settings.tracerFactory;
this.deprecatedExecutorProviderSet = settings.deprecatedExecutorProviderSet;
this.gdchApiAudience = settings.gdchApiAudience;
+ this.universeDomain = settings.universeDomain;
}
/** Get Quota Project ID from Client Context * */
@@ -272,6 +304,7 @@ protected Builder(ClientContext clientContext) {
this.headerProvider = new NoHeaderProvider();
this.internalHeaderProvider = new NoHeaderProvider();
this.clock = NanoClock.getDefaultClock();
+ this.serviceName = null;
this.endpoint = null;
this.mtlsEndpoint = null;
this.quotaProjectId = null;
@@ -280,6 +313,7 @@ protected Builder(ClientContext clientContext) {
this.tracerFactory = BaseApiTracerFactory.getInstance();
this.deprecatedExecutorProviderSet = false;
this.gdchApiAudience = null;
+ this.universeDomain = null;
} else {
ExecutorProvider fixedExecutorProvider =
FixedExecutorProvider.create(clientContext.getExecutor());
@@ -292,6 +326,7 @@ protected Builder(ClientContext clientContext) {
this.internalHeaderProvider =
FixedHeaderProvider.create(clientContext.getInternalHeaders());
this.clock = clientContext.getClock();
+ this.serviceName = clientContext.getServiceName();
this.endpoint = clientContext.getEndpoint();
if (this.endpoint != null) {
this.mtlsEndpoint = this.endpoint.replace("googleapis.com", "mtls.googleapis.com");
@@ -302,6 +337,7 @@ protected Builder(ClientContext clientContext) {
this.tracerFactory = clientContext.getTracerFactory();
this.quotaProjectId = getQuotaProjectIdFromClientContext(clientContext);
this.gdchApiAudience = clientContext.getGdchApiAudience();
+ this.universeDomain = clientContext.getUniverseDomain();
}
}
@@ -414,6 +450,11 @@ public B setClock(ApiClock clock) {
return self();
}
+ public B setUniverseDomain(String universeDomain) {
+ this.universeDomain = universeDomain;
+ return self();
+ }
+
public B setEndpoint(String endpoint) {
this.endpoint = endpoint;
this.switchToMtlsEndpointAllowed = false;
@@ -563,6 +604,7 @@ public String toString() {
.add("headerProvider", headerProvider)
.add("internalHeaderProvider", internalHeaderProvider)
.add("clock", clock)
+ .add("universeDomain", universeDomain)
.add("endpoint", endpoint)
.add("mtlsEndpoint", mtlsEndpoint)
.add("switchToMtlsEndpointAllowed", switchToMtlsEndpointAllowed)
diff --git a/gax-java/gax/src/main/java/com/google/api/gax/rpc/TransportChannelProvider.java b/gax-java/gax/src/main/java/com/google/api/gax/rpc/TransportChannelProvider.java
index b2acd458f0..21f3c31f63 100644
--- a/gax-java/gax/src/main/java/com/google/api/gax/rpc/TransportChannelProvider.java
+++ b/gax-java/gax/src/main/java/com/google/api/gax/rpc/TransportChannelProvider.java
@@ -142,4 +142,13 @@ public interface TransportChannelProvider {
*
This string can be used for identifying transports for switching logic.
*/
String getTransportName();
+
+ /**
+ * User set custom endpoint for the Transport Channel Provider
+ *
+ *
This is the unresolved endpoint used by GAPICs
+ */
+ default String getEndpoint() {
+ return null;
+ }
}
diff --git a/gax-java/gax/src/main/java/com/google/api/gax/tracing/ApiTracer.java b/gax-java/gax/src/main/java/com/google/api/gax/tracing/ApiTracer.java
index 3176be4b92..6143772bac 100644
--- a/gax-java/gax/src/main/java/com/google/api/gax/tracing/ApiTracer.java
+++ b/gax-java/gax/src/main/java/com/google/api/gax/tracing/ApiTracer.java
@@ -49,19 +49,23 @@ public interface ApiTracer {
* between clients using gax and external resources to share the same implementation of the
* tracing. For example OpenCensus will install a thread local that can read by the GRPC.
*/
- Scope inScope();
+ default Scope inScope() {
+ return () -> {
+ // noop
+ };
+ };
/**
* Signals that the overall operation has finished successfully. The tracer is now considered
* closed and should no longer be used.
*/
- void operationSucceeded();
+ default void operationSucceeded() {};
/**
* Signals that the operation was cancelled by the user. The tracer is now considered closed and
* should no longer be used.
*/
- void operationCancelled();
+ default void operationCancelled() {};
/**
* Signals that the overall operation has failed and no further attempts will be made. The tracer
@@ -69,14 +73,14 @@ public interface ApiTracer {
*
* @param error the final error that caused the operation to fail.
*/
- void operationFailed(Throwable error);
+ default void operationFailed(Throwable error) {};
/**
* Annotates the operation with selected connection id from the {@code ChannelPool}.
*
* @param id the local connection identifier of the selected connection.
*/
- void connectionSelected(String id);
+ default void connectionSelected(String id) {};
/**
* Adds an annotation that an attempt is about to start. In general this should occur at the very
@@ -86,7 +90,7 @@ public interface ApiTracer {
* @deprecated Please use {@link #attemptStarted(Object, int)} instead.
*/
@Deprecated
- void attemptStarted(int attemptNumber);
+ default void attemptStarted(int attemptNumber) {};
/**
* Adds an annotation that an attempt is about to start with additional information from the
@@ -96,13 +100,13 @@ public interface ApiTracer {
* @param attemptNumber the zero based sequential attempt number.
* @param request request of this attempt.
*/
- void attemptStarted(Object request, int attemptNumber);
+ default void attemptStarted(Object request, int attemptNumber) {};
/** Adds an annotation that the attempt succeeded. */
- void attemptSucceeded();
+ default void attemptSucceeded() {};
/** Add an annotation that the attempt was cancelled by the user. */
- void attemptCancelled();
+ default void attemptCancelled() {};
/**
* Adds an annotation that the attempt failed, but another attempt will be made after the delay.
@@ -110,7 +114,7 @@ public interface ApiTracer {
* @param error the transient error that caused the attempt to fail.
* @param delay the amount of time to wait before the next attempt will start.
*/
- void attemptFailed(Throwable error, Duration delay);
+ default void attemptFailed(Throwable error, Duration delay) {};
/**
* Adds an annotation that the attempt failed and that no further attempts will be made because
@@ -118,7 +122,7 @@ public interface ApiTracer {
*
* @param error the last error received before retries were exhausted.
*/
- void attemptFailedRetriesExhausted(Throwable error);
+ default void attemptFailedRetriesExhausted(Throwable error) {};
/**
* Adds an annotation that the attempt failed and that no further attempts will be made because
@@ -126,26 +130,26 @@ public interface ApiTracer {
*
* @param error the error that caused the final attempt to fail.
*/
- void attemptPermanentFailure(Throwable error);
+ default void attemptPermanentFailure(Throwable error) {};
/**
* Signals that the initial RPC for the long running operation failed.
*
* @param error the error that caused the long running operation fail.
*/
- void lroStartFailed(Throwable error);
+ default void lroStartFailed(Throwable error) {};
/**
* Signals that the initial RPC successfully started the long running operation. The long running
* operation will now be polled for completion.
*/
- void lroStartSucceeded();
+ default void lroStartSucceeded() {};
/** Adds an annotation that a streaming response has been received. */
- void responseReceived();
+ default void responseReceived() {};
/** Adds an annotation that a streaming request has been sent. */
- void requestSent();
+ default void requestSent() {};
/**
* Adds an annotation that a batch of writes has been flushed.
@@ -153,7 +157,7 @@ public interface ApiTracer {
* @param elementCount the number of elements in the batch.
* @param requestSize the size of the batch in bytes.
*/
- void batchRequestSent(long elementCount, long requestSize);
+ default void batchRequestSent(long elementCount, long requestSize) {};
/**
* A context class to be used with {@link #inScope()} and a try-with-resources block. Closing a
diff --git a/gax-java/gax/src/main/java/com/google/api/gax/tracing/BaseApiTracer.java b/gax-java/gax/src/main/java/com/google/api/gax/tracing/BaseApiTracer.java
index 538708b879..1e542f124d 100644
--- a/gax-java/gax/src/main/java/com/google/api/gax/tracing/BaseApiTracer.java
+++ b/gax-java/gax/src/main/java/com/google/api/gax/tracing/BaseApiTracer.java
@@ -33,7 +33,10 @@
import org.threeten.bp.Duration;
/**
- * A base implementation of {@link ApiTracer} that does nothing.
+ * A base implementation of {@link ApiTracer} that does nothing. With the deprecation of Java 7
+ * support, all the methods in {@link ApiTracer} are now made default, we no longer need a base
+ * class that does nothing. This class should be removed once all the references to it are removed
+ * in Google Cloud Client Libraries.
*
*
For internal use only.
*/
diff --git a/gax-java/gax/src/main/java/com/google/api/gax/tracing/MethodName.java b/gax-java/gax/src/main/java/com/google/api/gax/tracing/MethodName.java
new file mode 100644
index 0000000000..1581c0ed38
--- /dev/null
+++ b/gax-java/gax/src/main/java/com/google/api/gax/tracing/MethodName.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.google.api.gax.tracing;
+
+import com.google.api.core.BetaApi;
+import com.google.api.core.InternalApi;
+import com.google.api.gax.rpc.StubSettings;
+import com.google.auto.value.AutoValue;
+
+/** A value class to represent the name of the RPC method in an {@link ApiTracer}. */
+@BetaApi
+@InternalApi
+@AutoValue
+public abstract class MethodName {
+ /**
+ * Creates a new instance of the RPC method name.
+ *
+ * @param serviceName The name of the service. In general this will be GAPIC generated service
+ * name {@link StubSettings#getServiceName()}. However, in some cases, when the GAPIC
+ * generated service is wrapped, this will be overridden to specify the manually written
+ * wrapper's name.
+ * @param methodName The name of the logical operation being traced.
+ */
+ public static MethodName of(String serviceName, String methodName) {
+ return new AutoValue_MethodName(serviceName, methodName);
+ }
+
+ /** The name of the service. ie BigtableData */
+ public abstract String getServiceName();
+
+ /** The name of the logical operation being traced. ie. ReadRows. */
+ public abstract String getMethodName();
+
+ @Override
+ public String toString() {
+ return getServiceName() + "." + getMethodName();
+ }
+}
diff --git a/gax-java/gax/src/main/java/com/google/api/gax/tracing/MetricsRecorder.java b/gax-java/gax/src/main/java/com/google/api/gax/tracing/MetricsRecorder.java
new file mode 100644
index 0000000000..d2e221fb5b
--- /dev/null
+++ b/gax-java/gax/src/main/java/com/google/api/gax/tracing/MetricsRecorder.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.google.api.gax.tracing;
+
+import com.google.api.core.BetaApi;
+import com.google.api.core.InternalApi;
+import java.util.Map;
+
+/**
+ * Provides an interface for metrics recording. The implementer is expected to use an observability
+ * framework, e.g. OpenTelemetry. There should be only one instance of MetricsRecorder per client,
+ * all the methods in this class are expected to be called from multiple threads, hence the
+ * implementation must be thread safe.
+ */
+@BetaApi
+@InternalApi
+public interface MetricsRecorder {
+
+ /** Records the latency of an RPC attempt */
+ default void recordAttemptLatency(double attemptLatency, Map attributes) {}
+
+ /** Records the count of RPC attempts */
+ default void recordAttemptCount(long count, Map attributes) {}
+
+ /**
+ * Records the total end-to-end latency for an operation, including the initial RPC attempts and
+ * subsequent retries.
+ */
+ default void recordOperationLatency(double operationLatency, Map attributes) {}
+
+ /** Records the count of operations */
+ default void recordOperationCount(long count, Map attributes) {}
+}
diff --git a/gax-java/gax/src/main/java/com/google/api/gax/tracing/MetricsTracer.java b/gax-java/gax/src/main/java/com/google/api/gax/tracing/MetricsTracer.java
new file mode 100644
index 0000000000..45a8558599
--- /dev/null
+++ b/gax-java/gax/src/main/java/com/google/api/gax/tracing/MetricsTracer.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.google.api.gax.tracing;
+
+import com.google.api.core.BetaApi;
+import com.google.api.core.InternalApi;
+import com.google.api.gax.rpc.ApiException;
+import com.google.api.gax.rpc.StatusCode;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Stopwatch;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.TimeUnit;
+import javax.annotation.Nullable;
+import org.threeten.bp.Duration;
+
+/**
+ * This class computes generic metrics that can be observed in the lifecycle of an RPC operation.
+ * The responsibility of recording metrics should delegate to {@link MetricsRecorder}, hence this
+ * class should not have any knowledge about the observability framework used for metrics recording.
+ */
+@BetaApi
+@InternalApi
+public class MetricsTracer implements ApiTracer {
+
+ private static final String STATUS_ATTRIBUTE = "status";
+
+ private Stopwatch attemptTimer;
+
+ private final Stopwatch operationTimer = Stopwatch.createStarted();
+
+ private final Map attributes = new HashMap<>();
+
+ private MetricsRecorder metricsRecorder;
+
+ public MetricsTracer(MethodName methodName, MetricsRecorder metricsRecorder) {
+ this.attributes.put("method_name", methodName.toString());
+ this.metricsRecorder = metricsRecorder;
+ }
+
+ /**
+ * Signals that the overall operation has finished successfully. The tracer is now considered
+ * closed and should no longer be used. Successful operation adds "OK" value to the status
+ * attribute key.
+ */
+ @Override
+ public void operationSucceeded() {
+ attributes.put(STATUS_ATTRIBUTE, StatusCode.Code.OK.toString());
+ metricsRecorder.recordOperationLatency(
+ operationTimer.elapsed(TimeUnit.MILLISECONDS), attributes);
+ metricsRecorder.recordOperationCount(1, attributes);
+ }
+
+ /**
+ * Signals that the operation was cancelled by the user. The tracer is now considered closed and
+ * should no longer be used. Cancelled operation adds "CANCELLED" value to the status attribute
+ * key.
+ */
+ @Override
+ public void operationCancelled() {
+ attributes.put(STATUS_ATTRIBUTE, StatusCode.Code.CANCELLED.toString());
+ metricsRecorder.recordOperationLatency(
+ operationTimer.elapsed(TimeUnit.MILLISECONDS), attributes);
+ metricsRecorder.recordOperationCount(1, attributes);
+ }
+
+ /**
+ * Signals that the operation was cancelled by the user. The tracer is now considered closed and
+ * should no longer be used. Failed operation extracts the error from the throwable and adds it to
+ * the status attribute key.
+ */
+ @Override
+ public void operationFailed(Throwable error) {
+ attributes.put(STATUS_ATTRIBUTE, extractStatus(error));
+ metricsRecorder.recordOperationLatency(
+ operationTimer.elapsed(TimeUnit.MILLISECONDS), attributes);
+ metricsRecorder.recordOperationCount(1, attributes);
+ }
+
+ /**
+ * Adds an annotation that an attempt is about to start with additional information from the
+ * request. In general this should occur at the very start of the operation. The attemptNumber is
+ * zero based. So the initial attempt will be 0. When the attempt starts, the attemptTimer starts
+ * the stopwatch.
+ *
+ * @param attemptNumber the zero based sequential attempt number.
+ * @param request request of this attempt.
+ */
+ @Override
+ public void attemptStarted(Object request, int attemptNumber) {
+ attemptTimer = Stopwatch.createStarted();
+ }
+
+ /**
+ * Adds an annotation that the attempt succeeded. Successful attempt add "OK" value to the status
+ * attribute key.
+ */
+ @Override
+ public void attemptSucceeded() {
+
+ attributes.put(STATUS_ATTRIBUTE, StatusCode.Code.OK.toString());
+ metricsRecorder.recordAttemptLatency(attemptTimer.elapsed(TimeUnit.MILLISECONDS), attributes);
+ metricsRecorder.recordAttemptCount(1, attributes);
+ }
+
+ /**
+ * Add an annotation that the attempt was cancelled by the user. Cancelled attempt add "CANCELLED"
+ * to the status attribute key.
+ */
+ @Override
+ public void attemptCancelled() {
+
+ attributes.put(STATUS_ATTRIBUTE, StatusCode.Code.CANCELLED.toString());
+ metricsRecorder.recordAttemptLatency(attemptTimer.elapsed(TimeUnit.MILLISECONDS), attributes);
+ metricsRecorder.recordAttemptCount(1, attributes);
+ }
+
+ /**
+ * Adds an annotation that the attempt failed, but another attempt will be made after the delay.
+ *
+ * @param error the error that caused the attempt to fail.
+ * @param delay the amount of time to wait before the next attempt will start.
+ *
Failed attempt extracts the error from the throwable and adds it to the status attribute
+ * key.
+ */
+ @Override
+ public void attemptFailed(Throwable error, Duration delay) {
+
+ attributes.put(STATUS_ATTRIBUTE, extractStatus(error));
+ metricsRecorder.recordAttemptLatency(attemptTimer.elapsed(TimeUnit.MILLISECONDS), attributes);
+ metricsRecorder.recordAttemptCount(1, attributes);
+ }
+
+ /**
+ * Adds an annotation that the attempt failed and that no further attempts will be made because
+ * retry limits have been reached. This extracts the error from the throwable and adds it to the
+ * status attribute key.
+ *
+ * @param error the last error received before retries were exhausted.
+ */
+ @Override
+ public void attemptFailedRetriesExhausted(Throwable error) {
+
+ attributes.put(STATUS_ATTRIBUTE, extractStatus(error));
+ metricsRecorder.recordAttemptLatency(attemptTimer.elapsed(TimeUnit.MILLISECONDS), attributes);
+ metricsRecorder.recordAttemptCount(1, attributes);
+ }
+
+ /**
+ * Adds an annotation that the attempt failed and that no further attempts will be made because
+ * the last error was not retryable. This extracts the error from the throwable and adds it to the
+ * status attribute key.
+ *
+ * @param error the error that caused the final attempt to fail.
+ */
+ @Override
+ public void attemptPermanentFailure(Throwable error) {
+
+ attributes.put(STATUS_ATTRIBUTE, extractStatus(error));
+ metricsRecorder.recordAttemptLatency(attemptTimer.elapsed(TimeUnit.MILLISECONDS), attributes);
+ metricsRecorder.recordAttemptCount(1, attributes);
+ }
+
+ /** Function to extract the status of the error as a string */
+ @VisibleForTesting
+ static String extractStatus(@Nullable Throwable error) {
+ final String statusString;
+
+ if (error == null) {
+ return StatusCode.Code.OK.toString();
+ } else if (error instanceof CancellationException) {
+ statusString = StatusCode.Code.CANCELLED.toString();
+ } else if (error instanceof ApiException) {
+ statusString = ((ApiException) error).getStatusCode().getCode().toString();
+ } else {
+ statusString = StatusCode.Code.UNKNOWN.toString();
+ }
+
+ return statusString;
+ }
+
+ /**
+ * Add attributes that will be attached to all metrics. This is expected to be called by
+ * handwritten client teams to add additional attributes that are not supposed be collected by
+ * Gax.
+ */
+ public void addAttributes(String key, String value) {
+ attributes.put(key, value);
+ };
+
+ @VisibleForTesting
+ Map getAttributes() {
+ return attributes;
+ }
+}
diff --git a/gax-java/gax/src/main/java/com/google/api/gax/tracing/MetricsTracerFactory.java b/gax-java/gax/src/main/java/com/google/api/gax/tracing/MetricsTracerFactory.java
new file mode 100644
index 0000000000..d2b8d87fb4
--- /dev/null
+++ b/gax-java/gax/src/main/java/com/google/api/gax/tracing/MetricsTracerFactory.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.google.api.gax.tracing;
+
+import com.google.api.core.BetaApi;
+import com.google.api.core.InternalApi;
+
+/**
+ * A {@link ApiTracerFactory} to build instances of {@link MetricsTracer}.
+ *
+ *
This class wraps the {@link MetricsRecorder} and pass it to {@link MetricsTracer}. It will be
+ * used to record metrics in {@link MetricsTracer}.
+ *
+ *
This class is expected to be initialized once during client initialization.
+ */
+@BetaApi
+@InternalApi
+public class MetricsTracerFactory implements ApiTracerFactory {
+ protected MetricsRecorder metricsRecorder;
+
+ public MetricsTracerFactory(MetricsRecorder metricsRecorder) {
+ this.metricsRecorder = metricsRecorder;
+ }
+
+ @Override
+ public ApiTracer newTracer(ApiTracer parent, SpanName spanName, OperationType operationType) {
+ return new MetricsTracer(
+ MethodName.of(spanName.getClientName(), spanName.getMethodName()), metricsRecorder);
+ }
+}
diff --git a/gax-java/gax/src/test/java/com/google/api/gax/batching/BatcherImplTest.java b/gax-java/gax/src/test/java/com/google/api/gax/batching/BatcherImplTest.java
index 846334e267..314fa81ae2 100644
--- a/gax-java/gax/src/test/java/com/google/api/gax/batching/BatcherImplTest.java
+++ b/gax-java/gax/src/test/java/com/google/api/gax/batching/BatcherImplTest.java
@@ -85,6 +85,8 @@
@RunWith(JUnit4.class)
public class BatcherImplTest {
+ private static final Logger logger = Logger.getLogger(BatcherImplTest.class.getName());
+
private static final ScheduledExecutorService EXECUTOR =
Executors.newSingleThreadScheduledExecutor();
@@ -871,6 +873,7 @@ public void testThrottlingBlocking() throws Exception {
public void run() {
batcherAddThreadHolder.add(Thread.currentThread());
batcher.add(1);
+ logger.info("Called batcher.add(1)");
}
});
@@ -885,20 +888,38 @@ public void run() {
} while (batcherAddThreadHolder.isEmpty()
|| batcherAddThreadHolder.get(0).getState() != Thread.State.WAITING);
+ long beforeGetCall = System.currentTimeMillis();
executor.submit(
() -> {
try {
Thread.sleep(throttledTime);
+ logger.info("Calling flowController.release");
flowController.release(1, 1);
+ logger.info("Called flowController.release");
} catch (InterruptedException e) {
}
});
try {
+ logger.info("Calling future.get(10 ms)");
future.get(10, TimeUnit.MILLISECONDS);
- assertWithMessage("adding elements to batcher should be blocked by FlowControlled").fail();
+ long afterGetCall = System.currentTimeMillis();
+ long actualWaitTimeMs = afterGetCall - beforeGetCall;
+
+ logger.info("future.get(10 ms) unexpectedly returned. Wait time: " + actualWaitTimeMs);
+ // In a flaky test troubleshooting
+ // (https://github.com/googleapis/sdk-platform-java/issues/1931), we observed that
+ // "future.get" method did not throw TimeoutException in this multithreaded test.
+ // It's because the thread calling "future.get" was not being run (i.e. in the wait queue of
+ // CPUs) in a timely manner.
+ // To avoid the flakiness, as long as the "future.get" does not return before the specified
+ // timeout, this assertion is considered as good.
+ assertWithMessage("adding elements to batcher should be blocked by FlowControlled")
+ .that(actualWaitTimeMs)
+ .isAtLeast(10);
} catch (TimeoutException e) {
// expected
+ logger.info("future.get(10 ms) timed out expectedly.");
}
try {
diff --git a/gax-java/gax/src/test/java/com/google/api/gax/longrunning/OperationTimedPollAlgorithmTest.java b/gax-java/gax/src/test/java/com/google/api/gax/longrunning/OperationTimedPollAlgorithmTest.java
new file mode 100644
index 0000000000..583afcc819
--- /dev/null
+++ b/gax-java/gax/src/test/java/com/google/api/gax/longrunning/OperationTimedPollAlgorithmTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.google.api.gax.longrunning;
+
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.api.gax.core.FakeApiClock;
+import com.google.api.gax.retrying.RetrySettings;
+import com.google.api.gax.retrying.TimedAttemptSettings;
+import com.google.api.gax.util.FakeLogHandler;
+import java.util.concurrent.CancellationException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.threeten.bp.Duration;
+
+public class OperationTimedPollAlgorithmTest {
+
+ private static final RetrySettings FAST_RETRY_SETTINGS =
+ RetrySettings.newBuilder()
+ .setInitialRetryDelay(Duration.ofMillis(1L))
+ .setRetryDelayMultiplier(1)
+ .setMaxRetryDelay(Duration.ofMillis(1L))
+ .setInitialRpcTimeout(Duration.ofMillis(1L))
+ .setMaxAttempts(0)
+ .setJittered(false)
+ .setRpcTimeoutMultiplier(1)
+ .setMaxRpcTimeout(Duration.ofMillis(1L))
+ .setTotalTimeout(Duration.ofMillis(5L))
+ .build();
+ private TimedAttemptSettings timedAttemptSettings;
+ private FakeApiClock clock;
+
+ private FakeLogHandler logHandler;
+
+ @Before
+ public void setUp() {
+ logHandler = new FakeLogHandler();
+ OperationTimedPollAlgorithm.LOGGER.addHandler(logHandler);
+ clock = new FakeApiClock(System.nanoTime());
+ timedAttemptSettings =
+ TimedAttemptSettings.newBuilder()
+ .setGlobalSettings(FAST_RETRY_SETTINGS)
+ .setRetryDelay(Duration.ofMillis(1l))
+ .setRpcTimeout(Duration.ofMillis(1l))
+ .setRandomizedRetryDelay(Duration.ofMillis(1l))
+ .setAttemptCount(0)
+ .setFirstAttemptStartTimeNanos(clock.nanoTime())
+ .build();
+ }
+
+ @After
+ public void tearDown() {
+ OperationTimedPollAlgorithm.LOGGER.removeHandler(logHandler);
+ // redundant null assignment for readability - a new log handler will be used
+ logHandler = null;
+ }
+
+ @Test
+ public void testAlgorithmThatShouldRetry_doesNotLogTimeoutHelpMessage() {
+ OperationTimedPollAlgorithm algorithm =
+ OperationTimedPollAlgorithm.create(FAST_RETRY_SETTINGS, clock);
+ try {
+ algorithm.shouldRetry(timedAttemptSettings);
+ } catch (CancellationException ex) {
+ fail("Unexpected unsuccessful shouldRetry()");
+ }
+ assertTrue(
+ logHandler.getAllMessages().stream()
+ .noneMatch(
+ entry -> entry.contains(OperationTimedPollAlgorithm.LRO_TROUBLESHOOTING_LINK)));
+ }
+
+ @Test
+ public void testAlgorithmThatShouldNotRetry_logsTimeoutHelpMessage() {
+ OperationTimedPollAlgorithm algorithm =
+ OperationTimedPollAlgorithm.create(FAST_RETRY_SETTINGS, clock);
+ clock.incrementNanoTime(1 * 1000 * 1000 * 1000); // force rpc timeout
+ assertThrows(CancellationException.class, () -> algorithm.shouldRetry(timedAttemptSettings));
+ assertTrue(
+ logHandler.getAllMessages().stream()
+ .anyMatch(
+ entry -> entry.contains(OperationTimedPollAlgorithm.LRO_TROUBLESHOOTING_LINK)));
+ }
+}
diff --git a/gax-java/gax/src/test/java/com/google/api/gax/retrying/ScheduledRetryingExecutorTest.java b/gax-java/gax/src/test/java/com/google/api/gax/retrying/ScheduledRetryingExecutorTest.java
index f4255b95a6..279bdd6ab7 100644
--- a/gax-java/gax/src/test/java/com/google/api/gax/retrying/ScheduledRetryingExecutorTest.java
+++ b/gax-java/gax/src/test/java/com/google/api/gax/retrying/ScheduledRetryingExecutorTest.java
@@ -288,6 +288,11 @@ public void testCancelOuterFutureAfterStart() throws Exception {
boolean res = future.cancel(false);
assertTrue(res);
assertFutureCancel(future);
+
+ // Verify that the cancelled future is traced. Every attempt increases the number
+ // of cancellation attempts from the tracer.
+ Mockito.verify(tracer, Mockito.times(executionsCount + 1)).attemptCancelled();
+
// Assert that future has at least been attempted once
// i.e. The future from executor.submit() has been run by the ScheduledExecutor
assertTrue(future.getAttemptSettings().getAttemptCount() > 0);
@@ -297,34 +302,6 @@ public void testCancelOuterFutureAfterStart() throws Exception {
localExecutor.awaitTermination(10, TimeUnit.SECONDS);
}
- @Test
- public void testCancelIsTraced() throws Exception {
- ScheduledExecutorService localExecutor = Executors.newSingleThreadScheduledExecutor();
- FailingCallable callable = new FailingCallable(4, "request", "SUCCESS", tracer);
- RetrySettings retrySettings =
- FAST_RETRY_SETTINGS
- .toBuilder()
- .setInitialRetryDelay(Duration.ofMillis(1_000L))
- .setMaxRetryDelay(Duration.ofMillis(1_000L))
- .setTotalTimeout(Duration.ofMillis(10_0000L))
- .build();
- RetryingExecutorWithContext executor =
- getRetryingExecutor(getAlgorithm(retrySettings, 0, null), localExecutor);
- RetryingFuture future =
- executor.createFuture(callable, FakeCallContext.createDefault().withTracer(tracer));
- callable.setExternalFuture(future);
- future.setAttemptFuture(executor.submit(future));
-
- Thread.sleep(30L);
-
- boolean res = future.cancel(false);
- assertTrue(res);
- assertFutureCancel(future);
-
- Mockito.verify(tracer).attemptCancelled();
- localExecutor.shutdownNow();
- }
-
@Test
public void testCancelProxiedFutureAfterStart() throws Exception {
// this is a heavy test, which takes a lot of time, so only few executions.
diff --git a/gax-java/gax/src/test/java/com/google/api/gax/rpc/CallableTest.java b/gax-java/gax/src/test/java/com/google/api/gax/rpc/CallableTest.java
index 04e025fecb..8d24a19c53 100644
--- a/gax-java/gax/src/test/java/com/google/api/gax/rpc/CallableTest.java
+++ b/gax-java/gax/src/test/java/com/google/api/gax/rpc/CallableTest.java
@@ -29,17 +29,20 @@
*/
package com.google.api.gax.rpc;
+import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import com.google.api.core.ApiFuture;
import com.google.api.core.SettableApiFuture;
import com.google.api.gax.retrying.RetrySettings;
import com.google.api.gax.rpc.testing.FakeCallContext;
import org.junit.Rule;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnit;
@@ -78,17 +81,28 @@ public void testNonRetriedCallable() throws Exception {
innerResult = SettableApiFuture.create();
when(innerCallable.futureCall(anyString(), any(ApiCallContext.class))).thenReturn(innerResult);
Duration timeout = Duration.ofMillis(5L);
+ String initialRequest = "Is your refrigerator running?";
+ String modifiedRequest = "What about now?";
+
+ RequestMutator requestMutator = (request -> modifiedRequest);
UnaryCallSettings