diff --git a/.github/.release-please.yml b/.github/.release-please.yml new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/.github/.release-please.yml @@ -0,0 +1 @@ + diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 54221045c..db2d8ad17 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1,13 @@ -* @googleapis/yoshi-java +# Code owners file. +# This file controls who is tagged for review for any given pull request. + +# For syntax help see: +# https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners#codeowners-syntax + +* @googleapis/yoshi-java + +# The java-samples-reviewers team is the default owner for samples changes +samples/**/*.java @googleapis/java-samples-reviewers + +# Generated snippets should not be owned by samples reviewers +samples/snippets/generated/ @googleapis/yoshi-java diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..fe674a052 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,51 @@ +--- +name: Bug report +about: Create a report to help us improve + +--- + +Thanks for stopping by to let us know something could be better! + +**PLEASE READ**: If you have a support contract with Google, please create an issue in the [support console](https://cloud.google.com/support/) instead of filing on GitHub. This will ensure a timely response. + +Please run down the following list and make sure you've tried the usual "quick fixes": + + - Search the issues already opened: https://github.com/googleapis/google-http-java-client/issues + - Check for answers on StackOverflow: http://stackoverflow.com/questions/tagged/google-cloud-platform + +If you are still having issues, please include as much information as possible: + +#### Environment details + +1. Specify the API at the beginning of the title. For example, "BigQuery: ..."). + General, Core, and Other are also allowed as types +2. OS type and version: +3. Java version: +4. version(s): + +#### Steps to reproduce + + 1. ? + 2. ? + +#### Code example + +```java +// example +``` + +#### Stack trace +``` +Any relevant stacktrace here. +``` + +#### External references such as API reference guides + +- ? + +#### Any additional information below + + +Following these steps guarantees the quickest resolution possible. + +Thanks! diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..754e30c68 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,21 @@ +--- +name: Feature request +about: Suggest an idea for this library + +--- + +Thanks for stopping by to let us know something could be better! + +**PLEASE READ**: If you have a support contract with Google, please create an issue in the [support console](https://cloud.google.com/support/) instead of filing on GitHub. This will ensure a timely response. + +**Is your feature request related to a problem? Please describe.** +What the problem is. Example: I'm always frustrated when [...] + +**Describe the solution you'd like** +What you want to happen. + +**Describe alternatives you've considered** +Any alternative solutions or features you've considered. + +**Additional context** +Any other context or screenshots about the feature request. diff --git a/.github/ISSUE_TEMPLATE/support_request.md b/.github/ISSUE_TEMPLATE/support_request.md new file mode 100644 index 000000000..995869032 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/support_request.md @@ -0,0 +1,7 @@ +--- +name: Support request +about: If you have a support contract with Google, please create an issue in the Google Cloud Support console. + +--- + +**PLEASE READ**: If you have a support contract with Google, please create an issue in the [support console](https://cloud.google.com/support/) instead of filing on GitHub. This will ensure a timely response. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 3af206e9c..a4b083ea1 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,4 +1,10 @@ -Fixes # (it's a good idea to open an issue first for discussion) - -- [ ] Tests pass +Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: +- [ ] Make sure to open an issue as a [bug/issue](https://github.com/googleapis/google-http-java-client/issues/new/choose) before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea +- [ ] Ensure the tests and linter pass +- [ ] Code coverage does not decrease (if any source code was changed) - [ ] Appropriate docs were updated (if necessary) + +Fixes # ☕️ + +If you write sample code, please follow the [samples format]( +https://github.com/GoogleCloudPlatform/java-docs-samples/blob/main/SAMPLE_FORMAT.md). diff --git a/.kokoro/release/drop.sh b/.github/auto-label.yaml old mode 100755 new mode 100644 similarity index 63% rename from .kokoro/release/drop.sh rename to .github/auto-label.yaml index abc381e5d..4caef688b --- a/.kokoro/release/drop.sh +++ b/.github/auto-label.yaml @@ -1,25 +1,15 @@ -#!/bin/bash -# Copyright 2018 Google Inc. +# Copyright 2021 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 +# 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 - -source $(dirname "$0")/common.sh - -pushd $(dirname "$0")/../../ - -setup_environment_secrets -create_settings_xml_file "settings.xml" - -mvn nexus-staging:drop --settings=settings.xml +requestsize: + enabled: true diff --git a/.github/blunderbuss.yml b/.github/blunderbuss.yml new file mode 100644 index 000000000..2176b0543 --- /dev/null +++ b/.github/blunderbuss.yml @@ -0,0 +1,7 @@ +# Configuration for the Blunderbuss GitHub app. For more info see +# https://github.com/googleapis/repo-automation-bots/tree/main/packages/blunderbuss +assign_prs_by: +- labels: + - samples + to: + - googleapis/java-samples-reviewers \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..203f9eacc --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,19 @@ +version: 2 +updates: + - package-ecosystem: "maven" + directory: "/" + schedule: + interval: "daily" + # Disable version updates for Maven dependencies + # we use renovate-bot as well as shared-dependencies BOM to update maven dependencies. + ignore: + - dependency-name: "*" + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "daily" + # Disable version updates for pip dependencies + # If a security vulnerability comes in, we will be notified about + # it via template in the synthtool repository. + ignore: + - dependency-name: "*" diff --git a/.github/release-please.yml b/.github/release-please.yml new file mode 100644 index 000000000..9f8d02d27 --- /dev/null +++ b/.github/release-please.yml @@ -0,0 +1,36 @@ +bumpMinorPreMajor: true +handleGHRelease: true +releaseType: java-yoshi +branches: + - bumpMinorPreMajor: true + handleGHRelease: true + releaseType: java-lts + branch: 1.39.2-sp + - bumpMinorPreMajor: true + handleGHRelease: true + releaseType: java-backport + branch: 1.40.x + - bumpMinorPreMajor: true + handleGHRelease: true + releaseType: java-backport + branch: 1.41.x + - bumpMinorPreMajor: true + handleGHRelease: true + releaseType: java-backport + branch: 1.42.x + - bumpMinorPreMajor: true + handleGHRelease: true + releaseType: java-backport + branch: 1.43.x + - bumpMinorPreMajor: true + handleGHRelease: true + releaseType: java-backport + branch: 1.44.x + - bumpMinorPreMajor: true + handleGHRelease: true + releaseType: java-backport + branch: 1.46.x + - bumpMinorPreMajor: true + handleGHRelease: true + releaseType: java-backport + branch: 1.47.x diff --git a/.github/release-trigger.yml b/.github/release-trigger.yml new file mode 100644 index 000000000..91997d697 --- /dev/null +++ b/.github/release-trigger.yml @@ -0,0 +1,2 @@ +enabled: true +multiScmName: google-http-java-client diff --git a/.github/snippet-bot.yml b/.github/snippet-bot.yml new file mode 100644 index 000000000..e69de29bb diff --git a/.github/sync-repo-settings.yaml b/.github/sync-repo-settings.yaml new file mode 100644 index 000000000..847471c38 --- /dev/null +++ b/.github/sync-repo-settings.yaml @@ -0,0 +1,134 @@ +rebaseMergeAllowed: false +squashMergeAllowed: true +mergeCommitAllowed: false +branchProtectionRules: + - pattern: main + isAdminEnforced: true + requiredApprovingReviewCount: 1 + requiresCodeOwnerReviews: true + requiresStrictStatusChecks: false + requiredStatusCheckContexts: + - units (8) + - units (11) + - windows + - dependencies (8) + - dependencies (11) + - clirr + - cla/google + - pattern: 1.39.2-sp + isAdminEnforced: true + requiredApprovingReviewCount: 1 + requiresCodeOwnerReviews: true + requiresStrictStatusChecks: false + requiredStatusCheckContexts: + - units (8) + - units (11) + - windows + - dependencies (8) + - dependencies (11) + - lint + - clirr + - cla/google + - pattern: 1.40.x + isAdminEnforced: true + requiredApprovingReviewCount: 1 + requiresCodeOwnerReviews: true + requiresStrictStatusChecks: false + requiredStatusCheckContexts: + - units (8) + - units (11) + - windows + - dependencies (8) + - dependencies (11) + - lint + - clirr + - cla/google + - pattern: 1.41.x + isAdminEnforced: true + requiredApprovingReviewCount: 1 + requiresCodeOwnerReviews: true + requiresStrictStatusChecks: false + requiredStatusCheckContexts: + - units (8) + - units (11) + - windows + - dependencies (8) + - dependencies (11) + - clirr + - cla/google + - pattern: 1.42.x + isAdminEnforced: true + requiredApprovingReviewCount: 1 + requiresCodeOwnerReviews: true + requiresStrictStatusChecks: false + requiredStatusCheckContexts: + - units (8) + - units (11) + - windows + - dependencies (8) + - dependencies (11) + - clirr + - cla/google + - pattern: 1.43.x + isAdminEnforced: true + requiredApprovingReviewCount: 1 + requiresCodeOwnerReviews: true + requiresStrictStatusChecks: false + requiredStatusCheckContexts: + - units (7) + - units (8) + - units (11) + - windows + - dependencies (8) + - dependencies (11) + - clirr + - cla/google + - pattern: 1.44.x + isAdminEnforced: true + requiredApprovingReviewCount: 1 + requiresCodeOwnerReviews: true + requiresStrictStatusChecks: false + requiredStatusCheckContexts: + - units (7) + - units (8) + - units (11) + - windows + - dependencies (8) + - dependencies (11) + - clirr + - cla/google + - pattern: 1.46.x + isAdminEnforced: true + requiredApprovingReviewCount: 1 + requiresCodeOwnerReviews: true + requiresStrictStatusChecks: false + requiredStatusCheckContexts: + - units (7) + - units (8) + - units (11) + - windows + - dependencies (8) + - dependencies (11) + - clirr + - cla/google + - pattern: 1.47.x + isAdminEnforced: true + requiredApprovingReviewCount: 1 + requiresCodeOwnerReviews: true + requiresStrictStatusChecks: false + requiredStatusCheckContexts: + - units (7) + - units (8) + - units (11) + - windows + - dependencies (8) + - dependencies (11) + - clirr + - cla/google +permissionRules: + - team: yoshi-admins + permission: admin + - team: yoshi-java-admins + permission: admin + - team: yoshi-java + permission: push diff --git a/.github/trusted-contribution.yml b/.github/trusted-contribution.yml new file mode 100644 index 000000000..88d3ac9bf --- /dev/null +++ b/.github/trusted-contribution.yml @@ -0,0 +1,9 @@ +trustedContributors: +- renovate-bot +- gcf-owl-bot[bot] + +annotations: +- type: comment + text: "/gcbrun" +- type: label + text: "kokoro:force-run" diff --git a/.github/workflows/approve-readme.yaml b/.github/workflows/approve-readme.yaml new file mode 100644 index 000000000..59f00b8eb --- /dev/null +++ b/.github/workflows/approve-readme.yaml @@ -0,0 +1,69 @@ +# 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. +# Github action job to test core java library features on +# downstream client libraries before they are released. +on: + pull_request: +name: auto-merge-readme +jobs: + approve: + runs-on: ubuntu-latest + if: github.repository_owner == 'googleapis' && github.head_ref == 'autosynth-readme' + steps: + - uses: actions/github-script@v7 + with: + github-token: ${{secrets.YOSHI_APPROVER_TOKEN}} + script: | + // only approve PRs from yoshi-automation + if (context.payload.pull_request.user.login !== "yoshi-automation") { + return; + } + + // only approve PRs like "chore: release " + if (!context.payload.pull_request.title === "chore: regenerate README") { + return; + } + + // only approve PRs with README.md and synth.metadata changes + const files = new Set( + ( + await github.paginate( + github.pulls.listFiles.endpoint({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number, + }) + ) + ).map(file => file.filename) + ); + if (files.size != 2 || !files.has("README.md") || !files.has(".github/readme/synth.metadata/synth.metadata")) { + return; + } + + // approve README regeneration PR + await github.pulls.createReview({ + owner: context.repo.owner, + repo: context.repo.repo, + body: 'Rubber stamped PR!', + pull_number: context.payload.pull_request.number, + event: 'APPROVE' + }); + + // attach automerge label + await github.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + labels: ['automerge'] + }); diff --git a/.github/workflows/auto-release.yaml b/.github/workflows/auto-release.yaml new file mode 100644 index 000000000..18d92e5a2 --- /dev/null +++ b/.github/workflows/auto-release.yaml @@ -0,0 +1,103 @@ +# 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. +# Github action job to test core java library features on +# downstream client libraries before they are released. +on: + pull_request: +name: auto-release +jobs: + approve: + runs-on: ubuntu-latest + if: contains(github.head_ref, 'release-please') + steps: + - uses: actions/github-script@v7 + with: + github-token: ${{secrets.YOSHI_APPROVER_TOKEN}} + debug: true + script: | + // only approve PRs from release-please[bot] + if (context.payload.pull_request.user.login !== "release-please[bot]") { + return; + } + + // only approve PRs like "chore(main): release " + if ( !context.payload.pull_request.title.startsWith("chore(main): release") ) { + return; + } + + // only approve PRs with pom.xml and versions.txt changes + const filesPromise = github.rest.pulls.listFiles.endpoint({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number, + }); + const changed_files = await github.paginate(filesPromise) + + if ( changed_files.length < 1 ) { + console.log( "Not proceeding since PR is empty!" ) + return; + } + + if ( !changed_files.some(v => v.filename.includes("pom")) || !changed_files.some(v => v.filename.includes("versions.txt")) ) { + console.log( "PR file changes do not have pom.xml or versions.txt -- something is wrong. PTAL!" ) + return; + } + + // trigger auto-release when + // 1) it is a SNAPSHOT release (auto-generated post regular release) + // 2) there are dependency updates only + // 3) there are no open dependency update PRs in this repo (to avoid multiple releases) + if ( + context.payload.pull_request.body.includes("Fix") || + context.payload.pull_request.body.includes("Build") || + context.payload.pull_request.body.includes("Documentation") || + context.payload.pull_request.body.includes("BREAKING CHANGES") || + context.payload.pull_request.body.includes("Features") + ) { + console.log( "Not auto-releasing since it is not a dependency-update-only release." ); + return; + } + + const promise = github.rest.pulls.list.endpoint({ + owner: context.repo.owner, + repo: context.repo.repo, + state: 'open' + }); + const open_pulls = await github.paginate(promise) + + if ( open_pulls.length > 1 && !context.payload.pull_request.title.includes("SNAPSHOT") ) { + for ( const pull of open_pulls ) { + if ( pull.title.startsWith("deps: update dependency") ) { + console.log( "Not auto-releasing yet since there are dependency update PRs open in this repo." ); + return; + } + } + } + + // approve release PR + await github.rest.pulls.createReview({ + owner: context.repo.owner, + repo: context.repo.repo, + body: 'Rubber stamped release!', + pull_number: context.payload.pull_request.number, + event: 'APPROVE' + }); + + // attach kokoro:force-run and automerge labels + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + labels: ['kokoro:force-run', 'automerge'] + }); diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 000000000..ed5494f3a --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,113 @@ +# 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. +# Github action job to test core java library features on +# downstream client libraries before they are released. +on: + push: + branches: + - main + pull_request: +name: ci +jobs: + units: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + java: [8, 11, 17, 25] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: ${{matrix.java}} + - run: java -version + - run: .kokoro/build.sh + env: + JOB_TYPE: test + windows: + runs-on: windows-latest + steps: + - name: Support longpaths + run: git config --system core.longpaths true + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 8 + - run: java -version + - run: .kokoro/build.bat + env: + JOB_TYPE: test + dependencies: + runs-on: ubuntu-latest + strategy: + matrix: + java: [8, 11, 17, 25] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: ${{matrix.java}} + - run: java -version + - run: .kokoro/dependencies.sh + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 11 + - run: java -version + - run: .kokoro/build.sh + env: + JOB_TYPE: lint + clirr: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 8 + - run: java -version + - run: .kokoro/build.sh + env: + JOB_TYPE: clirr + # compilation failure for sub-modules using source and target options 7 (this setting cannot be upgraded to Java 21 because some modules support max of Java 8) + # Hence compile in Java 8 and test in Java 21. + units-java21: + # Building using Java 8 and run the tests with Java 21 runtime + name: "units (21)" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + java-version: 21 + distribution: temurin + - name: "Set jvm system property environment variable for surefire plugin (unit tests)" + # Maven surefire plugin (unit tests) allows us to specify JVM to run the tests. + # https://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html#jvm + run: echo "SUREFIRE_JVM_OPT=-Djvm=${JAVA_HOME}/bin/java" >> $GITHUB_ENV + shell: bash + - uses: actions/setup-java@v4 + with: + java-version: 8 + distribution: temurin + - run: .kokoro/build.sh + env: + JOB_TYPE: test diff --git a/.github/workflows/downstream.yaml b/.github/workflows/downstream.yaml new file mode 100644 index 000000000..1f44d518d --- /dev/null +++ b/.github/workflows/downstream.yaml @@ -0,0 +1,144 @@ +on: + pull_request: + types: [ labeled ] + branches: + - main +name: downstream +jobs: + dependencies: + if: ${{ github.event.label.name == 'downstream-check:run' }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + java: [8] + repo: + # This list needs to be updated manually until an automated solution is in place. + - accessapproval + - accesscontextmanager + - aiplatform + - analytics-admin + - analytics-data + - api-gateway + - apigee-connect + - appengine-admin + - area120-tables + - artifact-registry + - asset + - assured-workloads + - automl + - bigquery + - bigqueryconnection + - bigquerydatatransfer + - bigquerymigration + - bigqueryreservation + - bigtable + - billing + - billingbudgets + - binary-authorization + - channel + - cloudbuild + - compute + - contact-center-insights + - container + - containeranalysis + - data-fusion + - datacatalog + - dataflow + - datalabeling + - dataproc + - dataproc-metastore + - datastore + - datastream + - debugger-client + - deploy + - dialogflow + - dialogflow-cx + - dlp + - dms + - dns + - document-ai + - domains + - errorreporting + - essential-contacts + - eventarc + - filestore + - firestore + - functions + - game-servers + - gke-connect-gateway + - gkehub + - gsuite-addons + - iam-admin + - iamcredentials + - iot + - kms + - language + - life-sciences + - logging + - logging-logback + - managed-identities + - mediatranslation + - memcache + - monitoring + - monitoring-dashboards + - network-management + - network-security + - networkconnectivity + - notebooks + - orchestration-airflow + - orgpolicy + - os-config + - os-login + - phishingprotection + - policy-troubleshooter + - private-catalog + - profiler + - pubsublite + - recaptchaenterprise + - recommendations-ai + - recommender + - redis + - resource-settings + - resourcemanager + - retail + - scheduler + - secretmanager + - security-private-ca + - securitycenter + - securitycenter-settings + - service-control + - service-management + - service-usage + - servicedirectory + - shell + - spanner + - spanner-jdbc + - speech + - storage + - storage-nio + - storage-transfer + - talent + - tasks + - texttospeech + - tpu + - trace + - translate + - video-intelligence + - video-transcoder + - vision + - vpcaccess + - webrisk + - websecurityscanner + - workflow-executions + - workflows + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + distribution: zulu + java-version: ${{matrix.java}} + - run: java -version + - run: sudo apt-get update -y + - run: sudo apt-get install libxml2-utils + - run: .kokoro/downstream-client-library-check.sh google-http-client-bom ${{matrix.repo}} diff --git a/.github/workflows/renovate_config_check.yaml b/.github/workflows/renovate_config_check.yaml new file mode 100644 index 000000000..36da117bc --- /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: + renovate_bot_config_validation: + runs-on: ubuntu-24.04 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install Renovate and Config Validator + run: | + npm install -g npm@latest + npm install --global renovate + renovate-config-validator diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml new file mode 100644 index 000000000..8f729e922 --- /dev/null +++ b/.github/workflows/scorecard.yml @@ -0,0 +1,72 @@ +# This workflow uses actions that are not certified by GitHub. They are provided +# by a third-party and are governed by separate terms of service, privacy +# policy, and support documentation. + +name: Scorecard supply-chain security +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + # To guarantee Maintained check is occasionally updated. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained + schedule: + - cron: '15 0 * * 4' + push: + branches: [ "main" ] + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + # Uncomment the permissions below if installing in a private repository. + # contents: read + # actions: read + + steps: + - name: "Checkout code" + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0 + with: + results_file: results.sarif + results_format: sarif + # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: + # - you want to enable the Branch-Protection check on a *public* repository, or + # - you are installing Scorecard on a *private* repository + # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. + # repo_token: ${{ secrets.SCORECARD_TOKEN }} + + # Public repositories: + # - Publish results to OpenSSF REST API for easy access by consumers + # - Allows the repository to include the Scorecard badge. + # - See https://github.com/ossf/scorecard-action#publishing-results. + # For private repositories: + # - `publish_results` will always be set to `false`, regardless + # of the value entered here. + publish_results: true + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard. + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16 + with: + sarif_file: results.sarif diff --git a/.gitignore b/.gitignore index 92c4b987a..b7c3b9ec7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,31 @@ -target/ -bin/ +.gitignore + +# Packages +dist +bin +var +sdist +target + +# Unit test / coverage reports +.coverage +.tox +nosetests.xml + +# Translations +*.mo + +# Mr Developer +.mr.developer.cfg + +**/.project +.pydevproject *.iml .idea +.settings +.DS_Store +**/.classpath +**/.checkstyle + +# Python utilities +*.pyc diff --git a/.kokoro/build.bat b/.kokoro/build.bat new file mode 100644 index 000000000..067cf4a4c --- /dev/null +++ b/.kokoro/build.bat @@ -0,0 +1,18 @@ +:: 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. +:: Github action job to test core java library features on +:: downstream client libraries before they are released. +:: See documentation in type-shell-output.bat + +"C:\Program Files\Git\bin\bash.exe" %~dp0build.sh diff --git a/.kokoro/build.sh b/.kokoro/build.sh index ac85fb805..8fc8df860 100755 --- a/.kokoro/build.sh +++ b/.kokoro/build.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright 2018 Google Inc. +# 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. @@ -15,13 +15,115 @@ set -eo pipefail -cd github/google-http-java-client/ +## Get the directory of the build script +scriptDir=$(realpath $(dirname "${BASH_SOURCE[0]}")) +## cd to the parent directory, i.e. the root of the git repo +cd ${scriptDir}/.. -# Print out Java -java -version -echo $JOB_TYPE +# include common functions +source ${scriptDir}/common.sh -export MAVEN_OPTS="-Xmx1024m -XX:MaxPermSize=128m" +# Print out Maven & Java version +mvn -version +echo ${JOB_TYPE} -mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -mvn test -B +# attempt to install 3 times with exponential backoff (starting with 10 seconds) +retry_with_backoff 3 10 \ + mvn install -B -V -ntp \ + -DskipTests=true \ + -Dclirr.skip=true \ + -Denforcer.skip=true \ + -Dmaven.javadoc.skip=true \ + -Dgcloud.download.skip=true \ + -T 1C + +# if GOOGLE_APPLICATION_CREDENTIALS is specified as a relative path, prepend Kokoro root directory onto it +if [[ ! -z "${GOOGLE_APPLICATION_CREDENTIALS}" && "${GOOGLE_APPLICATION_CREDENTIALS}" != /* ]]; then + export GOOGLE_APPLICATION_CREDENTIALS=$(realpath ${KOKORO_GFILE_DIR}/${GOOGLE_APPLICATION_CREDENTIALS}) +fi + +RETURN_CODE=0 +set +e + +case ${JOB_TYPE} in +test) + mvn test -B -ntp -Dclirr.skip=true -Denforcer.skip=true + RETURN_CODE=$? + ;; +lint) + mvn com.coveo:fmt-maven-plugin:check -B -ntp + RETURN_CODE=$? + ;; +javadoc) + mvn javadoc:javadoc javadoc:test-javadoc -B -ntp + RETURN_CODE=$? + ;; +integration) + mvn -B ${INTEGRATION_TEST_ARGS} \ + -ntp \ + -Penable-integration-tests \ + -DtrimStackTrace=false \ + -Dclirr.skip=true \ + -Denforcer.skip=true \ + -fae \ + verify + RETURN_CODE=$? + ;; +graalvm) + # Run Unit and Integration Tests with Native Image + mvn -B ${INTEGRATION_TEST_ARGS} -ntp -Pnative,native-tests,native-deps test -pl '!google-http-client-appengine' + RETURN_CODE=$? + ;; +samples) + SAMPLES_DIR=samples + # only run ITs in snapshot/ on presubmit PRs. run ITs in all 3 samples/ subdirectories otherwise. + if [[ ! -z ${KOKORO_GITHUB_PULL_REQUEST_NUMBER} ]] + then + SAMPLES_DIR=samples/snapshot + fi + + if [[ -f ${SAMPLES_DIR}/pom.xml ]] + then + for FILE in ${KOKORO_GFILE_DIR}/secret_manager/*-samples-secrets; do + [[ -f "$FILE" ]] || continue + source "$FILE" + done + + pushd ${SAMPLES_DIR} + mvn -B \ + -ntp \ + -DtrimStackTrace=false \ + -Dclirr.skip=true \ + -Denforcer.skip=true \ + -fae \ + verify + RETURN_CODE=$? + popd + else + echo "no sample pom.xml found - skipping sample tests" + fi + ;; +clirr) + mvn -B -ntp -Denforcer.skip=true clirr:check + RETURN_CODE=$? + ;; +*) + ;; +esac + +if [ "${REPORT_COVERAGE}" == "true" ] +then + bash ${KOKORO_GFILE_DIR}/codecov.sh +fi + +# fix output location of logs +bash .kokoro/coerce_logs.sh + +if [[ "${ENABLE_FLAKYBOT}" == "true" ]] +then + chmod +x ${KOKORO_GFILE_DIR}/linux_amd64/flakybot + ${KOKORO_GFILE_DIR}/linux_amd64/flakybot -repo=googleapis/google-http-java-client +fi + +echo "exiting with ${RETURN_CODE}" +exit ${RETURN_CODE} diff --git a/.kokoro/coerce_logs.sh b/.kokoro/coerce_logs.sh new file mode 100755 index 000000000..46edbf7f2 --- /dev/null +++ b/.kokoro/coerce_logs.sh @@ -0,0 +1,37 @@ +#!/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. + +# This script finds and moves sponge logs so that they can be found by placer +# and are not flagged as flaky by sponge. + +set -eo pipefail + +## Get the directory of the build script +scriptDir=$(realpath $(dirname "${BASH_SOURCE[0]}")) +## cd to the parent directory, i.e. the root of the git repo +cd ${scriptDir}/.. + +job=$(basename ${KOKORO_JOB_NAME}) + +echo "coercing sponge logs..." +for xml in `find . -name *-sponge_log.xml` +do + class=$(basename ${xml} | cut -d- -f2) + dir=$(dirname ${xml})/${job}/${class} + text=$(dirname ${xml})/${class}-sponge_log.txt + mkdir -p ${dir} + mv ${xml} ${dir}/sponge_log.xml + mv ${text} ${dir}/sponge_log.txt +done diff --git a/.kokoro/common.cfg b/.kokoro/common.cfg index 6687c6bde..8399354bf 100644 --- a/.kokoro/common.cfg +++ b/.kokoro/common.cfg @@ -8,6 +8,6 @@ build_file: "google-http-java-client/.kokoro/trampoline.sh" # Tell the trampoline which build file to use. env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/google-http-java-client/.kokoro/build.sh" + key: "TRAMPOLINE_BUILD_FILE" + value: "github/google-http-java-client/.kokoro/build.sh" } diff --git a/.kokoro/common.sh b/.kokoro/common.sh new file mode 100644 index 000000000..f8f957af1 --- /dev/null +++ b/.kokoro/common.sh @@ -0,0 +1,60 @@ +#!/bin/bash +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +function retry_with_backoff { + attempts_left=$1 + sleep_seconds=$2 + shift 2 + command=$@ + + + # store current flag state + flags=$- + + # allow a failures to continue + set +e + ${command} + exit_code=$? + + # restore "e" flag + if [[ ${flags} =~ e ]] + then set -e + else set +e + fi + + if [[ $exit_code == 0 ]] + then + return 0 + fi + + # failure + if [[ ${attempts_left} > 0 ]] + then + echo "failure (${exit_code}), sleeping ${sleep_seconds}..." + sleep ${sleep_seconds} + new_attempts=$((${attempts_left} - 1)) + new_sleep=$((${sleep_seconds} * 2)) + retry_with_backoff ${new_attempts} ${new_sleep} ${command} + fi + + return $exit_code +} + +## Helper functionss +function now() { date +"%Y-%m-%d %H:%M:%S" | tr -d '\n'; } +function msg() { println "$*" >&2; } +function println() { printf '%s\n' "$(now) $*"; } + +## Helper comment to trigger updated repo dependency release \ No newline at end of file diff --git a/.kokoro/continuous/common.cfg b/.kokoro/continuous/common.cfg index a5178e08c..5ba7070f2 100644 --- a/.kokoro/continuous/common.cfg +++ b/.kokoro/continuous/common.cfg @@ -4,6 +4,7 @@ action { define_artifacts { regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.txt" } } diff --git a/.kokoro/continuous/java8.cfg b/.kokoro/continuous/java8.cfg index 3b017fc80..495cc7bac 100644 --- a/.kokoro/continuous/java8.cfg +++ b/.kokoro/continuous/java8.cfg @@ -5,3 +5,8 @@ env_vars: { key: "TRAMPOLINE_IMAGE" value: "gcr.io/cloud-devrel-kokoro-resources/java8" } + +env_vars: { + key: "REPORT_COVERAGE" + value: "true" +} diff --git a/.kokoro/continuous/propose_release.sh b/.kokoro/continuous/propose_release.sh new file mode 100755 index 000000000..062063eb4 --- /dev/null +++ b/.kokoro/continuous/propose_release.sh @@ -0,0 +1,29 @@ +#!/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 +# +# https://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 + +export NPM_CONFIG_PREFIX=/home/node/.npm-global + +if [ -f ${KOKORO_KEYSTORE_DIR}/73713_github-magic-proxy-url-release-please ]; then + # Groom the release PR as new commits are merged. + npx release-please release-pr --token=${KOKORO_KEYSTORE_DIR}/73713_github-magic-proxy-token-release-please \ + --repo-url=googleapis/google-http-java-client \ + --package-name="google-http-client" \ + --api-url=${KOKORO_KEYSTORE_DIR}/73713_github-magic-proxy-url-release-please \ + --proxy-key=${KOKORO_KEYSTORE_DIR}/73713_github-magic-proxy-key-release-please \ + --release-type=java-yoshi +fi diff --git a/.kokoro/dependencies.sh b/.kokoro/dependencies.sh new file mode 100755 index 000000000..a66c44c5e --- /dev/null +++ b/.kokoro/dependencies.sh @@ -0,0 +1,62 @@ +#!/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 +shopt -s nullglob + +## Get the directory of the build script +scriptDir=$(realpath $(dirname "${BASH_SOURCE[0]}")) +## cd to the parent directory, i.e. the root of the git repo +cd ${scriptDir}/.. + +# include common functions +source ${scriptDir}/common.sh + +# Print out Java +java -version +echo $JOB_TYPE + +function determineMavenOpts() { + local javaVersion=$( + # filter down to the version line, then pull out the version between quotes, + # then trim the version number down to its minimal number (removing any + # update or suffix number). + java -version 2>&1 | grep "version" \ + | sed -E 's/^.*"(.*?)".*$/\1/g' \ + | sed -E 's/^(1\.[0-9]\.0).*$/\1/g' + ) + + # Extract the major version for simple comparison + local javaMajorVersion=$(echo $javaVersion | cut -d'.' -f1) + + if [[ $javaMajorVersion -ge 17 ]] + then + # MaxPermSize is no longer supported as of jdk 17 + echo -n "-Xmx1024m" + else + echo -n "-Xmx1024m -XX:MaxPermSize=128m" + fi +} + +export MAVEN_OPTS=$(determineMavenOpts) + +# this should run maven enforcer +retry_with_backoff 3 10 \ + mvn install -B -V -ntp \ + -DskipTests=true \ + -Dmaven.javadoc.skip=true \ + -Dclirr.skip=true + +mvn -B dependency:analyze -DfailOnWarning=true diff --git a/.kokoro/downstream-client-library-check.sh b/.kokoro/downstream-client-library-check.sh new file mode 100755 index 000000000..852db9e21 --- /dev/null +++ b/.kokoro/downstream-client-library-check.sh @@ -0,0 +1,104 @@ +#!/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 +# Display commands being run. +set -x + + +CORE_LIBRARY_ARTIFACT=$1 +CLIENT_LIBRARY=$2 +## Get the directory of the build script +scriptDir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")" +## cd to the parent directory, i.e. the root of the git repo +cd "${scriptDir}"/.. + +# Make java core library artifacts available for 'mvn verify' at the bottom +mvn verify install -B -V -ntp -fae \ +-DskipTests=true \ +-Dmaven.javadoc.skip=true \ +-Dgcloud.download.skip=true \ +-Denforcer.skip=true + +# Read the current version of this java core library in the POM. Example version: '0.116.1-alpha-SNAPSHOT' +CORE_VERSION_POM=pom.xml +# Namespace (xmlns) prevents xmllint from specifying tag names in XPath +CORE_VERSION=$(sed -e 's/xmlns=".*"//' ${CORE_VERSION_POM} | xmllint --xpath '/project/version/text()' -) + +if [ -z "${CORE_VERSION}" ]; then + echo "Version is not found in ${CORE_VERSION_POM}" + exit 1 +fi +echo "Version: ${CORE_VERSION}" + +# Round 1 +# Check this java core library against HEAD of java-shared dependencies + +git clone "https://github.com/googleapis/java-shared-dependencies.git" --depth=1 +pushd java-shared-dependencies/first-party-dependencies + +# replace version +xmllint --shell <(cat pom.xml) << EOF +setns x=http://maven.apache.org/POM/4.0.0 +cd .//x:artifactId[text()="${CORE_LIBRARY_ARTIFACT}"] +cd ../x:version +set ${CORE_VERSION} +save pom.xml +EOF + +# run dependencies script +cd .. +mvn verify install -B -V -ntp -fae \ +-DskipTests=true \ +-Dmaven.javadoc.skip=true \ +-Dgcloud.download.skip=true \ +-Denforcer.skip=true + +SHARED_DEPS_VERSION_POM=pom.xml +# Namespace (xmlns) prevents xmllint from specifying tag names in XPath +SHARED_DEPS_VERSION=$(sed -e 's/xmlns=".*"//' ${SHARED_DEPS_VERSION_POM} | xmllint --xpath '/project/version/text()' -) + +if [ -z "${SHARED_DEPS_VERSION}" ]; then + echo "Version is not found in ${SHARED_DEPS_VERSION_POM}" + exit 1 +fi + +# Round 2 + +# Check this BOM against java client libraries +git clone "https://github.com/googleapis/java-${CLIENT_LIBRARY}.git" --depth=1 +pushd java-"${CLIENT_LIBRARY}" + +if [[ $CLIENT_LIBRARY == "bigtable" ]]; then + pushd google-cloud-bigtable-deps-bom +fi + +# replace version +xmllint --shell <(cat pom.xml) << EOF +setns x=http://maven.apache.org/POM/4.0.0 +cd .//x:artifactId[text()="google-cloud-shared-dependencies"] +cd ../x:version +set ${SHARED_DEPS_VERSION} +save pom.xml +EOF + +if [[ $CLIENT_LIBRARY == "bigtable" ]]; then + popd +fi + +mvn verify install -B -V -ntp -fae \ +-Dmaven.javadoc.skip=true \ +-Dgcloud.download.skip=true \ +-Denforcer.skip=true diff --git a/.kokoro/nightly/common.cfg b/.kokoro/nightly/common.cfg new file mode 100644 index 000000000..5ba7070f2 --- /dev/null +++ b/.kokoro/nightly/common.cfg @@ -0,0 +1,25 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Build logs will be here +action { + define_artifacts { + regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.txt" + } +} + +# Download trampoline resources. +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" + +# Use the trampoline script to run in docker. +build_file: "google-http-java-client/.kokoro/trampoline.sh" + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/google-http-java-client/.kokoro/build.sh" +} + +env_vars: { + key: "JOB_TYPE" + value: "test" +} diff --git a/.kokoro/nightly/integration.cfg b/.kokoro/nightly/integration.cfg new file mode 100644 index 000000000..5a95c6828 --- /dev/null +++ b/.kokoro/nightly/integration.cfg @@ -0,0 +1,38 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Configure the docker image for kokoro-trampoline. +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/java8" +} + +env_vars: { + key: "JOB_TYPE" + value: "integration" +} +# TODO: remove this after we've migrated all tests and scripts +env_vars: { + key: "GCLOUD_PROJECT" + value: "java-docs-samples-testing" +} + +env_vars: { + key: "GOOGLE_CLOUD_PROJECT" + value: "java-docs-samples-testing" +} + +env_vars: { + key: "ENABLE_FLAKYBOT" + value: "true" +} + +env_vars: { + key: "GOOGLE_APPLICATION_CREDENTIALS" + value: "secret_manager/java-it-service-account" +} + +env_vars: { + key: "SECRET_MANAGER_KEYS" + value: "java-it-service-account" +} + diff --git a/.kokoro/nightly/java11-integration.cfg b/.kokoro/nightly/java11-integration.cfg new file mode 100644 index 000000000..6a6ef94ef --- /dev/null +++ b/.kokoro/nightly/java11-integration.cfg @@ -0,0 +1,38 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Configure the docker image for kokoro-trampoline. +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-public-resources/java11014" +} + +env_vars: { + key: "JOB_TYPE" + value: "integration" +} +# TODO: remove this after we've migrated all tests and scripts +env_vars: { + key: "GCLOUD_PROJECT" + value: "gcloud-devel" +} + +env_vars: { + key: "GOOGLE_CLOUD_PROJECT" + value: "gcloud-devel" +} + +env_vars: { + key: "ENABLE_FLAKYBOT" + value: "true" +} + +env_vars: { + key: "GOOGLE_APPLICATION_CREDENTIALS" + value: "secret_manager/java-it-service-account" +} + +env_vars: { + key: "SECRET_MANAGER_KEYS" + value: "java-it-service-account" +} + diff --git a/.kokoro/continuous/java11.cfg b/.kokoro/nightly/java11.cfg similarity index 100% rename from .kokoro/continuous/java11.cfg rename to .kokoro/nightly/java11.cfg diff --git a/.kokoro/continuous/java7.cfg b/.kokoro/nightly/java7.cfg similarity index 100% rename from .kokoro/continuous/java7.cfg rename to .kokoro/nightly/java7.cfg diff --git a/.kokoro/nightly/java8-osx.cfg b/.kokoro/nightly/java8-osx.cfg new file mode 100644 index 000000000..b2354168e --- /dev/null +++ b/.kokoro/nightly/java8-osx.cfg @@ -0,0 +1,3 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +build_file: "google-http-java-client/.kokoro/build.sh" diff --git a/.kokoro/nightly/java8-win.cfg b/.kokoro/nightly/java8-win.cfg new file mode 100644 index 000000000..b44537d03 --- /dev/null +++ b/.kokoro/nightly/java8-win.cfg @@ -0,0 +1,3 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +build_file: "google-http-java-client/.kokoro/build.bat" diff --git a/.kokoro/nightly/java8.cfg b/.kokoro/nightly/java8.cfg new file mode 100644 index 000000000..495cc7bac --- /dev/null +++ b/.kokoro/nightly/java8.cfg @@ -0,0 +1,12 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Configure the docker image for kokoro-trampoline. +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/java8" +} + +env_vars: { + key: "REPORT_COVERAGE" + value: "true" +} diff --git a/.kokoro/nightly/samples.cfg b/.kokoro/nightly/samples.cfg new file mode 100644 index 000000000..9761fd864 --- /dev/null +++ b/.kokoro/nightly/samples.cfg @@ -0,0 +1,38 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Configure the docker image for kokoro-trampoline. +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/java8" +} + +env_vars: { + key: "JOB_TYPE" + value: "samples" +} + +# TODO: remove this after we've migrated all tests and scripts +env_vars: { + key: "GCLOUD_PROJECT" + value: "java-docs-samples-testing" +} + +env_vars: { + key: "GOOGLE_CLOUD_PROJECT" + value: "java-docs-samples-testing" +} + +env_vars: { + key: "GOOGLE_APPLICATION_CREDENTIALS" + value: "secret_manager/java-docs-samples-service-account" +} + +env_vars: { + key: "SECRET_MANAGER_KEYS" + value: "java-docs-samples-service-account" +} + +env_vars: { + key: "ENABLE_FLAKYBOT" + value: "true" +} diff --git a/.kokoro/populate-secrets.sh b/.kokoro/populate-secrets.sh new file mode 100755 index 000000000..f52514257 --- /dev/null +++ b/.kokoro/populate-secrets.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# Copyright 2020 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -eo pipefail + +function now { date +"%Y-%m-%d %H:%M:%S" | tr -d '\n' ;} +function msg { println "$*" >&2 ;} +function println { printf '%s\n' "$(now) $*" ;} + + +# Populates requested secrets set in SECRET_MANAGER_KEYS from service account: +# kokoro-trampoline@cloud-devrel-kokoro-resources.iam.gserviceaccount.com +SECRET_LOCATION="${KOKORO_GFILE_DIR}/secret_manager" +msg "Creating folder on disk for secrets: ${SECRET_LOCATION}" +mkdir -p ${SECRET_LOCATION} +for key in $(echo ${SECRET_MANAGER_KEYS} | sed "s/,/ /g") +do + msg "Retrieving secret ${key}" + docker run --entrypoint=gcloud \ + --volume=${KOKORO_GFILE_DIR}:${KOKORO_GFILE_DIR} \ + gcr.io/google.com/cloudsdktool/cloud-sdk \ + secrets versions access latest \ + --project cloud-devrel-kokoro-resources \ + --secret ${key} > \ + "${SECRET_LOCATION}/${key}" + if [[ $? == 0 ]]; then + msg "Secret written to ${SECRET_LOCATION}/${key}" + else + msg "Error retrieving secret ${key}" + fi +done diff --git a/.kokoro/presubmit/clirr.cfg b/.kokoro/presubmit/clirr.cfg new file mode 100644 index 000000000..ec572442e --- /dev/null +++ b/.kokoro/presubmit/clirr.cfg @@ -0,0 +1,13 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Configure the docker image for kokoro-trampoline. + +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/java8" +} + +env_vars: { + key: "JOB_TYPE" + value: "clirr" +} \ No newline at end of file diff --git a/.kokoro/presubmit/common.cfg b/.kokoro/presubmit/common.cfg index a5178e08c..5ba7070f2 100644 --- a/.kokoro/presubmit/common.cfg +++ b/.kokoro/presubmit/common.cfg @@ -4,6 +4,7 @@ action { define_artifacts { regex: "**/*sponge_log.xml" + regex: "**/*sponge_log.txt" } } diff --git a/.kokoro/presubmit/dependencies.cfg b/.kokoro/presubmit/dependencies.cfg new file mode 100644 index 000000000..b89a20740 --- /dev/null +++ b/.kokoro/presubmit/dependencies.cfg @@ -0,0 +1,12 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Configure the docker image for kokoro-trampoline. +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/java8" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/google-http-java-client/.kokoro/dependencies.sh" +} diff --git a/.kokoro/presubmit/graalvm-native-a.cfg b/.kokoro/presubmit/graalvm-native-a.cfg new file mode 100644 index 000000000..7efff1613 --- /dev/null +++ b/.kokoro/presubmit/graalvm-native-a.cfg @@ -0,0 +1,33 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +build_file: "google-http-java-client/.kokoro/build.sh" + +env_vars: { + key: "JOB_TYPE" + value: "graalvm" +} + +# TODO: remove this after we've migrated all tests and scripts +env_vars: { + key: "GCLOUD_PROJECT" + value: "gcloud-devel" +} + +env_vars: { + key: "GOOGLE_CLOUD_PROJECT" + value: "gcloud-devel" +} + +env_vars: { + key: "GOOGLE_APPLICATION_CREDENTIALS" + value: "secret_manager/java-it-service-account" +} + +env_vars: { + key: "SECRET_MANAGER_KEYS" + value: "java-it-service-account" +} + +container_properties { + docker_image: "us-docker.pkg.dev/java-graalvm-ci-prod/graalvm-integration-testing/graalvm_a:1.17.0" +} diff --git a/.kokoro/presubmit/graalvm-native-b.cfg b/.kokoro/presubmit/graalvm-native-b.cfg new file mode 100644 index 000000000..5f8625f71 --- /dev/null +++ b/.kokoro/presubmit/graalvm-native-b.cfg @@ -0,0 +1,33 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +build_file: "google-http-java-client/.kokoro/build.sh" + +env_vars: { + key: "JOB_TYPE" + value: "graalvm" +} + +# TODO: remove this after we've migrated all tests and scripts +env_vars: { + key: "GCLOUD_PROJECT" + value: "gcloud-devel" +} + +env_vars: { + key: "GOOGLE_CLOUD_PROJECT" + value: "gcloud-devel" +} + +env_vars: { + key: "GOOGLE_APPLICATION_CREDENTIALS" + value: "secret_manager/java-it-service-account" +} + +env_vars: { + key: "SECRET_MANAGER_KEYS" + value: "java-it-service-account" +} + +container_properties { + docker_image: "us-docker.pkg.dev/java-graalvm-ci-prod/graalvm-integration-testing/graalvm_b:1.17.0" +} diff --git a/.kokoro/presubmit/graalvm-native-c.cfg b/.kokoro/presubmit/graalvm-native-c.cfg new file mode 100644 index 000000000..19b286524 --- /dev/null +++ b/.kokoro/presubmit/graalvm-native-c.cfg @@ -0,0 +1,33 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +build_file: "google-http-java-client/.kokoro/build.sh" + +env_vars: { + key: "JOB_TYPE" + value: "graalvm" +} + +# TODO: remove this after we've migrated all tests and scripts +env_vars: { + key: "GCLOUD_PROJECT" + value: "gcloud-devel" +} + +env_vars: { + key: "GOOGLE_CLOUD_PROJECT" + value: "gcloud-devel" +} + +env_vars: { + key: "GOOGLE_APPLICATION_CREDENTIALS" + value: "secret_manager/java-it-service-account" +} + +env_vars: { + key: "SECRET_MANAGER_KEYS" + value: "java-it-service-account" +} + +container_properties { + docker_image: "us-docker.pkg.dev/java-graalvm-ci-prod/graalvm-integration-testing/graalvm_c:1.17.0" +} diff --git a/.kokoro/presubmit/integration.cfg b/.kokoro/presubmit/integration.cfg new file mode 100644 index 000000000..5864c603e --- /dev/null +++ b/.kokoro/presubmit/integration.cfg @@ -0,0 +1,34 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Configure the docker image for kokoro-trampoline. +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/java8" +} + +env_vars: { + key: "JOB_TYPE" + value: "integration" +} + +# TODO: remove this after we've migrated all tests and scripts +env_vars: { + key: "GCLOUD_PROJECT" + value: "gcloud-devel" +} + +env_vars: { + key: "GOOGLE_CLOUD_PROJECT" + value: "gcloud-devel" +} + +env_vars: { + key: "GOOGLE_APPLICATION_CREDENTIALS" + value: "secret_manager/java-it-service-account" +} + +env_vars: { + key: "SECRET_MANAGER_KEYS" + value: "java-it-service-account" +} + diff --git a/.kokoro/presubmit/java8-osx.cfg b/.kokoro/presubmit/java8-osx.cfg new file mode 100644 index 000000000..b2354168e --- /dev/null +++ b/.kokoro/presubmit/java8-osx.cfg @@ -0,0 +1,3 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +build_file: "google-http-java-client/.kokoro/build.sh" diff --git a/.kokoro/presubmit/java8-win.cfg b/.kokoro/presubmit/java8-win.cfg new file mode 100644 index 000000000..b44537d03 --- /dev/null +++ b/.kokoro/presubmit/java8-win.cfg @@ -0,0 +1,3 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +build_file: "google-http-java-client/.kokoro/build.bat" diff --git a/.kokoro/presubmit/java8.cfg b/.kokoro/presubmit/java8.cfg index 3b017fc80..495cc7bac 100644 --- a/.kokoro/presubmit/java8.cfg +++ b/.kokoro/presubmit/java8.cfg @@ -5,3 +5,8 @@ env_vars: { key: "TRAMPOLINE_IMAGE" value: "gcr.io/cloud-devrel-kokoro-resources/java8" } + +env_vars: { + key: "REPORT_COVERAGE" + value: "true" +} diff --git a/.kokoro/presubmit/linkage-monitor.cfg b/.kokoro/presubmit/linkage-monitor.cfg new file mode 100644 index 000000000..20ab48cc8 --- /dev/null +++ b/.kokoro/presubmit/linkage-monitor.cfg @@ -0,0 +1,12 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Configure the docker image for kokoro-trampoline. +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/java8" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/google-http-java-client/.kokoro/linkage-monitor.sh" +} \ No newline at end of file diff --git a/.kokoro/presubmit/lint.cfg b/.kokoro/presubmit/lint.cfg new file mode 100644 index 000000000..6d323c8ae --- /dev/null +++ b/.kokoro/presubmit/lint.cfg @@ -0,0 +1,13 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Configure the docker image for kokoro-trampoline. + +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/java8" +} + +env_vars: { + key: "JOB_TYPE" + value: "lint" +} \ No newline at end of file diff --git a/.kokoro/presubmit/samples.cfg b/.kokoro/presubmit/samples.cfg new file mode 100644 index 000000000..01e096004 --- /dev/null +++ b/.kokoro/presubmit/samples.cfg @@ -0,0 +1,33 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Configure the docker image for kokoro-trampoline. +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/java8" +} + +env_vars: { + key: "JOB_TYPE" + value: "samples" +} + +# TODO: remove this after we've migrated all tests and scripts +env_vars: { + key: "GCLOUD_PROJECT" + value: "java-docs-samples-testing" +} + +env_vars: { + key: "GOOGLE_CLOUD_PROJECT" + value: "java-docs-samples-testing" +} + +env_vars: { + key: "GOOGLE_APPLICATION_CREDENTIALS" + value: "secret_manager/java-docs-samples-service-account" +} + +env_vars: { + key: "SECRET_MANAGER_KEYS" + value: "java-docs-samples-service-account" +} \ No newline at end of file diff --git a/.kokoro/readme.sh b/.kokoro/readme.sh new file mode 100755 index 000000000..97103b119 --- /dev/null +++ b/.kokoro/readme.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -eo pipefail + +cd ${KOKORO_ARTIFACTS_DIR}/github/google-http-java-client + +# Disable buffering, so that the logs stream through. +export PYTHONUNBUFFERED=1 + +# Kokoro exposes this as a file, but the scripts expect just a plain variable. +export GITHUB_TOKEN=$(cat ${KOKORO_KEYSTORE_DIR}/73713_yoshi-automation-github-key) + +# Setup git credentials +echo "https://${GITHUB_TOKEN}:@github.com" >> ~/.git-credentials +git config --global credential.helper 'store --file ~/.git-credentials' + +python3.6 -m pip install git+https://github.com/googleapis/synthtool.git#egg=gcp-synthtool + +set +e +python3.6 -m autosynth.synth \ + --repository=googleapis/google-http-java-client \ + --synth-file-name=.github/readme/synth.py \ + --metadata-path=.github/readme/synth.metadata \ + --pr-title="chore: regenerate README" \ + --branch-suffix="readme" + +# autosynth returns 28 to signal there are no changes +RETURN_CODE=$? +if [[ ${RETURN_CODE} -ne 0 && ${RETURN_CODE} -ne 28 ]] +then + exit ${RETURN_CODE} +fi diff --git a/.kokoro/release/common.cfg b/.kokoro/release/common.cfg deleted file mode 100644 index 4377eeb04..000000000 --- a/.kokoro/release/common.cfg +++ /dev/null @@ -1,56 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Build logs will be here -action { - define_artifacts { - regex: "**/*sponge_log.xml" - } -} - -# Download trampoline resources. -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" - -# Use the trampoline script to run in docker. -build_file: "google-http-java-client/.kokoro/trampoline.sh" - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/java8" -} - -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 100644 index f7d538b3a..000000000 --- a/.kokoro/release/common.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash -# Copyright 2018 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -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 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 -} diff --git a/.kokoro/release/drop.cfg b/.kokoro/release/drop.cfg deleted file mode 100644 index 278e44d75..000000000 --- a/.kokoro/release/drop.cfg +++ /dev/null @@ -1,7 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Tell the trampoline which build file to use. -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/google-http-java-client/.kokoro/release/drop.sh" -} diff --git a/.kokoro/release/promote.cfg b/.kokoro/release/promote.cfg deleted file mode 100644 index e6d8b8fdf..000000000 --- a/.kokoro/release/promote.cfg +++ /dev/null @@ -1,7 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Tell the trampoline which build file to use. -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/google-http-java-client/.kokoro/release/promote.sh" -} diff --git a/.kokoro/release/promote.sh b/.kokoro/release/promote.sh deleted file mode 100755 index 439e0fc3e..000000000 --- a/.kokoro/release/promote.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash -# Copyright 2018 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -eo pipefail - -source $(dirname "$0")/common.sh - -pushd $(dirname "$0")/../../ - -setup_environment_secrets -create_settings_xml_file "settings.xml" - -mvn nexus-staging:release -DperformRelease=true --settings=settings.xml diff --git a/.kokoro/release/stage.cfg b/.kokoro/release/stage.cfg deleted file mode 100644 index c49930316..000000000 --- a/.kokoro/release/stage.cfg +++ /dev/null @@ -1,7 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Tell the trampoline which build file to use. -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/google-http-java-client/.kokoro/release/stage.sh" -} diff --git a/.kokoro/release/stage.sh b/.kokoro/release/stage.sh deleted file mode 100755 index 02809c918..000000000 --- a/.kokoro/release/stage.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash -# Copyright 2018 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -eo pipefail - -source $(dirname "$0")/common.sh - -pushd $(dirname "$0")/../../ - -setup_environment_secrets -create_settings_xml_file "settings.xml" - -mvn clean install deploy \ - --settings settings.xml \ - -DperformRelease=true \ - -Dgpg.executable=gpg \ - -Dgpg.passphrase=${GPG_PASSPHRASE} \ - -Dgpg.homedir=${GPG_HOMEDIR} - - diff --git a/.kokoro/trampoline.sh b/.kokoro/trampoline.sh index ba17ce014..8b69b793c 100644 --- a/.kokoro/trampoline.sh +++ b/.kokoro/trampoline.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright 2018 Google Inc. +# Copyright 2018 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,4 +21,6 @@ function cleanup() { echo "cleanup"; } trap cleanup EXIT + +$(dirname $0)/populate-secrets.sh # Secret Manager secrets. python3 "${KOKORO_GFILE_DIR}/trampoline_v1.py" diff --git a/.repo-metadata.json b/.repo-metadata.json new file mode 100644 index 000000000..ab8e8d5e9 --- /dev/null +++ b/.repo-metadata.json @@ -0,0 +1,11 @@ +{ + "api_shortname": "google-http-client", + "name_pretty": "Google HTTP Java Client", + "client_documentation": "https://cloud.google.com/java/docs/reference/google-http-client/latest/history", + "release_level": "stable", + "language": "java", + "repo": "googleapis/google-http-java-client", + "repo_short": "google-http-java-client", + "library_type": "CORE", + "distribution_name": "com.google.http-client:google-http-client" +} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 4ffc615a8..000000000 --- a/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -sudo: false - -language: java - -notifications: - email: false - -jdk: - - oraclejdk8 - - openjdk7 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..59dd55424 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,748 @@ +# Changelog + +## [2.0.2](https://github.com/googleapis/google-http-java-client/compare/v2.0.1...v2.0.2) (2025-10-13) + + +### Dependencies + +* Update dependency com.google.cloud:native-image-shared-config to v1.17.0 ([9efc208](https://github.com/googleapis/google-http-java-client/commit/9efc20820de4d48cf6b4375d26793094aef6696d)) + +## [2.0.1](https://github.com/googleapis/google-http-java-client/compare/v2.0.0...v2.0.1) (2025-09-24) + + +### Bug Fixes + +* UriTemplate expansion reserved ("+") and fragment("#") should not encode already percent encoded parts ([#2108](https://github.com/googleapis/google-http-java-client/issues/2108)) ([30766a8](https://github.com/googleapis/google-http-java-client/commit/30766a8a74df49c37e80ec41f1021d4ad69a8fda)) + +## [2.0.0](https://github.com/googleapis/google-http-java-client/compare/v1.47.1...v2.0.0) (2025-08-08) + + +### ⚠ BREAKING CHANGES + +* update guava to 33.4.8-android ([#2112](https://github.com/googleapis/google-http-java-client/issues/2112)) + +### Features + +* Next release from main branch is 1.48.0 ([#2110](https://github.com/googleapis/google-http-java-client/issues/2110)) ([33c6793](https://github.com/googleapis/google-http-java-client/commit/33c6793487c082e7ff7288983b5d64611b433a55)) + + +### Dependencies + +* Update guava to 33.4.8-android ([#2112](https://github.com/googleapis/google-http-java-client/issues/2112)) ([941038c](https://github.com/googleapis/google-http-java-client/commit/941038c81b99c4eaaab8935349d7b652362ab20c)) + +## [1.47.1](https://github.com/googleapis/google-http-java-client/compare/v1.47.0...v1.47.1) (2025-06-23) + + +### Bug Fixes + +* Add special handling for `Object` in `Types.newInstance()` ([#2102](https://github.com/googleapis/google-http-java-client/issues/2102)) ([a855eca](https://github.com/googleapis/google-http-java-client/commit/a855eca50201d99aee25f4d68c240d06140fe7c6)) + +## [1.47.0](https://github.com/googleapis/google-http-java-client/compare/v1.46.3...v1.47.0) (2025-04-28) + + +### Features + +* Next release from main branch is 1.47.0 ([#2087](https://github.com/googleapis/google-http-java-client/issues/2087)) ([f89cc4c](https://github.com/googleapis/google-http-java-client/commit/f89cc4c485c0acf0f22f5efe9706c404f997961d)) + + +### Bug Fixes + +* Encode + sign in url with %2B ([#2094](https://github.com/googleapis/google-http-java-client/issues/2094)) ([1f8aca7](https://github.com/googleapis/google-http-java-client/commit/1f8aca768250f46490e3d3e47903693d3a8e30b8)) + + +### Dependencies + +* Update github/codeql-action action to v3.28.16 ([#2057](https://github.com/googleapis/google-http-java-client/issues/2057)) ([4fc3e3a](https://github.com/googleapis/google-http-java-client/commit/4fc3e3a70cd9d0f1ebe5c498920844d3fc020316)) + +## [1.46.3](https://github.com/googleapis/google-http-java-client/compare/v1.46.2...v1.46.3) (2025-02-25) + + +### Dependencies + +* Update native-image-shared-config to 1.14.4 ([1ab8c28](https://github.com/googleapis/google-http-java-client/commit/1ab8c28d3b629c58523581c15f86fb7054364dcb)) + +## [1.46.2](https://github.com/googleapis/google-http-java-client/compare/v1.46.1...v1.46.2) (2025-02-24) + + +### Dependencies + +* Update grpc-context-io to 1.70.0 ([#2078](https://github.com/googleapis/google-http-java-client/issues/2078)) ([3a82a5f](https://github.com/googleapis/google-http-java-client/commit/3a82a5f7d01860782dc5724cc4089ca94f71509d)) + +## [1.46.1](https://github.com/googleapis/google-http-java-client/compare/v1.46.0...v1.46.1) (2025-02-07) + + +### Bug Fixes + +* Remove unnecessary nexus plugin activation ([#2071](https://github.com/googleapis/google-http-java-client/issues/2071)) ([e3a3523](https://github.com/googleapis/google-http-java-client/commit/e3a3523eebc169ec87353ed94c12419e4dfa8b28)) + + +### Dependencies + +* Revert dependency io.grpc:grpc-context back to v1.69.0 ([5790ac4](https://github.com/googleapis/google-http-java-client/commit/5790ac4d27ebf2aa75155a4dcc24e7f74ca7b588)) + +## [1.46.0](https://github.com/googleapis/google-http-java-client/compare/v1.45.3...v1.46.0) (2025-02-06) + + +### Features + +* Support JDK 23 ([#2064](https://github.com/googleapis/google-http-java-client/issues/2064)) ([b6a3616](https://github.com/googleapis/google-http-java-client/commit/b6a3616a16c9ac565610e630bb9ad73589829334)) +* Update and adapt to GraalVM for JDK 23 ([#2069](https://github.com/googleapis/google-http-java-client/issues/2069)) ([7a0fab3](https://github.com/googleapis/google-http-java-client/commit/7a0fab3efd2104144dff752da334f8fa8a82951d)) + + +### Dependencies + +* Update actions/upload-artifact action to v4.6.0 ([#2056](https://github.com/googleapis/google-http-java-client/issues/2056)) ([1e70d04](https://github.com/googleapis/google-http-java-client/commit/1e70d04948d9bc141647df3fea61d2ee1e70f7d1)) +* Update dependency io.grpc:grpc-context to v1.70.0 ([#2068](https://github.com/googleapis/google-http-java-client/issues/2068)) ([7a580bf](https://github.com/googleapis/google-http-java-client/commit/7a580bf568bd2a5bc0519880f4a213f6c47c9849)) +* Update project.appengine.version to v2.0.32 ([#2065](https://github.com/googleapis/google-http-java-client/issues/2065)) ([2285bb1](https://github.com/googleapis/google-http-java-client/commit/2285bb1c48edd6c07a7ded557fe58d57e570f1b3)) + +## [1.45.3](https://github.com/googleapis/google-http-java-client/compare/v1.45.2...v1.45.3) (2024-12-11) + + +### Dependencies + +* Update dependency io.grpc:grpc-context to v1.69.0 ([#2050](https://github.com/googleapis/google-http-java-client/issues/2050)) ([9f4f6ab](https://github.com/googleapis/google-http-java-client/commit/9f4f6ab5e2729bebe8416a40d092172e7ba3313a)) +* Update github/codeql-action action to v3.27.7 ([#2049](https://github.com/googleapis/google-http-java-client/issues/2049)) ([9190382](https://github.com/googleapis/google-http-java-client/commit/91903825dd1af1ea1f39a96107d9518610212278)) + +## [1.45.2](https://github.com/googleapis/google-http-java-client/compare/v1.45.1...v1.45.2) (2024-12-05) + + +### Bug Fixes + +* NPE if response entity is null ([#2043](https://github.com/googleapis/google-http-java-client/issues/2043)) ([12c742b](https://github.com/googleapis/google-http-java-client/commit/12c742b1f7536fd1fd408a74071007b15480b149)) + + +### Dependencies + +* Update actions/checkout action to v4.2.2 ([#2034](https://github.com/googleapis/google-http-java-client/issues/2034)) ([024fd71](https://github.com/googleapis/google-http-java-client/commit/024fd718793d20f9439d538b9c342daeb84b89bc)) +* Update actions/upload-artifact action to v4.4.3 ([#2035](https://github.com/googleapis/google-http-java-client/issues/2035)) ([443157c](https://github.com/googleapis/google-http-java-client/commit/443157c5ff20fdddaf40193e005f43b7bc6a6f54)) +* Update dependency com.fasterxml.jackson.core:jackson-core to v2.18.2 ([#2036](https://github.com/googleapis/google-http-java-client/issues/2036)) ([5d24785](https://github.com/googleapis/google-http-java-client/commit/5d247854d65075c9e0f8e9076c210f0e93742c46)) +* Update dependency com.google.errorprone:error_prone_annotations to v2.36.0 ([#2037](https://github.com/googleapis/google-http-java-client/issues/2037)) ([cc6eb61](https://github.com/googleapis/google-http-java-client/commit/cc6eb61a9f7ae550951ee7b5c8a383d755e76959)) +* Update dependency io.grpc:grpc-context to v1.68.2 ([#2038](https://github.com/googleapis/google-http-java-client/issues/2038)) ([9fba799](https://github.com/googleapis/google-http-java-client/commit/9fba799ac04c7870f3ee5c425ccb0a51dc7e0d16)) +* Update dependency ubuntu to v24 ([#2041](https://github.com/googleapis/google-http-java-client/issues/2041)) ([ac83eb2](https://github.com/googleapis/google-http-java-client/commit/ac83eb259331de683806787c172514592d27de01)) +* Update github/codeql-action action to v3.27.6 ([#2003](https://github.com/googleapis/google-http-java-client/issues/2003)) ([dc8e46a](https://github.com/googleapis/google-http-java-client/commit/dc8e46a6b6308985380e312fad82b7c182dd9e6f)) +* Update project.appengine.version to v2.0.31 ([#2027](https://github.com/googleapis/google-http-java-client/issues/2027)) ([8bb79e5](https://github.com/googleapis/google-http-java-client/commit/8bb79e5448e0fa2767b029e7101e3d5d5112eaf2)) + +## [1.45.1](https://github.com/googleapis/google-http-java-client/compare/v1.45.0...v1.45.1) (2024-11-12) + + +### Bug Fixes + +* Add google-http-client-apache-v5 to bom ([#2021](https://github.com/googleapis/google-http-java-client/issues/2021)) ([4830ad7](https://github.com/googleapis/google-http-java-client/commit/4830ad788a62fe9cd4f64873b771e6ef8ef92193)) + +## [1.45.0](https://github.com/googleapis/google-http-java-client/compare/v1.44.2...v1.45.0) (2024-08-21) + + +### Features + +* Introduce google-http-client-apache-v5 (Apache Client/Core 5.x) ([#1960](https://github.com/googleapis/google-http-java-client/issues/1960)) ([5d527dc](https://github.com/googleapis/google-http-java-client/commit/5d527dc3afade0834a82e5280f22e0129b5f1297)) +* Next release from main is 1.45.0 ([#1972](https://github.com/googleapis/google-http-java-client/issues/1972)) ([094dcc8](https://github.com/googleapis/google-http-java-client/commit/094dcc87f22d16686242ce6cb06151b2a199ec2a)) + + +### Dependencies + +* Update actions/checkout action to v4 ([#1993](https://github.com/googleapis/google-http-java-client/issues/1993)) ([f8b0cc1](https://github.com/googleapis/google-http-java-client/commit/f8b0cc1c908b4ebdf3c30a9f9a9b50c96d33f781)) +* Update actions/github-script action to v7 ([#1994](https://github.com/googleapis/google-http-java-client/issues/1994)) ([e527f0d](https://github.com/googleapis/google-http-java-client/commit/e527f0d9624e47c37a551590cf555f6207751fe9)) +* Update actions/setup-java action to v4 ([#1995](https://github.com/googleapis/google-http-java-client/issues/1995)) ([07aa01c](https://github.com/googleapis/google-http-java-client/commit/07aa01c2f86dc35fd6d1f968cbf111315ef26aa5)) +* Update actions/upload-artifact action to v4 ([#1996](https://github.com/googleapis/google-http-java-client/issues/1996)) ([5ba7021](https://github.com/googleapis/google-http-java-client/commit/5ba70218d8bdae18094b1031f9c800688aa58fb4)) +* Update dependency com.fasterxml.jackson.core:jackson-core to v2.17.2 ([#1987](https://github.com/googleapis/google-http-java-client/issues/1987)) ([4202d32](https://github.com/googleapis/google-http-java-client/commit/4202d328ea2cefff0771036b010fbfd80928f88c)) +* Update dependency com.google.cloud:native-image-shared-config to v1.7.7 ([#1937](https://github.com/googleapis/google-http-java-client/issues/1937)) ([b224a1d](https://github.com/googleapis/google-http-java-client/commit/b224a1d64cae44878f1bb0af83fb8e33e2e12d63)) +* Update dependency com.google.cloud:native-image-shared-config to v1.9.0 ([#1961](https://github.com/googleapis/google-http-java-client/issues/1961)) ([792e44f](https://github.com/googleapis/google-http-java-client/commit/792e44f6a2b7678fef30c3bdfc0955be533a7613)) +* Update dependency com.google.code.gson:gson to v2.11.0 ([#1988](https://github.com/googleapis/google-http-java-client/issues/1988)) ([63afd35](https://github.com/googleapis/google-http-java-client/commit/63afd35f0a3c15f5a66f5b6a06bae01f82b54504)) +* Update dependency com.google.errorprone:error_prone_annotations to v2.30.0 ([#1989](https://github.com/googleapis/google-http-java-client/issues/1989)) ([6e19c5c](https://github.com/googleapis/google-http-java-client/commit/6e19c5ca66c67dad53272998535ed197559dfe02)) +* Update dependency com.google.j2objc:j2objc-annotations to v3 ([#1998](https://github.com/googleapis/google-http-java-client/issues/1998)) ([3d70537](https://github.com/googleapis/google-http-java-client/commit/3d7053747076e599f819c41f8362f6070a96ce8a)) +* Update dependency io.grpc:grpc-context to v1.66.0 ([#1990](https://github.com/googleapis/google-http-java-client/issues/1990)) ([66a9f15](https://github.com/googleapis/google-http-java-client/commit/66a9f15c35bc64c25f10094c424e025ea6b0a693)) +* Update dependency org.apache.httpcomponents.core5:httpcore5 to v5.2.5 ([#2002](https://github.com/googleapis/google-http-java-client/issues/2002)) ([8c61065](https://github.com/googleapis/google-http-java-client/commit/8c6106505a119380e555c64151542445f0e1a5f8)) +* Update github/codeql-action action to v3 ([#2000](https://github.com/googleapis/google-http-java-client/issues/2000)) ([7250f64](https://github.com/googleapis/google-http-java-client/commit/7250f649be7a989dc0a855d6f6ddff987ac0ebaa)) +* Update ossf/scorecard-action action to v2.4.0 ([#1992](https://github.com/googleapis/google-http-java-client/issues/1992)) ([08c5e5a](https://github.com/googleapis/google-http-java-client/commit/08c5e5a7f7a887aaccc03a40c784d34ecbc45984)) +* Update project.appengine.version to v2.0.27 ([#1938](https://github.com/googleapis/google-http-java-client/issues/1938)) ([3f27cc8](https://github.com/googleapis/google-http-java-client/commit/3f27cc800db62c2208160234a5afad641f1a3781)) +* Update project.appengine.version to v2.0.29 ([#1978](https://github.com/googleapis/google-http-java-client/issues/1978)) ([a3fd1e3](https://github.com/googleapis/google-http-java-client/commit/a3fd1e34925a531d47613145d5f3b5473cef2d82)) + +## [1.44.2](https://github.com/googleapis/google-http-java-client/compare/v1.44.1...v1.44.2) (2024-05-16) + + +### Bug Fixes + +* Base64 decoding to discard newline characters ([#1941](https://github.com/googleapis/google-http-java-client/issues/1941)) ([4e153db](https://github.com/googleapis/google-http-java-client/commit/4e153db9443832dbe6ca56b6fe10dcea26921b6f)) + + +### Dependencies + +* Update actions/upload-artifact action to v3.1.3 ([#1860](https://github.com/googleapis/google-http-java-client/issues/1860)) ([60deab2](https://github.com/googleapis/google-http-java-client/commit/60deab2fc93c70f4f37733a90a07b11e19b9f508)) +* Update dependency com.google.cloud:native-image-shared-config to v1.7.6 ([#1928](https://github.com/googleapis/google-http-java-client/issues/1928)) ([3dd6b79](https://github.com/googleapis/google-http-java-client/commit/3dd6b79368dd31efaf293009e8f424c905e9cc38)) +* Update dependency org.apache.felix:maven-bundle-plugin to v5.1.9 ([#1888](https://github.com/googleapis/google-http-java-client/issues/1888)) ([41c16b9](https://github.com/googleapis/google-http-java-client/commit/41c16b9334e218412826c1c07cdd303611bc7dbf)) +* Update project.appengine.version to v2.0.25 ([#1931](https://github.com/googleapis/google-http-java-client/issues/1931)) ([53eb6a1](https://github.com/googleapis/google-http-java-client/commit/53eb6a1f62d106190cc75af1e6b6eebec481f874)) + +## [1.44.1](https://github.com/googleapis/google-http-java-client/compare/v1.44.0...v1.44.1) (2024-01-26) + + +### Bug Fixes + +* Fixing declaration of maven-source-plugin for release job ([f555f56](https://github.com/googleapis/google-http-java-client/commit/f555f562d44ab83b2cb672fa351c665336043880)) + +## [1.44.0](https://github.com/googleapis/google-http-java-client/compare/v1.43.3...v1.44.0) (2024-01-23) + + +### Features + +* Add isShutdown in HttpTransport ([#1901](https://github.com/googleapis/google-http-java-client/issues/1901)) ([be00ce1](https://github.com/googleapis/google-http-java-client/commit/be00ce1ef1873d3b80830966ddb83097e460601d)) +* Setup 1.43.x lts branch ([#1869](https://github.com/googleapis/google-http-java-client/issues/1869)) ([13edd13](https://github.com/googleapis/google-http-java-client/commit/13edd1357eb79535d943f5288ab3adf4d9dfb52a)) + + +### Bug Fixes + +* Native image configs for google-http-java-client ([#1893](https://github.com/googleapis/google-http-java-client/issues/1893)) ([1acedf7](https://github.com/googleapis/google-http-java-client/commit/1acedf75368f11ab03e5f84dd2c58a8a8a662d41)) +* SerialVersionUID fix for Serializable Classes ([#1883](https://github.com/googleapis/google-http-java-client/issues/1883)) ([f390020](https://github.com/googleapis/google-http-java-client/commit/f39002001bd666cb1eb839d54454aa742638f642)) + + +### Dependencies + +* Newer grpc-context to override old one ([#1916](https://github.com/googleapis/google-http-java-client/issues/1916)) ([f23e9b1](https://github.com/googleapis/google-http-java-client/commit/f23e9b1c549b0a0668f433729eea846385ea822c)) +* Testing exlcusing commons-logging ([#1905](https://github.com/googleapis/google-http-java-client/issues/1905)) ([8e3f4d5](https://github.com/googleapis/google-http-java-client/commit/8e3f4d50fbaa93bfada9e20c67293a63a64e68f9)) +* Update dependency com.google.errorprone:error_prone_annotations to v2.23.0 ([#1887](https://github.com/googleapis/google-http-java-client/issues/1887)) ([ce5dbfc](https://github.com/googleapis/google-http-java-client/commit/ce5dbfc68c2cca989f57468a5a915cf9411267bb)) +* Update project.appengine.version to v2.0.24 ([#1889](https://github.com/googleapis/google-http-java-client/issues/1889)) ([0091413](https://github.com/googleapis/google-http-java-client/commit/00914131afe5b12c14e472ee0a88ff298374965b)) + +## [1.43.3](https://github.com/googleapis/google-http-java-client/compare/v1.43.2...v1.43.3) (2023-06-21) + + +### Dependencies + +* Update dependency com.google.j2objc:j2objc-annotations to v2 ([#1805](https://github.com/googleapis/google-http-java-client/issues/1805)) ([00eb7b1](https://github.com/googleapis/google-http-java-client/commit/00eb7b1d6b29148ee76b4cf59be7bf8288cc5152)) +* Update doclet version to v1.9.0 ([#1853](https://github.com/googleapis/google-http-java-client/issues/1853)) ([eeea739](https://github.com/googleapis/google-http-java-client/commit/eeea739f855cdeaef2dd6c38246660723656dc36)) + +## [1.43.2](https://github.com/googleapis/google-http-java-client/compare/v1.43.1...v1.43.2) (2023-05-09) + + +### Bug Fixes + +* UriTemplate reserved expansion does not escape reserved chars ([#1844](https://github.com/googleapis/google-http-java-client/issues/1844)) ([91c46a9](https://github.com/googleapis/google-http-java-client/commit/91c46a99b0b9464d01b5aca2116bbe073b878725)), closes [#1838](https://github.com/googleapis/google-http-java-client/issues/1838) + +## [1.43.1](https://github.com/googleapis/google-http-java-client/compare/v1.43.0...v1.43.1) (2023-03-14) + + +### Bug Fixes + +* JSON deserialization setter case priority ([#1831](https://github.com/googleapis/google-http-java-client/issues/1831)) ([30182e1](https://github.com/googleapis/google-http-java-client/commit/30182e13e7b294b8a0771e47a84b0ed45a628a1f)) + + +### Dependencies + +* Update project.appengine.version to v2.0.12 ([#1816](https://github.com/googleapis/google-http-java-client/issues/1816)) ([ba84066](https://github.com/googleapis/google-http-java-client/commit/ba8406642c47045378153e5687667dda6c37c7be)) + +## [1.43.0](https://github.com/googleapis/google-http-java-client/compare/v1.42.3...v1.43.0) (2023-02-24) + + +### Features + +* GsonFactory to have read leniency option via `GsonFactory.builder().setReadLeniency(true).build()` ([00d61b9](https://github.com/googleapis/google-http-java-client/commit/00d61b96dff050ec4b061bead047239b21a48764)) +* Next release from main branch is 1.43.0 ([#1764](https://github.com/googleapis/google-http-java-client/issues/1764)) ([9fbae6c](https://github.com/googleapis/google-http-java-client/commit/9fbae6c0721cce7cb4a9042f8fed4823ce291e80)) + + +### Dependencies + +* Update dependency com.fasterxml.jackson.core:jackson-core to v2.14.0 ([#1774](https://github.com/googleapis/google-http-java-client/issues/1774)) ([dc41010](https://github.com/googleapis/google-http-java-client/commit/dc410107c98e06531021e5a44ac68ff7621dc47f)) +* Update dependency com.fasterxml.jackson.core:jackson-core to v2.14.1 ([#1785](https://github.com/googleapis/google-http-java-client/issues/1785)) ([234e7b5](https://github.com/googleapis/google-http-java-client/commit/234e7b53a1fc2f3b8a8b7a80a4c9fa9118dcbc37)) +* Update dependency com.fasterxml.jackson.core:jackson-core to v2.14.2 ([#1810](https://github.com/googleapis/google-http-java-client/issues/1810)) ([23094ff](https://github.com/googleapis/google-http-java-client/commit/23094ffa028acdee63ed868ea070d877f2c5ea95)) +* Update dependency com.google.code.gson:gson to v2.10.1 ([#1799](https://github.com/googleapis/google-http-java-client/issues/1799)) ([a114c7e](https://github.com/googleapis/google-http-java-client/commit/a114c7ed815216dccf165fc8763a768892a58723)) +* Update dependency com.google.errorprone:error_prone_annotations to v2.18.0 ([#1797](https://github.com/googleapis/google-http-java-client/issues/1797)) ([09f3607](https://github.com/googleapis/google-http-java-client/commit/09f360775001c035d4d26d29f9e28e5f47fb5bd5)) +* Update dependency com.google.protobuf:protobuf-java to v3.21.12 ([#1789](https://github.com/googleapis/google-http-java-client/issues/1789)) ([03b5b32](https://github.com/googleapis/google-http-java-client/commit/03b5b321f20543c354447f52669f05a9d1bd00b1)) +* Update dependency kr.motd.maven:os-maven-plugin to v1.7.1 ([#1777](https://github.com/googleapis/google-http-java-client/issues/1777)) ([3f318f4](https://github.com/googleapis/google-http-java-client/commit/3f318f44305d9b59aecbdd980abdad525ca47bf3)) +* Update dependency org.apache.httpcomponents:httpclient to v4.5.14 ([#1790](https://github.com/googleapis/google-http-java-client/issues/1790)) ([0664e17](https://github.com/googleapis/google-http-java-client/commit/0664e1744e0885a1cb8787481ccfbab0de845fe9)) +* Update dependency org.apache.httpcomponents:httpcore to v4.4.16 ([#1787](https://github.com/googleapis/google-http-java-client/issues/1787)) ([512aa23](https://github.com/googleapis/google-http-java-client/commit/512aa2398adf64b89e27b505de03b6e3f2a32875)) +* Update project.appengine.version to v2.0.10 ([#1773](https://github.com/googleapis/google-http-java-client/issues/1773)) ([5ddb634](https://github.com/googleapis/google-http-java-client/commit/5ddb634887601bfad64ac482643f65c820b55fd4)) + +## [1.42.3](https://github.com/googleapis/google-http-java-client/compare/v1.42.2...v1.42.3) (2022-10-27) + + +### Bug Fixes + +* Add @CanIgnoreReturnValue to avoid errorprone errors ([#1716](https://github.com/googleapis/google-http-java-client/issues/1716)) ([cba2f82](https://github.com/googleapis/google-http-java-client/commit/cba2f82b8ff7f4ca44616564accd67f95f08247a)) + + +### Dependencies + +* Update actions/checkout action to v3 ([#1719](https://github.com/googleapis/google-http-java-client/issues/1719)) ([6b9585b](https://github.com/googleapis/google-http-java-client/commit/6b9585b0539af6b4631d005a61bb2af60804453a)) +* Update dependency cachetools to v5 ([#1732](https://github.com/googleapis/google-http-java-client/issues/1732)) ([7d153d3](https://github.com/googleapis/google-http-java-client/commit/7d153d3c5e92375bb933f6f12d3a2c5df391b34f)) +* Update dependency certifi to v2022.9.24 ([#1734](https://github.com/googleapis/google-http-java-client/issues/1734)) ([3b345df](https://github.com/googleapis/google-http-java-client/commit/3b345df3be561bae1e2e4ac4229ab5b66e9b7176)) +* Update dependency charset-normalizer to v2.1.1 ([#1738](https://github.com/googleapis/google-http-java-client/issues/1738)) ([a3cbf66](https://github.com/googleapis/google-http-java-client/commit/a3cbf66737a166942c3ac499cae85686fdecd512)) +* Update dependency click to v8.1.3 ([#1739](https://github.com/googleapis/google-http-java-client/issues/1739)) ([0b2c204](https://github.com/googleapis/google-http-java-client/commit/0b2c204bb1e16575c82f165803af5f84d46c5c8a)) +* Update dependency com.fasterxml.jackson.core:jackson-core to v2.13.4 ([#1718](https://github.com/googleapis/google-http-java-client/issues/1718)) ([394aa98](https://github.com/googleapis/google-http-java-client/commit/394aa98271b02ac62ed35d7040194e8f9c7f41ee)) +* Update dependency com.google.code.gson:gson to v2.10 ([#1761](https://github.com/googleapis/google-http-java-client/issues/1761)) ([7d15ad6](https://github.com/googleapis/google-http-java-client/commit/7d15ad6a38e5338c42d972d6bacbd8849c35d851)) +* Update dependency com.google.code.gson:gson to v2.9.1 ([#1700](https://github.com/googleapis/google-http-java-client/issues/1700)) ([5c17e2b](https://github.com/googleapis/google-http-java-client/commit/5c17e2ba56ec094a375f986f58867856ba3192cf)) +* Update dependency com.google.errorprone:error_prone_annotations to v2.15.0 ([#1701](https://github.com/googleapis/google-http-java-client/issues/1701)) ([0a2e437](https://github.com/googleapis/google-http-java-client/commit/0a2e437017bec6ddf09cff99f535c012a43a5fd6)) +* Update dependency com.google.errorprone:error_prone_annotations to v2.16 ([#1755](https://github.com/googleapis/google-http-java-client/issues/1755)) ([1126e53](https://github.com/googleapis/google-http-java-client/commit/1126e53cf6cbcd1170e5ae5a54da31d245115713)) +* Update dependency com.google.protobuf:protobuf-java to v3.21.3 ([#1694](https://github.com/googleapis/google-http-java-client/issues/1694)) ([f86112d](https://github.com/googleapis/google-http-java-client/commit/f86112d90ce138dc5cbdca6ddcc50aec3e952740)) +* Update dependency com.google.protobuf:protobuf-java to v3.21.4 ([#1698](https://github.com/googleapis/google-http-java-client/issues/1698)) ([fdabd56](https://github.com/googleapis/google-http-java-client/commit/fdabd5672c571c473351ac36248e365f7dd7dcf5)) +* Update dependency com.google.protobuf:protobuf-java to v3.21.5 ([#1703](https://github.com/googleapis/google-http-java-client/issues/1703)) ([bdb8cbd](https://github.com/googleapis/google-http-java-client/commit/bdb8cbd83e7c77454e782a7c824e37ef1d011281)) +* Update dependency com.google.protobuf:protobuf-java to v3.21.6 ([#1722](https://github.com/googleapis/google-http-java-client/issues/1722)) ([28ee333](https://github.com/googleapis/google-http-java-client/commit/28ee333576e3078a0ad888ee4cc2c664eb8a60e2)) +* Update dependency com.google.protobuf:protobuf-java to v3.21.7 ([#1751](https://github.com/googleapis/google-http-java-client/issues/1751)) ([af16206](https://github.com/googleapis/google-http-java-client/commit/af1620620af90f29b12790166b21c9cbb7086ca6)) +* Update dependency com.google.protobuf:protobuf-java to v3.21.8 ([#1756](https://github.com/googleapis/google-http-java-client/issues/1756)) ([9119d85](https://github.com/googleapis/google-http-java-client/commit/9119d85b2911747358684b6f8ef83374a44734d7)) +* Update dependency com.google.protobuf:protobuf-java to v3.21.9 ([#1762](https://github.com/googleapis/google-http-java-client/issues/1762)) ([02581b8](https://github.com/googleapis/google-http-java-client/commit/02581b8d06d781f6349e6a6d963e20cf66769ef7)) +* Update dependency gcp-releasetool to v1.8.8 ([#1735](https://github.com/googleapis/google-http-java-client/issues/1735)) ([f24c984](https://github.com/googleapis/google-http-java-client/commit/f24c98454f46081eb8c9af8809341ebd605b7915)) +* Update dependency google-api-core to v2.10.1 ([#1740](https://github.com/googleapis/google-http-java-client/issues/1740)) ([eacf983](https://github.com/googleapis/google-http-java-client/commit/eacf9834fcaa807c891eb6f9bb7957f1830b0b72)) +* Update dependency google-auth to v2.12.0 ([#1741](https://github.com/googleapis/google-http-java-client/issues/1741)) ([bfea196](https://github.com/googleapis/google-http-java-client/commit/bfea196499c8989e17c7f90ee025a6a840d75aeb)) +* Update dependency google-cloud-core to v2.3.2 ([#1736](https://github.com/googleapis/google-http-java-client/issues/1736)) ([a333e1f](https://github.com/googleapis/google-http-java-client/commit/a333e1f2a2517bcfa51f945d65781fe8a0579676)) +* Update dependency google-cloud-storage to v2.5.0 ([#1742](https://github.com/googleapis/google-http-java-client/issues/1742)) ([8335e66](https://github.com/googleapis/google-http-java-client/commit/8335e66f8d175d1669dd02c8ce9007cf6d26eaeb)) +* Update dependency google-crc32c to v1.5.0 ([#1743](https://github.com/googleapis/google-http-java-client/issues/1743)) ([3fd3292](https://github.com/googleapis/google-http-java-client/commit/3fd32925fcd3464de74e02a4c7ead5f7469fed8e)) +* Update dependency importlib-metadata to v4.12.0 ([#1746](https://github.com/googleapis/google-http-java-client/issues/1746)) ([4658601](https://github.com/googleapis/google-http-java-client/commit/465860164392085b5cfb8d355529565e3f53721a)) +* Update dependency jeepney to v0.8.0 ([#1747](https://github.com/googleapis/google-http-java-client/issues/1747)) ([0866e4d](https://github.com/googleapis/google-http-java-client/commit/0866e4dbd882de6385df56ef47e03d56c2c102b1)) +* Update dependency jinja2 to v3.1.2 ([#1748](https://github.com/googleapis/google-http-java-client/issues/1748)) ([1507e04](https://github.com/googleapis/google-http-java-client/commit/1507e04d99f6d160f7b0c070d63e2d42dab76c2c)) +* Update dependency keyring to v23.9.3 ([#1749](https://github.com/googleapis/google-http-java-client/issues/1749)) ([55bcbd7](https://github.com/googleapis/google-http-java-client/commit/55bcbd7ede201e3a7ed9ee8b8c43510905fd61c5)) +* Update dependency markupsafe to v2.1.1 ([#1744](https://github.com/googleapis/google-http-java-client/issues/1744)) ([a62cace](https://github.com/googleapis/google-http-java-client/commit/a62cace610211ca6e9470e5b8e77e42a005733f0)) +* Update dependency org.apache.felix:maven-bundle-plugin to v5.1.7 ([#1688](https://github.com/googleapis/google-http-java-client/issues/1688)) ([8bea209](https://github.com/googleapis/google-http-java-client/commit/8bea209c7b23ffb5a57f683ae21889a87f9b7f55)) +* Update dependency org.apache.felix:maven-bundle-plugin to v5.1.8 ([#1699](https://github.com/googleapis/google-http-java-client/issues/1699)) ([fa578e0](https://github.com/googleapis/google-http-java-client/commit/fa578e0f7ad6a6c45a0b9de54a936a16a8d345a7)) +* Update dependency protobuf to v3.20.2 ([#1745](https://github.com/googleapis/google-http-java-client/issues/1745)) ([3b0fc85](https://github.com/googleapis/google-http-java-client/commit/3b0fc8567e55c26676524d81927feb7a6bd82a2f)) +* Update dependency protobuf to v4 ([#1733](https://github.com/googleapis/google-http-java-client/issues/1733)) ([99457dd](https://github.com/googleapis/google-http-java-client/commit/99457dddbd56e7d284d99227990a5a74fdb6e2e9)) +* Update dependency pyjwt to v2.5.0 ([#1728](https://github.com/googleapis/google-http-java-client/issues/1728)) ([c285b9a](https://github.com/googleapis/google-http-java-client/commit/c285b9a36bb8b07942f2b7d616b3653465fc2ae2)) +* Update dependency requests to v2.28.1 ([#1729](https://github.com/googleapis/google-http-java-client/issues/1729)) ([ee9fc81](https://github.com/googleapis/google-http-java-client/commit/ee9fc81d759f2ebb8a36e0eb36c58f7f634b893f)) +* Update dependency typing-extensions to v4.3.0 ([#1730](https://github.com/googleapis/google-http-java-client/issues/1730)) ([f8980a4](https://github.com/googleapis/google-http-java-client/commit/f8980a41fc77eabeba76326fee5553520a95861d)) +* Update dependency zipp to v3.8.1 ([#1731](https://github.com/googleapis/google-http-java-client/issues/1731)) ([49477d4](https://github.com/googleapis/google-http-java-client/commit/49477d4207d07bb6dfb00666201f219a01d87d72)) +* Update project.appengine.version to v2.0.6 ([#1704](https://github.com/googleapis/google-http-java-client/issues/1704)) ([b33a9c1](https://github.com/googleapis/google-http-java-client/commit/b33a9c173a74e631e9d7e04f51df4370f979da10)) +* Update project.appengine.version to v2.0.7 ([#1711](https://github.com/googleapis/google-http-java-client/issues/1711)) ([523a260](https://github.com/googleapis/google-http-java-client/commit/523a2609bef4b2d4a539a327d353e26f61d9a2c2)) +* Update project.appengine.version to v2.0.8 ([#1723](https://github.com/googleapis/google-http-java-client/issues/1723)) ([12a455c](https://github.com/googleapis/google-http-java-client/commit/12a455c38b4de3470033be61b06e2beafd911041)) +* Update project.appengine.version to v2.0.9 ([#1753](https://github.com/googleapis/google-http-java-client/issues/1753)) ([d047334](https://github.com/googleapis/google-http-java-client/commit/d047334616c9a88b00b20e749d2033fc1a6ca6ca)) + +## [1.42.2](https://github.com/googleapis/google-http-java-client/compare/v1.42.1...v1.42.2) (2022-07-13) + + +### Bug Fixes + +* enable longpaths support for windows test ([#1485](https://github.com/googleapis/google-http-java-client/issues/1485)) ([#1684](https://github.com/googleapis/google-http-java-client/issues/1684)) ([9d789f5](https://github.com/googleapis/google-http-java-client/commit/9d789f511b907c3970ed9845a4c092fe5458755d)) + +## [1.42.1](https://github.com/googleapis/google-http-java-client/compare/v1.42.0...v1.42.1) (2022-06-30) + + +### Dependencies + +* update dependency com.google.protobuf:protobuf-java to v3.21.2 ([#1676](https://github.com/googleapis/google-http-java-client/issues/1676)) ([d7638ec](https://github.com/googleapis/google-http-java-client/commit/d7638ec8a3e626790f33f4fb04889fe4dfb31575)) + +## [1.42.0](https://github.com/googleapis/google-http-java-client/compare/v1.41.7...v1.42.0) (2022-06-09) + + +### Features + +* add build scripts for native image testing in Java 17 ([#1440](https://github.com/googleapis/google-http-java-client/issues/1440)) ([#1666](https://github.com/googleapis/google-http-java-client/issues/1666)) ([05d4019](https://github.com/googleapis/google-http-java-client/commit/05d40193d40097e5a793154a0951f2577fc80f04)) +* next release from main branch is 1.42.0 ([#1633](https://github.com/googleapis/google-http-java-client/issues/1633)) ([9acb1ab](https://github.com/googleapis/google-http-java-client/commit/9acb1abaa97392174dd35c5e0e68346f8f653b5b)) + + +### Dependencies + +* update dependency com.fasterxml.jackson.core:jackson-core to v2.13.3 ([#1665](https://github.com/googleapis/google-http-java-client/issues/1665)) ([e4f0959](https://github.com/googleapis/google-http-java-client/commit/e4f095997050047d9a6cc20f034f5ef744aefd44)) +* update dependency com.google.errorprone:error_prone_annotations to v2.13.0 ([#1630](https://github.com/googleapis/google-http-java-client/issues/1630)) ([bf777b3](https://github.com/googleapis/google-http-java-client/commit/bf777b364c8aafec09c486dc965587eae90549df)) +* update dependency com.google.errorprone:error_prone_annotations to v2.13.1 ([#1632](https://github.com/googleapis/google-http-java-client/issues/1632)) ([9e46cd8](https://github.com/googleapis/google-http-java-client/commit/9e46cd85ed1c14161f6473f926802bf281edc4ad)) +* update dependency com.google.errorprone:error_prone_annotations to v2.14.0 ([#1667](https://github.com/googleapis/google-http-java-client/issues/1667)) ([3516e18](https://github.com/googleapis/google-http-java-client/commit/3516e185b811d1935eebce31ba65da4813f7e998)) +* update dependency com.google.protobuf:protobuf-java to v3.20.1 ([#1639](https://github.com/googleapis/google-http-java-client/issues/1639)) ([90a99e2](https://github.com/googleapis/google-http-java-client/commit/90a99e27b053f5dc6078d6d8cd9bfe150237e2b4)) +* update dependency com.google.protobuf:protobuf-java to v3.21.0 ([#1668](https://github.com/googleapis/google-http-java-client/issues/1668)) ([babbe94](https://github.com/googleapis/google-http-java-client/commit/babbe94104710db7b4b428756d7db6c069674ff1)) +* update dependency com.google.protobuf:protobuf-java to v3.21.1 ([#1669](https://github.com/googleapis/google-http-java-client/issues/1669)) ([30ec091](https://github.com/googleapis/google-http-java-client/commit/30ec091faea7b5ec9f130cb3fdee396e9923a4b9)) +* update dependency org.apache.felix:maven-bundle-plugin to v5.1.6 ([#1643](https://github.com/googleapis/google-http-java-client/issues/1643)) ([8547f5f](https://github.com/googleapis/google-http-java-client/commit/8547f5fff9b27782162b0b6f0db7445c02918a45)) +* update project.appengine.version to v2.0.5 ([#1662](https://github.com/googleapis/google-http-java-client/issues/1662)) ([2c82c0d](https://github.com/googleapis/google-http-java-client/commit/2c82c0d4da1162cbc6950cdd6b2f4472b884db13)) +* update project.opencensus.version to v0.31.1 ([#1644](https://github.com/googleapis/google-http-java-client/issues/1644)) ([3c65a07](https://github.com/googleapis/google-http-java-client/commit/3c65a07c14d2bf7aa6cce25122df85670955d459)) + +### [1.41.7](https://github.com/googleapis/google-http-java-client/compare/v1.41.6...v1.41.7) (2022-04-11) + + +### Dependencies + +* revert dependency com.google.protobuf:protobuf-java to v3.19.4 ([#1626](https://github.com/googleapis/google-http-java-client/issues/1626)) ([076433f](https://github.com/googleapis/google-http-java-client/commit/076433f3c233a757f31d5fa39bb6cedbb43b8361)) + +### [1.41.6](https://github.com/googleapis/google-http-java-client/compare/v1.41.5...v1.41.6) (2022-04-06) + + +### Bug Fixes + +* `Content-Encoding: gzip` along with `Transfer-Encoding: chunked` sometimes terminates early ([#1608](https://github.com/googleapis/google-http-java-client/issues/1608)) ([941da8b](https://github.com/googleapis/google-http-java-client/commit/941da8badf64068d11a53ac57a4ba35b2ad13490)) + + +### Dependencies + +* update dependency com.google.errorprone:error_prone_annotations to v2.12.1 ([#1622](https://github.com/googleapis/google-http-java-client/issues/1622)) ([4e1101d](https://github.com/googleapis/google-http-java-client/commit/4e1101d7674cb5715b88a00750cdd5286a9ae077)) +* update dependency com.google.protobuf:protobuf-java to v3.20.0 ([#1621](https://github.com/googleapis/google-http-java-client/issues/1621)) ([640dc40](https://github.com/googleapis/google-http-java-client/commit/640dc4080249b65e5cabb7e1ae6cd9cd5b11bd8e)) + +### [1.41.5](https://github.com/googleapis/google-http-java-client/compare/v1.41.4...v1.41.5) (2022-03-21) + + +### Documentation + +* **deps:** libraries-bom 24.4.0 release ([#1596](https://github.com/googleapis/google-http-java-client/issues/1596)) ([327fe12](https://github.com/googleapis/google-http-java-client/commit/327fe12a122ebb4022a2da55694217233a2badaf)) + + +### Dependencies + +* update actions/checkout action to v3 ([#1593](https://github.com/googleapis/google-http-java-client/issues/1593)) ([92002c0](https://github.com/googleapis/google-http-java-client/commit/92002c07d60b738657383e2484f56abc1cde6920)) +* update dependency com.fasterxml.jackson.core:jackson-core to v2.13.2 ([#1598](https://github.com/googleapis/google-http-java-client/issues/1598)) ([41ac833](https://github.com/googleapis/google-http-java-client/commit/41ac833249e18cbbd304f825b12202e51bebec85)) +* update project.appengine.version to v2 (major) ([#1597](https://github.com/googleapis/google-http-java-client/issues/1597)) ([c06cf95](https://github.com/googleapis/google-http-java-client/commit/c06cf95f9b1be77e2229c3b2f78ece0789eaec15)) + +### [1.41.4](https://github.com/googleapis/google-http-java-client/compare/v1.41.3...v1.41.4) (2022-02-11) + + +### Dependencies + +* update dependency com.google.code.gson:gson to v2.9.0 ([#1582](https://github.com/googleapis/google-http-java-client/issues/1582)) ([8772778](https://github.com/googleapis/google-http-java-client/commit/877277821dad65545518b06123e6e7b9801147a1)) + +### [1.41.3](https://github.com/googleapis/google-http-java-client/compare/v1.41.2...v1.41.3) (2022-02-09) + + +### Dependencies + +* update dependency com.google.protobuf:protobuf-java to v3.19.4 ([#1568](https://github.com/googleapis/google-http-java-client/issues/1568)) ([416e5d7](https://github.com/googleapis/google-http-java-client/commit/416e5d7146ad145e3d5140110144b5119c6126df)) +* update dependency com.puppycrawl.tools:checkstyle to v9.3 ([#1569](https://github.com/googleapis/google-http-java-client/issues/1569)) ([9c7ade8](https://github.com/googleapis/google-http-java-client/commit/9c7ade85eceb2dc348e1f9aa0637d0509d634160)) +* update project.opencensus.version to v0.31.0 ([#1563](https://github.com/googleapis/google-http-java-client/issues/1563)) ([0f9d2b7](https://github.com/googleapis/google-http-java-client/commit/0f9d2b77ae23ea143b5b8caaa21af6548ca92345)) + +### [1.41.2](https://github.com/googleapis/google-http-java-client/compare/v1.41.1...v1.41.2) (2022-01-27) + + +### Dependencies + +* **java:** update actions/github-script action to v5 ([#1339](https://github.com/googleapis/google-http-java-client/issues/1339)) ([#1561](https://github.com/googleapis/google-http-java-client/issues/1561)) ([c5dbec1](https://github.com/googleapis/google-http-java-client/commit/c5dbec1bbfb5f26f952cb8d80f607327594ab7a8)) +* update dependency com.google.errorprone:error_prone_annotations to v2.11.0 ([#1560](https://github.com/googleapis/google-http-java-client/issues/1560)) ([d9609b0](https://github.com/googleapis/google-http-java-client/commit/d9609b00089952d816deffa178640bfcae1f2c3a)) + +### [1.41.1](https://github.com/googleapis/google-http-java-client/compare/v1.41.0...v1.41.1) (2022-01-21) + + +### Dependencies + +* update dependency com.fasterxml.jackson.core:jackson-core to v2.13.1 ([#1527](https://github.com/googleapis/google-http-java-client/issues/1527)) ([7750398](https://github.com/googleapis/google-http-java-client/commit/7750398d6f4d6e447bfe078092f5cb146f747e50)) +* update dependency com.google.protobuf:protobuf-java to v3.19.3 ([#1549](https://github.com/googleapis/google-http-java-client/issues/1549)) ([50c0765](https://github.com/googleapis/google-http-java-client/commit/50c0765f1eadbf7aef2dccf5f78ab62e2533c6f6)) +* update dependency com.puppycrawl.tools:checkstyle to v9.2.1 ([#1532](https://github.com/googleapis/google-http-java-client/issues/1532)) ([e13eebd](https://github.com/googleapis/google-http-java-client/commit/e13eebd288afbde3aa7bdc0229c2d0db90ebbd4c)) +* update dependency kr.motd.maven:os-maven-plugin to v1.7.0 ([#1547](https://github.com/googleapis/google-http-java-client/issues/1547)) ([8df0dbe](https://github.com/googleapis/google-http-java-client/commit/8df0dbe53521e918985e8f4882392cd2e0a0a1c3)) +* update dependency org.apache.felix:maven-bundle-plugin to v5 ([#1548](https://github.com/googleapis/google-http-java-client/issues/1548)) ([ac10b6c](https://github.com/googleapis/google-http-java-client/commit/ac10b6c9fbe4986b8bf130d9f83ae77e84d74e5f)) +* update project.appengine.version to v1.9.94 ([#1557](https://github.com/googleapis/google-http-java-client/issues/1557)) ([05c78f4](https://github.com/googleapis/google-http-java-client/commit/05c78f4bee92cc501aa084ad970ed6ac9c0e0444)) +* update project.opencensus.version to v0.30.0 ([#1526](https://github.com/googleapis/google-http-java-client/issues/1526)) ([318e54a](https://github.com/googleapis/google-http-java-client/commit/318e54ae9be6bfeb4f5af0af0cb954031d95d1f9)) + +## [1.41.0](https://www.github.com/googleapis/google-http-java-client/compare/v1.40.1...v1.41.0) (2022-01-05) + + +### Features + +* add AttemptCount to HttpResponseException ([#1505](https://www.github.com/googleapis/google-http-java-client/issues/1505)) ([ea0f6c0](https://www.github.com/googleapis/google-http-java-client/commit/ea0f6c0f58e8abffae1362feb344a9309d6d814e)) +* next release from main branch is 1.41.0 ([#1478](https://www.github.com/googleapis/google-http-java-client/issues/1478)) ([3ad4831](https://www.github.com/googleapis/google-http-java-client/commit/3ad4831da00579f534ff7eb7de3a0386068902ba)) + + +### Bug Fixes + +* **java:** add -ntp flag to native image testing command ([#1299](https://www.github.com/googleapis/google-http-java-client/issues/1299)) ([#1522](https://www.github.com/googleapis/google-http-java-client/issues/1522)) ([39f63c3](https://www.github.com/googleapis/google-http-java-client/commit/39f63c3ea255fe256391567e66ada7b4122b16f6)) +* **java:** java 17 dependency arguments ([#1266](https://www.github.com/googleapis/google-http-java-client/issues/1266)) ([#1489](https://www.github.com/googleapis/google-http-java-client/issues/1489)) ([4a26e18](https://www.github.com/googleapis/google-http-java-client/commit/4a26e1881075a4f361ec746c2444111c911a8d9f)) + + +### Dependencies + +* update dependency com.coveo:fmt-maven-plugin to v2.12 ([#1487](https://www.github.com/googleapis/google-http-java-client/issues/1487)) ([8b1b8f2](https://www.github.com/googleapis/google-http-java-client/commit/8b1b8f280774115d0521e0f5eada6dd0ef995ca2)) +* update dependency com.google.code.gson:gson to v2.8.9 ([#1492](https://www.github.com/googleapis/google-http-java-client/issues/1492)) ([6615933](https://www.github.com/googleapis/google-http-java-client/commit/6615933e3162969f16d8a0d887afe9f4011e9e5c)) +* update dependency com.google.errorprone:error_prone_annotations to v2.10.0 ([#1498](https://www.github.com/googleapis/google-http-java-client/issues/1498)) ([a6a73c2](https://www.github.com/googleapis/google-http-java-client/commit/a6a73c25104aa2074b0a2bcf021513f943c727d4)) +* update dependency com.google.protobuf:protobuf-java to v3.19.1 ([#1488](https://www.github.com/googleapis/google-http-java-client/issues/1488)) ([24e6c51](https://www.github.com/googleapis/google-http-java-client/commit/24e6c51112e42f12701b5213a4c5f96466d3f7e2)) +* update dependency com.google.protobuf:protobuf-java to v3.19.2 ([#1539](https://www.github.com/googleapis/google-http-java-client/issues/1539)) ([772370a](https://www.github.com/googleapis/google-http-java-client/commit/772370aad7269d30971a38b4471e534d1af9c45a)) +* update dependency com.puppycrawl.tools:checkstyle to v9.1 ([#1493](https://www.github.com/googleapis/google-http-java-client/issues/1493)) ([87b980b](https://www.github.com/googleapis/google-http-java-client/commit/87b980b72f7764aae2a1c5f38d321b25ed7471c4)) +* update dependency com.puppycrawl.tools:checkstyle to v9.2 ([#1510](https://www.github.com/googleapis/google-http-java-client/issues/1510)) ([0922b67](https://www.github.com/googleapis/google-http-java-client/commit/0922b670e4949ca45b2b25a2d89eea2818349a35)) +* update dependency org.apache.httpcomponents:httpcore to v4.4.15 ([#1523](https://www.github.com/googleapis/google-http-java-client/issues/1523)) ([6148d97](https://www.github.com/googleapis/google-http-java-client/commit/6148d9732a7bd745064d68706de75707a9acbb8f)) +* update project.appengine.version to v1.9.92 ([#1495](https://www.github.com/googleapis/google-http-java-client/issues/1495)) ([43c3b11](https://www.github.com/googleapis/google-http-java-client/commit/43c3b116a173d639a1214121e21ffea2fc32935c)) +* update project.appengine.version to v1.9.93 ([#1516](https://www.github.com/googleapis/google-http-java-client/issues/1516)) ([2fa47c6](https://www.github.com/googleapis/google-http-java-client/commit/2fa47c63e5422bf88fe1320e97e0f61265792d8a)) + +### [1.40.1](https://www.github.com/googleapis/google-http-java-client/compare/v1.40.0...v1.40.1) (2021-10-07) + + +### Bug Fixes + +* add used packages to OSGI manifest again ([#1439](https://www.github.com/googleapis/google-http-java-client/issues/1439)) ([#1440](https://www.github.com/googleapis/google-http-java-client/issues/1440)) ([59fc8b0](https://www.github.com/googleapis/google-http-java-client/commit/59fc8b03e5518864c60ce4dd47664e8935da343b)) +* update NetHttpRequest to prevent silent retry of DELETE requests ([#1472](https://www.github.com/googleapis/google-http-java-client/issues/1472)) ([57ef11a](https://www.github.com/googleapis/google-http-java-client/commit/57ef11a0e1904bb932e5493a30f0a2ca2a2798cf)), closes [#1471](https://www.github.com/googleapis/google-http-java-client/issues/1471) + + +### Dependencies + +* update dependency com.fasterxml.jackson.core:jackson-core to v2.12.5 ([#1437](https://www.github.com/googleapis/google-http-java-client/issues/1437)) ([0ce8467](https://www.github.com/googleapis/google-http-java-client/commit/0ce84676bfbe4cc8e237d5e33dfaa532b13e798c)) +* update dependency com.fasterxml.jackson.core:jackson-core to v2.13.0 ([#1469](https://www.github.com/googleapis/google-http-java-client/issues/1469)) ([7d9a042](https://www.github.com/googleapis/google-http-java-client/commit/7d9a042110b8879b592d7e80bd73e77c7a84d8b7)) +* update dependency com.google.protobuf:protobuf-java to v3.18.0 ([#1454](https://www.github.com/googleapis/google-http-java-client/issues/1454)) ([cc63e41](https://www.github.com/googleapis/google-http-java-client/commit/cc63e41fac8295c7fea751191a6fe9537c1f70e3)) +* update dependency com.google.protobuf:protobuf-java to v3.18.1 ([#1470](https://www.github.com/googleapis/google-http-java-client/issues/1470)) ([c36637a](https://www.github.com/googleapis/google-http-java-client/commit/c36637acbca536992349970664026cf145ad8964)) +* update dependency com.puppycrawl.tools:checkstyle to v9 ([#1441](https://www.github.com/googleapis/google-http-java-client/issues/1441)) ([a95cd97](https://www.github.com/googleapis/google-http-java-client/commit/a95cd9717fc8accd80252b12357971cb71887d90)) +* update project.appengine.version to v1.9.91 ([#1287](https://www.github.com/googleapis/google-http-java-client/issues/1287)) ([09ebf8d](https://www.github.com/googleapis/google-http-java-client/commit/09ebf8d7e3860f2b94a6fea0ef134c93904d4ed1)) + +## [1.40.0](https://www.github.com/googleapis/google-http-java-client/compare/v1.39.2...v1.40.0) (2021-08-20) + + +### Features + +* add `gcf-owl-bot[bot]` to `ignoreAuthors` ([#1380](https://www.github.com/googleapis/google-http-java-client/issues/1380)) ([e69275e](https://www.github.com/googleapis/google-http-java-client/commit/e69275ecaa4d85372ebc253dd415a02ba63075be)) + + +### Bug Fixes + +* GSON parser now throws IOException on invalid JSON input ([#1355](https://www.github.com/googleapis/google-http-java-client/issues/1355)) ([0a505a7](https://www.github.com/googleapis/google-http-java-client/commit/0a505a7ce012efcce14af94aa130d0eab2ac89b6)) +* Add shopt -s nullglob to dependencies script ([#1412](https://www.github.com/googleapis/google-http-java-client/issues/1412)) ([933b0bd](https://www.github.com/googleapis/google-http-java-client/commit/933b0bd386f413bd960f81c706edae81d9dc030a)) +* default charset to UTF-8 for text/csv if not specified ([#1423](https://www.github.com/googleapis/google-http-java-client/issues/1423)) ([26f3da4](https://www.github.com/googleapis/google-http-java-client/commit/26f3da4b6426625d0d88afdad525dbf99c65bc8b)) +* make depencence on javax.annotation optional ([#1323](https://www.github.com/googleapis/google-http-java-client/issues/1323)) ([#1405](https://www.github.com/googleapis/google-http-java-client/issues/1405)) ([4ccad0e](https://www.github.com/googleapis/google-http-java-client/commit/4ccad0e9f37adaf5adac469e8dec478eb424a410)) +* release scripts from issuing overlapping phases ([#1344](https://www.github.com/googleapis/google-http-java-client/issues/1344)) ([539407e](https://www.github.com/googleapis/google-http-java-client/commit/539407ef7133df7f5b1e0f371c673dbc75e79ff2)) +* test error responses such as 403 ([#1345](https://www.github.com/googleapis/google-http-java-client/issues/1345)) ([a83c43f](https://www.github.com/googleapis/google-http-java-client/commit/a83c43fa86966ca1be625086a211211e3861f7b1)) +* typo ([#1342](https://www.github.com/googleapis/google-http-java-client/issues/1342)) ([2bbc0e4](https://www.github.com/googleapis/google-http-java-client/commit/2bbc0e4b77ab2c9956b0a65af0e927d5052a7752)) +* Update dependencies.sh to not break on mac ([933b0bd](https://www.github.com/googleapis/google-http-java-client/commit/933b0bd386f413bd960f81c706edae81d9dc030a)) +* Use BufferedInputStream to inspect HttpResponse error ([#1411](https://www.github.com/googleapis/google-http-java-client/issues/1411)) ([33acb86](https://www.github.com/googleapis/google-http-java-client/commit/33acb8621d6e8dc088cf3bd3324a3db25dafb185)) + + +### Documentation + +* bom 20.3.0 ([#1368](https://www.github.com/googleapis/google-http-java-client/issues/1368)) ([0d8d2fe](https://www.github.com/googleapis/google-http-java-client/commit/0d8d2fee8750bcaa79f2c8ee106f17b89de81e58)) +* libraries-bom 20.1.0 ([#1347](https://www.github.com/googleapis/google-http-java-client/issues/1347)) ([2570889](https://www.github.com/googleapis/google-http-java-client/commit/2570889e95c7c3bf26d5666dc69a7bb09efd7655)) +* libraries-bom 20.5.0 ([#1388](https://www.github.com/googleapis/google-http-java-client/issues/1388)) ([38dc3f6](https://www.github.com/googleapis/google-http-java-client/commit/38dc3f64d24868f90bfc9728ace0ce6aaeb2940a)) +* libraries-bom 20.9.0 ([#1416](https://www.github.com/googleapis/google-http-java-client/issues/1416)) ([c6aba10](https://www.github.com/googleapis/google-http-java-client/commit/c6aba10ea9a5c5acc9d07317c5b983309b45e2eb)) + + +### Dependencies + +* update dependency com.fasterxml.jackson.core:jackson-core to v2.12.3 ([#1340](https://www.github.com/googleapis/google-http-java-client/issues/1340)) ([81e479a](https://www.github.com/googleapis/google-http-java-client/commit/81e479ac59797ad49e503eb2d41ff17c9cb77d7b)) +* update dependency com.fasterxml.jackson.core:jackson-core to v2.12.4 ([#1406](https://www.github.com/googleapis/google-http-java-client/issues/1406)) ([fa07715](https://www.github.com/googleapis/google-http-java-client/commit/fa07715f528f74e0ef1c5737c6730c505746a7ad)) +* update dependency com.google.code.gson:gson to v2.8.7 ([#1386](https://www.github.com/googleapis/google-http-java-client/issues/1386)) ([550abc1](https://www.github.com/googleapis/google-http-java-client/commit/550abc1e9f3209ec87b20f81c9e0ecdb27aedb7c)) +* update dependency com.google.code.gson:gson to v2.8.8 ([#1430](https://www.github.com/googleapis/google-http-java-client/issues/1430)) ([ae4b0db](https://www.github.com/googleapis/google-http-java-client/commit/ae4b0dbbcf2535e660c70dd9ac0ea20d7f040181)) +* update dependency com.google.errorprone:error_prone_annotations to v2.7.1 ([#1378](https://www.github.com/googleapis/google-http-java-client/issues/1378)) ([83b1642](https://www.github.com/googleapis/google-http-java-client/commit/83b164245d4e3298c7cee5b10ab7917f6c85e7b1)) +* update dependency com.google.errorprone:error_prone_annotations to v2.8.0 ([#1414](https://www.github.com/googleapis/google-http-java-client/issues/1414)) ([1508657](https://www.github.com/googleapis/google-http-java-client/commit/1508657d27b41babb530a914bd2708c567ac08ef)) +* update dependency com.google.errorprone:error_prone_annotations to v2.8.1 ([#1420](https://www.github.com/googleapis/google-http-java-client/issues/1420)) ([1f8be1c](https://www.github.com/googleapis/google-http-java-client/commit/1f8be1c222d7f3fd165abe57387d2f8d9e63d82f)) +* update dependency com.google.errorprone:error_prone_annotations to v2.9.0 ([#1429](https://www.github.com/googleapis/google-http-java-client/issues/1429)) ([834ade3](https://www.github.com/googleapis/google-http-java-client/commit/834ade362070c9c93f9eb8a08df3308df46d51f2)) +* update dependency com.google.protobuf:protobuf-java to v3.16.0 ([#1366](https://www.github.com/googleapis/google-http-java-client/issues/1366)) ([3148f5d](https://www.github.com/googleapis/google-http-java-client/commit/3148f5daab8598957e05849eaec2eab0b634321d)) +* update dependency com.google.protobuf:protobuf-java to v3.17.0 ([#1373](https://www.github.com/googleapis/google-http-java-client/issues/1373)) ([d147628](https://www.github.com/googleapis/google-http-java-client/commit/d147628742bbd327a405e87b1645d1d4bf1f7610)) +* update dependency com.google.protobuf:protobuf-java to v3.17.1 ([#1384](https://www.github.com/googleapis/google-http-java-client/issues/1384)) ([c22a0e0](https://www.github.com/googleapis/google-http-java-client/commit/c22a0e0e1c1a4a6e8c93b38db519b49eba4e2f14)) +* update dependency com.google.protobuf:protobuf-java to v3.17.2 ([#1390](https://www.github.com/googleapis/google-http-java-client/issues/1390)) ([b34349f](https://www.github.com/googleapis/google-http-java-client/commit/b34349f5d303f15b28c69a995763f3842738177c)) +* update dependency com.google.protobuf:protobuf-java to v3.17.3 ([#1394](https://www.github.com/googleapis/google-http-java-client/issues/1394)) ([4e3b3c3](https://www.github.com/googleapis/google-http-java-client/commit/4e3b3c3cebeb8439e729a9f99b58e5fc5e13e2cf)) + +### [1.39.2](https://www.github.com/googleapis/google-http-java-client/compare/v1.39.1...v1.39.2) (2021-04-09) + + +### Dependencies + +* update dependency com.google.errorprone:error_prone_annotations to v2.6.0 ([#1327](https://www.github.com/googleapis/google-http-java-client/issues/1327)) ([3feef0c](https://www.github.com/googleapis/google-http-java-client/commit/3feef0ccd2ca298bdf136da14b4e4b864df423db)) +* update dependency com.google.protobuf:protobuf-java to v3.15.7 ([#1329](https://www.github.com/googleapis/google-http-java-client/issues/1329)) ([afbbb3f](https://www.github.com/googleapis/google-http-java-client/commit/afbbb3fe441a41e8f0d4ecdb3f46b798c708a46b)) +* update dependency com.google.protobuf:protobuf-java to v3.15.8 ([#1334](https://www.github.com/googleapis/google-http-java-client/issues/1334)) ([e10565a](https://www.github.com/googleapis/google-http-java-client/commit/e10565af31e531f7a1fbd8bbac0a9a69fbef5a80)) +* update Guava patch ([#1333](https://www.github.com/googleapis/google-http-java-client/issues/1333)) ([854942a](https://www.github.com/googleapis/google-http-java-client/commit/854942aff7302be77e6f62f9cf7b5dc5e1928c90)) + +### [1.39.1](https://www.github.com/googleapis/google-http-java-client/compare/v1.39.0...v1.39.1) (2021-03-15) + + +### Bug Fixes + +* default application/json charset to utf-8 ([#1305](https://www.github.com/googleapis/google-http-java-client/issues/1305)) ([c4dfb48](https://www.github.com/googleapis/google-http-java-client/commit/c4dfb48cb8248564b19efdf1a4272eb6fafe3138)), closes [#1102](https://www.github.com/googleapis/google-http-java-client/issues/1102) +* when disconnecting, close the underlying connection before the response InputStream ([#1315](https://www.github.com/googleapis/google-http-java-client/issues/1315)) ([f84ed59](https://www.github.com/googleapis/google-http-java-client/commit/f84ed5964f376ada5eb724a3d1f3ac526d31d9c5)), closes [#1303](https://www.github.com/googleapis/google-http-java-client/issues/1303) + + +### Documentation + +* 19.0.0 libraries-bom ([#1312](https://www.github.com/googleapis/google-http-java-client/issues/1312)) ([62be21b](https://www.github.com/googleapis/google-http-java-client/commit/62be21b84a5394455d828b0f97f9e53352b8aa18)) +* update version ([#1296](https://www.github.com/googleapis/google-http-java-client/issues/1296)) ([f17755c](https://www.github.com/googleapis/google-http-java-client/commit/f17755cf5e8ccbf441131ebb13fe60028fb63850)) + + +### Dependencies + +* update dependency com.fasterxml.jackson.core:jackson-core to v2.12.2 ([#1309](https://www.github.com/googleapis/google-http-java-client/issues/1309)) ([aa7d703](https://www.github.com/googleapis/google-http-java-client/commit/aa7d703d94e5e34d849bc753cfe8bd332ff80443)) +* update dependency com.google.protobuf:protobuf-java to v3.15.3 ([#1301](https://www.github.com/googleapis/google-http-java-client/issues/1301)) ([1db338b](https://www.github.com/googleapis/google-http-java-client/commit/1db338b8b98465e03e93013b40fd8d821ac245c8)) +* update dependency com.google.protobuf:protobuf-java to v3.15.6 ([#1310](https://www.github.com/googleapis/google-http-java-client/issues/1310)) ([9cb50e4](https://www.github.com/googleapis/google-http-java-client/commit/9cb50e49e1cfc196b915465bb6ecbd90fb6d04d7)) + +## [1.39.0](https://www.github.com/googleapis/google-http-java-client/compare/v1.38.1...v1.39.0) (2021-02-24) + + +### Features + +* add http.status_code attribute to all Spans that have at least a low level http response ([#986](https://www.github.com/googleapis/google-http-java-client/issues/986)) ([fb02042](https://www.github.com/googleapis/google-http-java-client/commit/fb02042ac216379820950879cea45d06eec5278c)) + + +### Bug Fixes + +* deprecate obsolete utility methods ([#1231](https://www.github.com/googleapis/google-http-java-client/issues/1231)) ([8f95371](https://www.github.com/googleapis/google-http-java-client/commit/8f95371cf5681fbc67bd598d74089f38742a1177)) +* fix buildRequest setUrl order ([#1255](https://www.github.com/googleapis/google-http-java-client/issues/1255)) ([97ffee1](https://www.github.com/googleapis/google-http-java-client/commit/97ffee1a68af6637dd5d53fcd70e2ce02c9c9604)) +* refactor to use StandardCharsets ([#1243](https://www.github.com/googleapis/google-http-java-client/issues/1243)) ([03ec798](https://www.github.com/googleapis/google-http-java-client/commit/03ec798d7637ff454614415be7b324cd8dc7c77c)) +* remove old broken link ([#1275](https://www.github.com/googleapis/google-http-java-client/issues/1275)) ([12f80e0](https://www.github.com/googleapis/google-http-java-client/commit/12f80e09e71a41b967db548ab93cab2e3f4e549c)), closes [#1278](https://www.github.com/googleapis/google-http-java-client/issues/1278) +* remove unused logger ([#1228](https://www.github.com/googleapis/google-http-java-client/issues/1228)) ([779d383](https://www.github.com/googleapis/google-http-java-client/commit/779d3832ffce741b7c4055a14855ce8755695fce)) + + +### Documentation + +* Jackson is unable to maintain stable Javadocs ([#1265](https://www.github.com/googleapis/google-http-java-client/issues/1265)) ([9e8fcff](https://www.github.com/googleapis/google-http-java-client/commit/9e8fcfffc6d92505528aff0a89c169bf3e812c41)) + + +### Dependencies + +* update dependency com.google.protobuf:protobuf-java to v3.15.1 ([#1270](https://www.github.com/googleapis/google-http-java-client/issues/1270)) ([213726a](https://www.github.com/googleapis/google-http-java-client/commit/213726a0b65f35fdc65713027833d22b553bbc20)) +* update dependency com.google.protobuf:protobuf-java to v3.15.2 ([#1284](https://www.github.com/googleapis/google-http-java-client/issues/1284)) ([dfa06bc](https://www.github.com/googleapis/google-http-java-client/commit/dfa06bca432f644a7146e3987555f19c5d1be7c5)) +* update OpenCensus to 0.28.0 for consistency with gRPC ([#1242](https://www.github.com/googleapis/google-http-java-client/issues/1242)) ([b810d53](https://www.github.com/googleapis/google-http-java-client/commit/b810d53c8f63380c1b4f398408cfb47c6ab134cc)) +* version manage error_prone_annotations to 2.5.1 ([#1268](https://www.github.com/googleapis/google-http-java-client/issues/1268)) ([6a95f6f](https://www.github.com/googleapis/google-http-java-client/commit/6a95f6f2494a9dafd968d212b15c9b329416864f)) + +### [1.38.1](https://www.github.com/googleapis/google-http-java-client/compare/v1.38.0...v1.38.1) (2021-01-12) + + +### Bug Fixes + +* address some deprecation warnings in Java 9+ ([#1215](https://www.github.com/googleapis/google-http-java-client/issues/1215)) ([9f53a67](https://www.github.com/googleapis/google-http-java-client/commit/9f53a6788e20bbded1b5937a5e8fe19ace31beaa)) +* deprecate JacksonFactory in favor of GsonFactory to align with security team advice ([#1216](https://www.github.com/googleapis/google-http-java-client/issues/1216)) ([6b9b6c5](https://www.github.com/googleapis/google-http-java-client/commit/6b9b6c57734c4917394d0e256e745d69b61b5517)) +* JSON spec mandates UTF-8 ([#1220](https://www.github.com/googleapis/google-http-java-client/issues/1220)) ([adb2ea4](https://www.github.com/googleapis/google-http-java-client/commit/adb2ea41c4eee61174ec6e588dec576fc53169f6)) + + +### Documentation + +* BOM 15.0.0 ([#1177](https://www.github.com/googleapis/google-http-java-client/issues/1177)) ([125a697](https://www.github.com/googleapis/google-http-java-client/commit/125a697c5cb5535894e46fd59e73663c50f3a6fa)) + + +### Dependencies + +* update guava to 30.1-android ([#1199](https://www.github.com/googleapis/google-http-java-client/issues/1199)) ([7922dc0](https://www.github.com/googleapis/google-http-java-client/commit/7922dc0517bd82669a18b81af38e5ba211bc2e0b)) + +## [1.38.0](https://www.github.com/googleapis/google-http-java-client/compare/v1.37.0...v1.38.0) (2020-11-02) + + +### Features + +* add isMtls property to ApacheHttpTransport ([#1168](https://www.github.com/googleapis/google-http-java-client/issues/1168)) ([c416e20](https://www.github.com/googleapis/google-http-java-client/commit/c416e201c92a5c5fc1b1c59c5dd63e8ec1463f5f)) +* add mtls support for NetHttpTransport ([#1147](https://www.github.com/googleapis/google-http-java-client/issues/1147)) ([51762f2](https://www.github.com/googleapis/google-http-java-client/commit/51762f221ec8ab38da03149c8012e63aec0433dc)) + + +### Dependencies + +* guava 30.0-android ([#1151](https://www.github.com/googleapis/google-http-java-client/issues/1151)) ([969dbbf](https://www.github.com/googleapis/google-http-java-client/commit/969dbbf127708aff16309f82538aca6f0a651638)) + + +### Documentation + +* libraries-bom 13.4.0 ([#1170](https://www.github.com/googleapis/google-http-java-client/issues/1170)) ([6818a02](https://www.github.com/googleapis/google-http-java-client/commit/6818a02a15e1bef8e9f5ea56a4ecc2b8d0646f9b)) + +## [1.37.0](https://www.github.com/googleapis/google-http-java-client/compare/v1.36.0...v1.37.0) (2020-10-13) + + +### Features + +* add flag to allow UrlEncodedContent to use UriPath escaping ([#1100](https://www.github.com/googleapis/google-http-java-client/issues/1100)) ([9ab7016](https://www.github.com/googleapis/google-http-java-client/commit/9ab7016032327f6fb0f91970dfbd511b029dd949)), closes [#1098](https://www.github.com/googleapis/google-http-java-client/issues/1098) + + +### Bug Fixes + +* make google-http-client.properties file shading friendly ([#1046](https://www.github.com/googleapis/google-http-java-client/issues/1046)) ([860bb05](https://www.github.com/googleapis/google-http-java-client/commit/860bb0541bcd7fc516cad14dd0d52481c7c7b414)) + + +### Dependencies + +* update protobuf-java to 3.13.0 ([#1093](https://www.github.com/googleapis/google-http-java-client/issues/1093)) ([b7e9663](https://www.github.com/googleapis/google-http-java-client/commit/b7e96632234e944e0e476dedfc822333716756bb)) + + +### Documentation + +* libraries-bom 12.0.0 ([#1136](https://www.github.com/googleapis/google-http-java-client/issues/1136)) ([450fcb2](https://www.github.com/googleapis/google-http-java-client/commit/450fcb2293cf3fa7c788cf0cc8ae48e865ae8de8)) + +## [1.36.0](https://www.github.com/googleapis/google-http-java-client/compare/v1.35.0...v1.36.0) (2020-06-30) + + +### Features + +* add Android 19 compatible FileDataStoreFactory implementation ([#1070](https://www.github.com/googleapis/google-http-java-client/issues/1070)) ([1150acd](https://www.github.com/googleapis/google-http-java-client/commit/1150acd38aa3139eea4f2f718545c20d2493877e)) + + +### Bug Fixes + +* restore the thread's interrupted status after catching InterruptedException ([#1005](https://www.github.com/googleapis/google-http-java-client/issues/1005)) ([#1006](https://www.github.com/googleapis/google-http-java-client/issues/1006)) ([0a73a46](https://www.github.com/googleapis/google-http-java-client/commit/0a73a4628b6ec4420db6b9cdbcc68899f3807c5b)) + +## [1.35.0](https://www.github.com/googleapis/google-http-java-client/compare/v1.34.2...v1.35.0) (2020-04-27) + + +### Features + +* add logic for verifying ES256 JsonWebSignatures ([#1033](https://www.github.com/googleapis/google-http-java-client/issues/1033)) ([bb4227f](https://www.github.com/googleapis/google-http-java-client/commit/bb4227f9daec44fc2976fa9947e2ff5ee07ed21a)) + + +### Bug Fixes + +* add linkage monitor plugin ([#1000](https://www.github.com/googleapis/google-http-java-client/issues/1000)) ([027c227](https://www.github.com/googleapis/google-http-java-client/commit/027c227e558164f77be204152fb47023850b543f)) +* Correctly handling chunked response streams with gzip ([#990](https://www.github.com/googleapis/google-http-java-client/issues/990)) ([1ba2197](https://www.github.com/googleapis/google-http-java-client/commit/1ba219743e65c89bc3fdb196acc5d2042e01f542)), closes [#367](https://www.github.com/googleapis/google-http-java-client/issues/367) +* FileDataStoreFactory will throw IOException for any permissions errors ([#1012](https://www.github.com/googleapis/google-http-java-client/issues/1012)) ([fd33073](https://www.github.com/googleapis/google-http-java-client/commit/fd33073da3674997897d7a9057d1d0e9d42d7cd4)) +* include request method and URL into HttpResponseException message ([#1002](https://www.github.com/googleapis/google-http-java-client/issues/1002)) ([15111a1](https://www.github.com/googleapis/google-http-java-client/commit/15111a1001d6f72cb92cd2d76aaed6f1229bc14a)) +* incorrect check for Windows OS in FileDataStoreFactory ([#927](https://www.github.com/googleapis/google-http-java-client/issues/927)) ([8b4eabe](https://www.github.com/googleapis/google-http-java-client/commit/8b4eabe985794fc64ad6a4a53f8f96201cf73fb8)) +* reuse reference instead of calling getter twice ([#983](https://www.github.com/googleapis/google-http-java-client/issues/983)) ([1f66222](https://www.github.com/googleapis/google-http-java-client/commit/1f662224d7bee6e27e8d66975fda39feae0c9359)), closes [#982](https://www.github.com/googleapis/google-http-java-client/issues/982) +* **android:** set minimum API level to 19 a.k.a. 4.4 Kit Kat ([#1016](https://www.github.com/googleapis/google-http-java-client/issues/1016)) ([b9a8023](https://www.github.com/googleapis/google-http-java-client/commit/b9a80232c9c8b16a3c3277458835f72e346f6b2c)), closes [#1015](https://www.github.com/googleapis/google-http-java-client/issues/1015) + + +### Documentation + +* android 4.4 or later is required ([#1008](https://www.github.com/googleapis/google-http-java-client/issues/1008)) ([bcc41dd](https://www.github.com/googleapis/google-http-java-client/commit/bcc41dd615af41ae6fb58287931cbf9c2144a075)) +* libraries-bom 4.0.1 ([#976](https://www.github.com/googleapis/google-http-java-client/issues/976)) ([fc21dc4](https://www.github.com/googleapis/google-http-java-client/commit/fc21dc412566ef60d23f1f82db5caf3cfd5d447b)) +* libraries-bom 4.1.1 ([#984](https://www.github.com/googleapis/google-http-java-client/issues/984)) ([635c813](https://www.github.com/googleapis/google-http-java-client/commit/635c81352ae383b3abfe6d7c141d987a6944b3e9)) +* libraries-bom 5.2.0 ([#1032](https://www.github.com/googleapis/google-http-java-client/issues/1032)) ([ca34202](https://www.github.com/googleapis/google-http-java-client/commit/ca34202bfa077adb70313b6c4562c7a5d904e064)) +* require Android 4.4 ([#1007](https://www.github.com/googleapis/google-http-java-client/issues/1007)) ([f9d2bb0](https://www.github.com/googleapis/google-http-java-client/commit/f9d2bb030398fe09e3c47b84ea468603355e08e9)) + + +### Dependencies + +* httpclient 4.5.12 ([#991](https://www.github.com/googleapis/google-http-java-client/issues/991)) ([79bc1c7](https://www.github.com/googleapis/google-http-java-client/commit/79bc1c76ebd48d396a080ef715b9f07cd056b7ef)) +* update to Guava 29 ([#1024](https://www.github.com/googleapis/google-http-java-client/issues/1024)) ([ca9520f](https://www.github.com/googleapis/google-http-java-client/commit/ca9520f2da4babc5bbd28c828da1deb7dbdc87e5)) + +### [1.34.2](https://www.github.com/googleapis/google-http-java-client/compare/v1.34.1...v1.34.2) (2020-02-12) + + +### Bug Fixes + +* use %20 to escpae spaces in URI templates ([#973](https://www.github.com/googleapis/google-http-java-client/issues/973)) ([60ba4ea](https://www.github.com/googleapis/google-http-java-client/commit/60ba4ea771d8ad0a98eddca10a77c5241187d28c)) + + +### Documentation + +* bom 4.0.0 ([#970](https://www.github.com/googleapis/google-http-java-client/issues/970)) ([198453b](https://www.github.com/googleapis/google-http-java-client/commit/198453b8b9e0765439ac430deaf10ef9df084665)) + +### [1.34.1](https://www.github.com/googleapis/google-http-java-client/compare/v1.34.0...v1.34.1) (2020-01-26) + + +### Bug Fixes + +* include '+' in SAFEPATHCHARS_URLENCODER ([#955](https://www.github.com/googleapis/google-http-java-client/issues/955)) ([9384459](https://www.github.com/googleapis/google-http-java-client/commit/9384459015b37e1671aebadc4b8c25dc9e1e033f)) +* use random UUID for multipart boundary delimiter ([#916](https://www.github.com/googleapis/google-http-java-client/issues/916)) ([91c20a3](https://www.github.com/googleapis/google-http-java-client/commit/91c20a3dfb654e85104b1c09a0b2befbae356c19)) + + +### Dependencies + +* remove unnecessary MySQL dependency ([#943](https://www.github.com/googleapis/google-http-java-client/issues/943)) ([14736ca](https://www.github.com/googleapis/google-http-java-client/commit/14736cab3dc060ea5b60522ea587cfaf66f29699)) +* update dependency mysql:mysql-connector-java to v8.0.19 ([#940](https://www.github.com/googleapis/google-http-java-client/issues/940)) ([e76368e](https://www.github.com/googleapis/google-http-java-client/commit/e76368ef9479a3bf06f7c7cb878d4e8e241bb58c)) +* update dependency org.apache.httpcomponents:httpcore to v4.4.13 ([#941](https://www.github.com/googleapis/google-http-java-client/issues/941)) ([fd904d2](https://www.github.com/googleapis/google-http-java-client/commit/fd904d26d67b06fac807d38f8fe4141891ef0330)) + + +### Documentation + +* fix various paragraph issues in javadoc ([#867](https://www.github.com/googleapis/google-http-java-client/issues/867)) ([029bbbf](https://www.github.com/googleapis/google-http-java-client/commit/029bbbfb5ddfefe64e64ecca4b1413ae1c93ddd8)) +* libraries-bom 3.3.0 ([#921](https://www.github.com/googleapis/google-http-java-client/issues/921)) ([7e0b952](https://www.github.com/googleapis/google-http-java-client/commit/7e0b952a0d9c84ac43dff43914567c98f3e81f66)) + +## [1.34.0](https://www.github.com/googleapis/google-http-java-client/compare/v1.33.0...v1.34.0) (2019-12-17) + + +### Features + +* add option to pass redirect Location: header value as-is without encoding, decoding, or escaping ([#871](https://www.github.com/googleapis/google-http-java-client/issues/871)) ([2c4f49e](https://www.github.com/googleapis/google-http-java-client/commit/2c4f49e0e5f9c6b8f21f35edae373eaada87119b)) +* decode uri path components correctly ([#913](https://www.github.com/googleapis/google-http-java-client/issues/913)) ([7d4a048](https://www.github.com/googleapis/google-http-java-client/commit/7d4a048233d0d3e7c0266b7faaac9f61141aeef9)), closes [#398](https://www.github.com/googleapis/google-http-java-client/issues/398) +* support chunked transfer encoding ([#910](https://www.github.com/googleapis/google-http-java-client/issues/910)) ([b8d6abe](https://www.github.com/googleapis/google-http-java-client/commit/b8d6abe0367bd497b68831263753ad262914aa97)), closes [#648](https://www.github.com/googleapis/google-http-java-client/issues/648) + + +### Bug Fixes + +* redirect on 308 (Permanent Redirect) too ([#876](https://www.github.com/googleapis/google-http-java-client/issues/876)) ([501ede8](https://www.github.com/googleapis/google-http-java-client/commit/501ede83ef332207f0ed67c3d7120b20a1416cec)) +* set mediaType to null if contentType cannot be parsed ([#911](https://www.github.com/googleapis/google-http-java-client/issues/911)) ([7ea53eb](https://www.github.com/googleapis/google-http-java-client/commit/7ea53ebdb641a9611cbf5736c55f08a83606101e)) +* update HttpRequest#getVersion to use stable logic ([#919](https://www.github.com/googleapis/google-http-java-client/issues/919)) ([853ab4b](https://www.github.com/googleapis/google-http-java-client/commit/853ab4ba1bd81420f7b236c2c8f40c4a253a482e)), closes [#892](https://www.github.com/googleapis/google-http-java-client/issues/892) + +## [1.32.2](https://www.github.com/googleapis/google-http-java-client/compare/v1.32.1...v1.32.2) (2019-10-29) + + +### Bug Fixes + +* wrap GZIPInputStream for connection reuse ([#840](https://www.github.com/googleapis/google-http-java-client/issues/840)) ([087a428](https://www.github.com/googleapis/google-http-java-client/commit/087a428390a334bd761a8a3d66475aa4dde72ed1)), closes [#749](https://www.github.com/googleapis/google-http-java-client/issues/749) [#367](https://www.github.com/googleapis/google-http-java-client/issues/367) +* HttpResponse GZip content encoding equality change ([#843](https://www.github.com/googleapis/google-http-java-client/issues/843)) ([9c73e1d](https://www.github.com/googleapis/google-http-java-client/commit/9c73e1db7ab371c57ff6246fa39fa514051ef99c)), closes [#842](https://www.github.com/googleapis/google-http-java-client/issues/842) [#842](https://www.github.com/googleapis/google-http-java-client/issues/842) [#842](https://www.github.com/googleapis/google-http-java-client/issues/842) [#842](https://www.github.com/googleapis/google-http-java-client/issues/842) [#842](https://www.github.com/googleapis/google-http-java-client/issues/842) +* use platform default TCP buffer sizes ([#855](https://www.github.com/googleapis/google-http-java-client/issues/855)) ([238f4c5](https://www.github.com/googleapis/google-http-java-client/commit/238f4c52086defc5a055f2e8d91e7450454d5792)) + + + +### Documentation + +* fix HttpResponseException Markup ([#829](https://www.github.com/googleapis/google-http-java-client/issues/829)) ([99d64e0](https://www.github.com/googleapis/google-http-java-client/commit/99d64e0d88bdcc3b00d54ee9370e052e5f949680)) +* include HTTP Transport page in navigation, add support page ([#854](https://www.github.com/googleapis/google-http-java-client/issues/854)) ([57fd1d8](https://www.github.com/googleapis/google-http-java-client/commit/57fd1d859dad486b37b4b4c4ccda5c7f8fa1b356)) +* remove theme details ([#859](https://www.github.com/googleapis/google-http-java-client/issues/859)) ([eee85cd](https://www.github.com/googleapis/google-http-java-client/commit/eee85cd8aaaacd6e38271841a6eafe27a0c9d6ec)) +* update libraries-bom to 2.7.1 in setup ([#857](https://www.github.com/googleapis/google-http-java-client/issues/857)) ([cc2ea16](https://www.github.com/googleapis/google-http-java-client/commit/cc2ea1697aceb5d3693b02fa87b0f8379f5d7a2b)) +* use libraries-bom 2.6.0 in setup instructions ([#847](https://www.github.com/googleapis/google-http-java-client/issues/847)) ([5253c6c](https://www.github.com/googleapis/google-http-java-client/commit/5253c6c5e2b2312206000fd887fe6f0d89a26570)) + + +### Dependencies + +* update dependency com.fasterxml.jackson.core:jackson-core to v2.10.0 ([#831](https://www.github.com/googleapis/google-http-java-client/issues/831)) ([ffb1a85](https://www.github.com/googleapis/google-http-java-client/commit/ffb1a857a31948472b2b62ff4f47905fa60fe1e2)) +* update dependency com.fasterxml.jackson.core:jackson-core to v2.9.10 ([#828](https://www.github.com/googleapis/google-http-java-client/issues/828)) ([15ba3c3](https://www.github.com/googleapis/google-http-java-client/commit/15ba3c3f7cee9e2e5362d69c1278f45531e56581)) +* update dependency com.google.code.gson:gson to v2.8.6 ([#833](https://www.github.com/googleapis/google-http-java-client/issues/833)) ([6c50997](https://www.github.com/googleapis/google-http-java-client/commit/6c50997361fee875d6b7e6db90e70d41622fc04c)) +* update dependency mysql:mysql-connector-java to v8.0.18 ([#839](https://www.github.com/googleapis/google-http-java-client/issues/839)) ([1522eb5](https://www.github.com/googleapis/google-http-java-client/commit/1522eb5c011b4f20199e2ec8cb5ec58d10cc399a)) + +### [1.32.1](https://www.github.com/googleapis/google-http-java-client/compare/v1.32.0...v1.32.1) (2019-09-20) + + +### Dependencies + +* update dependency com.google.protobuf:protobuf-java to v3.10.0 ([#824](https://www.github.com/googleapis/google-http-java-client/issues/824)) ([c51b62f](https://www.github.com/googleapis/google-http-java-client/commit/c51b62f)) +* update guava to 28.1-android ([#817](https://www.github.com/googleapis/google-http-java-client/issues/817)) ([e05b6a8](https://www.github.com/googleapis/google-http-java-client/commit/e05b6a8)) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 46b2a08ea..2add2547a 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,43 +1,94 @@ -# Contributor Code of Conduct + +# Code of Conduct -As contributors and maintainers of this project, -and in the interest of fostering an open and welcoming community, -we pledge to respect all people who contribute through reporting issues, -posting feature requests, updating documentation, -submitting pull requests or patches, and other activities. +## Our Pledge -We are committed to making participation in this project -a harassment-free experience for everyone, -regardless of level of experience, gender, gender identity and expression, -sexual orientation, disability, personal appearance, -body size, race, ethnicity, age, religion, or nationality. +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of +experience, education, socio-economic status, nationality, personal appearance, +race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members Examples of unacceptable behavior by participants include: -* The use of sexualized language or imagery -* Personal attacks -* Trolling or insulting/derogatory comments -* Public or private harassment -* Publishing other's private information, -such as physical or electronic -addresses, without explicit permission -* Other unethical or unprofessional conduct. +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct. -By adopting this Code of Conduct, -project maintainers commit themselves to fairly and consistently -applying these principles to every aspect of managing this project. -Project maintainers who do not follow or enforce the Code of Conduct -may be permanently removed from the project team. - -This code of conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. - -Instances of abusive, harassing, or otherwise unacceptable behavior -may be reported by opening an issue -or contacting one or more of the project maintainers. - -This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, -available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/) +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, or to ban temporarily or permanently any +contributor for other behaviors that they deem inappropriate, threatening, +offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +This Code of Conduct also applies outside the project spaces when the Project +Steward has a reasonable belief that an individual's behavior may have a +negative impact on the project or its community. + +## Conflict Resolution + +We do not believe that all conflict is bad; healthy debate and disagreement +often yield positive results. However, it is never okay to be disrespectful or +to engage in behavior that violates the project’s code of conduct. + +If you see someone violating the code of conduct, you are encouraged to address +the behavior directly with those involved. Many issues can be resolved quickly +and easily, and this gives people more control over the outcome of their +dispute. If you are unable to resolve the matter for any reason, or if the +behavior is threatening or harassing, report it. We are dedicated to providing +an environment where participants feel welcome and safe. + +Reports should be directed to *googleapis-stewards@google.com*, the +Project Steward(s) for *Google Cloud Client Libraries*. It is the Project Steward’s duty to +receive and address reported violations of the code of conduct. They will then +work with a committee consisting of representatives from the Open Source +Programs Office and the Google Open Source Strategy team. If for any reason you +are uncomfortable reaching out to the Project Steward, please email +opensource@google.com. + +We will investigate every complaint, but you may not receive a direct response. +We will use our discretion in determining when and how to follow up on reported +incidents, which may range from not taking action to permanent expulsion from +the project and project-sponsored spaces. We will notify the accused of the +report and provide them an opportunity to discuss it before any action is taken. +The identity of the reporter will be omitted from the details of the report +supplied to the accused. In potentially harmful situations, such as ongoing +harassment or threats to anyone's safety, we may take action without notice. + +## Attribution + +This Code of Conduct is adapted from the Contributor Covenant, version 1.4, +available at +https://www.contributor-covenant.org/version/1/4/code-of-conduct.html \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..a0dc4050d --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,96 @@ +# How to Contribute + +Please follow the guidelines below before opening an issue or a PR: +1. Ensure the issue was not already reported. +2. Open a new issue if you are unable to find an existing issue addressing your problem. Make sure to include a title and clear description, as much relevant information as possible, and a code sample or an executable test case demonstrating the expected behavior that is not occurring. +3. Discuss the priority and potential solutions with the maintainers in the issue. The maintainers would review the issue and add a label "Accepting Contributions" once the issue is ready for accepting contributions. +4. Open a PR only if the issue is labeled with "Accepting Contributions", ensure the PR description clearly describes the problem and solution. Note that an open PR without an issues labeled with "Accepting Contributions" will not be accepted. + + +## Contributor License Agreement + +Contributions to this project must be accompanied by a Contributor License +Agreement. You (or your employer) retain the copyright to your contribution; +this simply gives us permission to use and redistribute your contributions as +part of the project. Head over to to see +your current agreements on file or to sign a new one. + +You generally only need to submit a CLA once, so if you've already submitted one +(even if it was for a different project), you probably don't need to do it +again. + +## Code reviews + +All submissions, including submissions by project members, require review. We +use GitHub pull requests for this purpose. Consult +[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more +information on using pull requests. + +## Community Guidelines + +This project follows +[Google's Open Source Community Guidelines](https://opensource.google.com/conduct/). + +## Building the project + +To build, package, and run all unit tests run the command + +``` +mvn clean verify +``` + +### Running Integration tests + +To include integration tests when building the project, you need access to +a GCP Project with a valid service account. + +For instructions on how to generate a service account and corresponding +credentials JSON see: [Creating a Service Account][1]. + +Then run the following to build, package, run all unit tests and run all +integration tests. + +```bash +export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service/account.json +mvn -Penable-integration-tests clean verify +``` + +## Code Samples + +All code samples must be in compliance with the [java sample formatting guide][3]. +Code Samples must be bundled in separate Maven modules. + +The samples must be separate from the primary project for a few reasons: +1. Primary projects have a minimum Java version of Java 8 whereas samples can have + Java version of Java 11. Due to this we need the ability to + selectively exclude samples from a build run. +2. Many code samples depend on external GCP services and need + credentials to access the service. +3. Code samples are not released as Maven artifacts and must be excluded from + release builds. + +### Building + +```bash +mvn clean verify +``` + +Some samples require access to GCP services and require a service account: + +```bash +export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service/account.json +mvn clean verify +``` + +### Code Formatting + +Code in this repo is formatted with +[google-java-format](https://github.com/google/google-java-format). +To run formatting on your project, you can run: +``` +mvn com.coveo:fmt-maven-plugin:format +``` + +[1]: https://cloud.google.com/docs/authentication/getting-started#creating_a_service_account +[2]: https://maven.apache.org/settings.html#Active_Profiles +[3]: https://github.com/GoogleCloudPlatform/java-docs-samples/blob/main/SAMPLE_FORMAT.md diff --git a/LICENSE b/LICENSE index 980a15ac2..261eeb9e9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ - Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -178,7 +178,7 @@ APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" + boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright {yyyy} {name of copyright owner} + Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 424e5b071..999337f7b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # Google HTTP Client Library for Java +[![Maven][maven-version-image]][maven-version-link] +![Stability][stability-image] +[![CI Status][ci-status-image]][ci-status-link] + ## Description Written by Google, the Google HTTP Client Library for Java is a flexible, efficient, and powerful Java library for accessing any resource on the web via HTTP. The library has the following @@ -13,50 +17,45 @@ content. The JSON and XML libraries are also fully pluggable, and they include s The library supports the following Java environments: -- Java 6 (or higher) -- Android 4.0 (Ice Cream Sandwich) (or higher) +- Java 8 or higher +- Android 4.4 (Kit Kat) - GoogleAppEngine Google App Engine The following related projects are built on the Google HTTP Client Library for Java: -- [Google OAuth Client Library for Java](https://github.com/google/google-oauth-java-client), -for the OAuth 2.0 and OAuth 1.0a authorization standards. -- [Google APIs Client Library for Java](https://github.com/google/google-api-java-client), for -access to Google APIs. - -This is an open-source library, and -[contributions](https://developers.google.com/api-client-library/java/google-http-java-client/contribute) -are welcome. - -## Documentation - -- [Developer's Guide](https://developers.google.com/api-client-library/java/google-http-java-client/) -- [Setup Instructions](https://developers.google.com/api-client-library/java/google-http-java-client/setup) -- [JavaDoc](https://developers.google.com/api-client-library/java/google-http-java-client/reference/index) -- [Release Notes](https://developers.google.com/api-client-library/java/google-http-java-client/release-notes) -- [Support (Questions, Bugs)](https://developers.google.com/api-client-library/java/google-http-java-client/support) +- [Google OAuth Client Library for Java][google-oauth-client], for the OAuth 2.0 and OAuth 1.0a +authorization standards. +- [Google APIs Client Library for Java][google-api-client], for access to Google APIs. -## CI Status +This is an open-source library, and [contributions][contributions] are welcome. -Java Version | Status ------------- | ------ -Java 7 | [![Kokoro CI](https://storage.googleapis.com/cloud-devrel-public/java/badges/google-http-java-client/java7.svg)](https://storage.googleapis.com/cloud-devrel-public/java/badges/google-http-java-client/java7.html) -Java 8 | [![Kokoro CI](https://storage.googleapis.com/cloud-devrel-public/java/badges/google-http-java-client/java8.svg)](https://storage.googleapis.com/cloud-devrel-public/java/badges/google-http-java-client/java8.html) -Java 11 | [![Kokoro CI](https://storage.googleapis.com/cloud-devrel-public/java/badges/google-http-java-client/java11.svg)](https://storage.googleapis.com/cloud-devrel-public/java/badges/google-http-java-client/java11.html) +## Beta Features -## Links +Features marked with the `@Beta` annotation at the class or method level are subject to change. They +might be modified in any way, or even removed, in any major release. You should not use beta +features if your code is a library itself (that is, if your code is used on the `CLASSPATH` of users +outside your own control). -- [Discuss](https://groups.google.com/group/google-http-java-client) +## Deprecated Features -## Notice: Ending Java 6 Support +Deprecated non-beta features will be removed eighteen months after the release in which they are +first deprecated. You must fix your usages before this time. If you don't, any type of breakage +might result, and you are not guaranteed a compilation error. -Please note: since Java 6 extended support is being ended this December by Oracle, we will begin -ending Java 6 support in early 2019, with release 1.28.0 as a tentative goal. Users may stay still -use these libraries in Java 6 projects for some time, but going forward we will not ensure that -these libraries work in such an environment. After 1.28.0, our supported versions will include Java -7 and onward. +## Documentation -For Android users, we will continue our 4.0 support. +- [Developer's Guide](https://googleapis.github.io/google-http-java-client/) +- [Setup Instructions](https://googleapis.github.io/google-http-java-client/setup.html) +- [JavaDoc](https://googleapis.dev/java/google-http-client/latest/) +- [Release Notes](https://github.com/googleapis/google-http-java-client/releases) +- [Support (Questions, Bugs)](https://developers.google.com/api-client-library/java/google-http-java-client/support) -For questions or concerns, please file an issue in the GitHub repository. +[google-oauth-client]: https://github.com/googleapis/google-oauth-java-client +[google-api-client]: https://github.com/googleapis/google-api-java-client +[contributions]: CONTRIBUTING.md +[ci-status-image]: https://github.com/googleapis/google-http-java-client/actions/workflows/ci.yaml/badge.svg?event=push +[ci-status-link]: https://github.com/googleapis/google-http-java-client/actions?query=event%3Apush +[maven-version-image]: https://img.shields.io/maven-central/v/com.google.http-client/google-http-client.svg +[maven-version-link]: https://search.maven.org/search?q=g:com.google.http-client%20AND%20a:google-http-client&core=gav +[stability-image]: https://img.shields.io/badge/stability-stable-green diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..8b58ae9c0 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,7 @@ +# Security Policy + +To report a security issue, please use [g.co/vulnz](https://g.co/vulnz). + +The Google Security Team will respond within 5 working days of your report on g.co/vulnz. + +We use g.co/vulnz for our intake, and do coordination and disclosure here using GitHub Security Advisory to privately discuss and fix the issue. diff --git a/checkstyle.xml b/checkstyle.xml index 9d2f4bbe9..37934c4a6 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -1,318 +1,268 @@ - + + "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN" + "https://checkstyle.org/dtds/configuration_1_3.dtd"> - + - - - + Authors: Max Vetrenko, Ruslan Diachenko, Roman Ivanov. + --> + + + + + - - - - + + + + + + + + + + - - + + + - - - - - - - + + + + + - - - - - - - - - + + + + - - - - - - - - - - - - - - + + + + + + - - - - + + + + + - - - + + + + + - - - - - - - - - - + + + + + + + + - - - - - + + + + + + + + + - - - - - - - - - - - + + + + - - - - - - - - - - + + + + - - - - - - - - - - + + + + + - - - - - - + + + + + - - - - + + + + - - - - + + + - - - - + + - - - - - - - - - - - - + + + - - - - + + + - - - - - - + + + - - - - - + + + - - - - + + + + - - - - - + + + - - - - - - + + + - - - - - - - - + + + - - - - + + + + + + - - - - - - + + + + + + + - + + + + + + + + + + + + - - + - - - - - + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/clirr-ignored-differences.xml b/clirr-ignored-differences.xml deleted file mode 100644 index 898e94c20..000000000 --- a/clirr-ignored-differences.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - 8001 - com/google/api/client/repackaged/** - - diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 000000000..0c830d027 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1,2 @@ +theme: jekyll-theme-dinky +title: Google HTTP Client for Java diff --git a/docs/_data/navigation.yml b/docs/_data/navigation.yml new file mode 100644 index 000000000..827f1ce91 --- /dev/null +++ b/docs/_data/navigation.yml @@ -0,0 +1,21 @@ +toc: + - page: Overview + url: index.html + - page: Setup Instructions + url: setup.html + - page: Component Modules + url: component-modules.html + - page: Android + url: android.html + - page: Google App Engine + url: google-app-engine.html + - page: HTTP Transport + url: http-transport.html + - page: JSON + url: json.html + - page: Exponential Backoff + url: exponential-backoff.html + - page: Unit Testing + url: unit-testing.html + - page: Support + url: support.html \ No newline at end of file diff --git a/docs/_layouts/default.html b/docs/_layouts/default.html new file mode 100644 index 000000000..e83dfcd2a --- /dev/null +++ b/docs/_layouts/default.html @@ -0,0 +1,51 @@ + + + + + + +{% seo %} + + + + + + +
+
+

{{ site.title | default: site.github.repository_name }}

+ + {% for entry in site.data.navigation.toc %} + {{ entry.page }}
+ {% endfor %} +
+ + +
+ +
+ {{ content }} +
+ +
+ + {% if site.google_analytics %} + + {% endif %} + + diff --git a/docs/android.md b/docs/android.md new file mode 100644 index 000000000..35a607e13 --- /dev/null +++ b/docs/android.md @@ -0,0 +1,101 @@ +--- +title: Using the Google HTTP Client Library for Java on Android +--- + +# Using the Google HTTP Client Library for Java on Android + +If you are developing for Android and the Google API you want to use is included in the +[Google Play Services library](https://developer.android.com/google/play-services/index.html), use +that library for the best performance and experience. If the Google API you want to use with Android +is not part of the Google Play Services library, you can use the Google HTTP Client Library for +Java, which supports Android 1.5 (or higher), and which is described here. + +## Beta + +Android support for the Google HTTP Client Library for Java is `@Beta`. + +## Installation + +Follow the download instructions on the [setup][setup] page, and pay special attention to the +Android instructions for [ProGuard][proguard]. Using ProGuard or a similar tool to remove unused +code and compress it is critical for minimizing application size. For example, for the +[tasks-android-sample][tasks-android-sample], ProGuard reduces the application size ~88%, from +777KB to 93KB. + +Note that ProGuard only runs when preparing your application for release; it does not run when +preparing it for debugging, to make it easier to develop. However, be sure to test your application +in release mode, because if ProGuard is misconfigured it can cause problems that are sometimes a +challenge to debug. + +**Warning:** For Android, you MUST place the jar files in a directory named "libs" so that the APK +packager can find them. Otherwise, you will get a `NoClassDefFoundError` error at runtime. + +## Data models + +### JSON + +You have a choice of three [pluggable streaming JSON libraries][json]. Options include +[`JacksonFactory`][jackson-factory] for maximum efficiency, or +[`AndroidJsonFactory`][android-json-factory] for the smallest application size on Honeycomb +(SDK 3.0) or higher. + +### XML (`@Beta`) + +The [XML data model][xml] (`@Beta`) is optimized for efficient memory usage that minimizes parsing +and serialization time. Only the fields you need are actually parsed when processing an XML +response. + +Android already has an efficient, native, built-in XML full parser implementation, so no separate +library is needed or advised. + +## Authentication + +The best practice on Android (since the 2.1 SDK) is to use the [`AccountManager`][account-manager] +class (@Beta) for centralized identity management and credential token storage. We recommend against +using your own solution for storing user identities and credentials. + +For details about using the AccountManager with the HTTP service that you need, read the +documentation for that service. + +## HTTP transport + +If your application is targeted at Android 2.3 (Gingerbread) or higher, use the +[`NetHttpTransport`][net-http-transport] class. This class isbased on `HttpURLConnection`, which is +built into the Android SDK and is found in all Java SDKs. + +In prior Android SDKs, however, the implementation of `HttpURLConnection` was buggy, and the Apache +HTTP client was preferred. For those SDKs, use the [`ApacheHttpTransport`][apache-http-transport] +class. + +If your Android application needs to work with all Android SDKs, call +[`AndroidHttp.newCompatibleTransport()`][android-transport] (@Beta), and it will decide which of the +two HTTP transport classes to use, based on the Android SDK level. + +## Logging + +To enable logging of HTTP requests and responses, including URL, headers, and content: + +```java +Logger.getLogger(HttpTransport.class.getName()).setLevel(Level.CONFIG); +``` + +When you use `Level.CONFIG`, the value of the Authorization header is not shown. To show the +Authorization header, use `Level.ALL`. + +Furthermore, you must enable logging on your device as follows: + +```java +adb shell setprop log.tag.HttpTransport DEBUG +``` + +[setup]: https://googleapis.github.io/google-http-java-client/setup.html +[proguard]: https://googleapis.github.io/google-http-java-client/setup.html#proguard +[tasks-android-sample]: https://github.com/google/google-api-java-client-samples/tree/master/tasks-android-sample +[json]: https://googleapis.github.io/google-http-java-client/json.html +[jackson-factory]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/json/jackson2/JacksonFactory.html +[android-json-factory]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/extensions/android/json/AndroidJsonFactory.html +[xml]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/xml/package-summary.html +[account-manager]: http://developer.android.com/reference/android/accounts/AccountManager.html +[net-http-transport]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/http/javanet/NetHttpTransport.html +[apache-http-transport]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/http/apache/ApacheHttpTransport.html +[android-transport]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/extensions/android/http/AndroidHttp.html#newCompatibleTransport-- \ No newline at end of file diff --git a/docs/component-modules.md b/docs/component-modules.md new file mode 100644 index 000000000..29a064466 --- /dev/null +++ b/docs/component-modules.md @@ -0,0 +1,52 @@ +--- +title: Component Modules +--- + +# Component Modules + +This libraries is composed of several modules: + +## google-http-client + +Google HTTP Client Library for Java (google-http-client) is designed to be compatible with all +supported Java platforms, including Android. + +## google-http-client-android + +Android extensions to the Google HTTP Client Library for Java (`google-http-client-android`) support +Java Google Android (only for SDK >= 2.1) applications. This module depends on `google-http-client`. + +## google-http-client-apache-v2 + +Apache extension to the Google HTTP Client Library for Java (`google-http-client-apache-v2`) that +contains an implementation of `HttpTransport` based on the Apache HTTP Client. This module depends +on `google-http-client`. + +## google-http-client-appengine + +Google App Engine extensions to the Google HTTP Client Library for Java +(`google-http-client-appengine`) support Java Google App Engine applications. This module depends on +`google-http-client`. + +## google-http-client-gson + +GSON extension to the Google HTTP Client Library for Java (`google-http-client-gson`) that contains +an implementation of `JsonFactory` based on the GSON API. This module depends on google-http-client. + +## google-http-client-jackson2 + +Jackson2 extension to the Google HTTP Client Library for Java (`google-http-client-jackson2`) that +contains an implementation of `JsonFactory` based on the Jackson2 API. This module depends on +`google-http-client`. + +## google-http-client-protobuf + +[Protocol buffer][protobuf] extensions to the Google HTTP Client Library for Java +(`google-http-client-protobuf`) support protobuf data format. This module depends on `google-http-client`. + +## google-http-client-xml + +XML extensions to the Google HTTP Client Library for Java (`google-http-client-xml`) support the XML +data format. This module depends on `google-http-client`. + +[protobuf]: https://developers.google.com/protocol-buffers/docs/overview diff --git a/docs/exponential-backoff.md b/docs/exponential-backoff.md new file mode 100644 index 000000000..b7732e19e --- /dev/null +++ b/docs/exponential-backoff.md @@ -0,0 +1,78 @@ +--- +title: Exponential Backoff +--- + +# Exponential Backoff + +Exponential backoff is an algorithm that retries requests to the server based on certain status +codes in the server response. The retries exponentially increase the waiting time up to a certain +threshold. The idea is that if the server is down temporarily, it is not overwhelmed with requests +hitting at the same time when it comes back up. + +The exponential backoff feature of the Google HTTP Client Library for Java provides an easy way to +retry on transient failures: + +* Provide an instance of [`HttpUnsuccessfulResponseHandler`][http-unsuccessful-response-handler] to +the HTTP request in question. +* Use the library's [`HttpBackOffUnsuccessfulResponseHandler`][http-backoff-handler] implementation +to handle abnormal HTTP responses with some kind of [`BackOff`][backoff] policy. +* Use [`ExponentialBackOff`][exponential-backoff] for this backoff policy. + +Backoff is turned off by default in [`HttpRequest`][http-request]. The examples below demonstrate +how to turn it on. + +## Examples + +To set [`HttpRequest`][http-request] to use +[`HttpBackOffUnsuccessfulResponseHandler`][http-backoff-handler] with default values: + +```java +HttpRequest request = ... +request.setUnsuccessfulResponseHandler(new HttpBackOffUnsuccessfulResponseHandler(new ExponentialBackOff())); +// HttpBackOffUnsuccessfulResponseHandler is designed to work with only one HttpRequest at a time. +// As a result, you MUST create a new instance of HttpBackOffUnsuccessfulResponseHandler with a new +// instance of BackOff for each instance of HttpRequest. +HttpResponse = request.execute(); +``` + +To alter the detailed parameters of [`ExponentialBackOff`][exponential-backoff], use its +[`Builder`][exponential-backoff-builder] methods: + +```java +ExponentialBackOff backoff = new ExponentialBackOff.Builder() + .setInitialIntervalMillis(500) + .setMaxElapsedTimeMillis(900000) + .setMaxIntervalMillis(6000) + .setMultiplier(1.5) + .setRandomizationFactor(0.5) + .build(); +request.setUnsuccessfulResponseHandler(new HttpBackOffUnsuccessfulResponseHandler(backoff)); +``` + +To create your own implementation of [`BackOff`][backoff]: + +```java +class CustomBackOff implements BackOff { + + @Override + public long nextBackOffMillis() throws IOException { + ... + } + + @Override + public void reset() throws IOException { + ... + } +} + +request.setUnsuccessfulResponseHandler( + new HttpBackOffUnsuccessfulResponseHandler(new CustomBackOff())); +``` + + +[http-unsuccessful-response-handler]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/http/HttpUnsuccessfulResponseHandler.html +[http-backoff-handler]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/http/HttpBackOffUnsuccessfulResponseHandler.html +[backoff]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/util/BackOff.html +[exponential-backoff]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/util/ExponentialBackOff.html +[http-request]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/http/HttpRequest.html +[exponential-backoff-builder]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/util/ExponentialBackOff.Builder.html \ No newline at end of file diff --git a/docs/google-app-engine.md b/docs/google-app-engine.md new file mode 100644 index 000000000..ccd83e910 --- /dev/null +++ b/docs/google-app-engine.md @@ -0,0 +1,44 @@ +--- +title: Using the Google HTTP Client Library for Java on Google App Engine +--- + +# Using the Google HTTP Client Library for Java on Google App Engine + +Google App Engine is one of the supported Java environments for the Google HTTP Client Library for Java. + +## Data models + +### JSON + +The [JSON data model][json-package] is optimized for efficient memory usage that minimizes parsing +and serialization time. Only the fields you need are actually parsed when processing a JSON +response. + +For your JSON parser, we recommend [`JacksonFactory`][jackson-factory], which is based on the +popular Jackson library. It is considered the fastest in terms of parsing/serialization. You can +also use [`GsonFactory'][gson-factory], which is based on the [Google GSON][gson] library. It is a +lighter-weight option (smaller size) that is fairly fast, but it is not quite as fast as Jackson. + +### XML (@Beta) + +The [XML datamodel][xml-package] (`@Beta`) is optimized for efficient memory usage that minimizes +parsing and serialization time. Only the fields you need are actually parsed when processing an XML +response. + +## HTTP transport + +If you have configured Google App Engine to use [`urlfetch` as the stream handler][url-fetch], then +you will use the [`UrlFetchTransport`][url-fetch-transport] provided by +`google-http-client-appengine`. + +If you are not using `urlfetch`, then you can use any of the provided +[HttpTransport][http-transport] adapters. + +[json-package]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/json/package-summary.html +[jackson-factory]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/json/jackson2/JacksonFactory.html +[gson-factory]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/json/gson/GsonFactory.html +[gson]: https://github.com/google/gson +[xml-package]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/xml/package-summary.html +[url-fetch]: https://cloud.google.com/appengine/docs/standard/java/issue-requests#using_urlfetch_in_a_java_8_app +[url-fetch-transport]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/extensions/appengine/http/UrlFetchTransport.html +[http-transport]: https://googleapis.github.io/google-http-java-client/http-transport.html \ No newline at end of file diff --git a/docs/http-transport.md b/docs/http-transport.md new file mode 100644 index 000000000..b6d94cce2 --- /dev/null +++ b/docs/http-transport.md @@ -0,0 +1,143 @@ +--- +title: Pluggable HTTP Transport +--- + +# Pluggable HTTP Transport + +The HTTP library has a fully pluggable HTTP transport layer that allows you to build on top of the +low-level HTTP of your choice and optimize for the Java platform your application is running on. + +Thanks to this abstraction, code written for one platform works across all supported platforms, from +mobile applications such as those built for Android, to installed applications, to web applications +such as those built on Google App Engine. The HTTP library provides high-level functionality that is +compatible across these platforms, but at the same time takes advantage of lower-level functionality +when necessary. + +## Choosing a low-level HTTP transport library + +There are three built-in low-level HTTP transports: + +1. [`NetHttpTransport`][net-http-transport]: based on [`HttpURLConnection`][http-url-connection] +that is found in all Java SDKs, and thus usually the simplest choice. +1. [`Apache5HttpTransport`][apache-http-transport]: based on the popular + [Apache 5.x HttpClient][apache5-http-client] that allows for more customization. +1. [`ApacheHttpTransport`][apache-http-transport]: based on the popular +[Apache 4.x HttpClient][apache-http-client] that allows for more customization. Note that this transport implementation +relies on [Apache 4.x HttpCore][apache-http-core] which has reached end of life. It is recommended to use +[`Apache5HttpTransport`][apache-http-transport] instead. +1. [`UrlFetchTransport`][url-fetch-transport]: based on the [URL Fetch Java API][url-fetch] in the +Google App Engine SDK. + +## Logging + +[`java.util.logging.Logger`][logger] is used for logging HTTP request and response details, +including URL, headers, and content. + +Normally logging is managed using a [`logging.properties`][logging-properties] file. For example: + +```properties +# Properties file which configures the operation of the JDK logging facility. +# The system will look for this config file to be specified as a system property: +# -Djava.util.logging.config.file=${project_loc:googleplus-simple-cmdline-sample}/logging.properties + +# Set up the console handler (uncomment "level" to show more fine-grained messages) +handlers = java.util.logging.ConsoleHandler +java.util.logging.ConsoleHandler.level = CONFIG + +# Set up logging of HTTP requests and responses (uncomment "level" to show) +com.google.api.client.http.level = CONFIG +``` + +The following example uses the [`ConsoleHandler`][console-handler]. Another popular choice is +[`FileHandler`][file-handler]. + +Example for enabling logging in code: + +```java +import com.google.api.client.http.HttpTransport; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +public static void enableLogging() { + Logger logger = Logger.getLogger(HttpTransport.class.getName()); + logger.setLevel(Level.CONFIG); + logger.addHandler(new Handler() { + + @Override + public void close() throws SecurityException { + } + + @Override + public void flush() { + } + + @Override + public void publish(LogRecord record) { + // Default ConsoleHandler will print >= INFO to System.err. + if (record.getLevel().intValue() < Level.INFO.intValue()) { + System.out.println(record.getMessage()); + } + } + }); +} +``` + +**Note:** When using `Level.CONFIG`, the value of the Authorization header is not shown. To show +that also, use `Level.ALL` instead of `Level.CONFIG`. + +## Handling HTTP error responses + +When an HTTP error response (an HTTP status code of 300 or higher) is received, +[`HttpRequest.execute()`][request-execute] throws an [`HttpResponseException`][response-exception]. +Here's an example usage: + +```java +try { + request.execute() +} catch (HttpResponseException e) { + System.err.println(e.getStatusMessage()); +} +``` + +If you need to intercept error responses, it may be handy to use the +[`HttpUnsuccessfulResponseHandler`][http-unsuccessful-response-handler]. Example usage: + +```java +public static class MyInitializer implements HttpRequestInitializer, HttpUnsuccessfulResponseHandler { + + @Override + public boolean handleResponse( + HttpRequest request, HttpResponse response, boolean retrySupported) throws IOException { + System.out.println(response.getStatusCode() + " " + response.getStatusMessage()); + return false; + } + + @Override + public void initialize(HttpRequest request) throws IOException { + request.setUnsuccessfulResponseHandler(this); + } +} + +... + +HttpRequestFactory requestFactory = transport.createRequestFactory(new MyInitializer()); +``` + +[net-http-transport]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/http/javanet/NetHttpTransport.html +[http-url-connection]: http://docs.oracle.com/javase/7/docs/api/java/net/HttpURLConnection.html +[apache-http-transport]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/http/apache/v2/ApacheHttpTransport.html +[apache5-http-transport]: https://github.com/googleapis/google-http-java-client/blob/de8743587d1415e8a6046096ac1fc0a5e81490c3/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5HttpTransport.java +[apache-http-client]: https://hc.apache.org/httpcomponents-client-4.5.x/index.html +[apache-http-core]: https://hc.apache.org/httpcomponents-core-4.4.x/index.html +[apache5-http-client]: https://hc.apache.org/httpcomponents-client-5.3.x/index.html +[url-fetch-transport]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/extensions/appengine/http/UrlFetchTransport.html +[url-fetch]: https://cloud.google.com/appengine/docs/java/javadoc/com/google/appengine/api/urlfetch/package-summary +[logger]: https://docs.oracle.com/javase/7/docs/api/java/util/logging/Logger.html +[logging-properties]: https://github.com/google/google-http-java-client/blob/master/samples/googleplus-simple-cmdline-sample/logging.properties +[console-handler]: https://docs.oracle.com/javase/7/docs/api/java/util/logging/ConsoleHandler.html +[file-handler]: https://docs.oracle.com/javase/7/docs/api/java/util/logging/FileHandler.html +[request-execute]: https://googleapis.dev/java/google-http-client/latest/com/google/api/client/http/HttpRequest.html#execute-- +[response-exception]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/http/HttpResponseException.html +[http-unsuccessful-response-handler]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/http/HttpUnsuccessfulResponseHandler.html diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 000000000..e6162bd8d --- /dev/null +++ b/docs/index.md @@ -0,0 +1,49 @@ +--- +title: Overview +--- + +# Overview + +## Description + +Written by Google, the Google HTTP Client Library for Java is a flexible, efficient, and powerful +Java library for accessing any resource on the web via HTTP. The library has the following +features: + +- Pluggable HTTP transport abstraction that allows you to use any low-level library such as +`java.net.HttpURLConnection`, Apache HTTP Client, or URL Fetch on Google App Engine. +- Efficient JSON and XML data models for parsing and serialization of HTTP response and request +content. The JSON and XML libraries are also fully pluggable, and they include support for +[Jackson](https://github.com/FasterXML/jackson) and Android's GSON libraries for JSON. + +The library supports the following Java environments: + +- Java 7 or higher +- Android 4.4 (Kit Kat) or higher +- Google App Engine + +The following related projects are built on the Google HTTP Client Library for Java: + +- [Google OAuth Client Library for Java][google-oauth-client], for the OAuth 2.0 and OAuth 1.0a +authorization standards. +- [Google APIs Client Library for Java][google-api-client], for access to Google APIs. + +This is an open-source library, and [contributions][contributions] are welcome. + +## Beta Features + +Features marked with the `@Beta` annotation at the class or method level are subject to change. They +might be modified in any way, or even removed, in any major release. You should not use beta +features if your code is a library itself (that is, if your code is used on the `CLASSPATH` of users +outside your own control). + +## Deprecated Features + +Deprecated non-beta features will be removed eighteen months after the release in which they are +first deprecated. You must fix your usages before this time. If you don't, any type of breakage +might result, and you are not guaranteed a compilation error. + +[google-oauth-client]: https://github.com/googleapis/google-oauth-java-client +[google-api-client]: https://github.com/googleapis/google-api-java-client +[contributions]: CONTRIBUTING.md + diff --git a/docs/json.md b/docs/json.md new file mode 100644 index 000000000..1d6dbc7de --- /dev/null +++ b/docs/json.md @@ -0,0 +1,468 @@ +--- +title: JSON +--- + +# JSON + +## Pluggable streaming library + +A fully pluggable JSON streaming library abstraction allows you to take advantage of the native +platform's built-in JSON library support (for example the JSON library that is built into Android +Honeycomb). The streaming library enables you to write optimized code for efficient memory usage +that minimizes parsing and serialization time. + +A big advantage of this JSON library is that the choice of low-level streaming library is fully +pluggable. There are three built-in choices, all of which extend [`JsonFactory`][json-factory]. You +can easily plug in your own implementation. + +* [`JacksonFactory`][jackson-factory]: Based on the popular [Jackson][jackson] library, which is +considered the fastest in terms of parsing/serialization speed. Our JSON library provides +`JsonFactory` implementations based on Jackson 2. +* [`GsonFactory`][gson-factory]: Based on the [Google GSON][gson] library, which is a lighter-weight +option (small size) that is also fairly fast, though not as fast as Jackson. +* [`AndroidJsonFactory`][android-json-factory] (`@Beta`): Based on the JSON library built into +Android Honeycomb (SDK 3.0) and higher, and that is identical to the Google GSON library. + +## User-defined JSON data models + +User-defined JSON data models allow you to define Plain Old Java Objects (POJOs) and define how the +library parses and serializes them to and from JSON. The code snippets below are part of a more +complete example, [YouTube sample][youtube-sample], which demonstrates these +concepts. + +### Example + +The following JSON snippet shows the relevant fields of a typical [YouTube video search][youtube-search]: + +```json +{ + "kind": "youtube#searchListResponse", + "pageInfo": { + "totalResults": 1000000, + "resultsPerPage": 5 + }, + "items": [ + { + "kind": "youtube#searchResult", + "id": { + "kind": "youtube#video", + "videoId": "e6Tudp5lqt8" + }, + "snippet": { + "publishedAt": "2020-06-25T23:18:43Z", + "channelId": "UCKwGZZMrhNYKzucCtTPY2Nw", + "title": "Video 1 Title", + "description": "Video 1 Description", + "thumbnails": { + "default": { + "url": "https://i.ytimg.com/vi/e6Tudp5lqt8/default.jpg", + "width": 120, + "height": 90 + }, + "medium": { + "url": "https://i.ytimg.com/vi/e6Tudp5lqt8/mqdefault.jpg", + "width": 320, + "height": 180 + }, + "high": { + "url": "https://i.ytimg.com/vi/e6Tudp5lqt8/hqdefault.jpg", + "width": 480, + "height": 360 + } + } + } + }, + { + "kind": "youtube#searchResult", + "id": { + "kind": "youtube#video", + "videoId": "o-NtLpiMpw0" + }, + "snippet": { + "publishedAt": "2020-06-25T17:28:52Z", + "channelId": "UClljAz6ZKy0XeViKsohdjqA", + "title": "Video Title 2", + "description": "Video 2 Description", + "thumbnails": { + "default": { + "url": "https://i.ytimg.com/vi/o-NtLpiMpw0/default.jpg", + "width": 120, + "height": 90 + }, + "medium": { + "url": "https://i.ytimg.com/vi/o-NtLpiMpw0/mqdefault.jpg", + "width": 320, + "height": 180 + }, + "high": { + "url": "https://i.ytimg.com/vi/o-NtLpiMpw0/hqdefault.jpg", + "width": 480, + "height": 360 + } + } + } + }, + { + "kind": "youtube#searchResult", + "id": { + "kind": "youtube#video", + "videoId": "TPAahzXZFZo" + }, + "snippet": { + "publishedAt": "2020-06-26T15:45:00Z", + "channelId": "UCR4Yfr8HAZJd9X24dwuAt1Q", + "title": "Video 3 Title", + "description": "Video 3 Description", + "thumbnails": { + "default": { + "url": "https://i.ytimg.com/vi/TPAahzXZFZo/default.jpg", + "width": 120, + "height": 90 + }, + "medium": { + "url": "https://i.ytimg.com/vi/TPAahzXZFZo/mqdefault.jpg", + "width": 320, + "height": 180 + }, + "high": { + "url": "https://i.ytimg.com/vi/TPAahzXZFZo/hqdefault.jpg", + "width": 480, + "height": 360 + } + } + } + }, + { + "kind": "youtube#searchResult", + "id": { + "kind": "youtube#video", + "videoId": "gBL-AelsdFk" + }, + "snippet": { + "publishedAt": "2020-06-24T15:24:06Z", + "channelId": "UCFHZHhZaH7Rc_FOMIzUziJA", + "title": "Video 4 Title", + "description": "Video 4 Description", + "thumbnails": { + "default": { + "url": "https://i.ytimg.com/vi/gBL-AelsdFk/default.jpg", + "width": 120, + "height": 90 + }, + "medium": { + "url": "https://i.ytimg.com/vi/gBL-AelsdFk/mqdefault.jpg", + "width": 320, + "height": 180 + }, + "high": { + "url": "https://i.ytimg.com/vi/gBL-AelsdFk/hqdefault.jpg", + "width": 480, + "height": 360 + } + } + } + }, + { + "kind": "youtube#searchResult", + "id": { + "kind": "youtube#video", + "videoId": "9ofe8axKjH0" + }, + "snippet": { + "publishedAt": "2020-06-26T11:59:32Z", + "channelId": "UCtNpbO2MtsVY4qW23WfnxGg", + "title": "Video 5 Title", + "description": "Video 5 Description", + "thumbnails": { + "default": { + "url": "https://i.ytimg.com/vi/9ofe8axKjH0/default.jpg", + "width": 120, + "height": 90 + }, + "medium": { + "url": "https://i.ytimg.com/vi/9ofe8axKjH0/mqdefault.jpg", + "width": 320, + "height": 180 + }, + "high": { + "url": "https://i.ytimg.com/vi/9ofe8axKjH0/hqdefault.jpg", + "width": 480, + "height": 360 + } + } + } + } + ] +} + +``` + +Here's one possible way to design the Java data classes to represent this: + +```java +public static class ListResponse { + @Key("items") + private List searchResults; + + @Key + private PageInfo pageInfo; + + public List getSearchResults() { + return searchResults; + } + + public PageInfo getPageInfo() { + return pageInfo; + } +} + +public static class PageInfo { + @Key + private long totalResults; + + @Key + private long resultsPerPage; + + public long getTotalResults() { + return totalResults; + } + + public long getResultsPerPage() { + return resultsPerPage; + } +} + +public static class SearchResult { + @Key + private String kind; + + @Key("id") + private VideoId videoId; + + @Key + private Snippet snippet; + + public String getKind() { + return kind; + } + + public VideoId getId() { + return videoId; + } + + public Snippet getSnippet() { + return snippet; + } +} + +public static class VideoId { + @Key + private String kind; + + @Key + private String videoId; + + public String getKind() { + return kind; + } + + public String getVideoId() { + return videoId; + } +} + +public static class Snippet { + @Key + private String publishedAt; + + @Key + private String channelId; + + @Key + private String title; + + @Key + private String description; + + public String getPublishedAt() { + return publishedAt; + } + + public String getChannelId() { + return channelId; + } + + public String getTitle() { + return title; + } + + public String getDescription() { + return description; + } + + public Map getThumbnails() { + return thumbnails; + } +} + +public static class Thumbnail { + @Key + private String url; + + @Key + private long width; + + @Key + private long height; + + public String getUrl() { + return url; + } + + public long getWidth() { + return width; + } + + public long getHeight() { + return height; + } +} +``` + +A fully supported [HTTP JSON parser][json-parser] makes it easy to parse HTTP responses to objects +of these user defined classes: + +```java +public static ListResponse parseJson(HttpResponse httpResponse) throws IOException { + ListResponse listResponse = httpResponse.parseAs(ListResponse.class); + if (listResponse.getSearchResults().isEmpty()) { + System.out.println("No results found."); + } else { + for (SearchResult searchResult : listResponse.getSearchResults()) { + System.out.println(); + System.out.println("-----------------------------------------------"); + System.out.println("Kind: " + searchResult.getKind()); + System.out.println("Video ID: " + searchResult.getId().getVideoId()); + System.out.println("Title: " + searchResult.getSnippet().getTitle()); + System.out.println("Description: " + searchResult.getSnippet().getDescription()); + } + } + return listResponse; +} +``` + +### Key annotation + +Use the [`@Key`][key-annotation] annotation to indicate fields that need to be parsed from or +serialized to JSON. By default, the `@Key` annotation uses the Java field name as the JSON key. To +override this, specify the value of the `@Key` annotation. + +Fields that don't have the `@Key` annotation are considered internal data and are not parsed from or +serialized to JSON. + +### Visibility + +Visibility of the fields does not matter, nor does the existence of the getter or setter methods. So +for example, the following alternative representation for `VideoId` would work in the example +given above: + +```java +public static class VideoId { + @Key + public String kind; + + @Key + public String videoId; +} +``` + +### GenericJson + +Normally only the fields you declare are parsed when a JSON response is parsed. The actual Google+ +activity feed response contains a lot of content that we are not using in our example. The JSON +parser skips that other content when parsing the response from Google+. + +To retain the other content, declare your class to extend [`GenericJson`][generic-json]. Notice that +`GenericJson` implements [`Map`][map], so we can use the `get` and `put` methods to set JSON +content. See [`Youtube sample`][youtube-sample] for an example of how it was +used in the `Snippet` class above. + +### Map + +The JSON library supports any implementation of `Map`, which works similarly to `GenericJson`. The +downside, of course, is that you lose the static type information for the fields. + +### JSON null + +One advantage of this JSON library is its ability to support JSON nulls and distinguish them from +undeclared JSON keys. Although JSON nulls are relatively rare, when they do occur they often cause +confusion. + +Google+ doesn't use JSON null values, so the following example uses fictitious JSON data to +illustrate what can happen: + +```json +{ + "items": [ + { + "id": "1", + "value": "some value" + }, + { + "id": "2", + "value": null + } + { + "id": "3" + } + ] +} +``` + +We might represent each item as follows: + +```java +public class Item { + @Key + public String id; + @Key + public String value; +} +``` + +For items 2 and 3, what should be in the value field? The problem is that there is no obvious way in +Java to distinguish between a JSON key that is undeclared and a JSON key whose value is JSON null. +This JSON library solves the problem by using Java null for the common case of an undeclared JSON +key, and a special "magic" instance of String ([`Data.NULL_STRING`][null-string]) to identify it as +a JSON null rather than a normal value. + +The following example shows how you might take advantage of this functionality: + +```java +private static void show(List items) { + for (Item item : items) { + System.out.println("ID: " + item.id); + if (item.value == null) { + System.out.println("No Value"); + } else if (Data.isNull(item.value)) { + System.out.print("Null Value"); + } else { + System.out.println("Value: '" + item.value + "'"); + } + } +} +``` + +[json-factory]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/json/JsonFactory.html +[jackson-factory]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/json/jackson2/JacksonFactory.html +[jackson]: https://github.com/FasterXML/jackson +[gson-factory]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/json/gson/GsonFactory.html +[gson]: https://github.com/google/gson +[android-json-factory]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/extensions/android/json/AndroidJsonFactory.html +[youtube-sample]: https://github.com/googleapis/google-http-java-client/tree/master/samples/snippets/src/main/java/com/example/json/YouTubeSample.java +[youtube-search]: https://developers.google.com/youtube/v3/docs/search/list +[json-parser]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/json/JsonParser.html +[key-annotation]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/util/Key.html +[generic-json]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/json/GenericJson.html +[map]: https://docs.oracle.com/javase/7/docs/api/java/util/Map.html +[null-string]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/util/Data.html#NULL_STRING diff --git a/docs/setup.md b/docs/setup.md new file mode 100644 index 000000000..e0da06569 --- /dev/null +++ b/docs/setup.md @@ -0,0 +1,88 @@ +--- +title: Setup Instructions +--- + +# Setup Instructions + +You can download the Google HTTP Client Library for Java and its dependencies in a zip file, or you +can use a dependency manager such as Maven or gradle to install the necessary jars from the Maven +Central repository. + +## Maven + +The Google HTTP Client Library for Java is in the central Maven repository. The Maven `groupId` for +all artifacts for this library is `com.google.http-client`. + +To ensure all dependency versions work together and to avoid having to manually choose and specify +versions for each dependency, first import the `com.google.cloud:libraries-bom` in +the `dependencyManagement` section of your `pom.xml`: + +```xml + + + + com.google.cloud + libraries-bom + 25.4.0 + pom + import + + + +``` + +Then add the individual dependencies you need without version numbers to the `dependencies` +section: + +```xml + + com.google.http-client + google-http-client + +``` + +On Android, you may need to explicitly exclude unused dependencies: +```xml + + com.google.http-client + google-http-client + + + xpp3 + xpp3 + + + httpclient + org.apache.httpcomponents + + + junit + junit + + + android + com.google.android + + + +``` + +## Gradle + +If you are using Gradle, add this to your dependencies: + +``` +compile 'com.google.http-client:google-http-client:[VERSION]' +``` + +## Download the library with dependencies + +Download the latest assembly zip file from Maven Central and extract it on your computer. This zip +contains the client library class jar files and the associated source jar files for each artifact +and their dependencies. You can find dependency graphs and licenses for the different libraries in +the dependencies folder. For more details about the contents of the download, see the contained +`readme.html` file. + + + + diff --git a/docs/support.md b/docs/support.md new file mode 100644 index 000000000..1f20a477c --- /dev/null +++ b/docs/support.md @@ -0,0 +1,45 @@ +--- +title: Support +--- + +# Support + +## The Google HTTP Client Library for Java Community + +### Ask development questions + +Ask questions on StackOverflow: + +* Use the [google-api][so-google-api] tag, and optionally [java][so-java] or [android][so-android] + or [java google-app-engine][so-java-gae]. +* You can also use the [google-http-java-client][so-http-client] tag. +* For tips on asking StackOverflow questions, see [How to Ask][so-how-to-ask]. + +**Note:** Please do not email the project contributors directly. + +### File feature requests and defects + +You can suggest features and report issues on our public [Issue Tracker][issues]. This is a great +place for the community to discuss and track implementations of features or resolution of bug fixes, +as well as share workarounds and patches. + +If you find a bug: + +* View [known bugs][issues], and if a known bug corresponds to the issue you are seeing, "star" it + or comment on it. +* If the issue you are seeing has not yet been reported, [file a bug report][new-issue]. + +### Contribute + +This is an [open-source][http-client] library, and [contributions][contributions] are welcome. + +[so-google-api]: http://stackoverflow.com/questions/tagged/google-api +[so-java]: http://stackoverflow.com/questions/tagged/google-api+java +[so-android]: http://stackoverflow.com/questions/tagged/google-api+android +[so-java-gae]: http://stackoverflow.com/questions/tagged/google-api+java+google-app-engine +[so-http-client]: http://stackoverflow.com/questions/tagged/google-http-java-client +[so-how-to-ask]: http://stackoverflow.com/questions/ask +[issues]: https://github.com/googleapis/google-http-java-client/issues +[new-issue]: https://github.com/google/google-http-java-client/issues/new +[http-client]: https://github.com/googleapis/google-http-java-client +[contributions]: https://github.com/googleapis/google-http-java-client/blob/master/CONTRIBUTING.md diff --git a/docs/unit-testing.md b/docs/unit-testing.md new file mode 100644 index 000000000..b5568a3ff --- /dev/null +++ b/docs/unit-testing.md @@ -0,0 +1,52 @@ +--- +title: HTTP Unit Testing +--- + +# HTTP Unit Testing + +When writing unit tests using this HTTP framework, don't make requests to a real server. Instead, +mock the HTTP transport and inject fake HTTP requests and responses. The +[pluggable HTTP transport layer][transport] of the Google HTTP Client Library for Java makes this +flexible and simple to do. + +Also, some useful testing utilities are included in the +[`com.google.api.client.testing.http`][testing-package] package (`@Beta`). + +The following simple example generates a basic `HttpResponse`: + +```java +HttpTransport transport = new MockHttpTransport(); +HttpRequest request = transport.createRequestFactory().buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL); +HttpResponse response = request.execute(); +``` + +The following example shows how to override the implementation of the `MockHttpTransport` class: + +```java +HttpTransport transport = new MockHttpTransport() { + @Override + public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { + return new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + MockLowLevelHttpResponse response = new MockLowLevelHttpResponse(); + response.addHeader("custom_header", "value"); + response.setStatusCode(404); + response.setContentType(Json.MEDIA_TYPE); + response.setContent("{\"error\":\"not found\"}"); + return response; + } + }; + } +}; +HttpRequest request = transport.createRequestFactory().buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL); +HttpResponse response = request.execute(); +``` + +For more examples, see the [`HttpResponseTest.java`][http-response-test] and +[`HttpRequestTest.java`][http-request-test] files. + +[transport]: https://googleapis.github.io/google-http-java-client/http-transport.html +[testing-package]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/testing/http/package-summary.html +[http-response-test]: https://github.com/googleapis/google-http-java-client/blob/master/google-http-client/src/test/java/com/google/api/client/http/HttpResponseTest.java +[http-request-test]: https://github.com/googleapis/google-http-java-client/blob/master/google-http-client/src/test/java/com/google/api/client/http/HttpRequestTest.java \ No newline at end of file diff --git a/google-http-client-android-test/pom.xml b/google-http-client-android-test/pom.xml index d129d05e7..366d58d2d 100644 --- a/google-http-client-android-test/pom.xml +++ b/google-http-client-android-test/pom.xml @@ -4,7 +4,7 @@ google-http-client google-http-client-android-test Test project for google-http-client-android. - 1.26.1-SNAPSHOT + 2.0.3-SNAPSHOT apk @@ -12,7 +12,7 @@ com.jayway.maven.plugins.android.generation2 android-maven-plugin - 3.3.0 + 3.8.2 ${env.ANDROID_HOME} @@ -53,7 +53,7 @@ com.google.http-client google-http-client-android - 1.26.0-1.26.0 + 2.0.3-SNAPSHOT android @@ -72,7 +72,7 @@ com.google.http-client google-http-client-test - 1.26.0 + 2.0.3-SNAPSHOT junit diff --git a/google-http-client-android-test/src/main/java/com/google/api/client/extensions/android/json/AndroidJsonFactoryTest.java b/google-http-client-android-test/src/main/java/com/google/api/client/extensions/android/json/AndroidJsonFactoryTest.java index 9357ffdd0..0665acc2a 100644 --- a/google-http-client-android-test/src/main/java/com/google/api/client/extensions/android/json/AndroidJsonFactoryTest.java +++ b/google-http-client-android-test/src/main/java/com/google/api/client/extensions/android/json/AndroidJsonFactoryTest.java @@ -15,7 +15,6 @@ import com.google.api.client.json.JsonFactory; import com.google.api.client.test.json.AbstractJsonFactoryTest; - import java.util.ArrayList; /** @@ -24,16 +23,31 @@ * @author Yaniv Inbar */ public class AndroidJsonFactoryTest extends AbstractJsonFactoryTest { - + private static final String GSON_LINE_SEPARATOR = "\n"; private static final String JSON_ENTRY_PRETTY = "{" + GSON_LINE_SEPARATOR + " \"title\": \"foo\"" + GSON_LINE_SEPARATOR + "}"; - private static final String JSON_FEED_PRETTY = "{" + GSON_LINE_SEPARATOR + " \"entries\": [" - + GSON_LINE_SEPARATOR + " {" + GSON_LINE_SEPARATOR + " \"title\": \"foo\"" - + GSON_LINE_SEPARATOR + " }," + GSON_LINE_SEPARATOR + " {" - + GSON_LINE_SEPARATOR + " \"title\": \"bar\"" + GSON_LINE_SEPARATOR + " }" - + GSON_LINE_SEPARATOR + " ]" + GSON_LINE_SEPARATOR + "}"; + private static final String JSON_FEED_PRETTY = + "{" + + GSON_LINE_SEPARATOR + + " \"entries\": [" + + GSON_LINE_SEPARATOR + + " {" + + GSON_LINE_SEPARATOR + + " \"title\": \"foo\"" + + GSON_LINE_SEPARATOR + + " }," + + GSON_LINE_SEPARATOR + + " {" + + GSON_LINE_SEPARATOR + + " \"title\": \"bar\"" + + GSON_LINE_SEPARATOR + + " }" + + GSON_LINE_SEPARATOR + + " ]" + + GSON_LINE_SEPARATOR + + "}"; public AndroidJsonFactoryTest(String name) { super(name); @@ -61,5 +75,4 @@ public final void testToPrettyString_Feed() throws Exception { feed.entries.add(entryBar); assertEquals(JSON_FEED_PRETTY, newFactory().toPrettyString(feed)); } - } diff --git a/google-http-client-android-test/src/main/java/com/google/api/client/extensions/android/json/FakeTest.java b/google-http-client-android-test/src/main/java/com/google/api/client/extensions/android/json/FakeTest.java index 755613857..e917e114b 100644 --- a/google-http-client-android-test/src/main/java/com/google/api/client/extensions/android/json/FakeTest.java +++ b/google-http-client-android-test/src/main/java/com/google/api/client/extensions/android/json/FakeTest.java @@ -26,6 +26,5 @@ public FakeTest(String name) { super(name); } - public final void test() throws Exception { - } + public final void test() throws Exception {} } diff --git a/google-http-client-android-test/src/main/java/com/google/api/client/extensions/android/json/package-info.java b/google-http-client-android-test/src/main/java/com/google/api/client/extensions/android/json/package-info.java index 8fdf668d9..7ecefd770 100644 --- a/google-http-client-android-test/src/main/java/com/google/api/client/extensions/android/json/package-info.java +++ b/google-http-client-android-test/src/main/java/com/google/api/client/extensions/android/json/package-info.java @@ -17,6 +17,4 @@ * * @author Yaniv Inbar */ - package com.google.api.client.extensions.android.json; - diff --git a/google-http-client-android/AndroidManifest.xml b/google-http-client-android/AndroidManifest.xml index 9dc775d49..32d176f23 100644 --- a/google-http-client-android/AndroidManifest.xml +++ b/google-http-client-android/AndroidManifest.xml @@ -5,7 +5,7 @@ android:versionName="1.0" > + android:minSdkVersion="19" + android:targetSdkVersion="19" /> - \ No newline at end of file + diff --git a/google-http-client-android/pom.xml b/google-http-client-android/pom.xml index 99968587f..b07364eea 100644 --- a/google-http-client-android/pom.xml +++ b/google-http-client-android/pom.xml @@ -4,11 +4,11 @@ com.google.http-client google-http-client-parent - 1.26.1-SNAPSHOT + 2.0.3-SNAPSHOT ../pom.xml google-http-client-android - 1.26.1-SNAPSHOT + 2.0.3-SNAPSHOT Android Platform Extensions to the Google HTTP Client Library for Java. @@ -17,36 +17,37 @@ maven-javadoc-plugin - http://download.oracle.com/javase/6/docs/api/ + http://download.oracle.com/javase/7/docs/api/ ${project.name} ${project.version} ${project.artifactId} ${project.version} + + maven-jar-plugin + + + + com.google.api.client.extensions.android + + + + maven-source-plugin - - - source-jar - compile - - jar - - - + + com.google.http-client + google-http-client + com.google.android android 4.1.1.4 provided - - com.google.http-client - google-http-client - diff --git a/google-http-client-android/src/main/java/com/google/api/client/extensions/android/AndroidUtils.java b/google-http-client-android/src/main/java/com/google/api/client/extensions/android/AndroidUtils.java index 61522acc6..8fa595b43 100644 --- a/google-http-client-android/src/main/java/com/google/api/client/extensions/android/AndroidUtils.java +++ b/google-http-client-android/src/main/java/com/google/api/client/extensions/android/AndroidUtils.java @@ -19,7 +19,7 @@ import com.google.api.client.util.Preconditions; /** - * {@link Beta}
+ * {@link Beta}
* Utilities for Android. * * @since 1.11 @@ -44,11 +44,12 @@ public static boolean isMinimumSdkLevel(int minimumSdkLevel) { * @see android.os.Build.VERSION_CODES */ public static void checkMinimumSdkLevel(int minimumSdkLevel) { - Preconditions.checkArgument(isMinimumSdkLevel(minimumSdkLevel), - "running on Android SDK level %s but requires minimum %s", Build.VERSION.SDK_INT, + Preconditions.checkArgument( + isMinimumSdkLevel(minimumSdkLevel), + "running on Android SDK level %s but requires minimum %s", + Build.VERSION.SDK_INT, minimumSdkLevel); } - private AndroidUtils() { - } + private AndroidUtils() {} } diff --git a/google-http-client-android/src/main/java/com/google/api/client/extensions/android/http/AndroidHttp.java b/google-http-client-android/src/main/java/com/google/api/client/extensions/android/http/AndroidHttp.java index c7c08f3cb..2b908d469 100644 --- a/google-http-client-android/src/main/java/com/google/api/client/extensions/android/http/AndroidHttp.java +++ b/google-http-client-android/src/main/java/com/google/api/client/extensions/android/http/AndroidHttp.java @@ -19,41 +19,38 @@ import com.google.api.client.http.apache.ApacheHttpTransport; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.util.Beta; - import java.net.HttpURLConnection; /** - * {@link Beta}
+ * {@link Beta}
* Utilities for Android HTTP transport. * * @since 1.11 * @author Yaniv Inbar + * @deprecated Gingerbread is no longer supported by Google Play Services. Please use {@link + * NetHttpTransport} directly or switch to Cronet which is better supported. */ @Beta +@Deprecated public class AndroidHttp { /** * Returns a new thread-safe HTTP transport instance that is compatible with Android SDKs prior to * Gingerbread. * - *

- * Don't use this for Android applications that anyway require Gingerbread. Instead just call + *

Don't use this for Android applications that anyway require Gingerbread. Instead just call * {@code new NetHttpTransport()}. - *

* - *

- * Prior to Gingerbread, the {@link HttpURLConnection} implementation was buggy, and the Apache + *

Prior to Gingerbread, the {@link HttpURLConnection} implementation was buggy, and the Apache * HTTP Client was preferred. However, starting with Gingerbread, the {@link HttpURLConnection} * implementation bugs were fixed, and is now better supported than the Apache HTTP Client. There * is no guarantee that Apache HTTP transport will continue to work in future SDKs. Therefore, - * this method uses {@link NetHttpTransport} for Gingerbread or higher, and otherwise - * {@link ApacheHttpTransport}. - *

+ * this method uses {@link NetHttpTransport} for Gingerbread or higher, and otherwise {@link + * ApacheHttpTransport}. */ public static HttpTransport newCompatibleTransport() { return AndroidUtils.isMinimumSdkLevel(9) ? new NetHttpTransport() : new ApacheHttpTransport(); } - private AndroidHttp() { - } + private AndroidHttp() {} } diff --git a/google-http-client-android/src/main/java/com/google/api/client/extensions/android/http/package-info.java b/google-http-client-android/src/main/java/com/google/api/client/extensions/android/http/package-info.java index 1b9d72b75..1491436b7 100644 --- a/google-http-client-android/src/main/java/com/google/api/client/extensions/android/http/package-info.java +++ b/google-http-client-android/src/main/java/com/google/api/client/extensions/android/http/package-info.java @@ -13,7 +13,7 @@ */ /** - * {@link com.google.api.client.util.Beta}
+ * {@link com.google.api.client.util.Beta}
* Utilities for Android HTTP transport. * * @since 1.11 diff --git a/google-http-client-android/src/main/java/com/google/api/client/extensions/android/json/AndroidJsonFactory.java b/google-http-client-android/src/main/java/com/google/api/client/extensions/android/json/AndroidJsonFactory.java index e9b30c4b9..7b2b2cbbc 100644 --- a/google-http-client-android/src/main/java/com/google/api/client/extensions/android/json/AndroidJsonFactory.java +++ b/google-http-client-android/src/main/java/com/google/api/client/extensions/android/json/AndroidJsonFactory.java @@ -33,17 +33,13 @@ import java.nio.charset.Charset; /** - * {@link Beta}
+ * {@link Beta}
* Low-level JSON library implementation based on GSON. * - *

- * Implementation is thread-safe, and sub-classes must be thread-safe. For maximum efficiency, + *

Implementation is thread-safe, and sub-classes must be thread-safe. For maximum efficiency, * applications should use a single globally-shared instance of the JSON factory. - *

* - *

- * Required minimum Android SDK 3.0 (level 11). - *

+ *

Required minimum Android SDK 3.0 (level 11). * * @since 1.11 * @author Yaniv Inbar diff --git a/google-http-client-android/src/main/java/com/google/api/client/extensions/android/json/AndroidJsonGenerator.java b/google-http-client-android/src/main/java/com/google/api/client/extensions/android/json/AndroidJsonGenerator.java index 27b403804..fcda663f7 100644 --- a/google-http-client-android/src/main/java/com/google/api/client/extensions/android/json/AndroidJsonGenerator.java +++ b/google-http-client-android/src/main/java/com/google/api/client/extensions/android/json/AndroidJsonGenerator.java @@ -24,12 +24,10 @@ import java.math.BigInteger; /** - * {@link Beta}
+ * {@link Beta}
* Low-level JSON serializer implementation based on GSON. * - *

- * Implementation is not thread-safe. - *

+ *

Implementation is not thread-safe. * * @author Yaniv Inbar */ diff --git a/google-http-client-android/src/main/java/com/google/api/client/extensions/android/json/AndroidJsonParser.java b/google-http-client-android/src/main/java/com/google/api/client/extensions/android/json/AndroidJsonParser.java index b7af28466..622dcc971 100644 --- a/google-http-client-android/src/main/java/com/google/api/client/extensions/android/json/AndroidJsonParser.java +++ b/google-http-client-android/src/main/java/com/google/api/client/extensions/android/json/AndroidJsonParser.java @@ -29,12 +29,10 @@ import java.util.List; /** - * {@link Beta}
+ * {@link Beta}
* Low-level JSON serializer implementation based on GSON. * - *

- * Implementation is not thread-safe. - *

+ *

Implementation is not thread-safe. * * @author Yaniv Inbar */ @@ -87,7 +85,6 @@ public short getShortValue() { return Short.parseShort(currentText); } - @Override public int getIntValue() { checkNumber(); @@ -199,8 +196,10 @@ public JsonToken nextToken() throws IOException { break; case NUMBER: currentText = reader.nextString(); - currentToken = currentText.indexOf('.') == -1 - ? JsonToken.VALUE_NUMBER_INT : JsonToken.VALUE_NUMBER_FLOAT; + currentToken = + currentText.indexOf('.') == -1 + ? JsonToken.VALUE_NUMBER_INT + : JsonToken.VALUE_NUMBER_FLOAT; break; case NAME: currentText = reader.nextName(); diff --git a/google-http-client-android/src/main/java/com/google/api/client/extensions/android/json/package-info.java b/google-http-client-android/src/main/java/com/google/api/client/extensions/android/json/package-info.java index 0507f3dc9..cd99614f8 100644 --- a/google-http-client-android/src/main/java/com/google/api/client/extensions/android/json/package-info.java +++ b/google-http-client-android/src/main/java/com/google/api/client/extensions/android/json/package-info.java @@ -13,7 +13,7 @@ */ /** - * {@link com.google.api.client.util.Beta}
+ * {@link com.google.api.client.util.Beta}
* Low-level implementation of the GSON parser library built-in to the Android 3.0 SDK. * * @since 1.11 diff --git a/google-http-client-android/src/main/java/com/google/api/client/extensions/android/package-info.java b/google-http-client-android/src/main/java/com/google/api/client/extensions/android/package-info.java index 7149b88a7..3862d01ba 100644 --- a/google-http-client-android/src/main/java/com/google/api/client/extensions/android/package-info.java +++ b/google-http-client-android/src/main/java/com/google/api/client/extensions/android/package-info.java @@ -13,7 +13,7 @@ */ /** - * {@link com.google.api.client.util.Beta}
+ * {@link com.google.api.client.util.Beta}
* Utilities for Android. * * @since 1.11 diff --git a/google-http-client-android/src/main/java/com/google/api/client/extensions/android/util/store/FileDataStoreFactory.java b/google-http-client-android/src/main/java/com/google/api/client/extensions/android/util/store/FileDataStoreFactory.java new file mode 100644 index 000000000..b9c808b5a --- /dev/null +++ b/google-http-client-android/src/main/java/com/google/api/client/extensions/android/util/store/FileDataStoreFactory.java @@ -0,0 +1,141 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.api.client.extensions.android.util.store; + +import com.google.api.client.util.IOUtils; +import com.google.api.client.util.Maps; +import com.google.api.client.util.store.AbstractDataStoreFactory; +import com.google.api.client.util.store.AbstractMemoryDataStore; +import com.google.api.client.util.store.DataStore; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.Serializable; +import java.util.logging.Logger; + +/** + * Thread-safe file implementation of a credential store. + * + *

For security purposes, the file's permissions are set to be accessible only by the file's + * owner. + * + *

Note: This class was branched from the primary implementation in google-http-client to allow + * the mainline implementation to support Windows file permissions. + * + * @since 1.36 + * @author Yaniv Inbar + */ +public class FileDataStoreFactory extends AbstractDataStoreFactory { + + private static final Logger LOGGER = Logger.getLogger(FileDataStoreFactory.class.getName()); + + /** Directory to store data. */ + private final File dataDirectory; + + /** @param dataDirectory data directory */ + public FileDataStoreFactory(File dataDirectory) throws IOException { + dataDirectory = dataDirectory.getCanonicalFile(); + // error if it is a symbolic link + if (IOUtils.isSymbolicLink(dataDirectory)) { + throw new IOException("unable to use a symbolic link: " + dataDirectory); + } + // create parent directory (if necessary) + if (!dataDirectory.exists() && !dataDirectory.mkdirs()) { + throw new IOException("unable to create directory: " + dataDirectory); + } + this.dataDirectory = dataDirectory; + setPermissionsToOwnerOnly(dataDirectory); + } + + /** Returns the data directory. */ + public final File getDataDirectory() { + return dataDirectory; + } + + @Override + protected DataStore createDataStore(String id) throws IOException { + return new FileDataStore(this, dataDirectory, id); + } + + /** + * File data store that inherits from the abstract memory data store because the key-value pairs + * are stored in a memory cache, and saved in the file (see {@link #save()} when changing values. + * + * @param serializable type of the mapped value + */ + static class FileDataStore extends AbstractMemoryDataStore { + + /** File to store data. */ + private final File dataFile; + + FileDataStore(FileDataStoreFactory dataStore, File dataDirectory, String id) + throws IOException { + super(dataStore, id); + this.dataFile = new File(dataDirectory, id); + // error if it is a symbolic link + if (IOUtils.isSymbolicLink(dataFile)) { + throw new IOException("unable to use a symbolic link: " + dataFile); + } + // create new file (if necessary) + if (dataFile.createNewFile()) { + keyValueMap = Maps.newHashMap(); + // save the credentials to create a new file + save(); + } else { + // load credentials from existing file + keyValueMap = IOUtils.deserialize(new FileInputStream(dataFile)); + } + } + + @Override + public void save() throws IOException { + IOUtils.serialize(keyValueMap, new FileOutputStream(dataFile)); + } + + @Override + public FileDataStoreFactory getDataStoreFactory() { + return (FileDataStoreFactory) super.getDataStoreFactory(); + } + } + + /** + * Attempts to set the given file's permissions such that it can only be read, written, and + * executed by the file's owner. + * + * @param file the file's permissions to modify + */ + static void setPermissionsToOwnerOnly(File file) { + // Disable access by other users if O/S allows it and set file permissions to readable and + // writable by user. + try { + if (!file.setReadable(false, false) + || !file.setWritable(false, false) + || !file.setExecutable(false, false)) { + LOGGER.warning("unable to change permissions for everybody: " + file); + } + if (!file.setReadable(true, true) + || !file.setWritable(true, true) + || !file.setExecutable(true, true)) { + LOGGER.warning("unable to change permissions for owner: " + file); + } + } catch (SecurityException exception) { + // ignored + } catch (IllegalArgumentException exception) { + // ignored + } + } +} diff --git a/google-http-client-apache-v2/pom.xml b/google-http-client-apache-v2/pom.xml new file mode 100644 index 000000000..5c6e63b3e --- /dev/null +++ b/google-http-client-apache-v2/pom.xml @@ -0,0 +1,94 @@ + + 4.0.0 + + com.google.http-client + google-http-client-parent + 2.0.3-SNAPSHOT + ../pom.xml + + google-http-client-apache-v2 + 2.0.3-SNAPSHOT + Apache HTTP transport v2 for the Google HTTP Client Library for Java. + + + + + maven-javadoc-plugin + + + https://download.oracle.com/javase/7/docs/api/ + + ${project.name} ${project.version} + ${project.artifactId} ${project.version} + + + + maven-source-plugin + + + org.codehaus.mojo + build-helper-maven-plugin + 3.6.0 + + + add-test-source + generate-test-sources + + add-test-source + + + + target/generated-test-sources + + + + + + + maven-jar-plugin + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + com.google.api.client.http.apache.v2 + + + + + + org.apache.felix + maven-bundle-plugin + 5.1.9 + + + bundle-manifest + process-classes + + manifest + + + + + + + + + com.google.http-client + google-http-client + + + junit + junit + test + + + org.apache.httpcomponents + httpclient + + + org.apache.httpcomponents + httpcore + + + diff --git a/google-http-client-apache-v2/src/main/java/com/google/api/client/http/apache/v2/ApacheHttpRequest.java b/google-http-client-apache-v2/src/main/java/com/google/api/client/http/apache/v2/ApacheHttpRequest.java new file mode 100644 index 000000000..ae2aac763 --- /dev/null +++ b/google-http-client-apache-v2/src/main/java/com/google/api/client/http/apache/v2/ApacheHttpRequest.java @@ -0,0 +1,75 @@ +/* + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.http.apache.v2; + +import com.google.api.client.http.LowLevelHttpRequest; +import com.google.api.client.http.LowLevelHttpResponse; +import com.google.api.client.util.Preconditions; +import java.io.IOException; +import org.apache.http.HttpEntityEnclosingRequest; +import org.apache.http.client.HttpClient; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.HttpRequestBase; + +/** @author Yaniv Inbar */ +final class ApacheHttpRequest extends LowLevelHttpRequest { + private final HttpClient httpClient; + + private final HttpRequestBase request; + + private RequestConfig.Builder requestConfig; + + @SuppressWarnings("deprecation") + ApacheHttpRequest(HttpClient httpClient, HttpRequestBase request) { + this.httpClient = httpClient; + this.request = request; + // disable redirects as google-http-client handles redirects + this.requestConfig = + RequestConfig.custom() + .setRedirectsEnabled(false) + .setNormalizeUri(false) + // TODO(chingor): configure in HttpClientBuilder when available + .setStaleConnectionCheckEnabled(false); + } + + @Override + public void addHeader(String name, String value) { + request.addHeader(name, value); + } + + @Override + public void setTimeout(int connectTimeout, int readTimeout) throws IOException { + requestConfig.setConnectTimeout(connectTimeout).setSocketTimeout(readTimeout); + } + + @Override + public LowLevelHttpResponse execute() throws IOException { + if (getStreamingContent() != null) { + Preconditions.checkState( + request instanceof HttpEntityEnclosingRequest, + "Apache HTTP client does not support %s requests with content.", + request.getRequestLine().getMethod()); + ContentEntity entity = new ContentEntity(getContentLength(), getStreamingContent()); + entity.setContentEncoding(getContentEncoding()); + entity.setContentType(getContentType()); + if (getContentLength() == -1) { + entity.setChunked(true); + } + ((HttpEntityEnclosingRequest) request).setEntity(entity); + } + request.setConfig(requestConfig.build()); + return new ApacheHttpResponse(request, httpClient.execute(request)); + } +} diff --git a/google-http-client-apache-v2/src/main/java/com/google/api/client/http/apache/v2/ApacheHttpResponse.java b/google-http-client-apache-v2/src/main/java/com/google/api/client/http/apache/v2/ApacheHttpResponse.java new file mode 100644 index 000000000..ccf9c5788 --- /dev/null +++ b/google-http-client-apache-v2/src/main/java/com/google/api/client/http/apache/v2/ApacheHttpResponse.java @@ -0,0 +1,120 @@ +/* + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.http.apache.v2; + +import com.google.api.client.http.LowLevelHttpResponse; +import java.io.IOException; +import java.io.InputStream; +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.StatusLine; +import org.apache.http.client.methods.HttpRequestBase; + +final class ApacheHttpResponse extends LowLevelHttpResponse { + + private final HttpRequestBase request; + private final HttpResponse response; + private final Header[] allHeaders; + + ApacheHttpResponse(HttpRequestBase request, HttpResponse response) { + this.request = request; + this.response = response; + allHeaders = response.getAllHeaders(); + } + + @Override + public int getStatusCode() { + StatusLine statusLine = response.getStatusLine(); + return statusLine == null ? 0 : statusLine.getStatusCode(); + } + + @Override + public InputStream getContent() throws IOException { + HttpEntity entity = response.getEntity(); + return entity == null ? null : entity.getContent(); + } + + @Override + public String getContentEncoding() { + HttpEntity entity = response.getEntity(); + if (entity != null) { + Header contentEncodingHeader = entity.getContentEncoding(); + if (contentEncodingHeader != null) { + return contentEncodingHeader.getValue(); + } + } + return null; + } + + @Override + public long getContentLength() { + HttpEntity entity = response.getEntity(); + return entity == null ? -1 : entity.getContentLength(); + } + + @Override + public String getContentType() { + HttpEntity entity = response.getEntity(); + if (entity != null) { + Header contentTypeHeader = entity.getContentType(); + if (contentTypeHeader != null) { + return contentTypeHeader.getValue(); + } + } + return null; + } + + @Override + public String getReasonPhrase() { + StatusLine statusLine = response.getStatusLine(); + return statusLine == null ? null : statusLine.getReasonPhrase(); + } + + @Override + public String getStatusLine() { + StatusLine statusLine = response.getStatusLine(); + return statusLine == null ? null : statusLine.toString(); + } + + public String getHeaderValue(String name) { + return response.getLastHeader(name).getValue(); + } + + @Override + public int getHeaderCount() { + return allHeaders.length; + } + + @Override + public String getHeaderName(int index) { + return allHeaders[index].getName(); + } + + @Override + public String getHeaderValue(int index) { + return allHeaders[index].getValue(); + } + + /** + * Aborts execution of the request. + * + * @since 1.30 + */ + @Override + public void disconnect() { + request.abort(); + } +} diff --git a/google-http-client-apache-v2/src/main/java/com/google/api/client/http/apache/v2/ApacheHttpTransport.java b/google-http-client-apache-v2/src/main/java/com/google/api/client/http/apache/v2/ApacheHttpTransport.java new file mode 100644 index 000000000..ccf736f84 --- /dev/null +++ b/google-http-client-apache-v2/src/main/java/com/google/api/client/http/apache/v2/ApacheHttpTransport.java @@ -0,0 +1,231 @@ +/* + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.http.apache.v2; + +import com.google.api.client.http.HttpMethods; +import com.google.api.client.http.HttpTransport; +import com.google.api.client.util.Beta; +import java.io.IOException; +import java.net.ProxySelector; +import java.util.concurrent.TimeUnit; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpHead; +import org.apache.http.client.methods.HttpOptions; +import org.apache.http.client.methods.HttpPatch; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.client.methods.HttpTrace; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.impl.conn.SystemDefaultRoutePlanner; + +/** + * Thread-safe HTTP transport based on the Apache HTTP Client library. + * + *

Implementation is thread-safe, as long as any parameter modification to the {@link + * #getHttpClient() Apache HTTP Client} is only done at initialization time. For maximum efficiency, + * applications should use a single globally-shared instance of the HTTP transport. + * + *

Default settings are specified in {@link #newDefaultHttpClient()}. Use the {@link + * #ApacheHttpTransport(HttpClient)} constructor to override the Apache HTTP Client used. Please + * read the + * Apache HTTP Client connection management tutorial for more complex configuration options. + * + * @since 1.30 + * @author Yaniv Inbar + */ +public final class ApacheHttpTransport extends HttpTransport { + + /** Apache HTTP client. */ + private final HttpClient httpClient; + + /** If the HTTP client uses mTLS channel. */ + private final boolean isMtls; + + /** + * Constructor that uses {@link #newDefaultHttpClient()} for the Apache HTTP client. + * + * @since 1.30 + */ + public ApacheHttpTransport() { + this(newDefaultHttpClient(), false); + } + + /** + * Constructor that allows an alternative Apache HTTP client to be used. + * + *

Note that in the previous version, we overrode several settings. However, we are no longer + * able to do so. + * + *

If you choose to provide your own Apache HttpClient implementation, be sure that + * + *

    + *
  • HTTP version is set to 1.1. + *
  • Redirects are disabled (google-http-client handles redirects). + *
  • Retries are disabled (google-http-client handles retries). + *
+ * + * @param httpClient Apache HTTP client to use + * @since 1.30 + */ + public ApacheHttpTransport(HttpClient httpClient) { + this.httpClient = httpClient; + this.isMtls = false; + } + + /** + * {@link Beta}
+ * Constructor that allows an alternative Apache HTTP client to be used. + * + *

Note that in the previous version, we overrode several settings. However, we are no longer + * able to do so. + * + *

If you choose to provide your own Apache HttpClient implementation, be sure that + * + *

    + *
  • HTTP version is set to 1.1. + *
  • Redirects are disabled (google-http-client handles redirects). + *
  • Retries are disabled (google-http-client handles retries). + *
+ * + * @param httpClient Apache HTTP client to use + * @param isMtls If the HTTP client is mutual TLS + * @since 1.38 + */ + @Beta + public ApacheHttpTransport(HttpClient httpClient, boolean isMtls) { + this.httpClient = httpClient; + this.isMtls = isMtls; + } + + /** + * Creates a new instance of the Apache HTTP client that is used by the {@link + * #ApacheHttpTransport()} constructor. + * + *

Settings: + * + *

    + *
  • The client connection manager is set to {@link PoolingHttpClientConnectionManager}. + *
  • The route planner uses {@link SystemDefaultRoutePlanner} with {@link + * ProxySelector#getDefault()}, which uses the proxy settings from system + * properties. + *
+ * + * @return new instance of the Apache HTTP client + * @since 1.30 + */ + public static HttpClient newDefaultHttpClient() { + return newDefaultHttpClientBuilder().build(); + } + + /** + * Creates a new Apache HTTP client builder that is used by the {@link #ApacheHttpTransport()} + * constructor. + * + *

Settings: + * + *

    + *
  • The client connection manager is set to {@link PoolingHttpClientConnectionManager}. + *
  • The route planner uses {@link SystemDefaultRoutePlanner} with {@link + * ProxySelector#getDefault()}, which uses the proxy settings from system + * properties. + *
+ * + * @return new instance of the Apache HTTP client + * @since 1.31 + */ + public static HttpClientBuilder newDefaultHttpClientBuilder() { + + return HttpClientBuilder.create() + .useSystemProperties() + .setSSLSocketFactory(SSLConnectionSocketFactory.getSocketFactory()) + .setMaxConnTotal(200) + .setMaxConnPerRoute(20) + .setConnectionTimeToLive(-1, TimeUnit.MILLISECONDS) + .setRoutePlanner(new SystemDefaultRoutePlanner(ProxySelector.getDefault())) + .disableRedirectHandling() + .disableAutomaticRetries(); + } + + @Override + public boolean supportsMethod(String method) { + return true; + } + + @Override + protected ApacheHttpRequest buildRequest(String method, String url) { + HttpRequestBase requestBase; + if (method.equals(HttpMethods.DELETE)) { + requestBase = new HttpDelete(url); + } else if (method.equals(HttpMethods.GET)) { + requestBase = new HttpGet(url); + } else if (method.equals(HttpMethods.HEAD)) { + requestBase = new HttpHead(url); + } else if (method.equals(HttpMethods.PATCH)) { + requestBase = new HttpPatch(url); + } else if (method.equals(HttpMethods.POST)) { + requestBase = new HttpPost(url); + } else if (method.equals(HttpMethods.PUT)) { + requestBase = new HttpPut(url); + } else if (method.equals(HttpMethods.TRACE)) { + requestBase = new HttpTrace(url); + } else if (method.equals(HttpMethods.OPTIONS)) { + requestBase = new HttpOptions(url); + } else { + requestBase = new HttpExtensionMethod(method, url); + } + return new ApacheHttpRequest(httpClient, requestBase); + } + + /** + * Shuts down the connection manager and releases allocated resources. This closes all + * connections, whether they are currently used or not. + * + * @since 1.30 + */ + @Override + public void shutdown() throws IOException { + if (httpClient instanceof CloseableHttpClient) { + ((CloseableHttpClient) httpClient).close(); + } + } + + /** + * Returns the Apache HTTP client. + * + * @since 1.30 + */ + public HttpClient getHttpClient() { + return httpClient; + } + + /** Returns if the underlying HTTP client is mTLS. */ + @Override + public boolean isMtls() { + return isMtls; + } +} diff --git a/google-http-client-apache-v2/src/main/java/com/google/api/client/http/apache/v2/ContentEntity.java b/google-http-client-apache-v2/src/main/java/com/google/api/client/http/apache/v2/ContentEntity.java new file mode 100644 index 000000000..451b67f2c --- /dev/null +++ b/google-http-client-apache-v2/src/main/java/com/google/api/client/http/apache/v2/ContentEntity.java @@ -0,0 +1,68 @@ +/* + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.http.apache.v2; + +import com.google.api.client.util.Preconditions; +import com.google.api.client.util.StreamingContent; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import org.apache.http.entity.AbstractHttpEntity; + +/** @author Yaniv Inbar */ +final class ContentEntity extends AbstractHttpEntity { + + /** Content length or less than zero if not known. */ + private final long contentLength; + + /** Streaming content. */ + private final StreamingContent streamingContent; + + /** + * @param contentLength content length or less than zero if not known + * @param streamingContent streaming content + */ + ContentEntity(long contentLength, StreamingContent streamingContent) { + this.contentLength = contentLength; + this.streamingContent = Preconditions.checkNotNull(streamingContent); + } + + @Override + public InputStream getContent() { + throw new UnsupportedOperationException(); + } + + @Override + public long getContentLength() { + return contentLength; + } + + @Override + public boolean isRepeatable() { + return false; + } + + @Override + public boolean isStreaming() { + return true; + } + + @Override + public void writeTo(OutputStream out) throws IOException { + if (contentLength != 0) { + streamingContent.writeTo(out); + } + } +} diff --git a/google-http-client-apache-v2/src/main/java/com/google/api/client/http/apache/v2/HttpExtensionMethod.java b/google-http-client-apache-v2/src/main/java/com/google/api/client/http/apache/v2/HttpExtensionMethod.java new file mode 100644 index 000000000..360baaecb --- /dev/null +++ b/google-http-client-apache-v2/src/main/java/com/google/api/client/http/apache/v2/HttpExtensionMethod.java @@ -0,0 +1,44 @@ +/* + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.http.apache.v2; + +import com.google.api.client.util.Preconditions; +import java.net.URI; +import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; + +/** + * HTTP extension method. + * + * @author Yaniv Inbar + */ +final class HttpExtensionMethod extends HttpEntityEnclosingRequestBase { + + /** Request method name. */ + private final String methodName; + + /** + * @param methodName request method name + * @param uri URI + */ + public HttpExtensionMethod(String methodName, String uri) { + this.methodName = Preconditions.checkNotNull(methodName); + setURI(URI.create(uri)); + } + + @Override + public String getMethod() { + return methodName; + } +} diff --git a/google-http-client-jdo/src/main/java/com/google/api/client/extensions/jdo/package-info.java b/google-http-client-apache-v2/src/main/java/com/google/api/client/http/apache/v2/package-info.java similarity index 77% rename from google-http-client-jdo/src/main/java/com/google/api/client/extensions/jdo/package-info.java rename to google-http-client-apache-v2/src/main/java/com/google/api/client/http/apache/v2/package-info.java index 706030644..7ca1708ad 100644 --- a/google-http-client-jdo/src/main/java/com/google/api/client/extensions/jdo/package-info.java +++ b/google-http-client-apache-v2/src/main/java/com/google/api/client/http/apache/v2/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Google Inc. + * 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 @@ -13,10 +13,9 @@ */ /** - * Support for the JDO data store. + * HTTP Transport library for Google API's based on Apache HTTP Client version 4.5+. * - * @since 1.15 + * @since 1.30 * @author Yaniv Inbar */ -package com.google.api.client.extensions.jdo; - +package com.google.api.client.http.apache.v2; diff --git a/google-http-client-apache-v2/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-apache-v2/reflect-config.json b/google-http-client-apache-v2/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-apache-v2/reflect-config.json new file mode 100644 index 000000000..97a9fba46 --- /dev/null +++ b/google-http-client-apache-v2/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-apache-v2/reflect-config.json @@ -0,0 +1,59 @@ +[ + { + "name": "org.apache.commons.logging.impl.LogFactoryImpl", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true, + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "name": "org.apache.commons.logging.impl.Log4JLogger", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "org.apache.commons.logging.impl.Jdk14Logger", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "org.apache.commons.logging.impl.SimpleLog", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "org.apache.commons.logging.impl.Jdk13LumberjackLogger", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "org.apache.commons.logging.LogFactory", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + } +] \ No newline at end of file diff --git a/google-http-client-apache-v2/src/test/java/com/google/api/client/http/apache/v2/ApacheHttpRequestTest.java b/google-http-client-apache-v2/src/test/java/com/google/api/client/http/apache/v2/ApacheHttpRequestTest.java new file mode 100644 index 000000000..74c97244a --- /dev/null +++ b/google-http-client-apache-v2/src/test/java/com/google/api/client/http/apache/v2/ApacheHttpRequestTest.java @@ -0,0 +1,59 @@ +/* + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.http.apache.v2; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import com.google.api.client.http.ByteArrayContent; +import com.google.api.client.http.HttpContent; +import com.google.api.client.http.InputStreamContent; +import com.google.api.client.testing.http.apache.MockHttpClient; +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import org.junit.Test; + +public class ApacheHttpRequestTest { + + @Test + public void testContentLengthSet() throws Exception { + HttpExtensionMethod base = new HttpExtensionMethod("POST", "http://www.google.com"); + ApacheHttpRequest request = new ApacheHttpRequest(new MockHttpClient(), base); + HttpContent content = + new ByteArrayContent("text/plain", "sample".getBytes(StandardCharsets.UTF_8)); + request.setStreamingContent(content); + request.setContentLength(content.getLength()); + request.execute(); + + assertFalse(base.getEntity().isChunked()); + assertEquals(6, base.getEntity().getContentLength()); + } + + @Test + public void testChunked() throws Exception { + byte[] buf = new byte[300]; + Arrays.fill(buf, (byte) ' '); + HttpExtensionMethod base = new HttpExtensionMethod("POST", "http://www.google.com"); + ApacheHttpRequest request = new ApacheHttpRequest(new MockHttpClient(), base); + HttpContent content = new InputStreamContent("text/plain", new ByteArrayInputStream(buf)); + request.setStreamingContent(content); + request.execute(); + + assertTrue(base.getEntity().isChunked()); + assertEquals(-1, base.getEntity().getContentLength()); + } +} diff --git a/google-http-client-apache-v2/src/test/java/com/google/api/client/http/apache/v2/ApacheHttpTransportTest.java b/google-http-client-apache-v2/src/test/java/com/google/api/client/http/apache/v2/ApacheHttpTransportTest.java new file mode 100644 index 000000000..48a9d1c56 --- /dev/null +++ b/google-http-client-apache-v2/src/test/java/com/google/api/client/http/apache/v2/ApacheHttpTransportTest.java @@ -0,0 +1,341 @@ +/* + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.http.apache.v2; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeFalse; +import static org.junit.Assume.assumeTrue; + +import com.google.api.client.http.GenericUrl; +import com.google.api.client.http.HttpResponseException; +import com.google.api.client.http.HttpTransport; +import com.google.api.client.http.LowLevelHttpResponse; +import com.google.api.client.testing.http.apache.MockHttpClient; +import com.google.api.client.util.ByteArrayStreamingContent; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import org.apache.http.Header; +import org.apache.http.HttpClientConnection; +import org.apache.http.HttpException; +import org.apache.http.HttpRequest; +import org.apache.http.HttpRequestInterceptor; +import org.apache.http.HttpResponse; +import org.apache.http.HttpVersion; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.conn.ConnectTimeoutException; +import org.apache.http.conn.HttpHostConnectException; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.message.BasicHttpResponse; +import org.apache.http.protocol.HttpContext; +import org.apache.http.protocol.HttpRequestExecutor; +import org.junit.Assert; +import org.junit.Test; + +/** + * Tests {@link ApacheHttpTransport}. + * + * @author Yaniv Inbar + */ +public class ApacheHttpTransportTest { + + private static class MockHttpResponse extends BasicHttpResponse implements CloseableHttpResponse { + public MockHttpResponse() { + super(HttpVersion.HTTP_1_1, 200, "OK"); + } + + @Override + public void close() throws IOException {} + } + + @Test + public void testApacheHttpTransport() { + ApacheHttpTransport transport = new ApacheHttpTransport(); + checkHttpTransport(transport); + assertFalse(transport.isMtls()); + } + + @Test + public void testApacheHttpTransportWithParam() { + ApacheHttpTransport transport = new ApacheHttpTransport(HttpClients.custom().build(), true); + checkHttpTransport(transport); + assertTrue(transport.isMtls()); + } + + @Test + public void testNewDefaultHttpClient() { + HttpClient client = ApacheHttpTransport.newDefaultHttpClient(); + checkHttpClient(client); + } + + private void checkHttpTransport(ApacheHttpTransport transport) { + assertNotNull(transport); + HttpClient client = transport.getHttpClient(); + checkHttpClient(client); + } + + private void checkHttpClient(HttpClient client) { + assertNotNull(client); + // TODO(chingor): Is it possible to test this effectively? The newer HttpClient implementations + // are read-only and we're testing that we built the client with the right configuration + } + + @Test + public void testRequestsWithContent() throws IOException { + HttpClient mockClient = + new MockHttpClient() { + @Override + public CloseableHttpResponse execute(HttpUriRequest request) + throws IOException, ClientProtocolException { + return new MockHttpResponse(); + } + }; + ApacheHttpTransport transport = new ApacheHttpTransport(mockClient); + + // Test GET. + subtestUnsupportedRequestsWithContent( + transport.buildRequest("GET", "http://www.test.url"), "GET"); + // Test DELETE. + subtestUnsupportedRequestsWithContent( + transport.buildRequest("DELETE", "http://www.test.url"), "DELETE"); + // Test HEAD. + subtestUnsupportedRequestsWithContent( + transport.buildRequest("HEAD", "http://www.test.url"), "HEAD"); + + // Test PATCH. + execute(transport.buildRequest("PATCH", "http://www.test.url")); + // Test PUT. + execute(transport.buildRequest("PUT", "http://www.test.url")); + // Test POST. + execute(transport.buildRequest("POST", "http://www.test.url")); + // Test PATCH. + execute(transport.buildRequest("PATCH", "http://www.test.url")); + } + + private void subtestUnsupportedRequestsWithContent(ApacheHttpRequest request, String method) + throws IOException { + try { + execute(request); + fail("expected " + IllegalStateException.class); + } catch (IllegalStateException e) { + // expected + assertEquals( + e.getMessage(), + "Apache HTTP client does not support " + method + " requests with content."); + } + } + + private void execute(ApacheHttpRequest request) throws IOException { + byte[] bytes = "abc".getBytes(StandardCharsets.UTF_8); + request.setStreamingContent(new ByteArrayStreamingContent(bytes)); + request.setContentType("text/html"); + request.setContentLength(bytes.length); + request.execute(); + } + + @Test + public void testRequestShouldNotFollowRedirects() throws IOException { + final AtomicInteger requestsAttempted = new AtomicInteger(0); + HttpRequestExecutor requestExecutor = + new HttpRequestExecutor() { + @Override + public HttpResponse execute( + HttpRequest request, HttpClientConnection connection, HttpContext context) + throws IOException, HttpException { + HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 302, null); + response.addHeader("location", "https://google.com/path"); + requestsAttempted.incrementAndGet(); + return response; + } + }; + HttpClient client = HttpClients.custom().setRequestExecutor(requestExecutor).build(); + ApacheHttpTransport transport = new ApacheHttpTransport(client); + ApacheHttpRequest request = transport.buildRequest("GET", "https://google.com"); + LowLevelHttpResponse response = request.execute(); + assertEquals(1, requestsAttempted.get()); + assertEquals(302, response.getStatusCode()); + } + + @Test + public void testRequestCanSetHeaders() { + final AtomicBoolean interceptorCalled = new AtomicBoolean(false); + HttpClient client = + HttpClients.custom() + .addInterceptorFirst( + new HttpRequestInterceptor() { + @Override + public void process(HttpRequest request, HttpContext context) + throws HttpException, IOException { + Header header = request.getFirstHeader("foo"); + assertNotNull("Should have found header", header); + assertEquals("bar", header.getValue()); + interceptorCalled.set(true); + throw new IOException("cancelling request"); + } + }) + .build(); + + ApacheHttpTransport transport = new ApacheHttpTransport(client); + ApacheHttpRequest request = transport.buildRequest("GET", "https://google.com"); + request.addHeader("foo", "bar"); + try { + LowLevelHttpResponse response = request.execute(); + fail("should not actually make the request"); + } catch (IOException exception) { + assertEquals("cancelling request", exception.getMessage()); + } + assertTrue("Expected to have called our test interceptor", interceptorCalled.get()); + } + + @Test(timeout = 10_000L) + public void testConnectTimeout() { + // Apache HttpClient doesn't appear to behave correctly on windows + assumeFalse(isWindows()); + // TODO(chanseok): Java 17 returns an IOException (SocketException: Network is unreachable). + // Figure out a way to verify connection timeout works on Java 17+. + assumeTrue(System.getProperty("java.version").compareTo("17") < 0); + + HttpTransport httpTransport = new ApacheHttpTransport(); + GenericUrl url = new GenericUrl("http://google.com:81"); + try { + httpTransport.createRequestFactory().buildGetRequest(url).setConnectTimeout(100).execute(); + fail("should have thrown an exception"); + } catch (HttpHostConnectException | ConnectTimeoutException expected) { + // expected + } catch (IOException e) { + fail("unexpected IOException: " + e.getClass().getName() + ": " + e.getMessage()); + } + } + + private static class FakeServer implements AutoCloseable { + private final HttpServer server; + private final ExecutorService executorService; + + FakeServer(HttpHandler httpHandler) throws IOException { + server = HttpServer.create(new InetSocketAddress(0), 0); + executorService = Executors.newFixedThreadPool(1); + server.setExecutor(executorService); + server.createContext("/", httpHandler); + server.start(); + } + + public int getPort() { + return server.getAddress().getPort(); + } + + @Override + public void close() { + server.stop(0); + executorService.shutdownNow(); + } + } + + @Test + public void testNormalizedUrl() throws IOException { + final HttpHandler handler = + new HttpHandler() { + @Override + public void handle(HttpExchange httpExchange) throws IOException { + byte[] response = httpExchange.getRequestURI().toString().getBytes(); + httpExchange.sendResponseHeaders(200, response.length); + try (OutputStream out = httpExchange.getResponseBody()) { + out.write(response); + } + } + }; + try (FakeServer server = new FakeServer(handler)) { + HttpTransport transport = new ApacheHttpTransport(); + GenericUrl testUrl = new GenericUrl("http://localhost/foo//bar"); + testUrl.setPort(server.getPort()); + com.google.api.client.http.HttpResponse response = + transport.createRequestFactory().buildGetRequest(testUrl).execute(); + assertEquals(200, response.getStatusCode()); + assertEquals("/foo//bar", response.parseAsString()); + } + } + + @Test + public void testReadErrorStream() throws IOException { + final HttpHandler handler = + new HttpHandler() { + @Override + public void handle(HttpExchange httpExchange) throws IOException { + byte[] response = "Forbidden".getBytes(StandardCharsets.UTF_8); + httpExchange.sendResponseHeaders(403, response.length); + try (OutputStream out = httpExchange.getResponseBody()) { + out.write(response); + } + } + }; + try (FakeServer server = new FakeServer(handler)) { + HttpTransport transport = new ApacheHttpTransport(); + GenericUrl testUrl = new GenericUrl("http://localhost/foo//bar"); + testUrl.setPort(server.getPort()); + com.google.api.client.http.HttpRequest getRequest = + transport.createRequestFactory().buildGetRequest(testUrl); + getRequest.setThrowExceptionOnExecuteError(false); + com.google.api.client.http.HttpResponse response = getRequest.execute(); + assertEquals(403, response.getStatusCode()); + assertEquals("Forbidden", response.parseAsString()); + } + } + + @Test + public void testReadErrorStream_withException() throws IOException { + final HttpHandler handler = + new HttpHandler() { + @Override + public void handle(HttpExchange httpExchange) throws IOException { + byte[] response = "Forbidden".getBytes(StandardCharsets.UTF_8); + httpExchange.sendResponseHeaders(403, response.length); + try (OutputStream out = httpExchange.getResponseBody()) { + out.write(response); + } + } + }; + try (FakeServer server = new FakeServer(handler)) { + HttpTransport transport = new ApacheHttpTransport(); + GenericUrl testUrl = new GenericUrl("http://localhost/foo//bar"); + testUrl.setPort(server.getPort()); + com.google.api.client.http.HttpRequest getRequest = + transport.createRequestFactory().buildGetRequest(testUrl); + try { + getRequest.execute(); + Assert.fail(); + } catch (HttpResponseException ex) { + assertEquals("Forbidden", ex.getContent()); + } + } + } + + private boolean isWindows() { + return System.getProperty("os.name").startsWith("Windows"); + } +} diff --git a/google-http-client-jackson/pom.xml b/google-http-client-apache-v5/pom.xml similarity index 61% rename from google-http-client-jackson/pom.xml rename to google-http-client-apache-v5/pom.xml index 712e5366e..894859689 100644 --- a/google-http-client-jackson/pom.xml +++ b/google-http-client-apache-v5/pom.xml @@ -1,15 +1,15 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 com.google.http-client google-http-client-parent - 1.26.1-SNAPSHOT + 2.0.3-SNAPSHOT ../pom.xml - google-http-client-jackson - 1.26.1-SNAPSHOT - Jackson extensions to the Google HTTP Client Library for Java. + google-http-client-apache-v5 + 2.0.3-SNAPSHOT + Apache HTTP transport v5 for the Google HTTP Client Library for Java. @@ -17,8 +17,7 @@ maven-javadoc-plugin - http://download.oracle.com/javase/6/docs/api/ - https://jar-download.com/artifacts/org.codehaus.jackson/jackson-core-asl/${project.jackson-core-asl.version}/documentation + https://download.oracle.com/javase/7/docs/api/ ${project.name} ${project.version} ${project.artifactId} ${project.version} @@ -26,20 +25,11 @@ maven-source-plugin - - - source-jar - compile - - jar - - - org.codehaus.mojo build-helper-maven-plugin - 1.5 + 3.6.0 add-test-source @@ -58,52 +48,68 @@ maven-jar-plugin - + ${project.build.outputDirectory}/META-INF/MANIFEST.MF - com.google.api.client.json.jackson + com.google.api.client.http.apache.v5 - + - - + + org.apache.felix maven-bundle-plugin - 2.5.4 + 5.1.9 bundle-manifest process-classes - + manifest - + + + maven-compiler-plugin + 3.13.0 + + 1.8 + 1.8 + + com.google.http-client google-http-client + + + org.apache.httpcomponents + httpcore + + + org.apache.httpcomponents + httpclient + + - com.google.http-client - google-http-client-test - test + com.google.guava + guava - junit - junit - test + org.apache.httpcomponents.client5 + httpclient5 - org.codehaus.jackson - jackson-core-asl + org.apache.httpcomponents.core5 + httpcore5 - com.google.guava - guava + junit + junit test diff --git a/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5ContentEntity.java b/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5ContentEntity.java new file mode 100644 index 000000000..4a5ab84e6 --- /dev/null +++ b/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5ContentEntity.java @@ -0,0 +1,79 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.http.apache.v5; + +import com.google.api.client.util.Preconditions; +import com.google.api.client.util.StreamingContent; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import org.apache.hc.core5.http.io.entity.AbstractHttpEntity; + +/** + * Translation class to make google-http-client entity conform with Apache 5.x {@link + * AbstractHttpEntity} + */ +final class Apache5ContentEntity extends AbstractHttpEntity { + + /** Content length or less than zero if not known. */ + private final long contentLength; + + /** Streaming content. */ + private final StreamingContent streamingContent; + + /** + * @param contentLength content length or less than zero if not known + * @param streamingContent streaming content + */ + Apache5ContentEntity( + long contentLength, + StreamingContent streamingContent, + String contentType, + String contentEncoding) { + super(contentType, contentEncoding, contentLength == -1); + this.contentLength = contentLength; + this.streamingContent = Preconditions.checkNotNull(streamingContent); + } + + @Override + public InputStream getContent() { + throw new UnsupportedOperationException(); + } + + @Override + public long getContentLength() { + return contentLength; + } + + @Override + public boolean isRepeatable() { + return false; + } + + @Override + public boolean isStreaming() { + return true; + } + + @Override + public void writeTo(OutputStream out) throws IOException { + if (contentLength != 0) { + streamingContent.writeTo(out); + } + } + + @Override + public void close() throws IOException {} +} diff --git a/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5HttpRequest.java b/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5HttpRequest.java new file mode 100644 index 000000000..99d6eca8e --- /dev/null +++ b/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5HttpRequest.java @@ -0,0 +1,78 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.http.apache.v5; + +import com.google.api.client.http.LowLevelHttpRequest; +import com.google.api.client.http.LowLevelHttpResponse; +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import org.apache.hc.client5.http.ClientProtocolException; +import org.apache.hc.client5.http.classic.HttpClient; +import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; +import org.apache.hc.client5.http.config.RequestConfig; +import org.apache.hc.client5.http.routing.RoutingSupport; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.core5.http.HttpException; +import org.apache.hc.core5.http.HttpHost; +import org.apache.hc.core5.util.Timeout; + +public final class Apache5HttpRequest extends LowLevelHttpRequest { + + private final HttpUriRequestBase request; + + private final RequestConfig.Builder requestConfig; + + private final HttpClient httpClient; + + Apache5HttpRequest(HttpClient httpClient, HttpUriRequestBase request) { + this.httpClient = httpClient; + this.request = request; + // disable redirects as google-http-client handles redirects + this.requestConfig = RequestConfig.custom().setRedirectsEnabled(false); + } + + @Override + public void addHeader(String name, String value) { + request.addHeader(name, value); + } + + @Override + public void setTimeout(int connectTimeout, int readTimeout) throws IOException { + requestConfig + .setConnectTimeout(Timeout.of(connectTimeout, TimeUnit.MILLISECONDS)) + // ResponseTimeout behaves the same as 4.x's SocketTimeout + .setResponseTimeout(Timeout.of(readTimeout, TimeUnit.MILLISECONDS)); + } + + @Override + public LowLevelHttpResponse execute() throws IOException { + if (getStreamingContent() != null) { + Apache5ContentEntity entity = + new Apache5ContentEntity( + getContentLength(), getStreamingContent(), getContentType(), getContentEncoding()); + request.setEntity(entity); + } + request.setConfig(requestConfig.build()); + HttpHost target; + try { + target = RoutingSupport.determineHost(request); + } catch (HttpException e) { + throw new ClientProtocolException("The request's host is invalid.", e); + } + // we use a null context so the client creates the default one internally + ClassicHttpResponse httpResponse = httpClient.executeOpen(target, request, null); + return new Apache5HttpResponse(request, httpResponse); + } +} diff --git a/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5HttpResponse.java b/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5HttpResponse.java new file mode 100644 index 000000000..ae3c2ffb6 --- /dev/null +++ b/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5HttpResponse.java @@ -0,0 +1,103 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.http.apache.v5; + +import com.google.api.client.http.LowLevelHttpResponse; +import java.io.IOException; +import java.io.InputStream; +import java.util.logging.Logger; +import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.core5.http.Header; +import org.apache.hc.core5.http.HttpEntity; +import org.apache.hc.core5.http.message.StatusLine; + +final class Apache5HttpResponse extends LowLevelHttpResponse { + + private static final Logger LOGGER = Logger.getLogger(Apache5HttpResponse.class.getName()); + private final HttpUriRequestBase request; + private final ClassicHttpResponse response; + private final Header[] allHeaders; + private final HttpEntity entity; + + Apache5HttpResponse(HttpUriRequestBase request, ClassicHttpResponse response) { + this.request = request; + this.response = response; + this.allHeaders = response.getHeaders(); + this.entity = response.getEntity(); + } + + @Override + public int getStatusCode() { + return response.getCode(); + } + + @Override + public InputStream getContent() throws IOException { + InputStream content = entity == null ? null : entity.getContent(); + return new Apache5ResponseContent(content, response); + } + + @Override + public String getContentEncoding() { + return entity != null ? entity.getContentEncoding() : null; + } + + @Override + public long getContentLength() { + return entity == null ? -1 : entity.getContentLength(); + } + + @Override + public String getContentType() { + return entity == null ? null : entity.getContentType(); + } + + @Override + public String getReasonPhrase() { + return response.getReasonPhrase(); + } + + @Override + public String getStatusLine() { + return new StatusLine(response).toString(); + } + + public String getHeaderValue(String name) { + return response.getLastHeader(name).getValue(); + } + + @Override + public int getHeaderCount() { + return allHeaders.length; + } + + @Override + public String getHeaderName(int index) { + return allHeaders[index].getName(); + } + + @Override + public String getHeaderValue(int index) { + return allHeaders[index].getValue(); + } + + /** Aborts execution of the request. */ + @Override + public void disconnect() throws IOException { + request.abort(); + response.close(); + } +} diff --git a/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5HttpTransport.java b/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5HttpTransport.java new file mode 100644 index 000000000..868a2cf93 --- /dev/null +++ b/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5HttpTransport.java @@ -0,0 +1,221 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.http.apache.v5; + +import com.google.api.client.http.HttpMethods; +import com.google.api.client.http.HttpTransport; +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import java.io.IOException; +import java.net.ProxySelector; +import java.net.URI; +import java.util.concurrent.TimeUnit; +import org.apache.hc.client5.http.classic.HttpClient; +import org.apache.hc.client5.http.classic.methods.HttpDelete; +import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.classic.methods.HttpHead; +import org.apache.hc.client5.http.classic.methods.HttpOptions; +import org.apache.hc.client5.http.classic.methods.HttpPatch; +import org.apache.hc.client5.http.classic.methods.HttpPost; +import org.apache.hc.client5.http.classic.methods.HttpPut; +import org.apache.hc.client5.http.classic.methods.HttpTrace; +import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; +import org.apache.hc.client5.http.config.ConnectionConfig; +import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; +import org.apache.hc.client5.http.impl.routing.SystemDefaultRoutePlanner; +import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory; +import org.apache.hc.core5.io.CloseMode; +import org.apache.hc.core5.io.ModalCloseable; + +/** + * Thread-safe HTTP transport based on the Apache HTTP Client library. + * + *

Implementation is thread-safe, as long as any parameter modification to the {@link + * #getHttpClient() Apache HTTP Client} is only done at initialization time. For maximum efficiency, + * applications should use a single globally-shared instance of the HTTP transport. + * + *

Default settings are specified in {@link #newDefaultHttpClient()}. Use the {@link + * #Apache5HttpTransport(HttpClient)} constructor to override the Apache HTTP Client used. Please + * read the + * Apache HTTP Client 5.x configuration example for more complex configuration options. + */ +public final class Apache5HttpTransport extends HttpTransport { + + /** Apache HTTP client. */ + private final HttpClient httpClient; + + /** If the HTTP client uses mTLS channel. */ + private final boolean isMtls; + + /** Constructor that uses {@link #newDefaultHttpClient()} for the Apache HTTP client. */ + public Apache5HttpTransport() { + this(newDefaultHttpClient(), false); + } + + /** + * Constructor that allows an alternative Apache HTTP client to be used. + * + *

If you choose to provide your own Apache HttpClient implementation, be sure that + * + *

    + *
  • HTTP version is set to 1.1. + *
  • Retries are disabled (google-http-client handles retries). + *
+ * + * @param httpClient Apache HTTP client to use + */ + public Apache5HttpTransport(HttpClient httpClient) { + this.httpClient = httpClient; + this.isMtls = false; + } + + /** + * {@link Beta}
+ * Constructor that allows an alternative CLoseable Apache HTTP client to be used. + * + *

If you choose to provide your own Apache HttpClient implementation, be sure that + * + *

    + *
  • HTTP version is set to 1.1. + *
  • Retries are disabled (google-http-client handles retries). + *
  • Redirects are disabled (google-http-client handles retries). + *
+ * + * @param httpClient Apache HTTP client to use + * @param isMtls If the HTTP client is mutual TLS + */ + @Beta + public Apache5HttpTransport(HttpClient httpClient, boolean isMtls) { + this.httpClient = httpClient; + this.isMtls = isMtls; + } + + /** + * Creates a new instance of the Apache HTTP client that is used by the {@link + * #Apache5HttpTransport()} constructor. + * + *

Settings: + * + *

    + *
  • The client connection manager is set to {@link PoolingHttpClientConnectionManager}. + *
  • The retry mechanism is turned off using {@link + * HttpClientBuilder#disableAutomaticRetries()}. + *
  • Redirects are turned off using {@link HttpClientBuilder#disableRedirectHandling}. + *
  • The route planner uses {@link SystemDefaultRoutePlanner} with {@link + * ProxySelector#getDefault()}, which uses the proxy settings from system + * properties. + *
+ * + * @return new instance of the Apache HTTP client + */ + public static HttpClient newDefaultHttpClient() { + return newDefaultHttpClientBuilder().build(); + } + + /** + * Creates a new Apache HTTP client builder that is used by the {@link #Apache5HttpTransport()} + * constructor. + * + *

Settings: + * + *

    + *
  • The client connection manager is set to {@link PoolingHttpClientConnectionManager}. + *
  • The retry mechanism is turned off using {@link + * HttpClientBuilder#disableAutomaticRetries()}. + *
  • Redirects are turned off using {@link HttpClientBuilder#disableRedirectHandling}. + *
  • The route planner uses {@link SystemDefaultRoutePlanner} with {@link + * ProxySelector#getDefault()}, which uses the proxy settings from system + * properties. + *
+ * + * @return new instance of the Apache HTTP client builder + */ + public static HttpClientBuilder newDefaultHttpClientBuilder() { + PoolingHttpClientConnectionManager connectionManager = + PoolingHttpClientConnectionManagerBuilder.create() + .setSSLSocketFactory(SSLConnectionSocketFactory.getSocketFactory()) + .setMaxConnTotal(200) + .setMaxConnPerRoute(20) + .setDefaultConnectionConfig( + ConnectionConfig.custom().setTimeToLive(-1, TimeUnit.MILLISECONDS).build()) + .build(); + + return HttpClients.custom() + .useSystemProperties() + .setConnectionManager(connectionManager) + .setRoutePlanner(new SystemDefaultRoutePlanner(ProxySelector.getDefault())) + .disableRedirectHandling() + .disableAutomaticRetries(); + } + + @Override + public boolean supportsMethod(String method) { + return true; + } + + @Override + protected Apache5HttpRequest buildRequest(String method, String url) { + HttpUriRequestBase requestBase; + if (method.equals(HttpMethods.DELETE)) { + requestBase = new HttpDelete(url); + } else if (method.equals(HttpMethods.GET)) { + requestBase = new HttpGet(url); + } else if (method.equals(HttpMethods.HEAD)) { + requestBase = new HttpHead(url); + } else if (method.equals(HttpMethods.PATCH)) { + requestBase = new HttpPatch(url); + } else if (method.equals(HttpMethods.POST)) { + requestBase = new HttpPost(url); + } else if (method.equals(HttpMethods.PUT)) { + requestBase = new HttpPut(url); + } else if (method.equals(HttpMethods.TRACE)) { + requestBase = new HttpTrace(url); + } else if (method.equals(HttpMethods.OPTIONS)) { + requestBase = new HttpOptions(url); + } else { + requestBase = new HttpUriRequestBase(Preconditions.checkNotNull(method), URI.create(url)); + } + return new Apache5HttpRequest(httpClient, requestBase); + } + + /** + * Gracefully shuts down the connection manager and releases allocated resources. This closes all + * connections, whether they are currently used or not. + */ + @Override + public void shutdown() throws IOException { + if (httpClient instanceof ModalCloseable) { + ((ModalCloseable) httpClient).close(CloseMode.GRACEFUL); + } + // otherwise no-op + } + + /** Returns the Apache HTTP client. */ + public HttpClient getHttpClient() { + return httpClient; + } + + /** Returns if the underlying HTTP client is mTLS. */ + @Override + public boolean isMtls() { + return isMtls; + } +} diff --git a/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5ResponseContent.java b/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5ResponseContent.java new file mode 100644 index 000000000..dfb6da8a4 --- /dev/null +++ b/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5ResponseContent.java @@ -0,0 +1,79 @@ +package com.google.api.client.http.apache.v5; + +import com.google.common.annotations.VisibleForTesting; +import java.io.IOException; +import java.io.InputStream; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.core5.http.HttpResponse; + +/** + * Class that wraps an {@link org.apache.hc.core5.http.HttpEntity}'s content {@link InputStream} + * along with the {@link ClassicHttpResponse} that contains this entity. The main purpose is to be + * able to close the response as well as the content input stream when {@link #close()} is called, + * in order to not break the existing contract with clients using apache v4 that only required them + * to close the input stream to clean up all resources. + */ +public class Apache5ResponseContent extends InputStream { + private final ClassicHttpResponse response; + private final InputStream wrappedStream; + + public Apache5ResponseContent(InputStream wrappedStream, ClassicHttpResponse response) { + this.response = response; + this.wrappedStream = wrappedStream; + } + + @Override + public int read() throws IOException { + return wrappedStream.read(); + } + + @Override + public int read(byte b[]) throws IOException { + return wrappedStream.read(b); + } + + @Override + public int read(byte b[], int off, int len) throws IOException { + return wrappedStream.read(b, off, len); + } + + @Override + public long skip(long n) throws IOException { + return wrappedStream.skip(n); + } + + @Override + public int available() throws IOException { + return wrappedStream.available(); + } + + @Override + public synchronized void mark(int readlimit) { + wrappedStream.mark(readlimit); + } + + @Override + public synchronized void reset() throws IOException { + wrappedStream.reset(); + } + + @Override + public void close() throws IOException { + if (wrappedStream != null) { + wrappedStream.close(); + } + if (response != null) { + response.close(); + } + } + + @Override + public boolean markSupported() { + return wrappedStream.markSupported(); + } + + @VisibleForTesting + HttpResponse getResponse() { + return response; + } +} diff --git a/google-http-client-jackson/src/main/java/com/google/api/client/json/jackson/package-info.java b/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/package-info.java similarity index 67% rename from google-http-client-jackson/src/main/java/com/google/api/client/json/jackson/package-info.java rename to google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/package-info.java index 28df204df..223edc82d 100644 --- a/google-http-client-jackson/src/main/java/com/google/api/client/json/jackson/package-info.java +++ b/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Google Inc. + * Copyright 2024 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 @@ -12,13 +12,5 @@ * the License. */ -/** - * Low-level implementation of the JSON parser library based on the Jackson JSON library. - * - * @since 1.3 - * @author Yaniv Inbar - */ - -package com.google.api.client.json.jackson; - +/** HTTP Transport library for Google API's based on Apache HTTP Client/Core version 5.x */ +package com.google.api.client.http.apache.v5; diff --git a/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/Apache5HttpRequestTest.java b/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/Apache5HttpRequestTest.java new file mode 100644 index 000000000..3b7ca4a21 --- /dev/null +++ b/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/Apache5HttpRequestTest.java @@ -0,0 +1,131 @@ +/* + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.http.apache.v5; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import com.google.api.client.http.ByteArrayContent; +import com.google.api.client.http.HttpContent; +import com.google.api.client.http.InputStreamContent; +import com.google.api.client.http.LowLevelHttpResponse; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicInteger; +import org.apache.hc.client5.http.classic.methods.HttpPost; +import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; +import org.apache.hc.core5.http.ClassicHttpRequest; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.HttpEntity; +import org.apache.hc.core5.http.HttpHost; +import org.apache.hc.core5.http.io.entity.BasicHttpEntity; +import org.apache.hc.core5.http.protocol.HttpContext; +import org.junit.Test; + +public class Apache5HttpRequestTest { + @Test + public void testContentLengthSet() throws Exception { + HttpUriRequestBase base = new HttpPost("http://www.google.com"); + Apache5HttpRequest request = + new Apache5HttpRequest( + new MockHttpClient() { + @Override + public ClassicHttpResponse executeOpen( + HttpHost target, ClassicHttpRequest request, HttpContext context) { + return new MockClassicHttpResponse(); + } + }, + base); + HttpContent content = + new ByteArrayContent("text/plain", "sample".getBytes(StandardCharsets.UTF_8)); + request.setStreamingContent(content); + request.setContentLength(content.getLength()); + request.execute(); + + assertFalse(base.getEntity().isChunked()); + assertEquals(6, base.getEntity().getContentLength()); + } + + @Test + public void testChunked() throws Exception { + byte[] buf = new byte[300]; + Arrays.fill(buf, (byte) ' '); + HttpUriRequestBase base = new HttpPost("http://www.google.com"); + Apache5HttpRequest request = + new Apache5HttpRequest( + new MockHttpClient() { + @Override + public ClassicHttpResponse executeOpen( + HttpHost target, ClassicHttpRequest request, HttpContext context) { + return new MockClassicHttpResponse(); + } + }, + base); + HttpContent content = new InputStreamContent("text/plain", new ByteArrayInputStream(buf)); + request.setStreamingContent(content); + request.execute(); + + assertTrue(base.getEntity().isChunked()); + assertEquals(-1, base.getEntity().getContentLength()); + } + + @Test + public void testExecute_closeContent_closesResponse() throws Exception { + HttpUriRequestBase base = new HttpPost("http://www.google.com"); + final InputStream responseContentStream = new ByteArrayInputStream(new byte[] {1, 2, 3}); + BasicHttpEntity testEntity = + new BasicHttpEntity(responseContentStream, ContentType.DEFAULT_BINARY); + AtomicInteger closedResponseCounter = new AtomicInteger(0); + ClassicHttpResponse classicResponse = + new MockClassicHttpResponse() { + @Override + public HttpEntity getEntity() { + return testEntity; + } + + @Override + public void close() { + closedResponseCounter.incrementAndGet(); + } + }; + + Apache5HttpRequest request = + new Apache5HttpRequest( + new MockHttpClient() { + @Override + public ClassicHttpResponse executeOpen( + HttpHost target, ClassicHttpRequest request, HttpContext context) { + return classicResponse; + } + }, + base); + LowLevelHttpResponse response = request.execute(); + assertTrue(response instanceof Apache5HttpResponse); + + // we confirm that the classic response we prepared in this test is the same as the content's + // response + assertTrue(response.getContent() instanceof Apache5ResponseContent); + assertEquals(classicResponse, ((Apache5ResponseContent) response.getContent()).getResponse()); + + // we close the response's content stream and confirm the response is also closed + assertEquals(0, closedResponseCounter.get()); + response.getContent().close(); + assertEquals(1, closedResponseCounter.get()); + } +} diff --git a/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/Apache5HttpResponseTest.java b/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/Apache5HttpResponseTest.java new file mode 100644 index 000000000..d2712b356 --- /dev/null +++ b/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/Apache5HttpResponseTest.java @@ -0,0 +1,36 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.http.apache.v5; + +import static org.junit.Assert.assertNotNull; + +import java.io.InputStream; +import org.apache.hc.client5.http.classic.methods.HttpPost; +import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; +import org.junit.Test; + +public class Apache5HttpResponseTest { + @Test + public void testNullContent() throws Exception { + HttpUriRequestBase base = new HttpPost("http://www.google.com"); + MockClassicHttpResponse mockResponse = new MockClassicHttpResponse(); + mockResponse.setEntity(null); + Apache5HttpResponse response = new Apache5HttpResponse(base, mockResponse); + + InputStream content = response.getContent(); + + assertNotNull(content); + } +} diff --git a/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/Apache5HttpTransportTest.java b/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/Apache5HttpTransportTest.java new file mode 100644 index 000000000..99045d99d --- /dev/null +++ b/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/Apache5HttpTransportTest.java @@ -0,0 +1,353 @@ +/* + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.http.apache.v5; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; + +import com.google.api.client.http.GenericUrl; +import com.google.api.client.http.HttpResponseException; +import com.google.api.client.http.HttpTransport; +import com.google.api.client.http.LowLevelHttpResponse; +import com.google.api.client.util.ByteArrayStreamingContent; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import org.apache.hc.client5.http.ConnectTimeoutException; +import org.apache.hc.client5.http.HttpHostConnectException; +import org.apache.hc.client5.http.classic.HttpClient; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.core5.http.ClassicHttpRequest; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.EntityDetails; +import org.apache.hc.core5.http.Header; +import org.apache.hc.core5.http.HttpException; +import org.apache.hc.core5.http.HttpHeaders; +import org.apache.hc.core5.http.HttpHost; +import org.apache.hc.core5.http.HttpRequest; +import org.apache.hc.core5.http.HttpRequestInterceptor; +import org.apache.hc.core5.http.HttpRequestMapper; +import org.apache.hc.core5.http.HttpResponse; +import org.apache.hc.core5.http.HttpStatus; +import org.apache.hc.core5.http.impl.bootstrap.HttpServer; +import org.apache.hc.core5.http.impl.io.HttpRequestExecutor; +import org.apache.hc.core5.http.impl.io.HttpService; +import org.apache.hc.core5.http.io.HttpClientConnection; +import org.apache.hc.core5.http.io.HttpRequestHandler; +import org.apache.hc.core5.http.io.entity.ByteArrayEntity; +import org.apache.hc.core5.http.io.support.BasicHttpServerRequestHandler; +import org.apache.hc.core5.http.protocol.HttpContext; +import org.apache.hc.core5.http.protocol.HttpProcessor; +import org.junit.Assert; +import org.junit.Test; + +/** Tests {@link Apache5HttpTransport}. */ +public class Apache5HttpTransportTest { + + @Test + public void testApacheHttpTransport() { + Apache5HttpTransport transport = new Apache5HttpTransport(); + checkHttpTransport(transport); + assertFalse(transport.isMtls()); + } + + @Test + public void testApacheHttpTransportWithParam() { + Apache5HttpTransport transport = new Apache5HttpTransport(HttpClients.custom().build(), true); + checkHttpTransport(transport); + assertTrue(transport.isMtls()); + } + + @Test + public void testNewDefaultHttpClient() { + HttpClient client = Apache5HttpTransport.newDefaultHttpClient(); + checkHttpClient(client); + } + + private void checkHttpTransport(Apache5HttpTransport transport) { + assertNotNull(transport); + HttpClient client = transport.getHttpClient(); + checkHttpClient(client); + } + + private void checkHttpClient(HttpClient client) { + assertNotNull(client); + // TODO(chingor): Is it possible to test this effectively? The newer HttpClient implementations + // are read-only and we're testing that we built the client with the right configuration + } + + @Test + public void testRequestsWithContent() throws IOException { + // This test confirms that we can set the content on any type of request + HttpClient mockClient = + new MockHttpClient() { + @Override + public ClassicHttpResponse executeOpen( + HttpHost target, ClassicHttpRequest request, HttpContext context) { + return new MockClassicHttpResponse(); + } + }; + Apache5HttpTransport transport = new Apache5HttpTransport(mockClient); + + // Test GET. + execute(transport.buildRequest("GET", "http://www.test.url")); + // Test DELETE. + execute(transport.buildRequest("DELETE", "http://www.test.url")); + // Test HEAD. + execute(transport.buildRequest("HEAD", "http://www.test.url")); + + // Test PATCH. + execute(transport.buildRequest("PATCH", "http://www.test.url")); + // Test PUT. + execute(transport.buildRequest("PUT", "http://www.test.url")); + // Test POST. + execute(transport.buildRequest("POST", "http://www.test.url")); + // Test PATCH. + execute(transport.buildRequest("PATCH", "http://www.test.url")); + } + + private void execute(Apache5HttpRequest request) throws IOException { + byte[] bytes = "abc".getBytes(StandardCharsets.UTF_8); + request.setStreamingContent(new ByteArrayStreamingContent(bytes)); + request.setContentType("text/html"); + request.setContentLength(bytes.length); + request.execute(); + } + + @Test + public void testRequestShouldNotFollowRedirects() throws IOException { + final AtomicInteger requestsAttempted = new AtomicInteger(0); + HttpRequestExecutor requestExecutor = + new HttpRequestExecutor() { + @Override + public ClassicHttpResponse execute( + ClassicHttpRequest request, HttpClientConnection connection, HttpContext context) + throws IOException, HttpException { + ClassicHttpResponse response = new MockClassicHttpResponse(); + response.setCode(302); + response.setReasonPhrase(null); + response.addHeader("location", "https://google.com/path"); + response.addHeader(HttpHeaders.SET_COOKIE, ""); + requestsAttempted.incrementAndGet(); + return response; + } + }; + HttpClient client = HttpClients.custom().setRequestExecutor(requestExecutor).build(); + Apache5HttpTransport transport = new Apache5HttpTransport(client); + Apache5HttpRequest request = transport.buildRequest("GET", "https://google.com"); + LowLevelHttpResponse response = request.execute(); + assertEquals(1, requestsAttempted.get()); + assertEquals(302, response.getStatusCode()); + } + + @Test + public void testRequestCanSetHeaders() { + final AtomicBoolean interceptorCalled = new AtomicBoolean(false); + HttpClient client = + HttpClients.custom() + .addRequestInterceptorFirst( + new HttpRequestInterceptor() { + @Override + public void process( + HttpRequest request, EntityDetails details, HttpContext context) + throws HttpException, IOException { + Header header = request.getFirstHeader("foo"); + assertNotNull("Should have found header", header); + assertEquals("bar", header.getValue()); + interceptorCalled.set(true); + throw new IOException("cancelling request"); + } + }) + .build(); + + Apache5HttpTransport transport = new Apache5HttpTransport(client); + Apache5HttpRequest request = transport.buildRequest("GET", "https://google.com"); + request.addHeader("foo", "bar"); + try { + LowLevelHttpResponse response = request.execute(); + fail("should not actually make the request"); + } catch (IOException exception) { + assertEquals("cancelling request", exception.getMessage()); + } + assertTrue("Expected to have called our test interceptor", interceptorCalled.get()); + } + + @Test(timeout = 10_000L) + public void testConnectTimeout() { + // TODO(chanseok): Java 17 returns an IOException (SocketException: Network is unreachable). + // Figure out a way to verify connection timeout works on Java 17+. + assumeTrue(System.getProperty("java.version").compareTo("17") < 0); + + HttpTransport httpTransport = new Apache5HttpTransport(); + GenericUrl url = new GenericUrl("http://google.com:81"); + try { + httpTransport.createRequestFactory().buildGetRequest(url).setConnectTimeout(100).execute(); + fail("should have thrown an exception"); + } catch (HttpHostConnectException | ConnectTimeoutException expected) { + // expected + } catch (IOException e) { + fail("unexpected IOException: " + e.getClass().getName() + ": " + e.getMessage()); + } + } + + private static class FakeServer implements AutoCloseable { + private final HttpServer server; + + FakeServer(final HttpRequestHandler httpHandler) throws IOException { + HttpRequestMapper mapper = + new HttpRequestMapper() { + @Override + public HttpRequestHandler resolve(HttpRequest request, HttpContext context) + throws HttpException { + return httpHandler; + }; + }; + server = + new HttpServer( + 0, + HttpService.builder() + .withHttpProcessor( + new HttpProcessor() { + @Override + public void process( + HttpRequest request, EntityDetails entity, HttpContext context) + throws HttpException, IOException {} + + @Override + public void process( + HttpResponse response, EntityDetails entity, HttpContext context) + throws HttpException, IOException {} + }) + .withHttpServerRequestHandler(new BasicHttpServerRequestHandler(mapper)) + .build(), + null, + null, + null, + null, + null, + null); + // server.createContext("/", httpHandler); + server.start(); + } + + public int getPort() { + return server.getLocalPort(); + } + + @Override + public void close() { + server.initiateShutdown(); + } + } + + @Test + public void testNormalizedUrl() throws IOException { + final HttpRequestHandler handler = + new HttpRequestHandler() { + @Override + public void handle( + ClassicHttpRequest request, ClassicHttpResponse response, HttpContext context) + throws HttpException, IOException { + // Extract the request URI and convert to bytes + byte[] responseData = request.getRequestUri().getBytes(StandardCharsets.UTF_8); + + // Set the response headers (status code and content length) + response.setCode(HttpStatus.SC_OK); + response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(responseData.length)); + + // Set the response entity (body) + ByteArrayEntity entity = new ByteArrayEntity(responseData, ContentType.TEXT_PLAIN); + response.setEntity(entity); + } + }; + try (FakeServer server = new FakeServer(handler)) { + HttpTransport transport = new Apache5HttpTransport(); + GenericUrl testUrl = new GenericUrl("http://localhost/foo//bar"); + testUrl.setPort(server.getPort()); + com.google.api.client.http.HttpResponse response = + transport.createRequestFactory().buildGetRequest(testUrl).execute(); + assertEquals(200, response.getStatusCode()); + assertEquals("/foo//bar", response.parseAsString()); + } + } + + @Test + public void testReadErrorStream() throws IOException { + final HttpRequestHandler handler = + new HttpRequestHandler() { + @Override + public void handle( + ClassicHttpRequest request, ClassicHttpResponse response, HttpContext context) + throws HttpException, IOException { + byte[] responseData = "Forbidden".getBytes(StandardCharsets.UTF_8); + response.setCode(HttpStatus.SC_FORBIDDEN); // 403 Forbidden + response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(responseData.length)); + ByteArrayEntity entity = new ByteArrayEntity(responseData, ContentType.TEXT_PLAIN); + response.setEntity(entity); + } + }; + try (FakeServer server = new FakeServer(handler)) { + HttpTransport transport = new Apache5HttpTransport(); + GenericUrl testUrl = new GenericUrl("http://localhost/foo//bar"); + testUrl.setPort(server.getPort()); + com.google.api.client.http.HttpRequest getRequest = + transport.createRequestFactory().buildGetRequest(testUrl); + getRequest.setThrowExceptionOnExecuteError(false); + com.google.api.client.http.HttpResponse response = getRequest.execute(); + assertEquals(403, response.getStatusCode()); + assertEquals("Forbidden", response.parseAsString()); + } + } + + @Test + public void testReadErrorStream_withException() throws IOException { + final HttpRequestHandler handler = + new HttpRequestHandler() { + @Override + public void handle( + ClassicHttpRequest request, ClassicHttpResponse response, HttpContext context) + throws HttpException, IOException { + byte[] responseData = "Forbidden".getBytes(StandardCharsets.UTF_8); + response.setCode(HttpStatus.SC_FORBIDDEN); // 403 Forbidden + response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(responseData.length)); + ByteArrayEntity entity = new ByteArrayEntity(responseData, ContentType.TEXT_PLAIN); + response.setEntity(entity); + } + }; + try (FakeServer server = new FakeServer(handler)) { + HttpTransport transport = new Apache5HttpTransport(); + GenericUrl testUrl = new GenericUrl("http://localhost/foo//bar"); + testUrl.setPort(server.getPort()); + com.google.api.client.http.HttpRequest getRequest = + transport.createRequestFactory().buildGetRequest(testUrl); + try { + getRequest.execute(); + Assert.fail(); + } catch (HttpResponseException ex) { + assertEquals("Forbidden", ex.getContent()); + } + } + } + + private boolean isWindows() { + return System.getProperty("os.name").startsWith("Windows"); + } +} diff --git a/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/Apache5ResponseContentTest.java b/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/Apache5ResponseContentTest.java new file mode 100644 index 000000000..ddbda0dd5 --- /dev/null +++ b/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/Apache5ResponseContentTest.java @@ -0,0 +1,44 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.http.apache.v5; + +import java.io.IOException; +import java.io.InputStream; +import org.junit.Test; + +public class Apache5ResponseContentTest { + @Test + public void testNullResponseContent_doesNotThrowExceptionOnClose() throws Exception { + Apache5ResponseContent response = + new Apache5ResponseContent( + new InputStream() { + @Override + public int read() throws IOException { + return 0; + } + }, + null); + + response.close(); + } + + @Test + public void testNullWrappedContent_doesNotThrowExceptionOnClose() throws Exception { + MockClassicHttpResponse mockResponse = new MockClassicHttpResponse(); + Apache5ResponseContent response = new Apache5ResponseContent(null, mockResponse); + + response.close(); + } +} diff --git a/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/MockClassicHttpResponse.java b/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/MockClassicHttpResponse.java new file mode 100644 index 000000000..091721745 --- /dev/null +++ b/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/MockClassicHttpResponse.java @@ -0,0 +1,182 @@ +package com.google.api.client.http.apache.v5; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.core5.http.Header; +import org.apache.hc.core5.http.HttpEntity; +import org.apache.hc.core5.http.HttpVersion; +import org.apache.hc.core5.http.ProtocolException; +import org.apache.hc.core5.http.ProtocolVersion; + +public class MockClassicHttpResponse implements ClassicHttpResponse { + List
headers = new ArrayList<>(); + int code = 200; + + @Override + public int getCode() { + return code; + } + + @Override + public void setCode(int code) { + this.code = code; + } + + @Override + public String getReasonPhrase() { + return null; + } + + @Override + public void setReasonPhrase(String reason) {} + + @Override + public Locale getLocale() { + return null; + } + + @Override + public void setLocale(Locale loc) {} + + @Override + public void setVersion(ProtocolVersion version) {} + + @Override + public ProtocolVersion getVersion() { + return HttpVersion.HTTP_1_1; + } + + @Override + public void addHeader(Header header) { + headers.add(header); + } + + @Override + public void addHeader(String name, Object value) { + addHeader(newHeader(name, value)); + } + + private Header newHeader(String key, Object value) { + return new Header() { + @Override + public boolean isSensitive() { + return false; + } + + @Override + public String getName() { + return key; + } + + @Override + public String getValue() { + return value.toString(); + } + }; + } + + @Override + public void setHeader(Header header) { + if (headers.contains(header)) { + int index = headers.indexOf(header); + headers.set(index, header); + } else { + addHeader(header); + } + } + + @Override + public void setHeader(String name, Object value) { + setHeader(newHeader(name, value)); + } + + @Override + public void setHeaders(Header... headers) { + for (Header header : headers) { + setHeader(header); + } + } + + @Override + public boolean removeHeader(Header header) { + if (headers.contains(header)) { + headers.remove(headers.indexOf(header)); + return true; + } + return false; + } + + @Override + public boolean removeHeaders(String name) { + int initialSize = headers.size(); + for (Header header : + headers.stream().filter(h -> h.getName() == name).collect(Collectors.toList())) { + removeHeader(header); + } + return headers.size() < initialSize; + } + + @Override + public boolean containsHeader(String name) { + return headers.stream().anyMatch(h -> h.getName() == name); + } + + @Override + public int countHeaders(String name) { + return headers.size(); + } + + @Override + public Header getFirstHeader(String name) { + return headers.stream().findFirst().orElse(null); + } + + @Override + public Header getHeader(String name) throws ProtocolException { + return headers.stream().filter(h -> h.getName() == name).findFirst().orElse(null); + } + + @Override + public Header[] getHeaders() { + return headers.toArray(new Header[0]); + } + + @Override + public Header[] getHeaders(String name) { + return headers.stream() + .filter(h -> h.getName() == name) + .collect(Collectors.toList()) + .toArray(new Header[0]); + } + + @Override + public Header getLastHeader(String name) { + return headers.isEmpty() ? null : headers.get(headers.size() - 1); + } + + @Override + public Iterator
headerIterator() { + return headers.iterator(); + } + + @Override + public Iterator
headerIterator(String name) { + return headers.stream().filter(h -> h.getName() == name).iterator(); + } + + @Override + public void close() throws IOException {} + + @Override + public HttpEntity getEntity() { + return null; + } + + @Override + public void setEntity(HttpEntity entity) {} +} diff --git a/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/MockHttpClient.java b/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/MockHttpClient.java new file mode 100644 index 000000000..8d26096cf --- /dev/null +++ b/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/MockHttpClient.java @@ -0,0 +1,86 @@ +package com.google.api.client.http.apache.v5; + +import com.google.api.client.util.Preconditions; +import java.io.IOException; +import org.apache.hc.client5.http.classic.HttpClient; +import org.apache.hc.core5.http.ClassicHttpRequest; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.core5.http.HttpHost; +import org.apache.hc.core5.http.HttpResponse; +import org.apache.hc.core5.http.io.HttpClientResponseHandler; +import org.apache.hc.core5.http.protocol.HttpContext; + +public class MockHttpClient implements HttpClient { + + /** HTTP response code to use. */ + int responseCode; + + /** Returns the HTTP response code to use. */ + public final int getResponseCode() { + return responseCode; + } + + /** Sets the HTTP response code to use. */ + public MockHttpClient setResponseCode(int responseCode) { + Preconditions.checkArgument(responseCode >= 0); + this.responseCode = responseCode; + return this; + } + + @Override + public HttpResponse execute(ClassicHttpRequest request) throws IOException { + return null; + } + + @Override + public HttpResponse execute(ClassicHttpRequest request, HttpContext context) throws IOException { + return null; + } + + @Override + public ClassicHttpResponse execute(HttpHost target, ClassicHttpRequest request) + throws IOException { + return null; + } + + @Override + public HttpResponse execute(HttpHost target, ClassicHttpRequest request, HttpContext context) + throws IOException { + return null; + } + + @Override + public T execute( + ClassicHttpRequest request, HttpClientResponseHandler responseHandler) + throws IOException { + return null; + } + + @Override + public T execute( + ClassicHttpRequest request, + HttpContext context, + HttpClientResponseHandler responseHandler) + throws IOException { + return null; + } + + @Override + public T execute( + HttpHost target, + ClassicHttpRequest request, + HttpClientResponseHandler responseHandler) + throws IOException { + return null; + } + + @Override + public T execute( + HttpHost target, + ClassicHttpRequest request, + HttpContext context, + HttpClientResponseHandler responseHandler) + throws IOException { + return null; + } +} diff --git a/google-http-client-appengine/pom.xml b/google-http-client-appengine/pom.xml index 27013590a..b6f1117a6 100644 --- a/google-http-client-appengine/pom.xml +++ b/google-http-client-appengine/pom.xml @@ -4,20 +4,36 @@ com.google.http-client google-http-client-parent - 1.26.1-SNAPSHOT + 2.0.3-SNAPSHOT ../pom.xml google-http-client-appengine - 1.26.1-SNAPSHOT + 2.0.3-SNAPSHOT Google App Engine extensions to the Google HTTP Client Library for Java. + + + + + org.codehaus.mojo + animal-sniffer-maven-plugin + + + org.codehaus.mojo.signature + java17 + 1.0 + + + + + maven-javadoc-plugin - http://download.oracle.com/javase/6/docs/api/ + http://download.oracle.com/javase/7/docs/api/ https://cloud.google.com/appengine/docs/standard/java/javadoc/ ${project.name} ${project.version} @@ -26,28 +42,25 @@ maven-source-plugin - - - source-jar - compile - - jar - - - - - org.codehaus.mojo - animal-sniffer-maven-plugin + org.apache.maven.plugins + maven-dependency-plugin - - org.codehaus.mojo.signature - java16 - 1.0 - + + com.google.appengine:appengine-api-stubs + + maven-jar-plugin + + + + com.google.api.client.extensions.appengine + + + + @@ -80,10 +93,16 @@ junit test - - com.google.guava - guava - test - + + + native-deps + + + com.google.appengine + appengine-api-1.0-sdk + + + + diff --git a/google-http-client-appengine/src/main/java/com/google/api/client/extensions/appengine/datastore/AppEngineDataStoreFactory.java b/google-http-client-appengine/src/main/java/com/google/api/client/extensions/appengine/datastore/AppEngineDataStoreFactory.java index 5fca5a684..0894d8192 100644 --- a/google-http-client-appengine/src/main/java/com/google/api/client/extensions/appengine/datastore/AppEngineDataStoreFactory.java +++ b/google-http-client-appengine/src/main/java/com/google/api/client/extensions/appengine/datastore/AppEngineDataStoreFactory.java @@ -34,7 +34,6 @@ import com.google.appengine.api.memcache.Expiration; import com.google.appengine.api.memcache.MemcacheService; import com.google.appengine.api.memcache.MemcacheServiceFactory; - import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; @@ -50,18 +49,13 @@ * Thread-safe Google App Engine implementation of a data store factory that directly uses the App * Engine Data Store API. * - *

- * For convenience, a default global instance is provided in {@link #getDefaultInstance()}. - *

+ *

For convenience, a default global instance is provided in {@link #getDefaultInstance()}. * - *

- * By default, it uses the Memcache API as an in-memory data cache. To disable it, call - * {@link Builder#setDisableMemcache(boolean)}. The Memcache is only read to check if a key already - * has a value inside {@link DataStore#get(String)}. The values in the Memcache are updated in the - * {@link DataStore#get(String)}, {@link DataStore#set(String, Serializable)}, - * {@link DataStore#delete(String)}, {@link DataStore#values()}, and {@link DataStore#clear()} - * methods. - *

+ *

By default, it uses the Memcache API as an in-memory data cache. To disable it, call {@link + * Builder#setDisableMemcache(boolean)}. The Memcache is only read to check if a key already has a + * value inside {@link DataStore#get(String)}. The values in the Memcache are updated in the {@link + * DataStore#get(String)}, {@link DataStore#set(String, Serializable)}, {@link + * DataStore#delete(String)}, {@link DataStore#values()}, and {@link DataStore#clear()} methods. * * @since 1.16 * @author Yaniv Inbar @@ -83,9 +77,7 @@ public AppEngineDataStoreFactory() { this(new Builder()); } - /** - * @param builder builder - */ + /** @param builder builder */ public AppEngineDataStoreFactory(Builder builder) { disableMemcache = builder.disableMemcache; memcacheExpiration = builder.memcacheExpiration; @@ -97,8 +89,8 @@ public boolean getDisableMemcache() { } /** - * Returns a global thread-safe instance based on the default constructor - * {@link #AppEngineDataStoreFactory()}. + * Returns a global thread-safe instance based on the default constructor {@link + * #AppEngineDataStoreFactory()}. */ public static AppEngineDataStoreFactory getDefaultInstance() { return InstanceHolder.INSTANCE; @@ -296,9 +288,7 @@ private Iterable query(boolean keysOnly) { /** * App Engine data store factory builder. * - *

- * Implementation is not thread-safe. - *

+ *

Implementation is not thread-safe. * * @since 1.16 */ @@ -318,10 +308,8 @@ public final boolean getDisableMemcache() { /** * Sets whether to disable the memcache ({@code false} by default). * - *

- * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

+ *

Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Builder setDisableMemcache(boolean disableMemcache) { this.disableMemcache = disableMemcache; @@ -336,10 +324,8 @@ public final Expiration getMemcacheExpiration() { /** * Sets the Memcache expiration policy on puts. * - *

- * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

+ *

Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Builder setMemcacheExpiration(Expiration memcacheExpiration) { this.memcacheExpiration = memcacheExpiration; diff --git a/google-http-client-appengine/src/main/java/com/google/api/client/extensions/appengine/datastore/package-info.java b/google-http-client-appengine/src/main/java/com/google/api/client/extensions/appengine/datastore/package-info.java index fd9acf785..b81c6c4bc 100644 --- a/google-http-client-appengine/src/main/java/com/google/api/client/extensions/appengine/datastore/package-info.java +++ b/google-http-client-appengine/src/main/java/com/google/api/client/extensions/appengine/datastore/package-info.java @@ -19,4 +19,3 @@ * @author Yaniv Inbar */ package com.google.api.client.extensions.appengine.datastore; - diff --git a/google-http-client-appengine/src/main/java/com/google/api/client/extensions/appengine/http/UrlFetchRequest.java b/google-http-client-appengine/src/main/java/com/google/api/client/extensions/appengine/http/UrlFetchRequest.java index f79b93c6f..903b2cc43 100644 --- a/google-http-client-appengine/src/main/java/com/google/api/client/extensions/appengine/http/UrlFetchRequest.java +++ b/google-http-client-appengine/src/main/java/com/google/api/client/extensions/appengine/http/UrlFetchRequest.java @@ -23,14 +23,11 @@ import com.google.appengine.api.urlfetch.HTTPResponse; import com.google.appengine.api.urlfetch.URLFetchService; import com.google.appengine.api.urlfetch.URLFetchServiceFactory; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.URL; -/** - * @author Yaniv Inbar - */ +/** @author Yaniv Inbar */ final class UrlFetchRequest extends LowLevelHttpRequest { private final HTTPRequest request; @@ -46,8 +43,12 @@ public void addHeader(String name, String value) { @Override public void setTimeout(int connectTimeout, int readTimeout) { - request.getFetchOptions().setDeadline(connectTimeout == 0 || readTimeout == 0 - ? Double.MAX_VALUE : (connectTimeout + readTimeout) / 1000.0); + request + .getFetchOptions() + .setDeadline( + connectTimeout == 0 || readTimeout == 0 + ? Double.MAX_VALUE + : (connectTimeout + readTimeout) / 1000.0); } @Override diff --git a/google-http-client-appengine/src/main/java/com/google/api/client/extensions/appengine/http/UrlFetchResponse.java b/google-http-client-appengine/src/main/java/com/google/api/client/extensions/appengine/http/UrlFetchResponse.java index c10fdee6c..19e6a1ef3 100644 --- a/google-http-client-appengine/src/main/java/com/google/api/client/extensions/appengine/http/UrlFetchResponse.java +++ b/google-http-client-appengine/src/main/java/com/google/api/client/extensions/appengine/http/UrlFetchResponse.java @@ -17,7 +17,6 @@ import com.google.api.client.http.LowLevelHttpResponse; import com.google.appengine.api.urlfetch.HTTPHeader; import com.google.appengine.api.urlfetch.HTTPResponse; - import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.ArrayList; diff --git a/google-http-client-appengine/src/main/java/com/google/api/client/extensions/appengine/http/UrlFetchTransport.java b/google-http-client-appengine/src/main/java/com/google/api/client/extensions/appengine/http/UrlFetchTransport.java index a7ba1b940..b6dfa8d3d 100644 --- a/google-http-client-appengine/src/main/java/com/google/api/client/extensions/appengine/http/UrlFetchTransport.java +++ b/google-http-client-appengine/src/main/java/com/google/api/client/extensions/appengine/http/UrlFetchTransport.java @@ -19,7 +19,6 @@ import com.google.api.client.util.Preconditions; import com.google.appengine.api.urlfetch.FetchOptions; import com.google.appengine.api.urlfetch.HTTPMethod; - import java.io.IOException; import java.net.HttpURLConnection; import java.util.Arrays; @@ -28,21 +27,18 @@ * Thread-safe HTTP transport for Google App Engine based on URL Fetch. * - *

- * Implementation is thread-safe. For maximum efficiency, applications should use a single + *

Implementation is thread-safe. For maximum efficiency, applications should use a single * globally-shared instance of the HTTP transport. - *

* - *

- * URL Fetch is only available on Google App Engine (not on any other Java environment), and is the - * underlying HTTP transport used for App Engine. Their implementation of {@link HttpURLConnection} - * is simply an abstraction layer on top of URL Fetch. By implementing a transport that directly - * uses URL Fetch, we can optimize the behavior slightly, and can potentially take advantage of - * features in URL Fetch that are not available in {@link HttpURLConnection}. Furthermore, there is - * currently a serious bug in how HTTP headers are processed in the App Engine implementation of - * {@link HttpURLConnection}, which we are able to avoid using this implementation. Therefore, this - * is the recommended transport to use on App Engine. - *

+ *

URL Fetch is only available on Google App Engine (not on any other Java environment), and is + * the underlying HTTP transport used for App Engine. Their implementation of {@link + * HttpURLConnection} is simply an abstraction layer on top of URL Fetch. By implementing a + * transport that directly uses URL Fetch, we can optimize the behavior slightly, and can + * potentially take advantage of features in URL Fetch that are not available in {@link + * HttpURLConnection}. Furthermore, there is currently a serious bug in how HTTP headers are + * processed in the App Engine implementation of {@link HttpURLConnection}, which we are able to + * avoid using this implementation. Therefore, this is the recommended transport to use on App + * Engine. * * @since 1.10 * @author Yaniv Inbar @@ -54,16 +50,24 @@ public final class UrlFetchTransport extends HttpTransport { * {@link FetchOptions#validateCertificate()}. */ enum CertificateValidationBehavior { - DEFAULT, VALIDATE, DO_NOT_VALIDATE + DEFAULT, + VALIDATE, + DO_NOT_VALIDATE } /** * All valid request methods as specified in {@link HTTPMethod}, sorted in ascending alphabetical * order. */ - private static final String[] SUPPORTED_METHODS = - {HttpMethods.DELETE, HttpMethods.GET, HttpMethods.HEAD, HttpMethods.POST, - HttpMethods.PUT, HttpMethods.PATCH}; + private static final String[] SUPPORTED_METHODS = { + HttpMethods.DELETE, + HttpMethods.GET, + HttpMethods.HEAD, + HttpMethods.POST, + HttpMethods.PUT, + HttpMethods.PATCH + }; + static { Arrays.sort(SUPPORTED_METHODS); } @@ -74,17 +78,13 @@ enum CertificateValidationBehavior { /** * Constructor with the default fetch options. * - *

- * Use {@link Builder} to modify fetch options. - *

+ *

Use {@link Builder} to modify fetch options. */ public UrlFetchTransport() { this(new Builder()); } - /** - * @param builder builder - */ + /** @param builder builder */ UrlFetchTransport(Builder builder) { certificateValidationBehavior = builder.certificateValidationBehavior; } @@ -144,9 +144,7 @@ protected UrlFetchRequest buildRequest(String method, String url) throws IOExcep /** * Builder for {@link UrlFetchTransport}. * - *

- * Implementation is not thread-safe. - *

+ *

Implementation is not thread-safe. * * @since 1.13 */ @@ -160,10 +158,8 @@ public static final class Builder { * Sets whether to use {@link FetchOptions#doNotValidateCertificate()} ({@code false} by * default). * - *

- * Be careful! Disabling certificate validation is dangerous and should be done in testing + *

Be careful! Disabling certificate validation is dangerous and should be done in testing * environments only. - *

*/ public Builder doNotValidateCertificate() { this.certificateValidationBehavior = CertificateValidationBehavior.DO_NOT_VALIDATE; diff --git a/google-http-client-appengine/src/main/java/com/google/api/client/extensions/appengine/http/package-info.java b/google-http-client-appengine/src/main/java/com/google/api/client/extensions/appengine/http/package-info.java index ef321de84..e9c6e9b31 100644 --- a/google-http-client-appengine/src/main/java/com/google/api/client/extensions/appengine/http/package-info.java +++ b/google-http-client-appengine/src/main/java/com/google/api/client/extensions/appengine/http/package-info.java @@ -14,11 +14,10 @@ /** * HTTP Transport library for Google API's based on URL Fetch in Google App Engine. + * href="https://cloud.google.com/appengine/docs/standard/java/issue-requests">URL Fetch in Google + * App Engine. * * @since 1.10 * @author Yaniv Inbar */ - package com.google.api.client.extensions.appengine.http; - diff --git a/google-http-client-appengine/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-appengine/reflect-config.json b/google-http-client-appengine/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-appengine/reflect-config.json new file mode 100644 index 000000000..d2efd41b8 --- /dev/null +++ b/google-http-client-appengine/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-appengine/reflect-config.json @@ -0,0 +1,47 @@ +[ + { + "name": "com.google.apphosting.api.DatastorePb$DeleteRequest", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "name": "com.google.apphosting.api.DatastorePb$GetRequest", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "name": "com.google.apphosting.api.DatastorePb$NextRequest", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "name": "com.google.apphosting.api.DatastorePb$PutRequest", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "name": "com.google.apphosting.api.DatastorePb$Query", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + } +] diff --git a/google-http-client-appengine/src/test/java/com/google/api/client/extensions/appengine/datastore/AppEngineNoMemcacheDataStoreFactoryTest.java b/google-http-client-appengine/src/test/java/com/google/api/client/extensions/appengine/datastore/AppEngineNoMemcacheDataStoreFactoryTest.java index 2fb1594ec..33d1fc468 100644 --- a/google-http-client-appengine/src/test/java/com/google/api/client/extensions/appengine/datastore/AppEngineNoMemcacheDataStoreFactoryTest.java +++ b/google-http-client-appengine/src/test/java/com/google/api/client/extensions/appengine/datastore/AppEngineNoMemcacheDataStoreFactoryTest.java @@ -17,8 +17,8 @@ import com.google.api.client.util.store.DataStoreFactory; /** - * Tests {@link AppEngineDataStoreFactory} with - * {@link AppEngineDataStoreFactory.Builder#setDisableMemcache(boolean)}. + * Tests {@link AppEngineDataStoreFactory} with {@link + * AppEngineDataStoreFactory.Builder#setDisableMemcache(boolean)}. * * @author Yaniv Inbar */ diff --git a/google-http-client-appengine/src/test/java/com/google/api/client/extensions/appengine/http/UrlFetchTransportTest.java b/google-http-client-appengine/src/test/java/com/google/api/client/extensions/appengine/http/UrlFetchTransportTest.java index 3de60a6d7..b9ce9585d 100644 --- a/google-http-client-appengine/src/test/java/com/google/api/client/extensions/appengine/http/UrlFetchTransportTest.java +++ b/google-http-client-appengine/src/test/java/com/google/api/client/extensions/appengine/http/UrlFetchTransportTest.java @@ -15,14 +15,19 @@ package com.google.api.client.extensions.appengine.http; import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link UrlFetchTransport}. * * @author Tony Aiuto */ +@RunWith(JUnit4.class) public class UrlFetchTransportTest extends TestCase { + @Test public void test() { new UrlFetchTransport(); } diff --git a/google-http-client-appengine/src/test/resources/META-INF/native-image/com.google.http-client/google-http-client-appengine/native-image.properties b/google-http-client-appengine/src/test/resources/META-INF/native-image/com.google.http-client/google-http-client-appengine/native-image.properties new file mode 100644 index 000000000..33cc1b54d --- /dev/null +++ b/google-http-client-appengine/src/test/resources/META-INF/native-image/com.google.http-client/google-http-client-appengine/native-image.properties @@ -0,0 +1 @@ +Args=--initialize-at-build-time=org.junit.runner.RunWith,java.lang.annotation.Annotation \ diff --git a/google-http-client-assembly/android-properties/jackson-core-asl-1.9.13.jar.properties b/google-http-client-assembly/android-properties/jackson-core-asl-1.9.13.jar.properties deleted file mode 100644 index d613b8e65..000000000 --- a/google-http-client-assembly/android-properties/jackson-core-asl-1.9.13.jar.properties +++ /dev/null @@ -1 +0,0 @@ -src=../libs-sources/jackson-core-asl-${project.jackson-core-asl.version}-sources.jar diff --git a/google-http-client-assembly/assembly.xml b/google-http-client-assembly/assembly.xml index e20b89d80..5e8a4f708 100644 --- a/google-http-client-assembly/assembly.xml +++ b/google-http-client-assembly/assembly.xml @@ -25,6 +25,7 @@ true google-http-java-client + properties/google-http-client.jar.properties google-http-client-${project.version}.jar.properties @@ -38,14 +39,14 @@ true - properties/google-http-client-gson.jar.properties - google-http-client-gson-${project.version}.jar.properties + properties/google-http-client-apache-v2.jar.properties + google-http-client-apache-v2-${project.http-client.version}.jar.properties google-http-java-client/libs true - properties/google-http-client-jackson.jar.properties - google-http-client-jackson-${project.version}.jar.properties + properties/google-http-client-gson.jar.properties + google-http-client-gson-${project.version}.jar.properties google-http-java-client/libs true @@ -55,12 +56,6 @@ google-http-java-client/libs true - - properties/google-http-client-jdo.jar.properties - google-http-client-jdo-${project.version}.jar.properties - google-http-java-client/libs - true - properties/google-http-client-protobuf.jar.properties google-http-client-protobuf-${project.version}.jar.properties @@ -73,21 +68,77 @@ google-http-java-client/libs true + + properties/gson.jar.properties + gson-${project.gson.version}.jar.properties + google-http-java-client/libs + true + + + properties/jackson-core.jar.properties + jackson-core-${project.jackson-core2.version}.jar.properties + google-http-java-client/libs + true + + + properties/protobuf-java.jar.properties + protobuf-java-${project.protobuf-java.version}.jar.properties + google-http-java-client/libs + true + + + + ../google-http-client/target/site/dependencies.html + google-http-client-dependencies.html + google-http-java-client/dependencies + + + ../google-http-client-android/target/site/dependencies.html + google-http-client-android-dependencies.html + google-http-java-client/dependencies + + + ../google-http-client-apache-v2/target/site/dependencies.html + google-http-client-apache-v2-dependencies.html + google-http-java-client/dependencies + + + ../google-http-client-gson/target/site/dependencies.html + google-http-client-gson-dependencies.html + google-http-java-client/dependencies + + + ../google-http-client-jackson2/target/site/dependencies.html + google-http-client-jackson2-dependencies.html + google-http-java-client/dependencies + + + ../google-http-client-protobuf/target/site/dependencies.html + google-http-client-protobuf-dependencies.html + google-http-java-client/dependencies + + + ../google-http-client-xml/target/site/dependencies.html + google-http-client-xml-dependencies.html + google-http-java-client/dependencies + - dependencies - google-http-java-client/dependencies - true + target/site/css + google-http-java-client/dependencies/css - target/libs - google-http-java-client/libs + target/site/images + google-http-java-client/dependencies/images + + + licenses + google-oauth-java-client/dependencies - android-properties + target/libs google-http-java-client/libs - true target/libs-sources diff --git a/google-http-client-assembly/classpath-include b/google-http-client-assembly/classpath-include index 52d9bee51..c7bbd573f 100644 --- a/google-http-client-assembly/classpath-include +++ b/google-http-client-assembly/classpath-include @@ -3,17 +3,14 @@ - - - - + + - diff --git a/google-http-client-assembly/dependencies/css/maven-base.css b/google-http-client-assembly/dependencies/css/maven-base.css deleted file mode 100644 index 584ba23be..000000000 --- a/google-http-client-assembly/dependencies/css/maven-base.css +++ /dev/null @@ -1,151 +0,0 @@ -body { - margin: 0px; - padding: 0px; -} -img { - border:none; -} -table { - padding:0px; - width: 100%; - margin-left: -2px; - margin-right: -2px; -} -acronym { - cursor: help; - border-bottom: 1px dotted #feb; -} -table.bodyTable th, table.bodyTable td { - padding: 2px 4px 2px 4px; - vertical-align: top; -} -div.clear{ - clear:both; - visibility: hidden; -} -div.clear hr{ - display: none; -} -#bannerLeft, #bannerRight { - font-size: xx-large; - font-weight: bold; -} -#bannerLeft img, #bannerRight img { - margin: 0px; -} -.xleft, #bannerLeft img { - float:left; -} -.xright, #bannerRight { - float:right; -} -#banner { - padding: 0px; -} -#banner img { - border: none; -} -#breadcrumbs { - padding: 3px 10px 3px 10px; -} -#leftColumn { - width: 170px; - float:left; - overflow: auto; -} -#bodyColumn { - margin-right: 1.5em; - margin-left: 197px; -} -#legend { - padding: 8px 0 8px 0; -} -#navcolumn { - padding: 8px 4px 0 8px; -} -#navcolumn h5 { - margin: 0; - padding: 0; - font-size: small; -} -#navcolumn ul { - margin: 0; - padding: 0; - font-size: small; -} -#navcolumn li { - list-style-type: none; - background-image: none; - background-repeat: no-repeat; - background-position: 0 0.4em; - padding-left: 16px; - list-style-position: outside; - line-height: 1.2em; - font-size: smaller; -} -#navcolumn li.expanded { - background-image: url(../images/expanded.gif); -} -#navcolumn li.collapsed { - background-image: url(../images/collapsed.gif); -} -#poweredBy { - text-align: center; -} -#navcolumn img { - margin-top: 10px; - margin-bottom: 3px; -} -#poweredBy img { - display:block; - margin: 20px 0 20px 17px; -} -#search img { - margin: 0px; - display: block; -} -#search #q, #search #btnG { - border: 1px solid #999; - margin-bottom:10px; -} -#search form { - margin: 0px; -} -#lastPublished { - font-size: x-small; -} -.navSection { - margin-bottom: 2px; - padding: 8px; -} -.navSectionHead { - font-weight: bold; - font-size: x-small; -} -.section { - padding: 4px; -} -#footer { - padding: 3px 10px 3px 10px; - font-size: x-small; -} -#breadcrumbs { - font-size: x-small; - margin: 0pt; -} -.source { - padding: 12px; - margin: 1em 7px 1em 7px; -} -.source pre { - margin: 0px; - padding: 0px; -} -#navcolumn img.imageLink, .imageLink { - padding-left: 0px; - padding-bottom: 0px; - padding-top: 0px; - padding-right: 2px; - border: 0px; - margin: 0px; -} diff --git a/google-http-client-assembly/dependencies/css/maven-theme.css b/google-http-client-assembly/dependencies/css/maven-theme.css deleted file mode 100644 index c982168bf..000000000 --- a/google-http-client-assembly/dependencies/css/maven-theme.css +++ /dev/null @@ -1,141 +0,0 @@ -body { - padding: 0px 0px 10px 0px; -} -body, td, select, input, li{ - font-family: Verdana, Helvetica, Arial, sans-serif; - font-size: 13px; -} -code{ - font-family: Courier, monospace; - font-size: 13px; -} -a { - text-decoration: none; -} -a:link { - color:#36a; -} -a:visited { - color:#47a; -} -a:active, a:hover { - color:#69c; -} -#legend li.externalLink { - background: url(../images/external.png) left top no-repeat; - padding-left: 18px; -} -a.externalLink, a.externalLink:link, a.externalLink:visited, a.externalLink:active, a.externalLink:hover { - background: url(../images/external.png) right center no-repeat; - padding-right: 18px; -} -#legend li.newWindow { - background: url(../images/newwindow.png) left top no-repeat; - padding-left: 18px; -} -a.newWindow, a.newWindow:link, a.newWindow:visited, a.newWindow:active, a.newWindow:hover { - background: url(../images/newwindow.png) right center no-repeat; - padding-right: 18px; -} -h2 { - padding: 4px 4px 4px 6px; - border: 1px solid #999; - color: #900; - background-color: #ddd; - font-weight:900; - font-size: x-large; -} -h3 { - padding: 4px 4px 4px 6px; - border: 1px solid #aaa; - color: #900; - background-color: #eee; - font-weight: normal; - font-size: large; -} -h4 { - padding: 4px 4px 4px 6px; - border: 1px solid #bbb; - color: #900; - background-color: #fff; - font-weight: normal; - font-size: large; -} -h5 { - padding: 4px 4px 4px 6px; - color: #900; - font-size: normal; -} -p { - line-height: 1.3em; - font-size: small; -} -#breadcrumbs { - border-top: 1px solid #aaa; - border-bottom: 1px solid #aaa; - background-color: #ccc; -} -#leftColumn { - margin: 10px 0 0 5px; - border: 1px solid #999; - background-color: #eee; -} -#navcolumn h5 { - font-size: smaller; - border-bottom: 1px solid #aaaaaa; - padding-top: 2px; - color: #000; -} - -table.bodyTable th { - color: white; - background-color: #bbb; - text-align: left; - font-weight: bold; -} - -table.bodyTable th, table.bodyTable td { - font-size: 1em; -} - -table.bodyTable tr.a { - background-color: #ddd; -} - -table.bodyTable tr.b { - background-color: #eee; -} - -.source { - border: 1px solid #999; -} -dl { - padding: 4px 4px 4px 6px; - border: 1px solid #aaa; - background-color: #ffc; -} -dt { - color: #900; -} -#organizationLogo img, #projectLogo img, #projectLogo span{ - margin: 8px; -} -#banner { - border-bottom: 1px solid #fff; -} -.errormark, .warningmark, .donemark, .infomark { - background: url(../images/icon_error_sml.gif) no-repeat; -} - -.warningmark { - background-image: url(../images/icon_warning_sml.gif); -} - -.donemark { - background-image: url(../images/icon_success_sml.gif); -} - -.infomark { - background-image: url(../images/icon_info_sml.gif); -} - diff --git a/google-http-client-assembly/dependencies/css/print.css b/google-http-client-assembly/dependencies/css/print.css deleted file mode 100644 index 26ad7f0b5..000000000 --- a/google-http-client-assembly/dependencies/css/print.css +++ /dev/null @@ -1,7 +0,0 @@ -#banner, #footer, #leftcol, #breadcrumbs, .docs #toc, .docs .courtesylinks, #leftColumn, #navColumn { - display: none !important; -} -#bodyColumn, body.docs div.docs { - margin: 0 !important; - border: none !important -} diff --git a/google-http-client-assembly/dependencies/css/site.css b/google-http-client-assembly/dependencies/css/site.css deleted file mode 100644 index 055e7e286..000000000 --- a/google-http-client-assembly/dependencies/css/site.css +++ /dev/null @@ -1 +0,0 @@ -/* You can override this file with your own styles */ \ No newline at end of file diff --git a/google-http-client-assembly/dependencies/google-http-client-android-dependencies.html b/google-http-client-assembly/dependencies/google-http-client-android-dependencies.html deleted file mode 100644 index 694fd327c..000000000 --- a/google-http-client-assembly/dependencies/google-http-client-android-dependencies.html +++ /dev/null @@ -1,588 +0,0 @@ - - - - - - Project Dependencies - - - - - - - - - -
- -
-
-
- -
-

Project Dependencies

-
-

compile

-

The following is a list of compile dependencies for this project. These dependencies are required to compile and run the application:

- - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
com.google.http-clientgoogle-http-client${project.version}jarThe Apache Software License, Version 2.0
-
-

provided

-

The following is a list of provided dependencies for this project. These dependencies are required to compile the application, but should be provided by default when using the library:

- - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
com.google.androidandroid4.1.1.4jarApache 2.0
-
-

Project Transitive Dependencies

-

The following is a list of transitive dependencies for this project. Transitive dependencies are the dependencies of the project dependencies.

-
-

compile

-

The following is a list of compile dependencies for this project. These dependencies are required to compile and run the application:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
com.google.code.findbugsjsr3053.0.2jarThe Apache Software License, Version 2.0
com.google.j2objcj2objc-annotations1.1jarThe Apache Software License, Version 2.0
commons-codeccommons-codec1.6jarThe Apache Software License, Version 2.0
commons-loggingcommons-logging1.1.1jarThe Apache Software License, Version 2.0
org.apache.httpcomponentshttpclient4.5.5jarApache License, Version 2.0
org.apache.httpcomponentshttpcore4.4.6jarApache License, Version 2.0
-
-

provided

-

The following is a list of provided dependencies for this project. These dependencies are required to compile the application, but should be provided by default when using the library:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
org.jsonjson20080701jarprovided without support or warranty
org.khronosopengl-apigl1.1-android-2.1_r1jarApache 2.0
xercesxmlParserAPIs2.6.2jar-
xpp3xpp31.1.4cjarIndiana University Extreme! Lab Software License, vesion 1.1.1-Public Domain-Apache Software License, version 1.1
-
-

Project Dependency Graph

- -
-

Dependency Tree

-
-
-

Licenses

-

Apache Software License, version 1.1: MXP1: Xml Pull Parser 3rd Edition (XPP3)

-

Public Domain: MXP1: Xml Pull Parser 3rd Edition (XPP3)

-

Unknown: xmlParserAPIs

-

Apache License, Version 2.0: Apache HttpClient, Apache HttpCore

-

provided without support or warranty: JSON (JavaScript Object Notation)

-

Apache 2.0: Google Android Java ME Library (Khronos), Google Android Library

-

The Apache Software License, Version 2.0: Android Platform Extensions to the Google HTTP Client Library for Java., Commons Codec, Commons Logging, FindBugs-jsr305, Google HTTP Client Library for Java, J2ObjC Annotations

-

Indiana University Extreme! Lab Software License, vesion 1.1.1: MXP1: Xml Pull Parser 3rd Edition (XPP3)

-
-

Dependency File Details

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FilenameSizeEntriesClassesPackagesJDK RevDebug
android-4.1.1.4.jar12.35 MB7,2641,698711.5debug
jsr305-3.0.2.jar19.47 kB463531.5debug
google-http-client-${project.version}.jar261.81 kB223200161.6debug
j2objc-annotations-1.1.jar8.58 kB231211.5debug
commons-codec-1.6.jar227.32 kB2187661.5debug
commons-logging-1.1.1.jar59.26 kB422821.1debug
httpclient-4.5.5.jar730.27 kB507466241.6debug
httpcore-4.4.6.jar316.23 kB282252171.6debug
json-20080701.jar36.99 kB261711.3debug
opengl-api-gl1.1-android-2.1_r1.jar18.06 kB251321.5debug
xmlParserAPIs-2.6.2.jar121.80 kB238207171.1release
xpp3-1.1.4c.jar117.25 kB7856131.1debug
TotalSizeEntriesClassesPackagesJDK RevDebug
1214.22 MB8,9723,0601731.611
compile: 7compile: 1.58 MBcompile: 1,341compile: 1,069compile: 69-compile: 7
provided: 5provided: 12.64 MBprovided: 7,631provided: 1,991provided: 104-provided: 4
-
-

Dependency Repository Locations

- - - - - - - - - - - - - - - - - - - - -
Repo IDURLReleaseSnapshot
apache.snapshotshttp://repository.apache.org/snapshots-Yes
sonatype-nexus-snapshotshttps://oss.sonatype.org/content/repositories/snapshots-Yes
centralhttps://repo.maven.apache.org/maven2Yes-
-

Repository locations for each of the Dependencies.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Artifactapache.snapshotssonatype-nexus-snapshotscentral
com.google.android:android:jar:4.1.1.4---
com.google.code.findbugs:jsr305:jar:3.0.2---
com.google.http-client:google-http-client:jar:${project.version}---
com.google.j2objc:j2objc-annotations:jar:1.1---
commons-codec:commons-codec:jar:1.6---
commons-logging:commons-logging:jar:1.1.1---
org.apache.httpcomponents:httpclient:jar:4.5.5---
org.apache.httpcomponents:httpcore:jar:4.4.6---
org.json:json:jar:20080701---
org.khronos:opengl-api:jar:gl1.1-android-2.1_r1---
xerces:xmlParserAPIs:jar:2.6.2---
xpp3:xpp3:jar:1.1.4c---
Totalapache.snapshotssonatype-nexus-snapshotscentral
12 (compile: 7, provided: 5)000
-
-
-
-
-
- - - diff --git a/google-http-client-assembly/dependencies/google-http-client-appengine-dependencies.html b/google-http-client-assembly/dependencies/google-http-client-appengine-dependencies.html deleted file mode 100644 index 91510b1ae..000000000 --- a/google-http-client-assembly/dependencies/google-http-client-appengine-dependencies.html +++ /dev/null @@ -1,619 +0,0 @@ - - - - - - Project Dependencies - - - - - - - - - -
- -
-
-
- -
-

Project Dependencies

-
-

compile

-

The following is a list of compile dependencies for this project. These dependencies are required to compile and run the application:

- - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
com.google.http-clientgoogle-http-client${project.version}jarThe Apache Software License, Version 2.0
-
-

test

-

The following is a list of test dependencies for this project. These dependencies are only required to compile and run unit tests for the application:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
com.google.appengineappengine-api-stubs1.9.64jarGoogle App Engine Terms of Service
com.google.appengineappengine-testing1.9.64jarGoogle App Engine Terms of Service
com.google.guavaguava20.0jarThe Apache Software License, Version 2.0
com.google.http-clientgoogle-http-client-test${project.version}jarThe Apache Software License, Version 2.0
junitjunit4.8.2jarCommon Public License Version 1.0
-
-

provided

-

The following is a list of provided dependencies for this project. These dependencies are required to compile the application, but should be provided by default when using the library:

- - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
com.google.appengineappengine-api-1.0-sdk1.9.64jarGoogle App Engine Terms of Service
-
-

Project Transitive Dependencies

-

The following is a list of transitive dependencies for this project. Transitive dependencies are the dependencies of the project dependencies.

-
-

compile

-

The following is a list of compile dependencies for this project. These dependencies are required to compile and run the application:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
com.google.code.findbugsjsr3053.0.2jarThe Apache Software License, Version 2.0
com.google.j2objcj2objc-annotations1.1jarThe Apache Software License, Version 2.0
commons-codeccommons-codec1.6jarThe Apache Software License, Version 2.0
commons-loggingcommons-logging1.2jarThe Apache Software License, Version 2.0
org.apache.httpcomponentshttpclient4.5.5jarApache License, Version 2.0
org.apache.httpcomponentshttpcore4.4.6jarApache License, Version 2.0
-
-

Project Dependency Graph

- -
-

Dependency Tree

-
-
-

Licenses

-

Google App Engine Terms of Service: appengine-api-1.0-sdk, appengine-api-stubs, appengine-testing

-

Apache License, Version 2.0: Apache HttpClient, Apache HttpCore

-

Common Public License Version 1.0: JUnit

-

The Apache Software License, Version 2.0: Apache Commons Logging, Commons Codec, FindBugs-jsr305, Google App Engine extensions to the Google HTTP Client Library for Java., Google HTTP Client Library for Java, Guava: Google Core Libraries for Java, J2ObjC Annotations, Shared classes used for testing of artifacts in the Google HTTP Client Library for Java.

-
-

Dependency File Details

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FilenameSizeEntriesClassesPackagesJDK RevDebug
appengine-api-1.0-sdk-1.9.64.jar30.66 MB6,5666,044861.6debug
appengine-api-stubs-1.9.64.jar12.16 MB3,0982,7851671.6debug
appengine-testing-1.9.64.jar10.32 MB2,2932,265261.6debug
jsr305-3.0.2.jar19.47 kB463531.5debug
guava-20.0.jar2.33 MB1,8441,814181.6debug
google-http-client-${project.version}.jar261.81 kB223200161.6debug
google-http-client-test-${project.version}.jar51.73 kB594921.6debug
j2objc-annotations-1.1.jar8.58 kB231211.5debug
commons-codec-1.6.jar227.32 kB2187661.5debug
commons-logging-1.2.jar60.38 kB422821.2debug
junit-4.8.2.jar231.78 kB267230301.5debug
httpclient-4.5.5.jar730.27 kB507466241.6debug
httpcore-4.4.6.jar316.23 kB282252171.6debug
TotalSizeEntriesClassesPackagesJDK RevDebug
1357.33 MB15,46814,2563981.613
compile: 7compile: 1.59 MBcompile: 1,341compile: 1,069compile: 69-compile: 7
test: 5test: 25.08 MBtest: 7,561test: 7,143test: 243-test: 5
provided: 1provided: 30.66 MBprovided: 6,566provided: 6,044provided: 86-provided: 1
-
-

Dependency Repository Locations

- - - - - - - - - - - - - - - - - - - - -
Repo IDURLReleaseSnapshot
apache.snapshotshttp://repository.apache.org/snapshots-Yes
sonatype-nexus-snapshotshttps://oss.sonatype.org/content/repositories/snapshots-Yes
centralhttps://repo.maven.apache.org/maven2Yes-
-

Repository locations for each of the Dependencies.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Artifactapache.snapshotssonatype-nexus-snapshotscentral
com.google.appengine:appengine-api-1.0-sdk:jar:1.9.64---
com.google.appengine:appengine-api-stubs:jar:1.9.64---
com.google.appengine:appengine-testing:jar:1.9.64---
com.google.code.findbugs:jsr305:jar:3.0.2---
com.google.guava:guava:jar:20.0---
com.google.http-client:google-http-client:jar:${project.version}---
com.google.http-client:google-http-client-test:jar:${project.version}---
com.google.j2objc:j2objc-annotations:jar:1.1---
commons-codec:commons-codec:jar:1.6---
commons-logging:commons-logging:jar:1.2---
junit:junit:jar:4.8.2---
org.apache.httpcomponents:httpclient:jar:4.5.5---
org.apache.httpcomponents:httpcore:jar:4.4.6---
Totalapache.snapshotssonatype-nexus-snapshotscentral
13 (compile: 7, test: 5, provided: 1)000
-
-
-
-
-
- - - diff --git a/google-http-client-assembly/dependencies/google-http-client-dependencies.html b/google-http-client-assembly/dependencies/google-http-client-dependencies.html deleted file mode 100644 index 392600432..000000000 --- a/google-http-client-assembly/dependencies/google-http-client-dependencies.html +++ /dev/null @@ -1,758 +0,0 @@ - - - - - - Project Dependencies - - - - - - - - - -
- -
-
-
- -
-

Project Dependencies

-
-

compile

-

The following is a list of compile dependencies for this project. These dependencies are required to compile and run the application:

- - - - - - - - - - - - - - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
com.google.code.findbugsjsr3053.0.2jarThe Apache Software License, Version 2.0
com.google.j2objcj2objc-annotations1.1jarThe Apache Software License, Version 2.0
org.apache.httpcomponentshttpclient4.5.5jarApache License, Version 2.0
-
-

test

-

The following is a list of test dependencies for this project. These dependencies are only required to compile and run unit tests for the application:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
com.google.guavaguava-testlib20.0jarThe Apache Software License, Version 2.0
com.google.truthtruth0.40jarThe Apache Software License, Version 2.0
junitjunit4.8.2jarCommon Public License Version 1.0
org.mockitomockito-all1.9.0jarThe MIT License
-
-

provided

-

The following is a list of provided dependencies for this project. These dependencies are required to compile the application, but should be provided by default when using the library:

- - - - - - - - - - - - - - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
com.google.androidandroid1.5_r4jarApache 2.0
com.google.guavaguava20.0jarThe Apache Software License, Version 2.0
commons-codeccommons-codec1.6jarThe Apache Software License, Version 2.0
-
-

Project Transitive Dependencies

-

The following is a list of transitive dependencies for this project. Transitive dependencies are the dependencies of the project dependencies.

-
-

compile

-

The following is a list of compile dependencies for this project. These dependencies are required to compile and run the application:

- - - - - - - - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
commons-loggingcommons-logging1.1.1jarThe Apache Software License, Version 2.0
org.apache.httpcomponentshttpcore4.4.6jarApache License, Version 2.0
-
-

test

-

The following is a list of test dependencies for this project. These dependencies are only required to compile and run unit tests for the application:

- - - - - - - - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
com.google.errorproneerror_prone_annotations2.0.12jarApache 2.0
com.googlecode.java-diff-utilsdiffutils1.3.0jarThe Apache Software License, Version 2.0
-
-

provided

-

The following is a list of provided dependencies for this project. These dependencies are required to compile the application, but should be provided by default when using the library:

- - - - - - - - - - - - - - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
org.khronosopengl-apigl1.1-android-2.1_r1jarApache 2.0
xercesxmlParserAPIs2.6.2jar-
xpp3xpp31.1.4cjarIndiana University Extreme! Lab Software License, vesion 1.1.1-Public Domain-Apache Software License, version 1.1
-
-

Project Dependency Graph

- -
-

Dependency Tree

-
-
-

Licenses

-

Apache Software License, version 1.1: MXP1: Xml Pull Parser 3rd Edition (XPP3)

-

Public Domain: MXP1: Xml Pull Parser 3rd Edition (XPP3)

-

Unknown: xmlParserAPIs

-

Apache License, Version 2.0: Apache HttpClient, Apache HttpCore

-

Common Public License Version 1.0: JUnit

-

Apache 2.0: Google Android Java ME Library (Khronos), Google Android Library, error-prone annotations

-

The Apache Software License, Version 2.0: Commons Codec, Commons Logging, FindBugs-jsr305, Google HTTP Client Library for Java, Guava Testing Library, Guava: Google Core Libraries for Java, J2ObjC Annotations, Truth Core, java-diff-utils

-

The MIT License: Mockito

-

Indiana University Extreme! Lab Software License, vesion 1.1.1: MXP1: Xml Pull Parser 3rd Edition (XPP3)

-
-

Dependency File Details

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FilenameSizeEntriesClassesPackagesJDK RevDebug
android-1.5_r4.jar2.04 MB1,894965411.5debug
jsr305-3.0.2.jar19.47 kB463531.5debug
error_prone_annotations-2.0.12.jar10.06 kB281621.6release
guava-20.0.jar2.33 MB1,8441,814181.6debug
guava-testlib-20.0.jar748.27 kB59557471.6debug
j2objc-annotations-1.1.jar8.58 kB231211.5debug
truth-0.40.jar196.26 kB15013911.6debug
diffutils-1.3.0.jar33.33 kB352621.5debug
commons-codec-1.6.jar227.32 kB2187661.5debug
commons-logging-1.1.1.jar59.26 kB422821.1debug
junit-4.8.2.jar231.78 kB267230301.5debug
httpclient-4.5.5.jar730.27 kB507466241.6debug
httpcore-4.4.6.jar316.23 kB282252171.6debug
opengl-api-gl1.1-android-2.1_r1.jar18.06 kB251321.5debug
mockito-all-1.9.0.jar1.43 MB1,279654661.5debug
xmlParserAPIs-2.6.2.jar121.80 kB238207171.1release
xpp3-1.1.4c.jar117.25 kB7856131.1debug
TotalSizeEntriesClassesPackagesJDK RevDebug
178.56 MB7,5515,5632521.615
compile: 5compile: 1.11 MBcompile: 900compile: 793compile: 47-compile: 5
test: 6test: 2.62 MBtest: 2,354test: 1,639test: 108-test: 5
provided: 6provided: 4.84 MBprovided: 4,297provided: 3,131provided: 97-provided: 5
-
-

Dependency Repository Locations

- - - - - - - - - - - - - - - - - - - - -
Repo IDURLReleaseSnapshot
apache.snapshotshttp://repository.apache.org/snapshots-Yes
sonatype-nexus-snapshotshttps://oss.sonatype.org/content/repositories/snapshots-Yes
centralhttps://repo.maven.apache.org/maven2Yes-
-

Repository locations for each of the Dependencies.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Artifactapache.snapshotssonatype-nexus-snapshotscentral
com.google.android:android:jar:1.5_r4---
com.google.code.findbugs:jsr305:jar:3.0.2---
com.google.errorprone:error_prone_annotations:jar:2.0.12---
com.google.guava:guava:jar:20.0---
com.google.guava:guava-testlib:jar:20.0---
com.google.j2objc:j2objc-annotations:jar:1.1---
com.google.truth:truth:jar:0.40---
com.googlecode.java-diff-utils:diffutils:jar:1.3.0---
commons-codec:commons-codec:jar:1.6---
commons-logging:commons-logging:jar:1.1.1---
junit:junit:jar:4.8.2---
org.apache.httpcomponents:httpclient:jar:4.5.5---
org.apache.httpcomponents:httpcore:jar:4.4.6---
org.khronos:opengl-api:jar:gl1.1-android-2.1_r1---
org.mockito:mockito-all:jar:1.9.0---
xerces:xmlParserAPIs:jar:2.6.2---
xpp3:xpp3:jar:1.1.4c---
Totalapache.snapshotssonatype-nexus-snapshotscentral
17 (compile: 5, test: 6, provided: 6)000
-
-
-
-
-
- - - diff --git a/google-http-client-assembly/dependencies/google-http-client-gson-dependencies.html b/google-http-client-assembly/dependencies/google-http-client-gson-dependencies.html deleted file mode 100644 index 37205eb63..000000000 --- a/google-http-client-assembly/dependencies/google-http-client-gson-dependencies.html +++ /dev/null @@ -1,544 +0,0 @@ - - - - - - Project Dependencies - - - - - - - - - -
- -
-
-
- -
-

Project Dependencies

-
-

compile

-

The following is a list of compile dependencies for this project. These dependencies are required to compile and run the application:

- - - - - - - - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
com.google.code.gsongson2.1jarThe Apache Software License, Version 2.0
com.google.http-clientgoogle-http-client${project.version}jarThe Apache Software License, Version 2.0
-
-

test

-

The following is a list of test dependencies for this project. These dependencies are only required to compile and run unit tests for the application:

- - - - - - - - - - - - - - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
com.google.guavaguava20.0jarThe Apache Software License, Version 2.0
com.google.http-clientgoogle-http-client-test${project.version}jarThe Apache Software License, Version 2.0
junitjunit4.8.2jarCommon Public License Version 1.0
-
-

Project Transitive Dependencies

-

The following is a list of transitive dependencies for this project. Transitive dependencies are the dependencies of the project dependencies.

-
-

compile

-

The following is a list of compile dependencies for this project. These dependencies are required to compile and run the application:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
com.google.code.findbugsjsr3053.0.2jarThe Apache Software License, Version 2.0
com.google.j2objcj2objc-annotations1.1jarThe Apache Software License, Version 2.0
commons-codeccommons-codec1.6jarThe Apache Software License, Version 2.0
commons-loggingcommons-logging1.2jarThe Apache Software License, Version 2.0
org.apache.httpcomponentshttpclient4.5.5jarApache License, Version 2.0
org.apache.httpcomponentshttpcore4.4.6jarApache License, Version 2.0
-
-

Project Dependency Graph

- -
-

Dependency Tree

-
-
-

Licenses

-

Apache License, Version 2.0: Apache HttpClient, Apache HttpCore

-

Common Public License Version 1.0: JUnit

-

The Apache Software License, Version 2.0: Apache Commons Logging, Commons Codec, FindBugs-jsr305, GSON extensions to the Google HTTP Client Library for Java., Google HTTP Client Library for Java, Gson, Guava: Google Core Libraries for Java, J2ObjC Annotations, Shared classes used for testing of artifacts in the Google HTTP Client Library for Java.

-
-

Dependency File Details

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FilenameSizeEntriesClassesPackagesJDK RevDebug
jsr305-3.0.2.jar19.47 kB463531.5debug
gson-2.1.jar175.89 kB15814861.5debug
guava-20.0.jar2.33 MB1,8441,814181.6debug
google-http-client-${project.version}.jar261.81 kB223200161.6debug
google-http-client-test-${project.version}.jar51.73 kB594921.6debug
j2objc-annotations-1.1.jar8.58 kB231211.5debug
commons-codec-1.6.jar227.32 kB2187661.5debug
commons-logging-1.2.jar60.38 kB422821.2debug
junit-4.8.2.jar231.78 kB267230301.5debug
httpclient-4.5.5.jar730.27 kB507466241.6debug
httpcore-4.4.6.jar316.23 kB282252171.6debug
TotalSizeEntriesClassesPackagesJDK RevDebug
114.36 MB3,6693,3101251.611
compile: 8compile: 1.76 MBcompile: 1,499compile: 1,217compile: 75-compile: 8
test: 3test: 2.61 MBtest: 2,170test: 2,093test: 50-test: 3
-
-

Dependency Repository Locations

- - - - - - - - - - - - - - - - - - - - -
Repo IDURLReleaseSnapshot
apache.snapshotshttp://repository.apache.org/snapshots-Yes
sonatype-nexus-snapshotshttps://oss.sonatype.org/content/repositories/snapshots-Yes
centralhttps://repo.maven.apache.org/maven2Yes-
-

Repository locations for each of the Dependencies.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Artifactapache.snapshotssonatype-nexus-snapshotscentral
com.google.code.findbugs:jsr305:jar:3.0.2---
com.google.code.gson:gson:jar:2.1---
com.google.guava:guava:jar:20.0---
com.google.http-client:google-http-client:jar:${project.version}---
com.google.http-client:google-http-client-test:jar:${project.version}---
com.google.j2objc:j2objc-annotations:jar:1.1---
commons-codec:commons-codec:jar:1.6---
commons-logging:commons-logging:jar:1.2---
junit:junit:jar:4.8.2---
org.apache.httpcomponents:httpclient:jar:4.5.5---
org.apache.httpcomponents:httpcore:jar:4.4.6---
Totalapache.snapshotssonatype-nexus-snapshotscentral
11 (compile: 8, test: 3)000
-
-
-
-
-
- - - diff --git a/google-http-client-assembly/dependencies/google-http-client-jackson-dependencies.html b/google-http-client-assembly/dependencies/google-http-client-jackson-dependencies.html deleted file mode 100644 index 6abf838fd..000000000 --- a/google-http-client-assembly/dependencies/google-http-client-jackson-dependencies.html +++ /dev/null @@ -1,544 +0,0 @@ - - - - - - Project Dependencies - - - - - - - - - -
- -
-
-
- -
-

Project Dependencies

-
-

compile

-

The following is a list of compile dependencies for this project. These dependencies are required to compile and run the application:

- - - - - - - - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
com.google.http-clientgoogle-http-client${project.version}jarThe Apache Software License, Version 2.0
org.codehaus.jacksonjackson-core-asl1jarThe Apache Software License, Version 2.0
-
-

test

-

The following is a list of test dependencies for this project. These dependencies are only required to compile and run unit tests for the application:

- - - - - - - - - - - - - - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
com.google.guavaguava20.0jarThe Apache Software License, Version 2.0
com.google.http-clientgoogle-http-client-test${project.version}jarThe Apache Software License, Version 2.0
junitjunit4.8.2jarCommon Public License Version 1.0
-
-

Project Transitive Dependencies

-

The following is a list of transitive dependencies for this project. Transitive dependencies are the dependencies of the project dependencies.

-
-

compile

-

The following is a list of compile dependencies for this project. These dependencies are required to compile and run the application:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
com.google.code.findbugsjsr3053.0.2jarThe Apache Software License, Version 2.0
com.google.j2objcj2objc-annotations1.1jarThe Apache Software License, Version 2.0
commons-codeccommons-codec1.6jarThe Apache Software License, Version 2.0
commons-loggingcommons-logging1.2jarThe Apache Software License, Version 2.0
org.apache.httpcomponentshttpclient4.5.5jarApache License, Version 2.0
org.apache.httpcomponentshttpcore4.4.6jarApache License, Version 2.0
-
-

Project Dependency Graph

- -
-

Dependency Tree

-
-
-

Licenses

-

Apache License, Version 2.0: Apache HttpClient, Apache HttpCore

-

Common Public License Version 1.0: JUnit

-

The Apache Software License, Version 2.0: Apache Commons Logging, Commons Codec, FindBugs-jsr305, Google HTTP Client Library for Java, Guava: Google Core Libraries for Java, J2ObjC Annotations, Jackson, Jackson extensions to the Google HTTP Client Library for Java., Shared classes used for testing of artifacts in the Google HTTP Client Library for Java.

-
-

Dependency File Details

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FilenameSizeEntriesClassesPackagesJDK RevDebug
jsr305-3.0.2.jar19.47 kB463531.5debug
guava-20.0.jar2.33 MB1,8441,814181.6debug
google-http-client-${project.version}.jar261.81 kB223200161.6debug
google-http-client-test-${project.version}.jar51.73 kB594921.6debug
j2objc-annotations-1.1.jar8.58 kB231211.5debug
commons-codec-1.6.jar227.32 kB2187661.5debug
commons-logging-1.2.jar60.38 kB422821.2debug
junit-4.8.2.jar231.78 kB267230301.5debug
httpclient-4.5.5.jar730.27 kB507466241.6debug
httpcore-4.4.6.jar316.23 kB282252171.6debug
jackson-core-asl-1.9.13.jar226.69 kB13712181.5debug
TotalSizeEntriesClassesPackagesJDK RevDebug
114.41 MB3,6483,2831271.611
compile: 8compile: 1.81 MBcompile: 1,478compile: 1,190compile: 77-compile: 8
test: 3test: 2.61 MBtest: 2,170test: 2,093test: 50-test: 3
-
-

Dependency Repository Locations

- - - - - - - - - - - - - - - - - - - - -
Repo IDURLReleaseSnapshot
apache.snapshotshttp://repository.apache.org/snapshots-Yes
sonatype-nexus-snapshotshttps://oss.sonatype.org/content/repositories/snapshots-Yes
centralhttps://repo.maven.apache.org/maven2Yes-
-

Repository locations for each of the Dependencies.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Artifactapache.snapshotssonatype-nexus-snapshotscentral
com.google.code.findbugs:jsr305:jar:3.0.2---
com.google.guava:guava:jar:20.0---
com.google.http-client:google-http-client:jar:${project.version}---
com.google.http-client:google-http-client-test:jar:${project.version}---
com.google.j2objc:j2objc-annotations:jar:1.1---
commons-codec:commons-codec:jar:1.6---
commons-logging:commons-logging:jar:1.2---
junit:junit:jar:4.8.2---
org.apache.httpcomponents:httpclient:jar:4.5.5---
org.apache.httpcomponents:httpcore:jar:4.4.6---
org.codehaus.jackson:jackson-core-asl:jar:1.9.13---
Totalapache.snapshotssonatype-nexus-snapshotscentral
11 (compile: 8, test: 3)000
-
-
-
-
-
- - - diff --git a/google-http-client-assembly/dependencies/google-http-client-jackson2-dependencies.html b/google-http-client-assembly/dependencies/google-http-client-jackson2-dependencies.html deleted file mode 100644 index 16e83ecca..000000000 --- a/google-http-client-assembly/dependencies/google-http-client-jackson2-dependencies.html +++ /dev/null @@ -1,544 +0,0 @@ - - - - - - Project Dependencies - - - - - - - - - -
- -
-
-
- -
-

Project Dependencies

-
-

compile

-

The following is a list of compile dependencies for this project. These dependencies are required to compile and run the application:

- - - - - - - - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
com.fasterxml.jackson.corejackson-core2.9.6jarThe Apache Software License, Version 2.0
com.google.http-clientgoogle-http-client${project.version}jarThe Apache Software License, Version 2.0
-
-

test

-

The following is a list of test dependencies for this project. These dependencies are only required to compile and run unit tests for the application:

- - - - - - - - - - - - - - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
com.google.guavaguava20.0jarThe Apache Software License, Version 2.0
com.google.http-clientgoogle-http-client-test${project.version}jarThe Apache Software License, Version 2.0
junitjunit4.8.2jarCommon Public License Version 1.0
-
-

Project Transitive Dependencies

-

The following is a list of transitive dependencies for this project. Transitive dependencies are the dependencies of the project dependencies.

-
-

compile

-

The following is a list of compile dependencies for this project. These dependencies are required to compile and run the application:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
com.google.code.findbugsjsr3053.0.2jarThe Apache Software License, Version 2.0
com.google.j2objcj2objc-annotations1.1jarThe Apache Software License, Version 2.0
commons-codeccommons-codec1.6jarThe Apache Software License, Version 2.0
commons-loggingcommons-logging1.2jarThe Apache Software License, Version 2.0
org.apache.httpcomponentshttpclient4.5.5jarApache License, Version 2.0
org.apache.httpcomponentshttpcore4.4.6jarApache License, Version 2.0
-
-

Project Dependency Graph

- -
-

Dependency Tree

-
-
-

Licenses

-

Apache License, Version 2.0: Apache HttpClient, Apache HttpCore

-

Common Public License Version 1.0: JUnit

-

The Apache Software License, Version 2.0: Apache Commons Logging, Commons Codec, FindBugs-jsr305, Google HTTP Client Library for Java, Guava: Google Core Libraries for Java, J2ObjC Annotations, Jackson 2 extensions to the Google HTTP Client Library for Java., Jackson-core, Shared classes used for testing of artifacts in the Google HTTP Client Library for Java.

-
-

Dependency File Details

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FilenameSizeEntriesClassesPackagesJDK RevDebug
jackson-core-2.9.6.jar313.71 kB130105111.6debug
jsr305-3.0.2.jar19.47 kB463531.5debug
guava-20.0.jar2.33 MB1,8441,814181.6debug
google-http-client-${project.version}.jar261.81 kB223200161.6debug
google-http-client-test-${project.version}.jar51.73 kB594921.6debug
j2objc-annotations-1.1.jar8.58 kB231211.5debug
commons-codec-1.6.jar227.32 kB2187661.5debug
commons-logging-1.2.jar60.38 kB422821.2debug
junit-4.8.2.jar231.78 kB267230301.5debug
httpclient-4.5.5.jar730.27 kB507466241.6debug
httpcore-4.4.6.jar316.23 kB282252171.6debug
TotalSizeEntriesClassesPackagesJDK RevDebug
114.50 MB3,6413,2671301.611
compile: 8compile: 1.89 MBcompile: 1,471compile: 1,174compile: 80-compile: 8
test: 3test: 2.61 MBtest: 2,170test: 2,093test: 50-test: 3
-
-

Dependency Repository Locations

- - - - - - - - - - - - - - - - - - - - -
Repo IDURLReleaseSnapshot
apache.snapshotshttp://repository.apache.org/snapshots-Yes
sonatype-nexus-snapshotshttps://oss.sonatype.org/content/repositories/snapshots-Yes
centralhttps://repo.maven.apache.org/maven2Yes-
-

Repository locations for each of the Dependencies.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Artifactapache.snapshotssonatype-nexus-snapshotscentral
com.fasterxml.jackson.core:jackson-core:jar:2.9.6---
com.google.code.findbugs:jsr305:jar:3.0.2---
com.google.guava:guava:jar:20.0---
com.google.http-client:google-http-client:jar:${project.version}---
com.google.http-client:google-http-client-test:jar:${project.version}---
com.google.j2objc:j2objc-annotations:jar:1.1---
commons-codec:commons-codec:jar:1.6---
commons-logging:commons-logging:jar:1.2---
junit:junit:jar:4.8.2---
org.apache.httpcomponents:httpclient:jar:4.5.5---
org.apache.httpcomponents:httpcore:jar:4.4.6---
Totalapache.snapshotssonatype-nexus-snapshotscentral
11 (compile: 8, test: 3)000
-
-
-
-
-
- - - diff --git a/google-http-client-assembly/dependencies/google-http-client-jdo-dependencies.html b/google-http-client-assembly/dependencies/google-http-client-jdo-dependencies.html deleted file mode 100644 index 4e113f880..000000000 --- a/google-http-client-assembly/dependencies/google-http-client-jdo-dependencies.html +++ /dev/null @@ -1,712 +0,0 @@ - - - - - - Project Dependencies - - - - - - - - - -
- -
-
-
- -
-

Project Dependencies

-
-

compile

-

The following is a list of compile dependencies for this project. These dependencies are required to compile and run the application:

- - - - - - - - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
com.google.http-clientgoogle-http-client${project.version}jarThe Apache Software License, Version 2.0
javax.jdojdo2-api2.3-ebjarApache 2
-
-

test

-

The following is a list of test dependencies for this project. These dependencies are only required to compile and run unit tests for the application:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
com.google.guavaguava20.0jarThe Apache Software License, Version 2.0
com.google.http-clientgoogle-http-client-test${project.version}jarThe Apache Software License, Version 2.0
junitjunit4.8.2jarCommon Public License Version 1.0
mysqlmysql-connector-java5.1.18jarThe GNU General Public License, Version 2
org.datanucleusdatanucleus-api-jdo3.2.1jarThe Apache Software License, Version 2.0
org.datanucleusdatanucleus-core3.2.2jarThe Apache Software License, Version 2.0
org.datanucleusdatanucleus-rdbms3.2.1jarThe Apache Software License, Version 2.0
-
-

Project Transitive Dependencies

-

The following is a list of transitive dependencies for this project. Transitive dependencies are the dependencies of the project dependencies.

-
-

compile

-

The following is a list of compile dependencies for this project. These dependencies are required to compile and run the application:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
com.google.code.findbugsjsr3053.0.2jarThe Apache Software License, Version 2.0
com.google.j2objcj2objc-annotations1.1jarThe Apache Software License, Version 2.0
commons-codeccommons-codec1.6jarThe Apache Software License, Version 2.0
commons-loggingcommons-logging1.2jarThe Apache Software License, Version 2.0
javax.transactiontransaction-api1.1jar-
org.apache.httpcomponentshttpclient4.5.5jarApache License, Version 2.0
org.apache.httpcomponentshttpcore4.4.6jarApache License, Version 2.0
-
-

Project Dependency Graph

- -
-

Dependency Tree

-
-
-

Licenses

-

Apache 2: JDO2 API

-

Unknown: transaction-api

-

Apache License, Version 2.0: Apache HttpClient, Apache HttpCore

-

Common Public License Version 1.0: JUnit

-

The Apache Software License, Version 2.0: Apache Commons Logging, Commons Codec, DataNucleus Core, DataNucleus JDO API plugin, DataNucleus RDBMS, FindBugs-jsr305, Google HTTP Client Library for Java, Guava: Google Core Libraries for Java, J2ObjC Annotations, JDO extensions to the Google HTTP Client Library for Java., Shared classes used for testing of artifacts in the Google HTTP Client Library for Java.

-

The GNU General Public License, Version 2: MySQL Connector/J

-
-

Dependency File Details

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FilenameSizeEntriesClassesPackagesJDK RevDebug
jsr305-3.0.2.jar19.47 kB463531.5debug
guava-20.0.jar2.33 MB1,8441,814181.6debug
google-http-client-${project.version}.jar261.81 kB223200161.6debug
google-http-client-test-${project.version}.jar51.73 kB594921.6debug
j2objc-annotations-1.1.jar8.58 kB231211.5debug
commons-codec-1.6.jar227.32 kB2187661.5debug
commons-logging-1.2.jar60.38 kB422821.2debug
jdo2-api-2.3-eb.jar188.18 kB22618271.5debug
transaction-api-1.1.jar14.72 kB241821.3debug
junit-4.8.2.jar231.78 kB267230301.5debug
mysql-connector-java-5.1.18.jar771.37 kB279245121.6debug
httpclient-4.5.5.jar730.27 kB507466241.6debug
httpcore-4.4.6.jar316.23 kB282252171.6debug
datanucleus-api-jdo-3.2.1.jar329.11 kB18012661.5debug
datanucleus-core-3.2.2.jar1.72 MB943841501.5debug
datanucleus-rdbms-3.2.1.jar1.69 MB768715291.6debug
TotalSizeEntriesClassesPackagesJDK RevDebug
168.87 MB5,9315,2892251.616
compile: 9compile: 1.78 MBcompile: 1,591compile: 1,269compile: 78-compile: 9
test: 7test: 7.09 MBtest: 4,340test: 4,020test: 147-test: 7
-
-

Dependency Repository Locations

- - - - - - - - - - - - - - - - - - - - - - - - - -
Repo IDURLReleaseSnapshot
apache.snapshotshttp://repository.apache.org/snapshots-Yes
sonatype-nexus-snapshotshttps://oss.sonatype.org/content/repositories/snapshots-Yes
DN_M2_Repohttp://www.datanucleus.org/downloads/maven2/Yes-
centralhttps://repo.maven.apache.org/maven2Yes-
-

Repository locations for each of the Dependencies.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Artifactapache.snapshotssonatype-nexus-snapshotsDN_M2_Repocentral
com.google.code.findbugs:jsr305:jar:3.0.2----
com.google.guava:guava:jar:20.0----
com.google.http-client:google-http-client:jar:${project.version}----
com.google.http-client:google-http-client-test:jar:${project.version}----
com.google.j2objc:j2objc-annotations:jar:1.1----
commons-codec:commons-codec:jar:1.6----
commons-logging:commons-logging:jar:1.2----
javax.jdo:jdo2-api:jar:2.3-eb--Found at http://www.datanucleus.org/downloads/maven2/-
javax.transaction:transaction-api:jar:1.1----
junit:junit:jar:4.8.2----
mysql:mysql-connector-java:jar:5.1.18----
org.apache.httpcomponents:httpclient:jar:4.5.5----
org.apache.httpcomponents:httpcore:jar:4.4.6----
org.datanucleus:datanucleus-api-jdo:jar:3.2.1----
org.datanucleus:datanucleus-core:jar:3.2.2----
org.datanucleus:datanucleus-rdbms:jar:3.2.1----
Totalapache.snapshotssonatype-nexus-snapshotsDN_M2_Repocentral
16 (compile: 9, test: 7)0010
-
-
-
-
-
- - - diff --git a/google-http-client-assembly/dependencies/google-http-client-protobuf-dependencies.html b/google-http-client-assembly/dependencies/google-http-client-protobuf-dependencies.html deleted file mode 100644 index cc8787bbf..000000000 --- a/google-http-client-assembly/dependencies/google-http-client-protobuf-dependencies.html +++ /dev/null @@ -1,485 +0,0 @@ - - - - - - Project Dependencies - - - - - - - - - -
- -
-
-
- -
-

Project Dependencies

-
-

compile

-

The following is a list of compile dependencies for this project. These dependencies are required to compile and run the application:

- - - - - - - - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
com.google.http-clientgoogle-http-client${project.version}jarThe Apache Software License, Version 2.0
com.google.protobufprotobuf-java2.6.1jarNew BSD license
-
-

test

-

The following is a list of test dependencies for this project. These dependencies are only required to compile and run unit tests for the application:

- - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
junitjunit4.8.2jarCommon Public License Version 1.0
-
-

Project Transitive Dependencies

-

The following is a list of transitive dependencies for this project. Transitive dependencies are the dependencies of the project dependencies.

-
-

compile

-

The following is a list of compile dependencies for this project. These dependencies are required to compile and run the application:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
com.google.code.findbugsjsr3053.0.2jarThe Apache Software License, Version 2.0
com.google.j2objcj2objc-annotations1.1jarThe Apache Software License, Version 2.0
commons-codeccommons-codec1.6jarThe Apache Software License, Version 2.0
commons-loggingcommons-logging1.2jarThe Apache Software License, Version 2.0
org.apache.httpcomponentshttpclient4.5.5jarApache License, Version 2.0
org.apache.httpcomponentshttpcore4.4.6jarApache License, Version 2.0
-
-

Project Dependency Graph

- -
-

Dependency Tree

-
-
-

Licenses

-

New BSD license: Protocol Buffer Java API

-

Apache License, Version 2.0: Apache HttpClient, Apache HttpCore

-

Common Public License Version 1.0: JUnit

-

The Apache Software License, Version 2.0: Apache Commons Logging, Commons Codec, FindBugs-jsr305, Google HTTP Client Library for Java, J2ObjC Annotations, Protocol Buffer extensions to the Google HTTP Client Library for Java.

-
-

Dependency File Details

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FilenameSizeEntriesClassesPackagesJDK RevDebug
jsr305-3.0.2.jar19.47 kB463531.5debug
google-http-client-${project.version}.jar261.81 kB223200161.6debug
j2objc-annotations-1.1.jar8.58 kB231211.5debug
protobuf-java-2.6.1.jar582.66 kB28627611.5debug
commons-codec-1.6.jar227.32 kB2187661.5debug
commons-logging-1.2.jar60.38 kB422821.2debug
junit-4.8.2.jar231.78 kB267230301.5debug
httpclient-4.5.5.jar730.27 kB507466241.6debug
httpcore-4.4.6.jar316.23 kB282252171.6debug
TotalSizeEntriesClassesPackagesJDK RevDebug
92.38 MB1,8941,5751001.69
compile: 8compile: 2.15 MBcompile: 1,627compile: 1,345compile: 70-compile: 8
test: 1test: 231.78 kBtest: 267test: 230test: 30-test: 1
-
-

Dependency Repository Locations

- - - - - - - - - - - - - - - - - - - - -
Repo IDURLReleaseSnapshot
apache.snapshotshttp://repository.apache.org/snapshots-Yes
sonatype-nexus-snapshotshttps://oss.sonatype.org/content/repositories/snapshots-Yes
centralhttps://repo.maven.apache.org/maven2Yes-
-

Repository locations for each of the Dependencies.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Artifactapache.snapshotssonatype-nexus-snapshotscentral
com.google.code.findbugs:jsr305:jar:3.0.2---
com.google.http-client:google-http-client:jar:${project.version}---
com.google.j2objc:j2objc-annotations:jar:1.1---
com.google.protobuf:protobuf-java:jar:2.6.1---
commons-codec:commons-codec:jar:1.6---
commons-logging:commons-logging:jar:1.2---
junit:junit:jar:4.8.2---
org.apache.httpcomponents:httpclient:jar:4.5.5---
org.apache.httpcomponents:httpcore:jar:4.4.6---
Totalapache.snapshotssonatype-nexus-snapshotscentral
9 (compile: 8, test: 1)000
-
-
-
-
-
- - - diff --git a/google-http-client-assembly/dependencies/google-http-client-xml-dependencies.html b/google-http-client-assembly/dependencies/google-http-client-xml-dependencies.html deleted file mode 100644 index 980cd40f5..000000000 --- a/google-http-client-assembly/dependencies/google-http-client-xml-dependencies.html +++ /dev/null @@ -1,519 +0,0 @@ - - - - - - Project Dependencies - - - - - - - - - -
- -
-
-
- -
-

Project Dependencies

-
-

compile

-

The following is a list of compile dependencies for this project. These dependencies are required to compile and run the application:

- - - - - - - - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
com.google.http-clientgoogle-http-client${project.version}jarThe Apache Software License, Version 2.0
xpp3xpp31.1.4cjarIndiana University Extreme! Lab Software License, vesion 1.1.1-Public Domain-Apache Software License, version 1.1
-
-

test

-

The following is a list of test dependencies for this project. These dependencies are only required to compile and run unit tests for the application:

- - - - - - - - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
com.google.guavaguava20.0jarThe Apache Software License, Version 2.0
junitjunit4.8.2jarCommon Public License Version 1.0
-
-

Project Transitive Dependencies

-

The following is a list of transitive dependencies for this project. Transitive dependencies are the dependencies of the project dependencies.

-
-

compile

-

The following is a list of compile dependencies for this project. These dependencies are required to compile and run the application:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GroupIdArtifactIdVersionTypeLicense
com.google.code.findbugsjsr3053.0.2jarThe Apache Software License, Version 2.0
com.google.j2objcj2objc-annotations1.1jarThe Apache Software License, Version 2.0
commons-codeccommons-codec1.6jarThe Apache Software License, Version 2.0
commons-loggingcommons-logging1.2jarThe Apache Software License, Version 2.0
org.apache.httpcomponentshttpclient4.5.5jarApache License, Version 2.0
org.apache.httpcomponentshttpcore4.4.6jarApache License, Version 2.0
-
-

Project Dependency Graph

- -
-

Dependency Tree

-
-
-

Licenses

-

Apache Software License, version 1.1: MXP1: Xml Pull Parser 3rd Edition (XPP3)

-

Public Domain: MXP1: Xml Pull Parser 3rd Edition (XPP3)

-

Apache License, Version 2.0: Apache HttpClient, Apache HttpCore

-

Common Public License Version 1.0: JUnit

-

The Apache Software License, Version 2.0: Apache Commons Logging, Commons Codec, FindBugs-jsr305, Google HTTP Client Library for Java, Guava: Google Core Libraries for Java, J2ObjC Annotations, XML extensions to the Google HTTP Client Library for Java.

-

Indiana University Extreme! Lab Software License, vesion 1.1.1: MXP1: Xml Pull Parser 3rd Edition (XPP3)

-
-

Dependency File Details

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FilenameSizeEntriesClassesPackagesJDK RevDebug
jsr305-3.0.2.jar19.47 kB463531.5debug
guava-20.0.jar2.33 MB1,8441,814181.6debug
google-http-client-${project.version}.jar261.81 kB223200161.6debug
j2objc-annotations-1.1.jar8.58 kB231211.5debug
commons-codec-1.6.jar227.32 kB2187661.5debug
commons-logging-1.2.jar60.38 kB422821.2debug
junit-4.8.2.jar231.78 kB267230301.5debug
httpclient-4.5.5.jar730.27 kB507466241.6debug
httpcore-4.4.6.jar316.23 kB282252171.6debug
xpp3-1.1.4c.jar117.25 kB7856131.1debug
TotalSizeEntriesClassesPackagesJDK RevDebug
104.26 MB3,5303,1691301.610
compile: 8compile: 1.70 MBcompile: 1,419compile: 1,125compile: 82-compile: 8
test: 2test: 2.56 MBtest: 2,111test: 2,044test: 48-test: 2
-
-

Dependency Repository Locations

- - - - - - - - - - - - - - - - - - - - -
Repo IDURLReleaseSnapshot
apache.snapshotshttp://repository.apache.org/snapshots-Yes
sonatype-nexus-snapshotshttps://oss.sonatype.org/content/repositories/snapshots-Yes
centralhttps://repo.maven.apache.org/maven2Yes-
-

Repository locations for each of the Dependencies.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Artifactapache.snapshotssonatype-nexus-snapshotscentral
com.google.code.findbugs:jsr305:jar:3.0.2---
com.google.guava:guava:jar:20.0---
com.google.http-client:google-http-client:jar:${project.version}---
com.google.j2objc:j2objc-annotations:jar:1.1---
commons-codec:commons-codec:jar:1.6---
commons-logging:commons-logging:jar:1.2---
junit:junit:jar:4.8.2---
org.apache.httpcomponents:httpclient:jar:4.5.5---
org.apache.httpcomponents:httpcore:jar:4.4.6---
xpp3:xpp3:jar:1.1.4c---
Totalapache.snapshotssonatype-nexus-snapshotscentral
10 (compile: 8, test: 2)000
-
-
-
-
-
- - - diff --git a/google-http-client-assembly/dependencies/images/close.gif b/google-http-client-assembly/dependencies/images/close.gif deleted file mode 100644 index 1c26bbc52..000000000 Binary files a/google-http-client-assembly/dependencies/images/close.gif and /dev/null differ diff --git a/google-http-client-assembly/dependencies/images/collapsed.gif b/google-http-client-assembly/dependencies/images/collapsed.gif deleted file mode 100644 index 6e7108406..000000000 Binary files a/google-http-client-assembly/dependencies/images/collapsed.gif and /dev/null differ diff --git a/google-http-client-assembly/dependencies/images/expanded.gif b/google-http-client-assembly/dependencies/images/expanded.gif deleted file mode 100644 index 0fef3d89e..000000000 Binary files a/google-http-client-assembly/dependencies/images/expanded.gif and /dev/null differ diff --git a/google-http-client-assembly/dependencies/images/external.png b/google-http-client-assembly/dependencies/images/external.png deleted file mode 100644 index 3f999fc88..000000000 Binary files a/google-http-client-assembly/dependencies/images/external.png and /dev/null differ diff --git a/google-http-client-assembly/dependencies/images/icon_error_sml.gif b/google-http-client-assembly/dependencies/images/icon_error_sml.gif deleted file mode 100644 index 61132ef2b..000000000 Binary files a/google-http-client-assembly/dependencies/images/icon_error_sml.gif and /dev/null differ diff --git a/google-http-client-assembly/dependencies/images/icon_info_sml.gif b/google-http-client-assembly/dependencies/images/icon_info_sml.gif deleted file mode 100644 index c6cb9ad7c..000000000 Binary files a/google-http-client-assembly/dependencies/images/icon_info_sml.gif and /dev/null differ diff --git a/google-http-client-assembly/dependencies/images/icon_success_sml.gif b/google-http-client-assembly/dependencies/images/icon_success_sml.gif deleted file mode 100644 index 52e85a430..000000000 Binary files a/google-http-client-assembly/dependencies/images/icon_success_sml.gif and /dev/null differ diff --git a/google-http-client-assembly/dependencies/images/icon_warning_sml.gif b/google-http-client-assembly/dependencies/images/icon_warning_sml.gif deleted file mode 100644 index 873bbb52c..000000000 Binary files a/google-http-client-assembly/dependencies/images/icon_warning_sml.gif and /dev/null differ diff --git a/google-http-client-assembly/dependencies/images/logos/build-by-maven-black.png b/google-http-client-assembly/dependencies/images/logos/build-by-maven-black.png deleted file mode 100644 index 919fd0f66..000000000 Binary files a/google-http-client-assembly/dependencies/images/logos/build-by-maven-black.png and /dev/null differ diff --git a/google-http-client-assembly/dependencies/images/logos/build-by-maven-white.png b/google-http-client-assembly/dependencies/images/logos/build-by-maven-white.png deleted file mode 100644 index 7d44c9c2e..000000000 Binary files a/google-http-client-assembly/dependencies/images/logos/build-by-maven-white.png and /dev/null differ diff --git a/google-http-client-assembly/dependencies/images/logos/maven-feather.png b/google-http-client-assembly/dependencies/images/logos/maven-feather.png deleted file mode 100644 index b5ada836e..000000000 Binary files a/google-http-client-assembly/dependencies/images/logos/maven-feather.png and /dev/null differ diff --git a/google-http-client-assembly/dependencies/images/newwindow.png b/google-http-client-assembly/dependencies/images/newwindow.png deleted file mode 100644 index 6287f72bd..000000000 Binary files a/google-http-client-assembly/dependencies/images/newwindow.png and /dev/null differ diff --git a/google-http-client-assembly/dependencies/APACHE-LICENSE.txt b/google-http-client-assembly/licenses/APACHE-LICENSE.txt similarity index 100% rename from google-http-client-assembly/dependencies/APACHE-LICENSE.txt rename to google-http-client-assembly/licenses/APACHE-LICENSE.txt diff --git a/google-http-client-assembly/dependencies/BSD-LICENSE.txt b/google-http-client-assembly/licenses/BSD-LICENSE.txt similarity index 100% rename from google-http-client-assembly/dependencies/BSD-LICENSE.txt rename to google-http-client-assembly/licenses/BSD-LICENSE.txt diff --git a/google-http-client-assembly/licenses/CDDL-LICENSE.txt b/google-http-client-assembly/licenses/CDDL-LICENSE.txt new file mode 100644 index 000000000..1154e0aee --- /dev/null +++ b/google-http-client-assembly/licenses/CDDL-LICENSE.txt @@ -0,0 +1,119 @@ +COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 + +1. Definitions. + +1.1. Contributor means each individual or entity that creates or contributes to the creation of Modifications. + +1.2. Contributor Version means the combination of the Original Software, prior Modifications used by a Contributor (if any), and the Modifications made by that particular Contributor. + +1.3. Covered Software means (a) the Original Software, or (b) Modifications, or (c) the combination of files containing Original Software with files containing Modifications, in each case including portions thereof. + +1.4. Executable means the Covered Software in any form other than Source Code. + +1.5. Initial Developer means the individual or entity that first makes Original Software available under this License. + +1.6. Larger Work means a work which combines Covered Software or portions thereof with code not governed by the terms of this License. + +1.7. License means this document. + +1.8. Licensable means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein. + +1.9. Modifications means the Source Code and Executable form of any of the following: + +A. Any file that results from an addition to, deletion from or modification of the contents of a file containing Original Software or previous Modifications; + +B. Any new file that contains any part of the Original Software or previous Modification; or + +C. Any new file that is contributed or otherwise made available under the terms of this License. + +1.10. Original Software means the Source Code and Executable form of computer software code that is originally released under this License. + +1.11. Patent Claims means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor. + +1.12. Source Code means (a) the common form of computer software code in which modifications are made and (b) associated documentation included in or with such code. + +1.13. You (or Your) means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, You includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, control means (a)�the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b)�ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. + +2. License Grants. + +2.1. The Initial Developer Grant. +Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, the Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license: +(a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer, to use, reproduce, modify, display, perform, sublicense and distribute the Original Software (or portions thereof), with or without Modifications, and/or as part of a Larger Work; and +(b) under Patent Claims infringed by the making, using or selling of Original Software, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Software (or portions thereof). +(c) The licenses granted in Sections�2.1(a) and (b) are effective on the date Initial Developer first distributes or otherwise makes the Original Software available to a third party under the terms of this License. +(d) Notwithstanding Section�2.1(b) above, no patent license is granted: (1)�for code that You delete from the Original Software, or (2)�for infringements caused by: (i)�the modification of the Original Software, or (ii)�the combination of the Original Software with other software or devices. + +2.2. Contributor Grant. +Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: +(a) under intellectual property rights (other than patent or trademark) Licensable by Contributor to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof), either on an unmodified basis, with other Modifications, as Covered Software and/or as part of a Larger Work; and +(b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: (1)�Modifications made by that Contributor (or portions thereof); and (2)�the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination). +(c) The licenses granted in Sections�2.2(a) and 2.2(b) are effective on the date Contributor first distributes or otherwise makes the Modifications available to a third party. +(d) Notwithstanding Section�2.2(b) above, no patent license is granted: (1)�for any code that Contributor has deleted from the Contributor Version; (2)�for infringements caused by: (i)�third party modifications of Contributor Version, or (ii)�the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or (3)�under Patent Claims infringed by Covered Software in the absence of Modifications made by that Contributor. + +3. Distribution Obligations. + +3.1. Availability of Source Code. + +Any Covered Software that You distribute or otherwise make available in Executable form must also be made available in Source Code form and that Source Code form must be distributed only under the terms of this License. You must include a copy of this License with every copy of the Source Code form of the Covered Software You distribute or otherwise make available. You must inform recipients of any such Covered Software in Executable form as to how they can obtain such Covered Software in Source Code form in a reasonable manner on or through a medium customarily used for software exchange. + +3.2. Modifications. + +The Modifications that You create or to which You contribute are governed by the terms of this License. You represent that You believe Your Modifications are Your original creation(s) and/or You have sufficient rights to grant the rights conveyed by this License. + +3.3. Required Notices. +You must include a notice in each of Your Modifications that identifies You as the Contributor of the Modification. You may not remove or alter any copyright, patent or trademark notices contained within the Covered Software, or any notices of licensing or any descriptive text giving attribution to any Contributor or the Initial Developer. + +3.4. Application of Additional Terms. +You may not offer or impose any terms on any Covered Software in Source Code form that alters or restricts the applicable version of this License or the recipients rights hereunder. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, you may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear that any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer. + +3.5. Distribution of Executable Versions. +You may distribute the Executable form of the Covered Software under the terms of this License or under the terms of a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable form does not attempt to limit or alter the recipients rights in the Source Code form from the rights set forth in this License. If You distribute the Covered Software in Executable form under a different license, You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer. + +3.6. Larger Works. +You may create a Larger Work by combining Covered Software with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Software. + +4. Versions of the License. + +4.1. New Versions. +Sun Microsystems, Inc. is the initial license steward and may publish revised and/or new versions of this License from time to time. Each version will be given a distinguishing version number. Except as provided in Section 4.3, no one other than the license steward has the right to modify this License. + +4.2. Effect of New Versions. + +You may always continue to use, distribute or otherwise make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. If the Initial Developer includes a notice in the Original Software prohibiting it from being distributed or otherwise made available under any subsequent version of the License, You must distribute and make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. Otherwise, You may also choose to use, distribute or otherwise make the Covered Software available under the terms of any subsequent version of the License published by the license steward. +4.3. Modified Versions. + +When You are an Initial Developer and You want to create a new license for Your Original Software, You may create and use a modified version of this License if You: (a)�rename the license and remove any references to the name of the license steward (except to note that the license differs from this License); and (b)�otherwise make it clear that the license contains terms which differ from this License. + +5. DISCLAIMER OF WARRANTY. + +COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN AS IS BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + +6. TERMINATION. + +6.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive. + +6.2. If You assert a patent infringement claim (excluding declaratory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You assert such claim is referred to as Participant) alleging that the Participant Software (meaning the Contributor Version where the Participant is a Contributor or the Original Software where the Participant is the Initial Developer) directly or indirectly infringes any patent, then any and all rights granted directly or indirectly to You by such Participant, the Initial Developer (if the Initial Developer is not the Participant) and all Contributors under Sections�2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively and automatically at the expiration of such 60 day notice period, unless if within such 60 day period You withdraw Your claim with respect to the Participant Software against such Participant either unilaterally or pursuant to a written agreement with Participant. + +6.3. In the event of termination under Sections�6.1 or 6.2 above, all end user licenses that have been validly granted by You or any distributor hereunder prior to termination (excluding licenses granted to You by any distributor) shall survive termination. + +7. LIMITATION OF LIABILITY. + +UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTYS NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. + +8. U.S. GOVERNMENT END USERS. + +The Covered Software is a commercial item, as that term is defined in 48�C.F.R.�2.101 (Oct. 1995), consisting of commercial computer software (as that term is defined at 48 C.F.R. �252.227-7014(a)(1)) and commercial computer software documentation as such terms are used in 48�C.F.R.�12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Software with only those rights set forth herein. This U.S. Government Rights clause is in lieu of, and supersedes, any other FAR, DFAR, or other clause or provision that addresses Government rights in computer software under this License. + +9. MISCELLANEOUS. + +This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by the law of the jurisdiction specified in a notice contained within the Original Software (except to the extent applicable law, if any, provides otherwise), excluding such jurisdictions conflict-of-law provisions. Any litigation relating to this License shall be subject to the jurisdiction of the courts located in the jurisdiction and venue specified in a notice contained within the Original Software, with the losing party responsible for costs, including, without limitation, court costs and reasonable attorneys fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. You agree that You alone are responsible for compliance with the United States export administration regulations (and the export control laws and regulation of any other countries) when You use, distribute or otherwise make available any Covered Software. + +10. RESPONSIBILITY FOR CLAIMS. + +As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability. + +NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) +The GlassFish code released under the CDDL shall be governed by the laws of the State of California (excluding conflict-of-law provisions). Any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California and the state courts of the State of California, with venue lying in Santa Clara County, California. + + + diff --git a/google-http-client-assembly/dependencies/xpp3_LICENSE.txt b/google-http-client-assembly/licenses/xpp3_LICENSE.txt similarity index 100% rename from google-http-client-assembly/dependencies/xpp3_LICENSE.txt rename to google-http-client-assembly/licenses/xpp3_LICENSE.txt diff --git a/google-http-client-assembly/pom.xml b/google-http-client-assembly/pom.xml index e89325431..6df92301b 100644 --- a/google-http-client-assembly/pom.xml +++ b/google-http-client-assembly/pom.xml @@ -4,12 +4,12 @@ com.google.http-client google-http-client-parent - 1.26.1-SNAPSHOT + 2.0.3-SNAPSHOT ../pom.xml com.google.http-client google-http-client-assembly - 1.26.1-SNAPSHOT + 2.0.3-SNAPSHOT pom Assembly for the Google HTTP Client Library for Java @@ -18,25 +18,21 @@ com.google.http-client google-http-client - - com.google.http-client - google-http-client-appengine - com.google.http-client google-http-client-android com.google.http-client - google-http-client-protobuf + google-http-client-apache-v2 com.google.http-client - google-http-client-gson + google-http-client-appengine com.google.http-client - google-http-client-jackson + google-http-client-gson com.google.http-client @@ -44,7 +40,7 @@ com.google.http-client - google-http-client-jdo + google-http-client-protobuf com.google.http-client @@ -53,19 +49,6 @@ - - - - maven-assembly-plugin - - - assembly.xml - - google-http-java-client - - - - maven-dependency-plugin @@ -79,7 +62,7 @@ false ${project.build.directory}/libs - android,opengl-api,xmlParserAPIs,commons-codec,json + android,opengl-api,xmlParserAPIs,json @@ -92,7 +75,7 @@ sources false ${project.build.directory}/libs-sources - android,opengl-api,xmlParserAPIs,commons-codec,json + android,opengl-api,xmlParserAPIs,json @@ -108,6 +91,12 @@ + + + assembly.xml + + google-http-java-client + diff --git a/google-http-client-assembly/properties/google-http-client-apache-v2.jar.properties b/google-http-client-assembly/properties/google-http-client-apache-v2.jar.properties new file mode 100644 index 000000000..e1d1f0f97 --- /dev/null +++ b/google-http-client-assembly/properties/google-http-client-apache-v2.jar.properties @@ -0,0 +1 @@ +src=../libs-sources/google-http-client-apache-v2-${project.version}-sources.jar diff --git a/google-http-client-assembly/properties/google-http-client-jackson.jar.properties b/google-http-client-assembly/properties/google-http-client-jackson.jar.properties deleted file mode 100644 index a02a3062f..000000000 --- a/google-http-client-assembly/properties/google-http-client-jackson.jar.properties +++ /dev/null @@ -1 +0,0 @@ -src=../libs-sources/google-http-client-jackson-${project.version}-sources.jar diff --git a/google-http-client-assembly/properties/google-http-client-jdo.jar.properties b/google-http-client-assembly/properties/google-http-client-jdo.jar.properties deleted file mode 100644 index 60dae42e1..000000000 --- a/google-http-client-assembly/properties/google-http-client-jdo.jar.properties +++ /dev/null @@ -1 +0,0 @@ -src=../libs-sources/google-http-client-jdo-${project.version}-sources.jar diff --git a/google-http-client-assembly/android-properties/gson-2.1.jar.properties b/google-http-client-assembly/properties/gson.jar.properties similarity index 100% rename from google-http-client-assembly/android-properties/gson-2.1.jar.properties rename to google-http-client-assembly/properties/gson.jar.properties diff --git a/google-http-client-assembly/android-properties/jackson-core-2.9.6.jar.properties b/google-http-client-assembly/properties/jackson-core.jar.properties similarity index 100% rename from google-http-client-assembly/android-properties/jackson-core-2.9.6.jar.properties rename to google-http-client-assembly/properties/jackson-core.jar.properties diff --git a/google-http-client-assembly/android-properties/protobuf-java-2.6.1.jar.properties b/google-http-client-assembly/properties/protobuf-java.jar.properties similarity index 100% rename from google-http-client-assembly/android-properties/protobuf-java-2.6.1.jar.properties rename to google-http-client-assembly/properties/protobuf-java.jar.properties diff --git a/google-http-client-assembly/readme.html b/google-http-client-assembly/readme.html index 32eff5f54..8a146df1e 100644 --- a/google-http-client-assembly/readme.html +++ b/google-http-client-assembly/readme.html @@ -20,7 +20,7 @@

Overview

Dependencies and Licenses

The license can be found here. -
Dependent jars can be found in the +
Dependency jars can be found in the libs folder and the corresponding source jars can be found in the libs-sources folder. @@ -41,8 +41,6 @@

Dependencies and Licenses

href='dependencies/google-http-client-jackson-dependencies.html'>google-http-client-jackson-dependencies.html
  • google-http-client-jackson2: google-http-client-jackson2-dependencies.html
  • -
  • google-http-client-jdo: google-http-client-jdo-dependencies.html
  • google-http-client-xml: google-http-client-xml-dependencies.html
  • @@ -51,12 +49,6 @@

    Maven Usage

    For information on how to add these libraries to your Maven project please see https://developers.google.com/api-client-library/java/google-http-java-client/setup#maven. -

    Eclipse

    - A .classpath file snippet that can be included in your project's .classpath - has been provided - here. Please only use the classpathentry's you - actually need (see below for details). -

    ProGuard

    A ProGuard configuration file proguard-google-http-client.txt @@ -104,11 +96,6 @@

    Dependencies for all Platforms

  • jackson-core-asl-${project.jackson-core-asl.version}.jar
  • -
  • google-http-client-jdo-${project.version}.jar (when using JDO) -
      -
    • jdo2-api-${project.jdo2-api.version}.jar
    • -
    -
  • google-http-client-xml-${project.version}.jar (when using XML)
    • xpp3-${project.xpp3.version}.jar (when NOT on Android)
    • @@ -117,9 +104,9 @@

      Dependencies for all Platforms

    Android Dependencies

    - The following are the jars from the - libs folder required for Android applications or a newer - compatible version: + The following jars from the + libs folder or newer compatible versions + are required for Android applications:
    • google-http-client-android-${project.version}.jar
    @@ -131,9 +118,9 @@

    Android Dependencies

    wiki for the Android Developer's Guide.

    Google App Engine Dependencies

    - The following are the jars from the - libs folder required for Google App Engine applications or - a newer compatible version: + The following jars from the + libs folder or newer compatible versions + are required for Google App Engine applications.
    • google-http-client-appengine-${project.version}.jar
    @@ -142,14 +129,14 @@

    Google App Engine Dependencies

    href='https://developers.google.com/api-client-library/java/google-http-java-client/app-engine'>GoogleAppEngine wiki for the Google App Engine Developer's Guide. -

    General Purpose Java 5 Environment Dependencies

    - The following are the jars from the - libs folder required for general purpose Java 5 - applications or a newer compatible version: +

    General Purpose Java Environment Dependencies

    + The following jars from the + libs folder or newer compatible versions are + required for general purpose Java applications :
    • commons-logging-${project.commons-logging.version}.jar
    • -
    • httpclient-${project.httpclient.version}.jar
    • -
    • httpcore-${project.httpcore.version}.jar
    • +
    • httpclient-${project.apache-httpclient-4.version}.jar
    • +
    • httpcore-${project.apache-httpcore-4.version}.jar
    diff --git a/google-http-client-bom/README.md b/google-http-client-bom/README.md new file mode 100644 index 000000000..53c4a9c57 --- /dev/null +++ b/google-http-client-bom/README.md @@ -0,0 +1,28 @@ +# Google HTTP Client Library Bill of Materials + +The `google-http-client-bom` modules is a pom that can be used to import consistent +versions of `google-http-client` components plus its dependencies. + +To use it in Maven, add the following to your `pom.xml`: + +[//]: # ({x-version-update-start:google-http-client-bom:released}) +```xml + + + + com.google.http-client + google-http-client-bom + 1.39.0 + pom + import + + + +``` +[//]: # ({x-version-update-end}) + +## License + +Apache 2.0 - See [LICENSE] for more information. + +[LICENSE]: https://github.com/googleapis/google-http-java-client/blob/master/LICENSE diff --git a/google-http-client-bom/pom.xml b/google-http-client-bom/pom.xml new file mode 100644 index 000000000..c216762b0 --- /dev/null +++ b/google-http-client-bom/pom.xml @@ -0,0 +1,241 @@ + + + 4.0.0 + com.google.http-client + google-http-client-bom + 2.0.3-SNAPSHOT + pom + + Google HTTP Client Library for Java BOM + https://github.com/googleapis/google-http-java-client/tree/master/google-http-client-bom + + BOM for Google HTTP Client Library for Java + + + + Google LLC + + + + + chingor13 + Jeff Ching + chingor@google.com + Google LLC + + Developer + + + + + + scm:git:https://github.com/googleapis/google-http-java-client.git + scm:git:git@github.com:googleapis/google-http-java-client.git + https://github.com/googleapis/google-http-java-client + + + + + sonatype-nexus-snapshots + https://google.oss.sonatype.org/content/repositories/snapshots + + + sonatype-nexus-staging + https://google.oss.sonatype.org/service/local/staging/deploy/maven2/ + + + github-pages-site + Deployment through GitHub's site deployment plugin + site/google-cloud-bom + + + + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + + + com.google.http-client + google-http-client + 2.0.3-SNAPSHOT + + + com.google.http-client + google-http-client-android + 2.0.3-SNAPSHOT + + + com.google.http-client + google-http-client-apache-v2 + 2.0.3-SNAPSHOT + + + com.google.http-client + google-http-client-apache-v5 + 2.0.3-SNAPSHOT + + + com.google.http-client + google-http-client-appengine + 2.0.3-SNAPSHOT + + + com.google.http-client + google-http-client-findbugs + 2.0.3-SNAPSHOT + + + com.google.http-client + google-http-client-gson + 2.0.3-SNAPSHOT + + + com.google.http-client + google-http-client-jackson2 + 2.0.3-SNAPSHOT + + + com.google.http-client + google-http-client-protobuf + 2.0.3-SNAPSHOT + + + com.google.http-client + google-http-client-test + 2.0.3-SNAPSHOT + + + com.google.http-client + google-http-client-xml + 2.0.3-SNAPSHOT + + + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.7.0 + true + + sonatype-nexus-staging + https://google.oss.sonatype.org/ + false + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.11.2 + + true + + + + org.apache.maven.plugins + maven-site-plugin + 3.21.0 + + true + + + + com.coveo + fmt-maven-plugin + 2.13 + + true + + + + + + + + + release-sonatype + + + + !artifact-registry-url + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + + + + + + + release-gcp-artifact-registry + + artifactregistry://undefined-artifact-registry-url-value + + + + gcp-artifact-registry-repository + ${artifact-registry-url} + + + gcp-artifact-registry-repository + ${artifact-registry-url} + + + + + release-sign-artifacts + + + performRelease + true + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 3.2.7 + + + sign-artifacts + verify + + sign + + + + --pinentry-mode + loopback + + + + + + + + + + diff --git a/google-http-client-findbugs/google-http-client-findbugs-test/pom.xml b/google-http-client-findbugs/google-http-client-findbugs-test/pom.xml index 09ea2382c..461d1241a 100644 --- a/google-http-client-findbugs/google-http-client-findbugs-test/pom.xml +++ b/google-http-client-findbugs/google-http-client-findbugs-test/pom.xml @@ -11,15 +11,15 @@ maven-compiler-plugin - 2.3.2 + 3.13.0 - 1.5 - 1.5 + 1.7 + 1.7 maven-jar-plugin - 2.3.1 + 3.4.2 @@ -32,7 +32,7 @@ org.codehaus.mojo findbugs-maven-plugin - 2.5.2 + 3.0.5 diff --git a/google-http-client-findbugs/google-http-client-findbugs-test/src/main/java/com/google/api/client/findbugs/test/BetaClass.java b/google-http-client-findbugs/google-http-client-findbugs-test/src/main/java/com/google/api/client/findbugs/test/BetaClass.java index bd2ddb50e..13ed288cc 100644 --- a/google-http-client-findbugs/google-http-client-findbugs-test/src/main/java/com/google/api/client/findbugs/test/BetaClass.java +++ b/google-http-client-findbugs/google-http-client-findbugs-test/src/main/java/com/google/api/client/findbugs/test/BetaClass.java @@ -20,8 +20,7 @@ @Beta public class BetaClass { - public void method() { - } + public void method() {} @Beta public void betaMethod() { @@ -36,8 +35,7 @@ public BetaClass() { int field; - @Beta - int betaField; + @Beta int betaField; @Override public String toString() { diff --git a/google-http-client-findbugs/google-http-client-findbugs-test/src/main/java/com/google/api/client/findbugs/test/ClassWithBetaField.java b/google-http-client-findbugs/google-http-client-findbugs-test/src/main/java/com/google/api/client/findbugs/test/ClassWithBetaField.java index ea1c5315c..4ceb1cb17 100644 --- a/google-http-client-findbugs/google-http-client-findbugs-test/src/main/java/com/google/api/client/findbugs/test/ClassWithBetaField.java +++ b/google-http-client-findbugs/google-http-client-findbugs-test/src/main/java/com/google/api/client/findbugs/test/ClassWithBetaField.java @@ -19,12 +19,10 @@ /** A class which contains {@link Beta} fields. */ public class ClassWithBetaField { - @Beta - public int betaField; + @Beta public int betaField; public int field; - @Beta - public static final int betaStaticField = 10; + @Beta public static final int betaStaticField = 10; public static final int staticField = 20; public ClassWithBetaField() { diff --git a/google-http-client-findbugs/google-http-client-findbugs-test/src/main/java/com/google/api/client/findbugs/test/ClassWithBetaMethod.java b/google-http-client-findbugs/google-http-client-findbugs-test/src/main/java/com/google/api/client/findbugs/test/ClassWithBetaMethod.java index 85082217b..3c80ee4be 100644 --- a/google-http-client-findbugs/google-http-client-findbugs-test/src/main/java/com/google/api/client/findbugs/test/ClassWithBetaMethod.java +++ b/google-http-client-findbugs/google-http-client-findbugs-test/src/main/java/com/google/api/client/findbugs/test/ClassWithBetaMethod.java @@ -19,8 +19,7 @@ /** A class which contains {@link Beta} methods. */ public class ClassWithBetaMethod { - @Beta - int betaField = 10; + @Beta int betaField = 10; @Beta public void betaMethod() { diff --git a/google-http-client-findbugs/pom.xml b/google-http-client-findbugs/pom.xml index 719b36eeb..cb7a614b0 100644 --- a/google-http-client-findbugs/pom.xml +++ b/google-http-client-findbugs/pom.xml @@ -4,47 +4,55 @@ com.google.http-client google-http-client-parent - 1.26.1-SNAPSHOT + 2.0.3-SNAPSHOT ../pom.xml google-http-client-findbugs - 1.26.1-SNAPSHOT + 2.0.3-SNAPSHOT Google APIs Client Library Findbugs custom plugin. + + + + org.codehaus.mojo + animal-sniffer-maven-plugin + + true + + + + maven-source-plugin - - - source-jar - compile - - jar - - - - + - org.codehaus.mojo - animal-sniffer-maven-plugin - - true - + org.apache.maven.plugins + maven-dependency-plugin + + + com.google.http-client:google-http-client + + + maven-jar-plugin + + + + com.google.api.client.findbugs + + + + - - com.google.http-client - google-http-client - com.google.code.findbugs findbugs - 2.0.0 + 3.0.1 xalan @@ -60,5 +68,14 @@ + + com.google.code.findbugs + bcel-findbugs + 6.0 + + + com.google.http-client + google-http-client + diff --git a/google-http-client-findbugs/src/main/java/com/google/api/client/findbugs/BetaDetector.java b/google-http-client-findbugs/src/main/java/com/google/api/client/findbugs/BetaDetector.java index 97e138be3..791d37f39 100644 --- a/google-http-client-findbugs/src/main/java/com/google/api/client/findbugs/BetaDetector.java +++ b/google-http-client-findbugs/src/main/java/com/google/api/client/findbugs/BetaDetector.java @@ -15,7 +15,6 @@ package com.google.api.client.findbugs; import com.google.api.client.util.Beta; - import edu.umd.cs.findbugs.BugInstance; import edu.umd.cs.findbugs.BugReporter; import edu.umd.cs.findbugs.bcel.OpcodeStackDetector; @@ -36,19 +35,13 @@ public class BetaDetector extends OpcodeStackDetector { /** Beta annotation "signature". */ private static final String BETA_ANNOTATION = "Lcom/google/api/client/util/Beta;"; - /** - * A message indicating there is a usage of a method annotated with Beta annotation. - */ + /** A message indicating there is a usage of a method annotated with Beta annotation. */ private static final String BETA_METHOD_USAGE = "BETA_METHOD_USAGE"; - /** - * A message indicating there is a usage of a field annotated with Beta annotation. - */ + /** A message indicating there is a usage of a field annotated with Beta annotation. */ private static final String BETA_FIELD_USAGE = "BETA_FIELD_USAGE"; - /** - * A message indicating there is a usage of a class annotated with Beta annotation. - */ + /** A message indicating there is a usage of a class annotated with Beta annotation. */ private static final String BETA_CLASS_USAGE = "BETA_CLASS_USAGE"; /** The bug reporter is used to report errors. */ @@ -96,7 +89,7 @@ public void sawOpcode(int seen) { break; default: - // DO NOTHING + // DO NOTHING } } @@ -115,9 +108,7 @@ private static boolean isBeta(AnnotationEntry[] annotationEntries) { * class, it's not {@link Beta} and it doesn't appear in other {@link Beta} section. Otherwise * returns {@code null}. * - *

    - * Reports a bug in case the class is {@link Beta}. - *

    + *

    Reports a bug in case the class is {@link Beta}. */ private JavaClass checkClass() { // TODO(peleyal): check if caching the beta state of every class could improve @@ -167,9 +158,7 @@ private JavaClass getSuperclass(JavaClass javaClass) { /** * Reports bug in case the method defined by the given name and signature is {@link Beta}. * - *

    - * The method is searched in current class and all super classses as well. - *

    + *

    The method is searched in current class and all super classses as well. */ private void checkMethod(String methodName, String signature) { JavaClass javaClass = checkClass(); @@ -197,9 +186,7 @@ private void checkMethod(String methodName, String signature) { /** * Reports bug in case the field defined by the given name is {@link Beta}. * - *

    - * The field is searched in current class and all super classses as well. - *

    + *

    The field is searched in current class and all super classses as well. */ private void checkField(String fieldName) { JavaClass javaClass = checkClass(); diff --git a/google-http-client-findbugs/src/main/java/com/google/api/client/findbugs/package-info.java b/google-http-client-findbugs/src/main/java/com/google/api/client/findbugs/package-info.java index c22541fb6..83168807c 100644 --- a/google-http-client-findbugs/src/main/java/com/google/api/client/findbugs/package-info.java +++ b/google-http-client-findbugs/src/main/java/com/google/api/client/findbugs/package-info.java @@ -15,28 +15,26 @@ /** * Findbugs package which supports custom Google APIs Client library findbugs Plugins. * - * Usage on pom.xml: + *

    Usage on pom.xml: * *

    -  <plugin>
    -    <groupId>org.codehaus.mojo</groupId>
    -    <artifactId>findbugs-maven-plugin</artifactId>
    -    ...
    -    <configuration>
    -      <plugins>
    -        <plugin>
    -          <groupId>com.google.http-client</groupId>
    -          <artifactId>google-http-client-findbugs</artifactId>
    -          <version>${project.http.version}</version>
    -        </plugin>
    -       </plugins>
    -    </configuration>
    -    ...
    -  </plugin>
    + * <plugin>
    + * <groupId>org.codehaus.mojo</groupId>
    + * <artifactId>findbugs-maven-plugin</artifactId>
    + * ...
    + * <configuration>
    + * <plugins>
    + * <plugin>
    + * <groupId>com.google.http-client</groupId>
    + * <artifactId>google-http-client-findbugs</artifactId>
    + * <version>${project.http.version}</version>
    + * </plugin>
    + * </plugins>
    + * </configuration>
    + * ...
    + * </plugin>
      * 
    * * @author Eyal Peled */ - package com.google.api.client.findbugs; - diff --git a/google-http-client-gson/pom.xml b/google-http-client-gson/pom.xml index 443c23aef..1a01263a2 100644 --- a/google-http-client-gson/pom.xml +++ b/google-http-client-gson/pom.xml @@ -4,11 +4,11 @@ com.google.http-client google-http-client-parent - 1.26.1-SNAPSHOT + 2.0.3-SNAPSHOT ../pom.xml google-http-client-gson - 1.26.1-SNAPSHOT + 2.0.3-SNAPSHOT GSON extensions to the Google HTTP Client Library for Java. @@ -17,8 +17,8 @@ maven-javadoc-plugin - http://download.oracle.com/javase/6/docs/api/ - https://www.javadoc.io/doc/com.google.code.gson/gson/${project.gson.version} + http://download.oracle.com/javase/7/docs/api/ + https://static.javadoc.io/com.google.code.gson/gson/${project.gson.version} ${project.name} ${project.version} ${project.artifactId} ${project.version} @@ -26,20 +26,11 @@
    maven-source-plugin - - - source-jar - compile - - jar - - - org.codehaus.mojo build-helper-maven-plugin - 1.5 + 3.6.0 add-test-source @@ -55,6 +46,16 @@ + + maven-jar-plugin + + + + com.google.api.client.json.gson + + + +
    @@ -67,18 +68,13 @@ google-http-client-test test - - junit - junit - test - com.google.code.gson gson - com.google.guava - guava + junit + junit test diff --git a/google-http-client-gson/src/main/java/com/google/api/client/json/gson/GsonFactory.java b/google-http-client-gson/src/main/java/com/google/api/client/json/gson/GsonFactory.java index b604a8414..6c89dd16e 100644 --- a/google-http-client-gson/src/main/java/com/google/api/client/json/gson/GsonFactory.java +++ b/google-http-client-gson/src/main/java/com/google/api/client/json/gson/GsonFactory.java @@ -18,10 +18,8 @@ import com.google.api.client.json.JsonGenerator; import com.google.api.client.json.JsonParser; import com.google.api.client.util.Beta; -import com.google.api.client.util.Charsets; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; - import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; @@ -30,14 +28,13 @@ import java.io.StringReader; import java.io.Writer; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; /** * Low-level JSON library implementation based on GSON. * - *

    - * Implementation is thread-safe, and sub-classes must be thread-safe. For maximum efficiency, + *

    Implementation is thread-safe, and sub-classes must be thread-safe. For maximum efficiency, * applications should use a single globally-shared instance of the JSON factory. - *

    * * @since 1.3 * @author Yaniv Inbar @@ -45,7 +42,7 @@ public class GsonFactory extends JsonFactory { /** - * {@link Beta}
    + * {@link Beta}
    * Returns a global thread-safe instance. * * @since 1.16 @@ -55,17 +52,26 @@ public static GsonFactory getDefaultInstance() { return InstanceHolder.INSTANCE; } + /** Controls the behavior of leniency in reading JSON value */ + private boolean readLeniency = false; + /** Holder for the result of {@link #getDefaultInstance()}. */ @Beta static class InstanceHolder { static final GsonFactory INSTANCE = new GsonFactory(); } + // Keeping the default, non-arg constructor for backward compatibility. Users should use + // getDefaultInstance() or builder() instead. + public GsonFactory() {} + + private GsonFactory(Builder builder) { + readLeniency = builder.readLeniency; + } + @Override public JsonParser createJsonParser(InputStream in) { - // TODO(mlinder): Parser should try to detect the charset automatically when using GSON - // https://github.com/googleapis/google-http-java-client/issues/6 - return createJsonParser(new InputStreamReader(in, Charsets.UTF_8)); + return createJsonParser(new InputStreamReader(in, StandardCharsets.UTF_8)); } @Override @@ -95,4 +101,36 @@ public JsonGenerator createJsonGenerator(OutputStream out, Charset enc) { public JsonGenerator createJsonGenerator(Writer writer) { return new GsonGenerator(this, new JsonWriter(writer)); } + + /** Returns true if it is lenient to input JSON value. */ + boolean getReadLeniency() { + return readLeniency; + } + + /** Returns the builder * */ + public static Builder builder() { + return new Builder(); + } + + /** Builder for GsonFactory. */ + public static final class Builder { + // Do not directly call this constructor + private Builder() {} + + private boolean readLeniency = false; + + /** + * Set to {@code true} when you want to the JSON parser to be lenient to reading JSON value. By + * default, it is {@code false}. + */ + public Builder setReadLeniency(boolean readLeniency) { + this.readLeniency = readLeniency; + return this; + } + + /** Builds GsonFactory instance. */ + public GsonFactory build() { + return new GsonFactory(this); + } + } } diff --git a/google-http-client-gson/src/main/java/com/google/api/client/json/gson/GsonGenerator.java b/google-http-client-gson/src/main/java/com/google/api/client/json/gson/GsonGenerator.java index 73ac802e1..9fa561019 100644 --- a/google-http-client-gson/src/main/java/com/google/api/client/json/gson/GsonGenerator.java +++ b/google-http-client-gson/src/main/java/com/google/api/client/json/gson/GsonGenerator.java @@ -17,7 +17,6 @@ import com.google.api.client.json.JsonFactory; import com.google.api.client.json.JsonGenerator; import com.google.gson.stream.JsonWriter; - import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; @@ -25,9 +24,7 @@ /** * Low-level JSON serializer implementation based on GSON. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @author Yaniv Inbar */ diff --git a/google-http-client-gson/src/main/java/com/google/api/client/json/gson/GsonParser.java b/google-http-client-gson/src/main/java/com/google/api/client/json/gson/GsonParser.java index d7bbee1aa..cee6fadc4 100644 --- a/google-http-client-gson/src/main/java/com/google/api/client/json/gson/GsonParser.java +++ b/google-http-client-gson/src/main/java/com/google/api/client/json/gson/GsonParser.java @@ -17,7 +17,6 @@ import com.google.api.client.json.JsonFactory; import com.google.api.client.json.JsonParser; import com.google.api.client.json.JsonToken; -import com.google.api.client.util.Preconditions; import com.google.gson.stream.JsonReader; import java.io.EOFException; import java.io.IOException; @@ -29,9 +28,7 @@ /** * Low-level JSON serializer implementation based on GSON. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @author Yaniv Inbar */ @@ -46,8 +43,7 @@ class GsonParser extends JsonParser { GsonParser(GsonFactory factory, JsonReader reader) { this.factory = factory; this.reader = reader; - // lenient to allow top-level values of any type - reader.setLenient(true); + reader.setLenient(factory.getReadLeniency()); } @Override @@ -71,57 +67,58 @@ public JsonFactory getFactory() { } @Override - public byte getByteValue() { + public byte getByteValue() throws IOException { checkNumber(); return Byte.parseByte(currentText); } @Override - public short getShortValue() { + public short getShortValue() throws IOException { checkNumber(); return Short.parseShort(currentText); } - @Override - public int getIntValue() { + public int getIntValue() throws IOException { checkNumber(); return Integer.parseInt(currentText); } @Override - public float getFloatValue() { + public float getFloatValue() throws IOException { checkNumber(); return Float.parseFloat(currentText); } @Override - public BigInteger getBigIntegerValue() { + public BigInteger getBigIntegerValue() throws IOException { checkNumber(); return new BigInteger(currentText); } @Override - public BigDecimal getDecimalValue() { + public BigDecimal getDecimalValue() throws IOException { checkNumber(); return new BigDecimal(currentText); } @Override - public double getDoubleValue() { + public double getDoubleValue() throws IOException { checkNumber(); return Double.parseDouble(currentText); } @Override - public long getLongValue() { + public long getLongValue() throws IOException { checkNumber(); return Long.parseLong(currentText); } - private void checkNumber() { - Preconditions.checkArgument( - currentToken == JsonToken.VALUE_NUMBER_INT || currentToken == JsonToken.VALUE_NUMBER_FLOAT); + private void checkNumber() throws IOException { + if (currentToken != JsonToken.VALUE_NUMBER_INT + && currentToken != JsonToken.VALUE_NUMBER_FLOAT) { + throw new IOException("Token is not a number"); + } } @Override @@ -194,8 +191,10 @@ public JsonToken nextToken() throws IOException { break; case NUMBER: currentText = reader.nextString(); - currentToken = currentText.indexOf('.') == -1 - ? JsonToken.VALUE_NUMBER_INT : JsonToken.VALUE_NUMBER_FLOAT; + currentToken = + currentText.indexOf('.') == -1 + ? JsonToken.VALUE_NUMBER_INT + : JsonToken.VALUE_NUMBER_FLOAT; break; case NAME: currentText = reader.nextName(); diff --git a/google-http-client-gson/src/main/java/com/google/api/client/json/gson/package-info.java b/google-http-client-gson/src/main/java/com/google/api/client/json/gson/package-info.java index 943323c45..a68f6c97c 100644 --- a/google-http-client-gson/src/main/java/com/google/api/client/json/gson/package-info.java +++ b/google-http-client-gson/src/main/java/com/google/api/client/json/gson/package-info.java @@ -19,6 +19,4 @@ * @since 1.3 * @author Yaniv Inbar */ - package com.google.api.client.json.gson; - diff --git a/google-http-client-gson/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-gson/reflect-config.json b/google-http-client-gson/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-gson/reflect-config.json new file mode 100644 index 000000000..8bb0eb474 --- /dev/null +++ b/google-http-client-gson/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-gson/reflect-config.json @@ -0,0 +1,22 @@ +[ + { + "name": "com.google.api.client.json.GenericJson", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.util.ArrayMap", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + } +] \ No newline at end of file diff --git a/google-http-client-gson/src/test/java/com/google/api/client/json/gson/GsonFactoryTest.java b/google-http-client-gson/src/test/java/com/google/api/client/json/gson/GsonFactoryTest.java index 1dd970070..b256f74f5 100644 --- a/google-http-client-gson/src/test/java/com/google/api/client/json/gson/GsonFactoryTest.java +++ b/google-http-client-gson/src/test/java/com/google/api/client/json/gson/GsonFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Google Inc. + * Copyright 2010 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -14,13 +14,22 @@ package com.google.api.client.json.gson; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import com.google.api.client.json.GenericJson; import com.google.api.client.json.JsonFactory; +import com.google.api.client.json.JsonObjectParser; import com.google.api.client.json.JsonParser; import com.google.api.client.test.json.AbstractJsonFactoryTest; -import com.google.common.base.Charsets; - +import com.google.gson.stream.MalformedJsonException; import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import org.junit.Test; /** * Tests {@link GsonFactory}. @@ -33,27 +42,40 @@ public class GsonFactoryTest extends AbstractJsonFactoryTest { private static final String JSON_ENTRY_PRETTY = "{" + GSON_LINE_SEPARATOR + " \"title\": \"foo\"" + GSON_LINE_SEPARATOR + "}"; - private static final String JSON_FEED_PRETTY = "{" + GSON_LINE_SEPARATOR + " \"entries\": [" - + GSON_LINE_SEPARATOR + " {" + GSON_LINE_SEPARATOR + " \"title\": \"foo\"" - + GSON_LINE_SEPARATOR + " }," + GSON_LINE_SEPARATOR + " {" - + GSON_LINE_SEPARATOR + " \"title\": \"bar\"" + GSON_LINE_SEPARATOR + " }" - + GSON_LINE_SEPARATOR + " ]" + GSON_LINE_SEPARATOR + "}"; - - public GsonFactoryTest(String name) { - super(name); - } + private static final String JSON_FEED_PRETTY = + "{" + + GSON_LINE_SEPARATOR + + " \"entries\": [" + + GSON_LINE_SEPARATOR + + " {" + + GSON_LINE_SEPARATOR + + " \"title\": \"foo\"" + + GSON_LINE_SEPARATOR + + " }," + + GSON_LINE_SEPARATOR + + " {" + + GSON_LINE_SEPARATOR + + " \"title\": \"bar\"" + + GSON_LINE_SEPARATOR + + " }" + + GSON_LINE_SEPARATOR + + " ]" + + GSON_LINE_SEPARATOR + + "}"; @Override protected JsonFactory newFactory() { return new GsonFactory(); } + @Test public final void testToPrettyString_entry() throws Exception { Entry entry = new Entry(); entry.title = "foo"; assertEquals(JSON_ENTRY_PRETTY, newFactory().toPrettyString(entry)); } + @Test public final void testToPrettyString_Feed() throws Exception { Feed feed = new Feed(); Entry entryFoo = new Entry(); @@ -66,10 +88,49 @@ public final void testToPrettyString_Feed() throws Exception { assertEquals(JSON_FEED_PRETTY, newFactory().toPrettyString(feed)); } - public final void testParse_directValue() throws Exception { - byte[] jsonData = Charsets.UTF_8.encode("123").array(); - JsonParser jp = - newFactory().createJsonParser(new ByteArrayInputStream(jsonData), Charsets.UTF_8); - assertEquals(123, jp.parse(Integer.class, true)); + @Test + public final void testParse_directValue() throws IOException { + JsonParser parser = newFactory().createJsonParser("123"); + assertEquals(123, parser.parse(Integer.class, true)); + } + + @Test + public final void testGetByteValue() throws IOException { + JsonParser parser = newFactory().createJsonParser("123"); + + try { + parser.getByteValue(); + fail("should throw IOException"); + } catch (IOException ex) { + assertNotNull(ex.getMessage()); + } + } + + @Test + public final void testReaderLeniency_lenient() throws IOException { + JsonObjectParser parser = + new JsonObjectParser(GsonFactory.builder().setReadLeniency(true).build()); + + // This prefix in JSON body is used to prevent Cross-site script inclusion (XSSI). + InputStream inputStream = + new ByteArrayInputStream((")]}'\n" + JSON_ENTRY_PRETTY).getBytes(StandardCharsets.UTF_8)); + GenericJson json = parser.parseAndClose(inputStream, StandardCharsets.UTF_8, GenericJson.class); + + assertEquals("foo", json.get("title")); + } + + @Test + public final void testReaderLeniency_not_lenient_by_default() throws IOException { + JsonObjectParser parser = new JsonObjectParser(GsonFactory.getDefaultInstance()); + + try { + // This prefix in JSON body is used to prevent Cross-site script inclusion (XSSI). + InputStream inputStream = + new ByteArrayInputStream((")]}'\n" + JSON_ENTRY_PRETTY).getBytes(StandardCharsets.UTF_8)); + parser.parseAndClose(inputStream, StandardCharsets.UTF_8, GenericJson.class); + fail("The read leniency should fail the JSON input with XSSI prefix."); + } catch (MalformedJsonException ex) { + assertNotNull(ex.getMessage()); + } } } diff --git a/google-http-client-gson/src/test/java/com/google/api/client/json/gson/GsonGeneratorTest.java b/google-http-client-gson/src/test/java/com/google/api/client/json/gson/GsonGeneratorTest.java new file mode 100644 index 000000000..caa543836 --- /dev/null +++ b/google-http-client-gson/src/test/java/com/google/api/client/json/gson/GsonGeneratorTest.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.json.gson; + +import com.google.api.client.json.JsonGenerator; +import com.google.api.client.test.json.AbstractJsonGeneratorTest; +import java.io.IOException; +import java.io.Writer; + +public class GsonGeneratorTest extends AbstractJsonGeneratorTest { + + private static final GsonFactory FACTORY = new GsonFactory(); + + @Override + protected JsonGenerator newGenerator(Writer writer) throws IOException { + return FACTORY.createJsonGenerator(writer); + } +} diff --git a/google-http-client-gson/src/test/java/com/google/api/client/json/gson/GsonParserTest.java b/google-http-client-gson/src/test/java/com/google/api/client/json/gson/GsonParserTest.java new file mode 100644 index 000000000..aa5ed5aea --- /dev/null +++ b/google-http-client-gson/src/test/java/com/google/api/client/json/gson/GsonParserTest.java @@ -0,0 +1,25 @@ +/** + * 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 + * + *

    https://www.apache.org/licenses/LICENSE-2.0 + * + *

    Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.api.client.json.gson; + +import com.google.api.client.json.JsonFactory; +import com.google.api.client.test.json.AbstractJsonParserTest; + +public class GsonParserTest extends AbstractJsonParserTest { + + @Override + protected JsonFactory newJsonFactory() { + return new GsonFactory(); + } +} diff --git a/google-http-client-gson/src/test/resources/META-INF/native-image/com.google.http-client/google-http-client-gson/native-image.properties b/google-http-client-gson/src/test/resources/META-INF/native-image/com.google.http-client/google-http-client-gson/native-image.properties new file mode 100644 index 000000000..3cbd2b27f --- /dev/null +++ b/google-http-client-gson/src/test/resources/META-INF/native-image/com.google.http-client/google-http-client-gson/native-image.properties @@ -0,0 +1 @@ +Args=--initialize-at-build-time=org.junit.runner.RunWith,java.lang.annotation.Annotation diff --git a/google-http-client-jackson/src/main/java/com/google/api/client/json/jackson/JacksonFactory.java b/google-http-client-jackson/src/main/java/com/google/api/client/json/jackson/JacksonFactory.java deleted file mode 100644 index 8f0b86348..000000000 --- a/google-http-client-jackson/src/main/java/com/google/api/client/json/jackson/JacksonFactory.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2010 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package com.google.api.client.json.jackson; - -import com.google.api.client.json.JsonFactory; -import com.google.api.client.json.JsonGenerator; -import com.google.api.client.json.JsonParser; -import com.google.api.client.json.JsonToken; -import com.google.api.client.util.Preconditions; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.Reader; -import java.io.Writer; -import java.nio.charset.Charset; - -/** - * Low-level JSON library implementation based on Jackson. - * - *

    - * Implementation is thread-safe, and sub-classes must be thread-safe. For maximum efficiency, - * applications should use a single globally-shared instance of the JSON factory. - *

    - * - * @since 1.3 - * @author Yaniv Inbar - */ -public final class JacksonFactory extends JsonFactory { - - /** JSON factory. */ - private final org.codehaus.jackson.JsonFactory factory = new org.codehaus.jackson.JsonFactory(); - { - // don't auto-close JSON content in order to ensure consistent behavior across JSON factories - // TODO(rmistry): Should we disable the JsonGenerator.Feature.AUTO_CLOSE_TARGET feature? - factory.configure(org.codehaus.jackson.JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT, false); - } - - @Override - public JsonGenerator createJsonGenerator(OutputStream out, Charset enc) throws IOException { - return new JacksonGenerator( - this, factory.createJsonGenerator(out, org.codehaus.jackson.JsonEncoding.UTF8)); - } - - @Override - public JsonGenerator createJsonGenerator(Writer writer) throws IOException { - return new JacksonGenerator(this, factory.createJsonGenerator(writer)); - } - - @Override - public JsonParser createJsonParser(Reader reader) throws IOException { - Preconditions.checkNotNull(reader); - return new JacksonParser(this, factory.createJsonParser(reader)); - } - - @Override - public JsonParser createJsonParser(InputStream in) throws IOException { - Preconditions.checkNotNull(in); - return new JacksonParser(this, factory.createJsonParser(in)); - } - - @Override - public JsonParser createJsonParser(InputStream in, Charset charset) throws IOException { - Preconditions.checkNotNull(in); - return new JacksonParser(this, factory.createJsonParser(in)); - } - - @Override - public JsonParser createJsonParser(String value) throws IOException { - Preconditions.checkNotNull(value); - return new JacksonParser(this, factory.createJsonParser(value)); - } - - static JsonToken convert(org.codehaus.jackson.JsonToken token) { - if (token == null) { - return null; - } - switch (token) { - case END_ARRAY: - return JsonToken.END_ARRAY; - case START_ARRAY: - return JsonToken.START_ARRAY; - case END_OBJECT: - return JsonToken.END_OBJECT; - case START_OBJECT: - return JsonToken.START_OBJECT; - case VALUE_FALSE: - return JsonToken.VALUE_FALSE; - case VALUE_TRUE: - return JsonToken.VALUE_TRUE; - case VALUE_NULL: - return JsonToken.VALUE_NULL; - case VALUE_STRING: - return JsonToken.VALUE_STRING; - case VALUE_NUMBER_FLOAT: - return JsonToken.VALUE_NUMBER_FLOAT; - case VALUE_NUMBER_INT: - return JsonToken.VALUE_NUMBER_INT; - case FIELD_NAME: - return JsonToken.FIELD_NAME; - default: - return JsonToken.NOT_AVAILABLE; - } - } -} diff --git a/google-http-client-jackson/src/main/java/com/google/api/client/json/jackson/JacksonGenerator.java b/google-http-client-jackson/src/main/java/com/google/api/client/json/jackson/JacksonGenerator.java deleted file mode 100644 index 27f8c9d34..000000000 --- a/google-http-client-jackson/src/main/java/com/google/api/client/json/jackson/JacksonGenerator.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (c) 2010 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package com.google.api.client.json.jackson; - -import com.google.api.client.json.JsonGenerator; - -import java.io.IOException; -import java.math.BigDecimal; -import java.math.BigInteger; - -/** - * Low-level JSON serializer implementation based on Jackson. - * - *

    - * Implementation is not thread-safe. - *

    - * - * @author Yaniv Inbar - */ -final class JacksonGenerator extends JsonGenerator { - private final org.codehaus.jackson.JsonGenerator generator; - private final JacksonFactory factory; - - @Override - public JacksonFactory getFactory() { - return factory; - } - - JacksonGenerator(JacksonFactory factory, org.codehaus.jackson.JsonGenerator generator) { - this.factory = factory; - this.generator = generator; - } - - @Override - public void flush() throws IOException { - generator.flush(); - } - - @Override - public void close() throws IOException { - generator.close(); - } - - @Override - public void writeBoolean(boolean state) throws IOException { - generator.writeBoolean(state); - } - - @Override - public void writeEndArray() throws IOException { - generator.writeEndArray(); - } - - @Override - public void writeEndObject() throws IOException { - generator.writeEndObject(); - } - - @Override - public void writeFieldName(String name) throws IOException { - generator.writeFieldName(name); - } - - @Override - public void writeNull() throws IOException { - generator.writeNull(); - } - - @Override - public void writeNumber(int v) throws IOException { - generator.writeNumber(v); - } - - @Override - public void writeNumber(long v) throws IOException { - generator.writeNumber(v); - } - - @Override - public void writeNumber(BigInteger v) throws IOException { - generator.writeNumber(v); - } - - @Override - public void writeNumber(double v) throws IOException { - generator.writeNumber(v); - } - - @Override - public void writeNumber(float v) throws IOException { - generator.writeNumber(v); - } - - @Override - public void writeNumber(BigDecimal v) throws IOException { - generator.writeNumber(v); - } - - @Override - public void writeNumber(String encodedValue) throws IOException { - generator.writeNumber(encodedValue); - } - - @Override - public void writeStartArray() throws IOException { - generator.writeStartArray(); - } - - @Override - public void writeStartObject() throws IOException { - generator.writeStartObject(); - } - - @Override - public void writeString(String value) throws IOException { - generator.writeString(value); - } - - @Override - public void enablePrettyPrint() throws IOException { - generator.useDefaultPrettyPrinter(); - } -} diff --git a/google-http-client-jackson/src/main/java/com/google/api/client/json/jackson/JacksonParser.java b/google-http-client-jackson/src/main/java/com/google/api/client/json/jackson/JacksonParser.java deleted file mode 100644 index a7b3427a3..000000000 --- a/google-http-client-jackson/src/main/java/com/google/api/client/json/jackson/JacksonParser.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (c) 2010 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package com.google.api.client.json.jackson; - -import com.google.api.client.json.JsonParser; -import com.google.api.client.json.JsonToken; - -import java.io.IOException; -import java.math.BigDecimal; -import java.math.BigInteger; - -/** - * Low-level JSON serializer implementation based on Jackson. - * - *

    - * Implementation is not thread-safe. - *

    - * - * @author Yaniv Inbar - */ -final class JacksonParser extends JsonParser { - - private final org.codehaus.jackson.JsonParser parser; - private final JacksonFactory factory; - - @Override - public JacksonFactory getFactory() { - return factory; - } - - JacksonParser(JacksonFactory factory, org.codehaus.jackson.JsonParser parser) { - this.factory = factory; - this.parser = parser; - } - - @Override - public void close() throws IOException { - parser.close(); - } - - @Override - public JsonToken nextToken() throws IOException { - return JacksonFactory.convert(parser.nextToken()); - } - - @Override - public String getCurrentName() throws IOException { - return parser.getCurrentName(); - } - - @Override - public JsonToken getCurrentToken() { - return JacksonFactory.convert(parser.getCurrentToken()); - } - - @Override - public JsonParser skipChildren() throws IOException { - parser.skipChildren(); - return this; - } - - @Override - public String getText() throws IOException { - return parser.getText(); - } - - @Override - public byte getByteValue() throws IOException { - return parser.getByteValue(); - } - - @Override - public float getFloatValue() throws IOException { - return parser.getFloatValue(); - } - - @Override - public int getIntValue() throws IOException { - return parser.getIntValue(); - } - - @Override - public short getShortValue() throws IOException { - return parser.getShortValue(); - } - - @Override - public BigInteger getBigIntegerValue() throws IOException { - return parser.getBigIntegerValue(); - } - - @Override - public BigDecimal getDecimalValue() throws IOException { - return parser.getDecimalValue(); - } - - @Override - public double getDoubleValue() throws IOException { - return parser.getDoubleValue(); - } - - @Override - public long getLongValue() throws IOException { - return parser.getLongValue(); - } -} diff --git a/google-http-client-jackson/src/test/java/com/google/api/client/json/jackson/JacksonFactoryTest.java b/google-http-client-jackson/src/test/java/com/google/api/client/json/jackson/JacksonFactoryTest.java deleted file mode 100644 index 84502d37b..000000000 --- a/google-http-client-jackson/src/test/java/com/google/api/client/json/jackson/JacksonFactoryTest.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2010 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package com.google.api.client.json.jackson; - -import com.google.api.client.json.JsonFactory; -import com.google.api.client.json.JsonParser; -import com.google.api.client.test.json.AbstractJsonFactoryTest; -import com.google.api.client.util.StringUtils; -import com.google.common.base.Charsets; - -import java.io.ByteArrayInputStream; -import java.util.ArrayList; - -/** - * Tests {@link JacksonFactory}. - * - * @author Yaniv Inbar - */ -public class JacksonFactoryTest extends AbstractJsonFactoryTest { - - private static final String JSON_ENTRY_PRETTY = - "{" + StringUtils.LINE_SEPARATOR + " \"title\" : \"foo\"" + StringUtils.LINE_SEPARATOR + "}"; - private static final String JSON_FEED_PRETTY = "{" + StringUtils.LINE_SEPARATOR - + " \"entries\" : [ {" + StringUtils.LINE_SEPARATOR + " \"title\" : \"foo\"" - + StringUtils.LINE_SEPARATOR + " }, {" + StringUtils.LINE_SEPARATOR + " \"title\" : \"bar\"" - + StringUtils.LINE_SEPARATOR + " } ]" + StringUtils.LINE_SEPARATOR + "}"; - - public JacksonFactoryTest(String name) { - super(name); - } - - @Override - protected JsonFactory newFactory() { - return new JacksonFactory(); - } - - public final void testToPrettyString_entry() throws Exception { - Entry entry = new Entry(); - entry.title = "foo"; - assertEquals(JSON_ENTRY_PRETTY, newFactory().toPrettyString(entry)); - } - - public final void testToPrettyString_Feed() throws Exception { - Feed feed = new Feed(); - Entry entryFoo = new Entry(); - entryFoo.title = "foo"; - Entry entryBar = new Entry(); - entryBar.title = "bar"; - feed.entries = new ArrayList(); - feed.entries.add(entryFoo); - feed.entries.add(entryBar); - assertEquals(JSON_FEED_PRETTY, newFactory().toPrettyString(feed)); - } - - public final void testParse_directValue() throws Exception { - byte[] jsonData = Charsets.UTF_8.encode("123").array(); - JsonParser jp = - newFactory().createJsonParser(new ByteArrayInputStream(jsonData), Charsets.UTF_8); - assertEquals(123, jp.parse(Integer.class, true)); - } -} diff --git a/google-http-client-jackson2/pom.xml b/google-http-client-jackson2/pom.xml index c34bf6f19..9ec7e616a 100644 --- a/google-http-client-jackson2/pom.xml +++ b/google-http-client-jackson2/pom.xml @@ -4,11 +4,11 @@ com.google.http-client google-http-client-parent - 1.26.1-SNAPSHOT + 2.0.3-SNAPSHOT ../pom.xml google-http-client-jackson2 - 1.26.1-SNAPSHOT + 2.0.3-SNAPSHOT Jackson 2 extensions to the Google HTTP Client Library for Java. @@ -17,8 +17,7 @@ maven-javadoc-plugin - http://download.oracle.com/javase/6/docs/api/ - http://fasterxml.github.com/jackson-core/javadoc/${project.jackson-core2.version}/ + https://download.oracle.com/javase/7/docs/api/ ${project.name} ${project.version} ${project.artifactId} ${project.version} @@ -26,20 +25,11 @@
    maven-source-plugin - - - source-jar - compile - - jar - - - org.codehaus.mojo build-helper-maven-plugin - 1.5 + 3.6.0 add-test-source @@ -55,6 +45,16 @@ + + maven-jar-plugin + + + + com.google.api.client.json.jackson2 + + + +
    @@ -67,18 +67,13 @@ google-http-client-test test - - junit - junit - test - com.fasterxml.jackson.core jackson-core - com.google.guava - guava + junit + junit test diff --git a/google-http-client-jackson2/src/main/java/com/google/api/client/json/jackson2/JacksonFactory.java b/google-http-client-jackson2/src/main/java/com/google/api/client/json/jackson2/JacksonFactory.java index 6eacfa8bb..1079fb725 100644 --- a/google-http-client-jackson2/src/main/java/com/google/api/client/json/jackson2/JacksonFactory.java +++ b/google-http-client-jackson2/src/main/java/com/google/api/client/json/jackson2/JacksonFactory.java @@ -17,9 +17,7 @@ import com.google.api.client.json.JsonFactory; import com.google.api.client.json.JsonGenerator; import com.google.api.client.json.JsonParser; -import com.google.api.client.json.JsonToken; import com.google.api.client.util.Preconditions; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -30,19 +28,20 @@ /** * Low-level JSON library implementation based on Jackson 2. * - *

    - * Implementation is thread-safe, and sub-classes must be thread-safe. For maximum efficiency, - * applications should use a single globally-shared instance of the JSON factory. - *

    + *

    Implementation is thread-safe. For maximum efficiency, applications should use a single + * globally-shared instance of the JSON factory. * * @since 1.11 * @author Yaniv Inbar + * @deprecated use com.google.api.client.json.GsonFactory instead */ +@Deprecated public final class JacksonFactory extends JsonFactory { /** JSON factory. */ private final com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory(); + { // don't auto-close JSON content in order to ensure consistent behavior across JSON factories // TODO(rmistry): Should we disable the JsonGenerator.Feature.AUTO_CLOSE_TARGET feature? @@ -98,36 +97,4 @@ public JsonParser createJsonParser(String value) throws IOException { Preconditions.checkNotNull(value); return new JacksonParser(this, factory.createJsonParser(value)); } - - static JsonToken convert(com.fasterxml.jackson.core.JsonToken token) { - if (token == null) { - return null; - } - switch (token) { - case END_ARRAY: - return JsonToken.END_ARRAY; - case START_ARRAY: - return JsonToken.START_ARRAY; - case END_OBJECT: - return JsonToken.END_OBJECT; - case START_OBJECT: - return JsonToken.START_OBJECT; - case VALUE_FALSE: - return JsonToken.VALUE_FALSE; - case VALUE_TRUE: - return JsonToken.VALUE_TRUE; - case VALUE_NULL: - return JsonToken.VALUE_NULL; - case VALUE_STRING: - return JsonToken.VALUE_STRING; - case VALUE_NUMBER_FLOAT: - return JsonToken.VALUE_NUMBER_FLOAT; - case VALUE_NUMBER_INT: - return JsonToken.VALUE_NUMBER_INT; - case FIELD_NAME: - return JsonToken.FIELD_NAME; - default: - return JsonToken.NOT_AVAILABLE; - } - } } diff --git a/google-http-client-jackson2/src/main/java/com/google/api/client/json/jackson2/JacksonGenerator.java b/google-http-client-jackson2/src/main/java/com/google/api/client/json/jackson2/JacksonGenerator.java index 7288a442a..64d54db6d 100644 --- a/google-http-client-jackson2/src/main/java/com/google/api/client/json/jackson2/JacksonGenerator.java +++ b/google-http-client-jackson2/src/main/java/com/google/api/client/json/jackson2/JacksonGenerator.java @@ -14,8 +14,8 @@ package com.google.api.client.json.jackson2; +import com.google.api.client.json.JsonFactory; import com.google.api.client.json.JsonGenerator; - import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; @@ -23,22 +23,20 @@ /** * Low-level JSON serializer implementation based on Jackson. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @author Yaniv Inbar */ final class JacksonGenerator extends JsonGenerator { private final com.fasterxml.jackson.core.JsonGenerator generator; - private final JacksonFactory factory; + private final JsonFactory factory; @Override - public JacksonFactory getFactory() { + public JsonFactory getFactory() { return factory; } - JacksonGenerator(JacksonFactory factory, com.fasterxml.jackson.core.JsonGenerator generator) { + JacksonGenerator(JsonFactory factory, com.fasterxml.jackson.core.JsonGenerator generator) { this.factory = factory; this.generator = generator; } diff --git a/google-http-client-jackson2/src/main/java/com/google/api/client/json/jackson2/JacksonParser.java b/google-http-client-jackson2/src/main/java/com/google/api/client/json/jackson2/JacksonParser.java index 76f3f4a76..2000392b0 100644 --- a/google-http-client-jackson2/src/main/java/com/google/api/client/json/jackson2/JacksonParser.java +++ b/google-http-client-jackson2/src/main/java/com/google/api/client/json/jackson2/JacksonParser.java @@ -14,9 +14,9 @@ package com.google.api.client.json.jackson2; +import com.google.api.client.json.JsonFactory; import com.google.api.client.json.JsonParser; import com.google.api.client.json.JsonToken; - import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; @@ -24,23 +24,21 @@ /** * Low-level JSON serializer implementation based on Jackson. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @author Yaniv Inbar */ final class JacksonParser extends JsonParser { private final com.fasterxml.jackson.core.JsonParser parser; - private final JacksonFactory factory; + private final JsonFactory factory; @Override - public JacksonFactory getFactory() { + public JsonFactory getFactory() { return factory; } - JacksonParser(JacksonFactory factory, com.fasterxml.jackson.core.JsonParser parser) { + JacksonParser(JsonFactory factory, com.fasterxml.jackson.core.JsonParser parser) { this.factory = factory; this.parser = parser; } @@ -52,7 +50,7 @@ public void close() throws IOException { @Override public JsonToken nextToken() throws IOException { - return JacksonFactory.convert(parser.nextToken()); + return convert(parser.nextToken()); } @Override @@ -62,7 +60,7 @@ public String getCurrentName() throws IOException { @Override public JsonToken getCurrentToken() { - return JacksonFactory.convert(parser.getCurrentToken()); + return convert(parser.getCurrentToken()); } @Override @@ -115,4 +113,36 @@ public double getDoubleValue() throws IOException { public long getLongValue() throws IOException { return parser.getLongValue(); } + + private static JsonToken convert(com.fasterxml.jackson.core.JsonToken token) { + if (token == null) { + return null; + } + switch (token) { + case END_ARRAY: + return JsonToken.END_ARRAY; + case START_ARRAY: + return JsonToken.START_ARRAY; + case END_OBJECT: + return JsonToken.END_OBJECT; + case START_OBJECT: + return JsonToken.START_OBJECT; + case VALUE_FALSE: + return JsonToken.VALUE_FALSE; + case VALUE_TRUE: + return JsonToken.VALUE_TRUE; + case VALUE_NULL: + return JsonToken.VALUE_NULL; + case VALUE_STRING: + return JsonToken.VALUE_STRING; + case VALUE_NUMBER_FLOAT: + return JsonToken.VALUE_NUMBER_FLOAT; + case VALUE_NUMBER_INT: + return JsonToken.VALUE_NUMBER_INT; + case FIELD_NAME: + return JsonToken.FIELD_NAME; + default: + return JsonToken.NOT_AVAILABLE; + } + } } diff --git a/google-http-client-jackson2/src/main/java/com/google/api/client/json/jackson2/package-info.java b/google-http-client-jackson2/src/main/java/com/google/api/client/json/jackson2/package-info.java index 02d2b4191..e7fe19f76 100644 --- a/google-http-client-jackson2/src/main/java/com/google/api/client/json/jackson2/package-info.java +++ b/google-http-client-jackson2/src/main/java/com/google/api/client/json/jackson2/package-info.java @@ -13,12 +13,9 @@ */ /** - * Low-level implementation of the JSON parser library based on the Jackson 2 JSON library. + * Low-level implementation of the JSON parser library based on the Jackson 2 JSON library. * * @since 1.11 * @author Yaniv Inbar */ - package com.google.api.client.json.jackson2; - diff --git a/google-http-client-jackson2/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-jackson2/reflect-config.json b/google-http-client-jackson2/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-jackson2/reflect-config.json new file mode 100644 index 000000000..3531bc889 --- /dev/null +++ b/google-http-client-jackson2/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-jackson2/reflect-config.json @@ -0,0 +1,52 @@ +[ + { + "name": "com.google.api.client.json.GenericJson", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "java.util.HashMap", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "java.util.LinkedList", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "java.lang.Object", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.util.ArrayMap", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + } +] \ No newline at end of file diff --git a/google-http-client-jackson2/src/test/java/com/google/api/client/json/jackson2/JacksonFactoryTest.java b/google-http-client-jackson2/src/test/java/com/google/api/client/json/jackson2/JacksonFactoryTest.java index 68152aecc..89e7d0596 100644 --- a/google-http-client-jackson2/src/test/java/com/google/api/client/json/jackson2/JacksonFactoryTest.java +++ b/google-http-client-jackson2/src/test/java/com/google/api/client/json/jackson2/JacksonFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Google Inc. + * Copyright 2012 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -14,14 +14,17 @@ package com.google.api.client.json.jackson2; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + import com.google.api.client.json.JsonFactory; import com.google.api.client.json.JsonParser; import com.google.api.client.test.json.AbstractJsonFactoryTest; import com.google.api.client.util.StringUtils; -import com.google.common.base.Charsets; - -import java.io.ByteArrayInputStream; +import java.io.IOException; import java.util.ArrayList; +import org.junit.Test; /** * Tests {@link JacksonFactory}. @@ -32,26 +35,34 @@ public class JacksonFactoryTest extends AbstractJsonFactoryTest { private static final String JSON_ENTRY_PRETTY = "{" + StringUtils.LINE_SEPARATOR + " \"title\" : \"foo\"" + StringUtils.LINE_SEPARATOR + "}"; - private static final String JSON_FEED_PRETTY = "{" + StringUtils.LINE_SEPARATOR - + " \"entries\" : [ {" + StringUtils.LINE_SEPARATOR + " \"title\" : \"foo\"" - + StringUtils.LINE_SEPARATOR + " }, {" + StringUtils.LINE_SEPARATOR + " \"title\" : \"bar\"" - + StringUtils.LINE_SEPARATOR + " } ]" + StringUtils.LINE_SEPARATOR + "}"; - - public JacksonFactoryTest(String name) { - super(name); - } + private static final String JSON_FEED_PRETTY = + "{" + + StringUtils.LINE_SEPARATOR + + " \"entries\" : [ {" + + StringUtils.LINE_SEPARATOR + + " \"title\" : \"foo\"" + + StringUtils.LINE_SEPARATOR + + " }, {" + + StringUtils.LINE_SEPARATOR + + " \"title\" : \"bar\"" + + StringUtils.LINE_SEPARATOR + + " } ]" + + StringUtils.LINE_SEPARATOR + + "}"; @Override protected JsonFactory newFactory() { return new JacksonFactory(); } + @Test public final void testToPrettyString_entry() throws Exception { Entry entry = new Entry(); entry.title = "foo"; assertEquals(JSON_ENTRY_PRETTY, newFactory().toPrettyString(entry)); } + @Test public final void testToPrettyString_Feed() throws Exception { Feed feed = new Feed(); Entry entryFoo = new Entry(); @@ -64,10 +75,21 @@ public final void testToPrettyString_Feed() throws Exception { assertEquals(JSON_FEED_PRETTY, newFactory().toPrettyString(feed)); } + @Test public final void testParse_directValue() throws Exception { - byte[] jsonData = Charsets.UTF_8.encode("123").array(); - JsonParser jp = - newFactory().createJsonParser(new ByteArrayInputStream(jsonData), Charsets.UTF_8); - assertEquals(123, jp.parse(Integer.class, true)); + JsonParser parser = newFactory().createJsonParser("123"); + assertEquals(123, parser.parse(Integer.class, true)); + } + + @Test + public final void testGetByteValue() throws IOException { + JsonParser parser = newFactory().createJsonParser("123"); + + try { + parser.getByteValue(); + fail("should throw IOException"); + } catch (IOException ex) { + assertNotNull(ex.getMessage()); + } } } diff --git a/google-http-client-jackson2/src/test/java/com/google/api/client/json/jackson2/JacksonGeneratorTest.java b/google-http-client-jackson2/src/test/java/com/google/api/client/json/jackson2/JacksonGeneratorTest.java new file mode 100644 index 000000000..4b0af8478 --- /dev/null +++ b/google-http-client-jackson2/src/test/java/com/google/api/client/json/jackson2/JacksonGeneratorTest.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.json.jackson2; + +import com.google.api.client.json.JsonGenerator; +import com.google.api.client.test.json.AbstractJsonGeneratorTest; +import java.io.IOException; +import java.io.Writer; + +public class JacksonGeneratorTest extends AbstractJsonGeneratorTest { + + private static final JacksonFactory FACTORY = new JacksonFactory(); + + @Override + protected JsonGenerator newGenerator(Writer writer) throws IOException { + return FACTORY.createJsonGenerator(writer); + } +} diff --git a/google-http-client-jackson2/src/test/java/com/google/api/client/json/jackson2/JacksonParserTest.java b/google-http-client-jackson2/src/test/java/com/google/api/client/json/jackson2/JacksonParserTest.java new file mode 100644 index 000000000..ed185c7ab --- /dev/null +++ b/google-http-client-jackson2/src/test/java/com/google/api/client/json/jackson2/JacksonParserTest.java @@ -0,0 +1,25 @@ +/** + * 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 + * + *

    https://www.apache.org/licenses/LICENSE-2.0 + * + *

    Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.api.client.json.jackson2; + +import com.google.api.client.json.JsonFactory; +import com.google.api.client.test.json.AbstractJsonParserTest; + +public class JacksonParserTest extends AbstractJsonParserTest { + + @Override + protected JsonFactory newJsonFactory() { + return new JacksonFactory(); + } +} diff --git a/google-http-client-jackson2/src/test/resources/META-INF/native-image/com.google.http-client/google-http-client-jackson2/native-image.properties b/google-http-client-jackson2/src/test/resources/META-INF/native-image/com.google.http-client/google-http-client-jackson2/native-image.properties new file mode 100644 index 000000000..0bb622f01 --- /dev/null +++ b/google-http-client-jackson2/src/test/resources/META-INF/native-image/com.google.http-client/google-http-client-jackson2/native-image.properties @@ -0,0 +1,6 @@ +Args=--initialize-at-build-time=com.google.api.client.json.jackson2.JacksonFactoryTest \ +--initialize-at-build-time=com.google.api.client.json.jackson2.JacksonGeneratorTest \ +--initialize-at-build-time=com.google.api.client.json.jackson2.JacksonFactory \ +--initialize-at-build-time=com.google.api.client.util.StringUtils \ +--initialize-at-build-time=com.fasterxml.jackson.core \ +--initialize-at-build-time=org.junit.runner.RunWith,java.lang.annotation.Annotation \ No newline at end of file diff --git a/google-http-client-jdo/pom.xml b/google-http-client-jdo/pom.xml deleted file mode 100644 index 020b1de33..000000000 --- a/google-http-client-jdo/pom.xml +++ /dev/null @@ -1,132 +0,0 @@ - - 4.0.0 - - com.google.http-client - google-http-client-parent - 1.26.1-SNAPSHOT - ../pom.xml - - google-http-client-jdo - 1.26.1-SNAPSHOT - JDO extensions to the Google HTTP Client Library for Java. - - - - - maven-javadoc-plugin - - - http://download.oracle.com/javase/6/docs/api/ - - ${project.name} ${project.version} - ${project.artifactId} ${project.version} - - - - maven-jar-plugin - - - - true - - - - - - maven-source-plugin - - - source-jar - compile - - jar - - - - - - org.datanucleus - datanucleus-maven-plugin - ${project.datanucleus-maven-plugin.version} - - JDO - true - - - - org.datanucleus - datanucleus-core - ${project.datanucleus-core.version} - runtime - - - org.datanucleus - datanucleus-api-jdo - ${project.datanucleus-api-jdo.version} - runtime - - - javax.jdo - jdo2-api - ${project.jdo2-api.version} - runtime - - - - - process-classes - - enhance - - - - - - - - - com.google.http-client - google-http-client - - - com.google.http-client - google-http-client-test - test - - - junit - junit - test - - - javax.jdo - jdo2-api - - - org.datanucleus - datanucleus-core - test - - - org.datanucleus - datanucleus-api-jdo - test - - - org.datanucleus - datanucleus-rdbms - test - - - mysql - mysql-connector-java - test - - - com.google.guava - guava - test - - - diff --git a/google-http-client-jdo/src/main/java/com/google/api/client/extensions/jdo/JdoDataStoreFactory.java b/google-http-client-jdo/src/main/java/com/google/api/client/extensions/jdo/JdoDataStoreFactory.java deleted file mode 100644 index b9743e682..000000000 --- a/google-http-client-jdo/src/main/java/com/google/api/client/extensions/jdo/JdoDataStoreFactory.java +++ /dev/null @@ -1,383 +0,0 @@ -/* - * Copyright (c) 2013 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package com.google.api.client.extensions.jdo; - -import com.google.api.client.extensions.jdo.JdoDataStoreFactory.PrivateUtils.ComposedIdKey; -import com.google.api.client.util.IOUtils; -import com.google.api.client.util.Lists; -import com.google.api.client.util.Preconditions; -import com.google.api.client.util.Sets; -import com.google.api.client.util.store.AbstractDataStore; -import com.google.api.client.util.store.AbstractDataStoreFactory; -import com.google.api.client.util.store.DataStore; -import com.google.api.client.util.store.DataStoreUtils; - -import java.io.IOException; -import java.io.Serializable; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.StringTokenizer; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -import javax.jdo.PersistenceManager; -import javax.jdo.PersistenceManagerFactory; -import javax.jdo.Query; -import javax.jdo.annotations.PersistenceCapable; -import javax.jdo.annotations.Persistent; -import javax.jdo.annotations.PrimaryKey; - -/** - * Thread-safe JDO implementation of a data store factory. - * - * @since 1.16 - * @author Yaniv Inbar - */ -public class JdoDataStoreFactory extends AbstractDataStoreFactory { - - /** Persistence manager factory. */ - private final PersistenceManagerFactory persistenceManagerFactory; - - public JdoDataStoreFactory(PersistenceManagerFactory persistenceManagerFactory) { - this.persistenceManagerFactory = Preconditions.checkNotNull(persistenceManagerFactory); - } - - @Override - protected DataStore createDataStore(String id) throws IOException { - return new JdoDataStore(this, persistenceManagerFactory, id); - } - - static class JdoDataStore extends AbstractDataStore { - - /** Lock on storing, loading and deleting a credential. */ - private final Lock lock = new ReentrantLock(); - - /** Persistence manager factory. */ - private final PersistenceManagerFactory persistenceManagerFactory; - - JdoDataStore(JdoDataStoreFactory dataStore, PersistenceManagerFactory persistenceManagerFactory, - String id) { - super(dataStore, id); - this.persistenceManagerFactory = persistenceManagerFactory; - } - - public Set keySet() throws IOException { - lock.lock(); - try { - PersistenceManager persistenceManager = persistenceManagerFactory.getPersistenceManager(); - try { - Query query = newAllKeysQuery(persistenceManager); - try { - Set result = Sets.newHashSet(); - for (JdoValue jdoValue : executeAllKeysQuery(query)) { - result.add(jdoValue.getKey()); - } - return Collections.unmodifiableSet(result); - } finally { - query.closeAll(); - } - } finally { - persistenceManager.close(); - } - } finally { - lock.unlock(); - } - } - - public Collection values() throws IOException { - lock.lock(); - try { - PersistenceManager persistenceManager = persistenceManagerFactory.getPersistenceManager(); - try { - Query query = newAllKeysQuery(persistenceManager); - try { - List result = Lists.newArrayList(); - for (JdoValue jdoValue : executeAllKeysQuery(query)) { - result.add(jdoValue.deserialize()); - } - return Collections.unmodifiableList(result); - } finally { - query.closeAll(); - } - } finally { - persistenceManager.close(); - } - } finally { - lock.unlock(); - } - } - - public V get(String key) throws IOException { - if (key == null) { - return null; - } - lock.lock(); - try { - PersistenceManager persistenceManager = persistenceManagerFactory.getPersistenceManager(); - try { - Query query = newKeyQuery(persistenceManager); - try { - JdoValue jdoValue = executeKeyQuery(query, key); - return jdoValue == null ? null : jdoValue.deserialize(); - } finally { - query.closeAll(); - } - } finally { - persistenceManager.close(); - } - } finally { - lock.unlock(); - } - } - - public JdoDataStore set(String key, V value) throws IOException { - Preconditions.checkNotNull(key); - Preconditions.checkNotNull(value); - lock.lock(); - try { - PersistenceManager persistenceManager = persistenceManagerFactory.getPersistenceManager(); - try { - Query query = newKeyQuery(persistenceManager); - try { - JdoValue jdoValue = executeKeyQuery(query, key); - if (jdoValue != null) { - jdoValue.serialize(value); - } else { - jdoValue = new JdoValue(getId(), key, value); - persistenceManager.makePersistent(jdoValue); - } - } finally { - query.closeAll(); - } - } finally { - persistenceManager.close(); - } - } finally { - lock.unlock(); - } - return this; - } - - public DataStore delete(String key) throws IOException { - if (key == null) { - return this; - } - lock.lock(); - try { - PersistenceManager persistenceManager = persistenceManagerFactory.getPersistenceManager(); - try { - Query query = newKeyQuery(persistenceManager); - try { - JdoValue jdoValue = executeKeyQuery(query, key); - if (jdoValue != null) { - persistenceManager.deletePersistent(jdoValue); - } - } finally { - query.closeAll(); - } - } finally { - persistenceManager.close(); - } - } finally { - lock.unlock(); - } - return this; - } - - public JdoDataStore clear() throws IOException { - lock.lock(); - try { - PersistenceManager persistenceManager = persistenceManagerFactory.getPersistenceManager(); - try { - Query query = newAllKeysQuery(persistenceManager); - try { - persistenceManager.deletePersistentAll(executeAllKeysQuery(query)); - } finally { - query.closeAll(); - } - } finally { - persistenceManager.close(); - } - } finally { - lock.unlock(); - } - return this; - } - - @Override - public JdoDataStoreFactory getDataStoreFactory() { - return (JdoDataStoreFactory) super.getDataStoreFactory(); - } - - @Override - public String toString() { - return DataStoreUtils.toString(this); - } - - /** - * Returns a new query for all keys. - * - * @param persistenceManager persistence manager - * @return new query for all keys - */ - Query newAllKeysQuery(PersistenceManager persistenceManager) { - Query query = persistenceManager.newQuery(JdoValue.class); - query.setFilter("id == idParam"); - query.declareParameters("String idParam"); - return query; - } - - /** - * Executes the query for all keys. - * - * @param allKeysQuery query for all keys - * @return query result - */ - @SuppressWarnings("unchecked") - Collection executeAllKeysQuery(Query allKeysQuery) { - return (Collection) allKeysQuery.execute(getId()); - } - - /** - * Returns a new query for a given key. - * - * @param persistenceManager persistence manager - * @return new new query for a given key - */ - Query newKeyQuery(PersistenceManager persistenceManager) { - Query query = persistenceManager.newQuery(JdoValue.class); - query.setFilter("id == idParam && key == keyParam"); - query.declareParameters("String idParam, String keyParam"); - return query; - } - - /** - * Executes the query for a given key, and returns the {@link JdoValue}. - * - * @param keyQuery query for a given key - * @param key key - * @return found {@link JdoValue} or {@code null} for none found - */ - @SuppressWarnings("unchecked") - JdoValue executeKeyQuery(Query keyQuery, String key) { - Collection queryResult = (Collection) keyQuery.execute(getId(), key); - return queryResult.isEmpty() ? null : queryResult.iterator().next(); - } - } - - /** - * JDO value class that contains the key-value pair, as well as the data store ID. - */ - @PersistenceCapable(objectIdClass = ComposedIdKey.class) - static class JdoValue { - - /** Key. */ - @PrimaryKey - @Persistent - private String key; - - /** Data store ID. */ - @PrimaryKey - @Persistent - private String id; - - /** Byte array of value. */ - @Persistent - private byte[] bytes; - - /* Required by JDO. */ - @SuppressWarnings("unused") - JdoValue() { - } - - JdoValue(String id, String key, V value) throws IOException { - this.id = id; - this.key = key; - serialize(value); - } - - void serialize(V value) throws IOException { - bytes = IOUtils.serialize(value); - } - - V deserialize() throws IOException { - return IOUtils.deserialize(bytes); - } - - String getKey() { - return key; - } - } - - /** - * Package private utilities class so the classes here isn't considered to be an external part of - * the library. We need this because {@link ComposedIdKey} MUST be public because it is the - * objectIdClass of {@link JdoValue}. - */ - static class PrivateUtils { - - /** - * See JDO : - * PrimaryKey Classes for a reference. - */ - public static class ComposedIdKey implements Serializable { - - private static final long serialVersionUID = 1L; - - /** Key. */ - public String key; - - /** Data store ID. */ - public String id; - - public ComposedIdKey() { - } - - /** - * @param value matches the result of toString() - */ - public ComposedIdKey(String value) { - StringTokenizer token = new StringTokenizer(value, "::"); - token.nextToken(); // className - this.key = token.nextToken(); // key - this.id = token.nextToken(); // id - } - - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof ComposedIdKey)) { - return false; - } - ComposedIdKey other = (ComposedIdKey) obj; - return key.equals(other.key) && id.equals(other.id); - } - - @Override - public int hashCode() { - return this.key.hashCode() ^ this.id.hashCode(); - } - - @Override - public String toString() { - return this.getClass().getName() + "::" + this.key + "::" + this.id; - } - } - } -} diff --git a/google-http-client-jdo/src/test/java/com/google/api/client/extensions/jdo/JdoDataStoreFactoryTest.java b/google-http-client-jdo/src/test/java/com/google/api/client/extensions/jdo/JdoDataStoreFactoryTest.java deleted file mode 100644 index 3adc00c45..000000000 --- a/google-http-client-jdo/src/test/java/com/google/api/client/extensions/jdo/JdoDataStoreFactoryTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2013 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package com.google.api.client.extensions.jdo; - -import com.google.api.client.test.util.store.AbstractDataStoreFactoryTest; -import com.google.api.client.util.store.DataStoreFactory; -import com.google.api.client.util.store.MemoryDataStoreFactory; - -import java.io.IOException; -import java.util.Properties; - -import javax.jdo.JDOHelper; -import javax.jdo.PersistenceManagerFactory; - -/** - * Tests {@link JdoDataStoreFactory}. - * - * @author Yaniv Inbar - */ -public class JdoDataStoreFactoryTest extends AbstractDataStoreFactoryTest { - - /** - * This test is *disabled* by default just because you need to run special set up steps first. - * Specifically on Linux: - * - *

    -sudo apt-get install mysql-server
    -mysql -u root -p
    -create database mytest;
    -   * 
    - * - * Then update ConnectionUserName and ConnectionPassword below. - */ - static boolean ENABLE_TEST = false; - - static class PersistenceManagerFactoryHolder { - static PersistenceManagerFactory pmf; - - public static PersistenceManagerFactory get() { - return pmf; - } - - static { - Properties properties = new Properties(); - properties.setProperty("javax.jdo.PersistenceManagerFactoryClass", - "org.datanucleus.api.jdo.JDOPersistenceManagerFactory"); - properties.setProperty("javax.jdo.option.ConnectionDriverName", "com.mysql.jdbc.Driver"); - properties.setProperty( - "javax.jdo.option.ConnectionURL", "jdbc:mysql://localhost:3306/mytest"); - properties.setProperty("javax.jdo.option.ConnectionUserName", "root"); - properties.setProperty("javax.jdo.option.ConnectionPassword", ""); - properties.setProperty("datanucleus.autoCreateSchema", "true"); - pmf = JDOHelper.getPersistenceManagerFactory(properties); - } - } - - @Override - protected DataStoreFactory newDataStoreFactory() throws IOException { - // hack: if test not enabled run memory data store which we know works - return ENABLE_TEST - ? new JdoDataStoreFactory(PersistenceManagerFactoryHolder.get()) : new MemoryDataStoreFactory(); - } -} diff --git a/google-http-client-protobuf/pom.xml b/google-http-client-protobuf/pom.xml index 87acfc2f1..c9b87f5fd 100644 --- a/google-http-client-protobuf/pom.xml +++ b/google-http-client-protobuf/pom.xml @@ -4,11 +4,11 @@ com.google.http-client google-http-client-parent - 1.26.1-SNAPSHOT + 2.0.3-SNAPSHOT ../pom.xml google-http-client-protobuf - 1.26.1-SNAPSHOT + 2.0.3-SNAPSHOT Protocol Buffer extensions to the Google HTTP Client Library for Java. @@ -16,7 +16,7 @@ kr.motd.maven os-maven-plugin - 1.4.0.Final + 1.7.1 @@ -24,7 +24,7 @@ maven-javadoc-plugin - http://download.oracle.com/javase/6/docs/api/ + http://download.oracle.com/javase/7/docs/api/ ${project.name} ${project.version} ${project.artifactId} ${project.version} @@ -32,22 +32,13 @@ maven-source-plugin - - - source-jar - compile - - jar - - - - com.google.protobuf.tools - maven-protoc-plugin - 0.4.2 + org.xolstice.maven.plugins + protobuf-maven-plugin + 0.6.1 - com.google.protobuf:protoc:${project.protobuf-java.version}-build2:exe:${os.detected.classifier} + com.google.protobuf:protoc:${project.protobuf-java.version}:exe:${os.detected.classifier} @@ -57,6 +48,16 @@ + + maven-jar-plugin + + + + com.google.api.client.http.protobuf + + + + diff --git a/google-http-client-protobuf/src/main/java/com/google/api/client/http/protobuf/ProtoHttpContent.java b/google-http-client-protobuf/src/main/java/com/google/api/client/http/protobuf/ProtoHttpContent.java index c4fca7f58..c25f2793e 100644 --- a/google-http-client-protobuf/src/main/java/com/google/api/client/http/protobuf/ProtoHttpContent.java +++ b/google-http-client-protobuf/src/main/java/com/google/api/client/http/protobuf/ProtoHttpContent.java @@ -20,28 +20,23 @@ import com.google.api.client.util.Beta; import com.google.api.client.util.Preconditions; import com.google.protobuf.MessageLite; - import java.io.IOException; import java.io.OutputStream; /** - * {@link Beta}
    + * {@link Beta}
    * Serializes of a protocol buffer message to HTTP content. * - *

    - * Sample usage: - *

    + *

    Sample usage: * *

    -  static HttpRequest buildPostRequest(
    -      HttpRequestFactory requestFactory, GenericUrl url, MessageLite message) throws IOException {
    -    return requestFactory.buildPostRequest(url, new ProtoHttpContent(message));
    -  }
    + * static HttpRequest buildPostRequest(
    + * HttpRequestFactory requestFactory, GenericUrl url, MessageLite message) throws IOException {
    + * return requestFactory.buildPostRequest(url, new ProtoHttpContent(message));
    + * }
      * 
    * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @since 1.5 * @author Yaniv Inbar @@ -52,9 +47,7 @@ public class ProtoHttpContent extends AbstractHttpContent { /** Message to serialize. */ private final MessageLite message; - /** - * @param message message to serialize - */ + /** @param message message to serialize */ public ProtoHttpContent(MessageLite message) { super(ProtocolBuffers.CONTENT_TYPE); this.message = Preconditions.checkNotNull(message); diff --git a/google-http-client-protobuf/src/main/java/com/google/api/client/http/protobuf/package-info.java b/google-http-client-protobuf/src/main/java/com/google/api/client/http/protobuf/package-info.java index 7f028cb3d..b786dba70 100644 --- a/google-http-client-protobuf/src/main/java/com/google/api/client/http/protobuf/package-info.java +++ b/google-http-client-protobuf/src/main/java/com/google/api/client/http/protobuf/package-info.java @@ -13,9 +13,9 @@ */ /** - * {@link com.google.api.client.util.Beta}
    - * HTTP utilities for the Protocol Buffer format - * based on the pluggable HTTP library. + * {@link com.google.api.client.util.Beta}
    + * HTTP utilities for the Protocol Buffer + * format based on the pluggable HTTP library. * * @since 1.5 * @author Yaniv Inbar diff --git a/google-http-client-protobuf/src/main/java/com/google/api/client/protobuf/ProtoObjectParser.java b/google-http-client-protobuf/src/main/java/com/google/api/client/protobuf/ProtoObjectParser.java index 37b1e87f8..60ed3f46a 100644 --- a/google-http-client-protobuf/src/main/java/com/google/api/client/protobuf/ProtoObjectParser.java +++ b/google-http-client-protobuf/src/main/java/com/google/api/client/protobuf/ProtoObjectParser.java @@ -17,7 +17,6 @@ import com.google.api.client.util.Beta; import com.google.api.client.util.ObjectParser; import com.google.protobuf.MessageLite; - import java.io.IOException; import java.io.InputStream; import java.io.Reader; @@ -25,20 +24,14 @@ import java.nio.charset.Charset; /** - * {@link Beta}
    + * {@link Beta}
    * Parses protocol buffer HTTP response content into a protocol buffer message. * - *

    - * Implementation is immutable and therefore thread-safe. - *

    + *

    Implementation is immutable and therefore thread-safe. * - *

    - * Data-classes are expected to extend {@link MessageLite}. - *

    + *

    Data-classes are expected to extend {@link MessageLite}. * - *

    - * All Charset parameters are ignored for protocol buffers. - *

    + *

    All Charset parameters are ignored for protocol buffers. * * @author Matthias Linder (mlinder) * @since 1.10 diff --git a/google-http-client-protobuf/src/main/java/com/google/api/client/protobuf/ProtocolBuffers.java b/google-http-client-protobuf/src/main/java/com/google/api/client/protobuf/ProtocolBuffers.java index aa2138ea3..b9b383d1d 100644 --- a/google-http-client-protobuf/src/main/java/com/google/api/client/protobuf/ProtocolBuffers.java +++ b/google-http-client-protobuf/src/main/java/com/google/api/client/protobuf/ProtocolBuffers.java @@ -17,21 +17,18 @@ import com.google.api.client.util.Beta; import com.google.api.client.util.Throwables; import com.google.protobuf.MessageLite; - import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; /** - * {@link Beta}
    + * {@link Beta}
    * Utilities for protocol buffers. * - *

    - * There is no official media type for protocol buffers registered with the IANA. - * {@link #CONTENT_TYPE} and {@link #ALT_CONTENT_TYPE} are some of the more popular choices being - * used today, but other media types are also in use. - *

    + *

    There is no official media type for protocol buffers registered with the IANA. {@link + * #CONTENT_TYPE} and {@link #ALT_CONTENT_TYPE} are some of the more popular choices being used + * today, but other media types are also in use. * * @since 1.5 * @author Yaniv Inbar @@ -51,7 +48,7 @@ public class ProtocolBuffers { * * @param destination message type * @param messageClass destination message class that has a {@code parseFrom(InputStream)} public - * static method + * static method * @return new instance of the parsed destination message class */ public static T parseAndClose( @@ -69,6 +66,5 @@ public static T parseAndClose( } } - private ProtocolBuffers() { - } + private ProtocolBuffers() {} } diff --git a/google-http-client-protobuf/src/main/java/com/google/api/client/protobuf/package-info.java b/google-http-client-protobuf/src/main/java/com/google/api/client/protobuf/package-info.java index deec6ea8d..c6b19b0a2 100644 --- a/google-http-client-protobuf/src/main/java/com/google/api/client/protobuf/package-info.java +++ b/google-http-client-protobuf/src/main/java/com/google/api/client/protobuf/package-info.java @@ -13,12 +13,12 @@ */ /** - * {@link com.google.api.client.util.Beta}
    - * Utilities for the Protocol Buffer format. + * {@link com.google.api.client.util.Beta}
    + * Utilities for the Protocol Buffer + * format. * * @since 1.5 * @author Yaniv Inbar */ @com.google.api.client.util.Beta package com.google.api.client.protobuf; - diff --git a/google-http-client-protobuf/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-protobuf/native-image.properties b/google-http-client-protobuf/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-protobuf/native-image.properties new file mode 100644 index 000000000..3cbd2b27f --- /dev/null +++ b/google-http-client-protobuf/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-protobuf/native-image.properties @@ -0,0 +1 @@ +Args=--initialize-at-build-time=org.junit.runner.RunWith,java.lang.annotation.Annotation diff --git a/google-http-client-protobuf/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-protobuf/reflect-config.json b/google-http-client-protobuf/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-protobuf/reflect-config.json new file mode 100644 index 000000000..ab2deb4fd --- /dev/null +++ b/google-http-client-protobuf/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-protobuf/reflect-config.json @@ -0,0 +1,12 @@ +[ + { + "name": "com.google.api.client.protobuf.SimpleProto$TestMessage", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + } +] \ No newline at end of file diff --git a/google-http-client-protobuf/src/test/java/com/google/api/client/protobuf/ProtocolBuffersTest.java b/google-http-client-protobuf/src/test/java/com/google/api/client/protobuf/ProtocolBuffersTest.java index c8c5c6ec9..40265b8c3 100644 --- a/google-http-client-protobuf/src/test/java/com/google/api/client/protobuf/ProtocolBuffersTest.java +++ b/google-http-client-protobuf/src/test/java/com/google/api/client/protobuf/ProtocolBuffersTest.java @@ -14,25 +14,33 @@ package com.google.api.client.protobuf; +import static org.junit.Assert.assertEquals; + import java.io.ByteArrayInputStream; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link ProtocolBuffers}. * * @author Yaniv Inbar */ -public class ProtocolBuffersTest extends TestCase { +@RunWith(JUnit4.class) +public class ProtocolBuffersTest { + @Test public void testParseAndClose() throws Exception { - SimpleProto.TestMessage mockResponse = SimpleProto.TestMessage.newBuilder() - .setStatus(SimpleProto.TestStatus.SUCCESS) - .setName("This is a test!") - .setValue(123454321) - .build(); + SimpleProto.TestMessage mockResponse = + SimpleProto.TestMessage.newBuilder() + .setStatus(SimpleProto.TestStatus.SUCCESS) + .setName("This is a test!") + .setValue(123454321) + .build(); // Create the parser and test it with our mock response - SimpleProto.TestMessage parsedResponse = ProtocolBuffers.parseAndClose( - new ByteArrayInputStream(mockResponse.toByteArray()), SimpleProto.TestMessage.class); + SimpleProto.TestMessage parsedResponse = + ProtocolBuffers.parseAndClose( + new ByteArrayInputStream(mockResponse.toByteArray()), SimpleProto.TestMessage.class); // Validate the parser properly parsed the response // (i.e. it matches the original mock response) assertEquals(mockResponse.getSerializedSize(), parsedResponse.getSerializedSize()); diff --git a/google-http-client-test/clirr-ignored-differences.xml b/google-http-client-test/clirr-ignored-differences.xml new file mode 100644 index 000000000..34c8e4f44 --- /dev/null +++ b/google-http-client-test/clirr-ignored-differences.xml @@ -0,0 +1,75 @@ + + + + + + + 4001 + com/google/api/client/test/util/store/AbstractDataStoreFactoryTest + junit/framework/Test + + + + 4001 + com/google/api/client/test/json/AbstractJsonFactoryTest + junit/framework/Test + + + + 4001 + com/google/api/client/test/json/AbstractJsonGeneratorTest + junit/framework/Test + + + + 4001 + com/google/api/client/test/json/AbstractJsonParserTest + junit/framework/Test + + + + 5001 + com/google/api/client/test/util/store/AbstractDataStoreFactoryTest + junit/framework/* + + + + 5001 + com/google/api/client/test/json/AbstractJsonFactoryTest + junit/framework/* + + + + 5001 + com/google/api/client/test/json/AbstractJsonGeneratorTest + junit/framework/* + + + + 5001 + com/google/api/client/test/json/AbstractJsonParserTest + junit/framework/* + + + + 7004 + com/google/api/client/test/json/AbstractJsonFactoryTest + * + + diff --git a/google-http-client-test/pom.xml b/google-http-client-test/pom.xml index df038a2c1..457b74091 100644 --- a/google-http-client-test/pom.xml +++ b/google-http-client-test/pom.xml @@ -4,11 +4,11 @@ com.google.http-client google-http-client-parent - 1.26.1-SNAPSHOT + 2.0.3-SNAPSHOT ../pom.xml google-http-client-test - 1.26.1-SNAPSHOT + 2.0.3-SNAPSHOT Shared classes used for testing of artifacts in the Google HTTP Client Library for Java. @@ -17,7 +17,7 @@ maven-javadoc-plugin - http://download.oracle.com/javase/6/docs/api/ + http://download.oracle.com/javase/7/docs/api/ ${project.name} ${project.version} ${project.artifactId} ${project.version} @@ -25,20 +25,11 @@ maven-source-plugin - - - source-jar - compile - - jar - - - org.codehaus.mojo build-helper-maven-plugin - 1.5 + 3.6.0 add-test-source @@ -71,4 +62,16 @@ provided + + + + native-deps + + + com.google.guava + guava + + + + diff --git a/google-http-client-test/src/main/java/com/google/api/client/test/json/AbstractJsonFactoryTest.java b/google-http-client-test/src/main/java/com/google/api/client/test/json/AbstractJsonFactoryTest.java index ee3386eaf..a0629280f 100644 --- a/google-http-client-test/src/main/java/com/google/api/client/test/json/AbstractJsonFactoryTest.java +++ b/google-http-client-test/src/main/java/com/google/api/client/test/json/AbstractJsonFactoryTest.java @@ -14,6 +14,13 @@ package com.google.api.client.test.json; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import com.google.api.client.http.json.JsonHttpContent; import com.google.api.client.json.GenericJson; import com.google.api.client.json.JsonFactory; @@ -53,25 +60,36 @@ import java.util.List; import java.util.Map; import java.util.TreeMap; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Abstract test case for testing a {@link JsonFactory}. * * @author Yaniv Inbar */ -public abstract class AbstractJsonFactoryTest extends TestCase { - - public AbstractJsonFactoryTest(String name) { - super(name); - } +@RunWith(JUnit4.class) +public abstract class AbstractJsonFactoryTest { protected abstract JsonFactory newFactory(); - private static final String EMPTY = ""; - private static final String JSON_THREE_ELEMENTS = "{" + " \"one\": { \"num\": 1 }" - + ", \"two\": { \"num\": 2 }" + ", \"three\": { \"num\": 3 }" + "}"; + private static final String EMPTY; + private static final String JSON_THREE_ELEMENTS; + private static final String EMPTY_OBJECT; + static { + EMPTY = ""; + JSON_THREE_ELEMENTS = + "{" + + " \"one\": { \"num\": 1 }" + + ", \"two\": { \"num\": 2 }" + + ", \"three\": { \"num\": 3 }" + + "}"; + EMPTY_OBJECT = "{}"; + } + + @Test public void testParse_empty() throws Exception { JsonParser parser = newFactory().createJsonParser(EMPTY); parser.nextToken(); @@ -83,8 +101,7 @@ public void testParse_empty() throws Exception { } } - private static final String EMPTY_OBJECT = "{}"; - + @Test public void testParse_emptyMap() throws Exception { JsonParser parser = newFactory().createJsonParser(EMPTY_OBJECT); parser.nextToken(); @@ -93,6 +110,7 @@ public void testParse_emptyMap() throws Exception { assertTrue(map.isEmpty()); } + @Test public void testParse_emptyGenericJson() throws Exception { JsonParser parser = newFactory().createJsonParser(EMPTY_OBJECT); parser.nextToken(); @@ -100,6 +118,7 @@ public void testParse_emptyGenericJson() throws Exception { assertTrue(json.isEmpty()); } + @Test public void testParser_partialEmpty() throws Exception { JsonFactory factory = newFactory(); JsonParser parser; @@ -114,15 +133,21 @@ public void testParser_partialEmpty() throws Exception { assertTrue(result.isEmpty()); } - private static final String JSON_ENTRY = "{\"title\":\"foo\"}"; - private static final String JSON_FEED = - "{\"entries\":[" + "{\"title\":\"foo\"}," + "{\"title\":\"bar\"}]}"; + private static final String JSON_ENTRY; + private static final String JSON_FEED; + static { + JSON_ENTRY = "{\"title\":\"foo\"}"; + JSON_FEED = "{\"entries\":[" + "{\"title\":\"foo\"}," + "{\"title\":\"bar\"}]}"; + } + + @Test public void testParseEntry() throws Exception { Entry entry = newFactory().createJsonParser(JSON_ENTRY).parseAndClose(Entry.class); assertEquals("foo", entry.title); } + @Test public void testParser_partialEntry() throws Exception { JsonFactory factory = newFactory(); JsonParser parser; @@ -136,6 +161,7 @@ public void testParser_partialEntry() throws Exception { assertEquals("foo", result.title); } + @Test public void testParseFeed() throws Exception { JsonParser parser = newFactory().createJsonParser(JSON_FEED); parser.nextToken(); @@ -147,6 +173,7 @@ public void testParseFeed() throws Exception { } @SuppressWarnings("unchecked") + @Test public void testParseEntryAsMap() throws Exception { JsonParser parser = newFactory().createJsonParser(JSON_ENTRY); parser.nextToken(); @@ -155,6 +182,7 @@ public void testParseEntryAsMap() throws Exception { assertTrue(map.isEmpty()); } + @Test public void testSkipToKey_missingEmpty() throws Exception { JsonParser parser = newFactory().createJsonParser(EMPTY_OBJECT); parser.nextToken(); @@ -162,6 +190,7 @@ public void testSkipToKey_missingEmpty() throws Exception { assertEquals(JsonToken.END_OBJECT, parser.getCurrentToken()); } + @Test public void testSkipToKey_missing() throws Exception { JsonParser parser = newFactory().createJsonParser(JSON_ENTRY); parser.nextToken(); @@ -169,6 +198,7 @@ public void testSkipToKey_missing() throws Exception { assertEquals(JsonToken.END_OBJECT, parser.getCurrentToken()); } + @Test public void testSkipToKey_found() throws Exception { JsonParser parser = newFactory().createJsonParser(JSON_ENTRY); parser.nextToken(); @@ -177,6 +207,7 @@ public void testSkipToKey_found() throws Exception { assertEquals("foo", parser.getText()); } + @Test public void testSkipToKey_startWithFieldName() throws Exception { JsonParser parser = newFactory().createJsonParser(JSON_ENTRY); parser.nextToken(); @@ -186,6 +217,7 @@ public void testSkipToKey_startWithFieldName() throws Exception { assertEquals("foo", parser.getText()); } + @Test public void testSkipChildren_string() throws Exception { JsonParser parser = newFactory().createJsonParser(JSON_ENTRY); parser.nextToken(); @@ -195,6 +227,7 @@ public void testSkipChildren_string() throws Exception { assertEquals("foo", parser.getText()); } + @Test public void testSkipChildren_object() throws Exception { JsonParser parser = newFactory().createJsonParser(JSON_ENTRY); parser.nextToken(); @@ -202,6 +235,7 @@ public void testSkipChildren_object() throws Exception { assertEquals(JsonToken.END_OBJECT, parser.getCurrentToken()); } + @Test public void testSkipChildren_array() throws Exception { JsonParser parser = newFactory().createJsonParser(JSON_FEED); parser.nextToken(); @@ -210,6 +244,7 @@ public void testSkipChildren_array() throws Exception { assertEquals(JsonToken.END_ARRAY, parser.getCurrentToken()); } + @Test public void testNextToken() throws Exception { JsonParser parser = newFactory().createJsonParser(JSON_FEED); assertEquals(JsonToken.START_OBJECT, parser.nextToken()); @@ -228,6 +263,7 @@ public void testNextToken() throws Exception { assertNull(parser.nextToken()); } + @Test public void testCurrentToken() throws Exception { JsonParser parser = newFactory().createJsonParser(JSON_FEED); assertNull(parser.getCurrentToken()); @@ -262,22 +298,24 @@ public void testCurrentToken() throws Exception { } public static class Entry { - @Key - public String title; + @Key public String title; } public static class Feed { - @Key - public Collection entries; + @Key public Collection entries; } public static class A { - @Key - public Map map; + @Key public Map map; } - static final String CONTAINED_MAP = "{\"map\":{\"title\":\"foo\"}}"; + static final String CONTAINED_MAP; + static { + CONTAINED_MAP = "{\"map\":{\"title\":\"foo\"}}"; + } + + @Test public void testParse() throws Exception { JsonParser parser = newFactory().createJsonParser(CONTAINED_MAP); parser.nextToken(); @@ -286,115 +324,75 @@ public void testParse() throws Exception { } public static class NumberTypes { - @Key - byte byteValue; - @Key - Byte byteObjValue; - @Key - short shortValue; - @Key - Short shortObjValue; - @Key - int intValue; - @Key - Integer intObjValue; - @Key - float floatValue; - @Key - Float floatObjValue; - @Key - long longValue; - @Key - Long longObjValue; - @Key - double doubleValue; - @Key - Double doubleObjValue; - @Key - BigInteger bigIntegerValue; - @Key - BigDecimal bigDecimalValue; + @Key byte byteValue; + @Key Byte byteObjValue; + @Key short shortValue; + @Key Short shortObjValue; + @Key int intValue; + @Key Integer intObjValue; + @Key float floatValue; + @Key Float floatObjValue; + @Key long longValue; + @Key Long longObjValue; + @Key double doubleValue; + @Key Double doubleObjValue; + @Key BigInteger bigIntegerValue; + @Key BigDecimal bigDecimalValue; + @Key("yetAnotherBigDecimalValue") BigDecimal anotherBigDecimalValue; - @Key - List longListValue; + @Key List longListValue; - @Key - Map longMapValue; + @Key Map longMapValue; } public static class NumberTypesAsString { - @Key - @JsonString - byte byteValue; - @Key - @JsonString - Byte byteObjValue; - @Key - @JsonString - short shortValue; - @Key - @JsonString - Short shortObjValue; - @Key - @JsonString - int intValue; - @Key - @JsonString - Integer intObjValue; - @Key - @JsonString - float floatValue; - @Key - @JsonString - Float floatObjValue; - @Key - @JsonString - long longValue; - @Key - @JsonString - Long longObjValue; - @Key - @JsonString - double doubleValue; - @Key - @JsonString - Double doubleObjValue; - @Key - @JsonString - BigInteger bigIntegerValue; - @Key - @JsonString - BigDecimal bigDecimalValue; + @Key @JsonString byte byteValue; + @Key @JsonString Byte byteObjValue; + @Key @JsonString short shortValue; + @Key @JsonString Short shortObjValue; + @Key @JsonString int intValue; + @Key @JsonString Integer intObjValue; + @Key @JsonString float floatValue; + @Key @JsonString Float floatObjValue; + @Key @JsonString long longValue; + @Key @JsonString Long longObjValue; + @Key @JsonString double doubleValue; + @Key @JsonString Double doubleObjValue; + @Key @JsonString BigInteger bigIntegerValue; + @Key @JsonString BigDecimal bigDecimalValue; + @Key("yetAnotherBigDecimalValue") @JsonString BigDecimal anotherBigDecimalValue; - @Key - @JsonString - List longListValue; + @Key @JsonString List longListValue; - @Key - @JsonString - Map longMapValue; + @Key @JsonString Map longMapValue; } - static final String NUMBER_TYPES = - "{\"bigDecimalValue\":1.0,\"bigIntegerValue\":1,\"byteObjValue\":1,\"byteValue\":1," - + "\"doubleObjValue\":1.0,\"doubleValue\":1.0,\"floatObjValue\":1.0,\"floatValue\":1.0," - + "\"intObjValue\":1,\"intValue\":1,\"longListValue\":[1],\"longMapValue\":{\"a\":1}," - + "\"longObjValue\":1,\"longValue\":1,\"shortObjValue\":1,\"shortValue\":1," - + "\"yetAnotherBigDecimalValue\":1}"; + static final String NUMBER_TYPES; + static final String NUMBER_TYPES_AS_STRING; - static final String NUMBER_TYPES_AS_STRING = - "{\"bigDecimalValue\":\"1.0\",\"bigIntegerValue\":\"1\",\"byteObjValue\":\"1\"," - + "\"byteValue\":\"1\",\"doubleObjValue\":\"1.0\",\"doubleValue\":\"1.0\"," - + "\"floatObjValue\":\"1.0\",\"floatValue\":\"1.0\",\"intObjValue\":\"1\"," - + "\"intValue\":\"1\",\"longListValue\":[\"1\"],\"longMapValue\":{\"a\":\"1\"}," - + "\"longObjValue\":\"1\",\"longValue\":\"1\"," + "\"shortObjValue\":\"1\"," - + "\"shortValue\":\"1\",\"yetAnotherBigDecimalValue\":\"1\"}"; + static { + NUMBER_TYPES = + "{\"bigDecimalValue\":1.0,\"bigIntegerValue\":1,\"byteObjValue\":1,\"byteValue\":1," + + "\"doubleObjValue\":1.0,\"doubleValue\":1.0,\"floatObjValue\":1.0,\"floatValue\":1.0," + + "\"intObjValue\":1,\"intValue\":1,\"longListValue\":[1],\"longMapValue\":{\"a\":1}," + + "\"longObjValue\":1,\"longValue\":1,\"shortObjValue\":1,\"shortValue\":1," + + "\"yetAnotherBigDecimalValue\":1}"; + NUMBER_TYPES_AS_STRING = + "{\"bigDecimalValue\":\"1.0\",\"bigIntegerValue\":\"1\",\"byteObjValue\":\"1\"," + + "\"byteValue\":\"1\",\"doubleObjValue\":\"1.0\",\"doubleValue\":\"1.0\"," + + "\"floatObjValue\":\"1.0\",\"floatValue\":\"1.0\",\"intObjValue\":\"1\"," + + "\"intValue\":\"1\",\"longListValue\":[\"1\"],\"longMapValue\":{\"a\":\"1\"}," + + "\"longObjValue\":\"1\",\"longValue\":\"1\"," + + "\"shortObjValue\":\"1\"," + + "\"shortValue\":\"1\",\"yetAnotherBigDecimalValue\":\"1\"}"; + } + @Test public void testParser_numberTypes() throws Exception { JsonFactory factory = newFactory(); JsonParser parser; @@ -428,15 +426,22 @@ public void testParser_numberTypes() throws Exception { } } + @Test public void testToFromString() throws Exception { JsonFactory factory = newFactory(); NumberTypes result = factory.fromString(NUMBER_TYPES, NumberTypes.class); assertEquals(NUMBER_TYPES, factory.toString(result)); } - private static final String UTF8_VALUE = "123\u05D9\u05e0\u05D9\u05D1"; - private static final String UTF8_JSON = "{\"value\":\"" + UTF8_VALUE + "\"}"; + private static final String UTF8_VALUE; + private static final String UTF8_JSON; + static { + UTF8_VALUE = "123\u05D9\u05e0\u05D9\u05D1"; + UTF8_JSON = "{\"value\":\"" + UTF8_VALUE + "\"}"; + } + + @Test public void testToFromString_UTF8() throws Exception { JsonFactory factory = newFactory(); GenericJson result = factory.fromString(UTF8_JSON, GenericJson.class); @@ -445,23 +450,23 @@ public void testToFromString_UTF8() throws Exception { } public static class AnyType { - @Key - public Object arr; - @Key - public Object bool; - @Key - public Object num; - @Key - public Object obj; - @Key - public Object str; - @Key - public Object nul; + @Key public Object arr; + @Key public Object bool; + @Key public Object num; + @Key public Object obj; + @Key public Object str; + @Key public Object nul; } - static final String ANY_TYPE = "{\"arr\":[1],\"bool\":true,\"nul\":null,\"num\":5," - + "\"obj\":{\"key\":\"value\"},\"str\":\"value\"}"; + static final String ANY_TYPE; + static { + ANY_TYPE = + "{\"arr\":[1],\"bool\":true,\"nul\":null,\"num\":5," + + "\"obj\":{\"key\":\"value\"},\"str\":\"value\"}"; + } + + @Test public void testParser_anyType() throws Exception { JsonFactory factory = newFactory(); JsonParser parser; @@ -472,18 +477,20 @@ public void testParser_anyType() throws Exception { } public static class ArrayType { - @Key - int[] arr; + @Key int[] arr; - @Key - int[][] arr2; + @Key int[][] arr2; - @Key - public Integer[] integerArr; + @Key public Integer[] integerArr; } - static final String ARRAY_TYPE = "{\"arr\":[4,5],\"arr2\":[[1,2],[3]],\"integerArr\":[6,7]}"; + static final String ARRAY_TYPE; + + static { + ARRAY_TYPE = "{\"arr\":[4,5],\"arr2\":[[1,2],[3]],\"integerArr\":[6,7]}"; + } + @Test public void testParser_arrayType() throws Exception { JsonFactory factory = newFactory(); JsonParser parser; @@ -510,12 +517,16 @@ public void testParser_arrayType() throws Exception { } public static class CollectionOfCollectionType { - @Key - public LinkedList> arr; + @Key public LinkedList> arr; } - static final String COLLECTION_TYPE = "{\"arr\":[[\"a\",\"b\"],[\"c\"]]}"; + static final String COLLECTION_TYPE; + static { + COLLECTION_TYPE = "{\"arr\":[[\"a\",\"b\"],[\"c\"]]}"; + } + + @Test public void testParser_collectionType() throws Exception { JsonFactory factory = newFactory(); JsonParser parser; @@ -530,13 +541,16 @@ public void testParser_collectionType() throws Exception { } public static class MapOfMapType { - @Key - public Map>[] value; + @Key public Map>[] value; } - static final String MAP_TYPE = - "{\"value\":[{\"map1\":{\"k1\":1,\"k2\":2},\"map2\":{\"kk1\":3,\"kk2\":4}}]}"; + static final String MAP_TYPE; + + static { + MAP_TYPE = "{\"value\":[{\"map1\":{\"k1\":1,\"k2\":2},\"map2\":{\"kk1\":3,\"kk2\":4}}]}"; + } + @Test public void testParser_mapType() throws Exception { // parse JsonFactory factory = newFactory(); @@ -557,6 +571,7 @@ public void testParser_mapType() throws Exception { assertEquals(4, map2.get("kk2").intValue()); } + @Test public void testParser_hashmapForMapType() throws Exception { // parse JsonFactory factory = newFactory(); @@ -577,25 +592,24 @@ public void testParser_hashmapForMapType() throws Exception { } public static class WildCardTypes { - @Key - public Collection[] lower; - @Key - public Map map; - @Key - public Collection> mapInWild; - @Key - public Map mapUpper; - @Key - public Collection[] simple; - @Key - public Collection[] upper; + @Key public Collection[] lower; + @Key public Map map; + @Key public Collection> mapInWild; + @Key public Map mapUpper; + @Key public Collection[] simple; + @Key public Collection[] upper; } - static final String WILDCARD_TYPE = - "{\"lower\":[[1,2,3]],\"map\":{\"v\":1},\"mapInWild\":[{\"v\":1}]," - + "\"mapUpper\":{\"v\":1},\"simple\":[[1,2,3]],\"upper\":[[1,2,3]]}"; + static final String WILDCARD_TYPE; + + static { + WILDCARD_TYPE = + "{\"lower\":[[1,2,3]],\"map\":{\"v\":1},\"mapInWild\":[{\"v\":1}]," + + "\"mapUpper\":{\"v\":1},\"simple\":[[1,2,3]],\"upper\":[[1,2,3]]}"; + } @SuppressWarnings("unchecked") + @Test public void testParser_wildCardType() throws Exception { // parse JsonFactory factory = newFactory(); @@ -633,45 +647,48 @@ public void testParser_wildCardType() throws Exception { public static class TypeVariableType { - @Key - public T[][] arr; + @Key public T[][] arr; - @Key - public LinkedList> list; + @Key public LinkedList> list; - @Key - public T nullValue; + @Key public T nullValue; - @Key - public T value; + @Key public T value; } - public static class IntegerTypeVariableType extends TypeVariableType { - } + public static class IntegerTypeVariableType extends TypeVariableType {} - public static class IntArrayTypeVariableType extends TypeVariableType { - } + public static class IntArrayTypeVariableType extends TypeVariableType {} - public static class DoubleListTypeVariableType extends TypeVariableType> { - } + public static class DoubleListTypeVariableType extends TypeVariableType> {} - public static class FloatMapTypeVariableType extends TypeVariableType> { - } + public static class FloatMapTypeVariableType extends TypeVariableType> {} + + static final String INTEGER_TYPE_VARIABLE_TYPE; + + static final String INT_ARRAY_TYPE_VARIABLE_TYPE; + + static final String DOUBLE_LIST_TYPE_VARIABLE_TYPE; - static final String INTEGER_TYPE_VARIABLE_TYPE = - "{\"arr\":[null,[null,1]],\"list\":[null,[null,1]],\"nullValue\":null,\"value\":1}"; + static final String FLOAT_MAP_TYPE_VARIABLE_TYPE; - static final String INT_ARRAY_TYPE_VARIABLE_TYPE = - "{\"arr\":[null,[null,[1]]],\"list\":[null,[null,[1]]],\"nullValue\":null,\"value\":[1]}"; + static { + INTEGER_TYPE_VARIABLE_TYPE = + "{\"arr\":[null,[null,1]],\"list\":[null,[null,1]],\"nullValue\":null,\"value\":1}"; - static final String DOUBLE_LIST_TYPE_VARIABLE_TYPE = - "{\"arr\":[null,[null,[1.0]]],\"list\":[null,[null,[1.0]]]," - + "\"nullValue\":null,\"value\":[1.0]}"; + INT_ARRAY_TYPE_VARIABLE_TYPE = + "{\"arr\":[null,[null,[1]]],\"list\":[null,[null,[1]]],\"nullValue\":null,\"value\":[1]}"; - static final String FLOAT_MAP_TYPE_VARIABLE_TYPE = - "{\"arr\":[null,[null,{\"a\":1.0}]],\"list\":[null,[null,{\"a\":1.0}]]," - + "\"nullValue\":null,\"value\":{\"a\":1.0}}"; + DOUBLE_LIST_TYPE_VARIABLE_TYPE = + "{\"arr\":[null,[null,[1.0]]],\"list\":[null,[null,[1.0]]]," + + "\"nullValue\":null,\"value\":[1.0]}"; + FLOAT_MAP_TYPE_VARIABLE_TYPE = + "{\"arr\":[null,[null,{\"a\":1.0}]],\"list\":[null,[null,{\"a\":1.0}]]," + + "\"nullValue\":null,\"value\":{\"a\":1.0}}"; + } + + @Test public void testParser_integerTypeVariableType() throws Exception { // parse JsonFactory factory = newFactory(); @@ -708,6 +725,7 @@ public void testParser_integerTypeVariableType() throws Exception { assertEquals(1, value.intValue()); } + @Test public void testParser_intArrayTypeVariableType() throws Exception { // parse JsonFactory factory = newFactory(); @@ -744,6 +762,7 @@ public void testParser_intArrayTypeVariableType() throws Exception { assertTrue(Arrays.equals(new int[] {1}, value)); } + @Test public void testParser_doubleListTypeVariableType() throws Exception { // parse JsonFactory factory = newFactory(); @@ -764,7 +783,7 @@ public void testParser_doubleListTypeVariableType() throws Exception { List arrValue = subArr[1]; assertEquals(1, arrValue.size()); Double dValue = arrValue.get(0); - assertEquals(1.0, dValue); + assertEquals(Double.valueOf(1.0), dValue); // collection LinkedList>> list = result.list; assertEquals(2, list.size()); @@ -782,6 +801,7 @@ public void testParser_doubleListTypeVariableType() throws Exception { assertEquals(ImmutableList.of(Double.valueOf(1)), value); } + @Test public void testParser_floatMapTypeVariableType() throws Exception { // parse JsonFactory factory = newFactory(); @@ -802,7 +822,7 @@ public void testParser_floatMapTypeVariableType() throws Exception { Map arrValue = subArr[1]; assertEquals(1, arrValue.size()); Float fValue = arrValue.get("a"); - assertEquals(1.0f, fValue); + assertEquals(Float.valueOf(1.0f), fValue); // collection LinkedList>> list = result.list; assertEquals(2, list.size()); @@ -813,7 +833,7 @@ public void testParser_floatMapTypeVariableType() throws Exception { arrValue = subList.get(1); assertEquals(1, arrValue.size()); fValue = arrValue.get("a"); - assertEquals(1.0f, fValue); + assertEquals(Float.valueOf(1.0f), fValue); // null value Map nullValue = result.nullValue; assertEquals(Data.nullOf(HashMap.class), nullValue); @@ -821,10 +841,11 @@ public void testParser_floatMapTypeVariableType() throws Exception { Map value = result.value; assertEquals(1, value.size()); fValue = value.get("a"); - assertEquals(1.0f, fValue); + assertEquals(Float.valueOf(1.0f), fValue); } @SuppressWarnings("unchecked") + @Test public void testParser_treemapForTypeVariableType() throws Exception { // parse JsonFactory factory = newFactory(); @@ -853,16 +874,18 @@ public void testParser_treemapForTypeVariableType() throws Exception { } public static class StringNullValue { - @Key - public String[][] arr2; - @Key - public String[] arr; - @Key - public String value; + @Key public String[][] arr2; + @Key public String[] arr; + @Key public String value; } - static final String NULL_VALUE = "{\"arr\":[null],\"arr2\":[null,[null]],\"value\":null}"; + static final String NULL_VALUE; + static { + NULL_VALUE = "{\"arr\":[null],\"arr2\":[null,[null]],\"value\":null}"; + } + + @Test public void testParser_nullValue() throws Exception { // parse JsonFactory factory = newFactory(); @@ -886,27 +909,28 @@ public void testParser_nullValue() throws Exception { } public enum E { - @Value VALUE, @Value("other") OTHER_VALUE, @NullValue - NULL, IGNORED_VALUE + NULL, + IGNORED_VALUE } public static class EnumValue { - @Key - public E value; - @Key - public E otherValue; - @Key - public E nullValue; + @Key public E value; + @Key public E otherValue; + @Key public E nullValue; } - static final String ENUM_VALUE = - "{\"nullValue\":null,\"otherValue\":\"other\",\"value\":\"VALUE\"}"; + static final String ENUM_VALUE; + static { + ENUM_VALUE = "{\"nullValue\":null,\"otherValue\":\"other\",\"value\":\"VALUE\"}"; + } + + @Test public void testParser_enumValue() throws Exception { // parse JsonFactory factory = newFactory(); @@ -923,25 +947,26 @@ public void testParser_enumValue() throws Exception { } public static class X { - @Key - Y y; + @Key Y y; } public static class Y { - @Key - Z z; + @Key Z z; } public static class Z { - @Key - ZT f; + @Key ZT f; } - public static class TypeVariablesPassedAround extends X> { - } + public static class TypeVariablesPassedAround extends X> {} - static final String TYPE_VARS = "{\"y\":{\"z\":{\"f\":[\"abc\"]}}}"; + static final String TYPE_VARS; + static { + TYPE_VARS = "{\"y\":{\"z\":{\"f\":[\"abc\"]}}}"; + } + + @Test public void testParser_typeVariablesPassAround() throws Exception { // parse JsonFactory factory = newFactory(); @@ -956,8 +981,13 @@ public void testParser_typeVariablesPassAround() throws Exception { assertEquals("abc", f.get(0)); } - static final String STRING_ARRAY = "[\"a\",\"b\",\"c\"]"; + static final String STRING_ARRAY; + + static { + STRING_ARRAY = "[\"a\",\"b\",\"c\"]"; + } + @Test public void testParser_stringArray() throws Exception { JsonFactory factory = newFactory(); JsonParser parser; @@ -969,8 +999,13 @@ public void testParser_stringArray() throws Exception { assertTrue(Arrays.equals(new String[] {"a", "b", "c"}, result)); } - static final String INT_ARRAY = "[1,2,3]"; + static final String INT_ARRAY; + + static { + INT_ARRAY = "[1,2,3]"; + } + @Test public void testParser_intArray() throws Exception { JsonFactory factory = newFactory(); JsonParser parser; @@ -982,8 +1017,13 @@ public void testParser_intArray() throws Exception { assertTrue(Arrays.equals(new int[] {1, 2, 3}, result)); } - private static final String EMPTY_ARRAY = "[]"; + private static final String EMPTY_ARRAY; + static { + EMPTY_ARRAY = "[]"; + } + + @Test public void testParser_emptyArray() throws Exception { JsonFactory factory = newFactory(); String[] result = factory.createJsonParser(EMPTY_ARRAY).parse(String[].class); @@ -992,6 +1032,7 @@ public void testParser_emptyArray() throws Exception { assertEquals(0, result.length); } + @Test public void testParser_partialEmptyArray() throws Exception { JsonFactory factory = newFactory(); JsonParser parser; @@ -1005,8 +1046,13 @@ public void testParser_partialEmptyArray() throws Exception { assertEquals(0, result.length); } - private static final String NUMBER_TOP_VALUE = "1"; + private static final String NUMBER_TOP_VALUE; + + static { + NUMBER_TOP_VALUE = "1"; + } + @Test public void testParser_num() throws Exception { JsonFactory factory = newFactory(); int result = factory.createJsonParser(NUMBER_TOP_VALUE).parse(int.class); @@ -1015,8 +1061,13 @@ public void testParser_num() throws Exception { assertEquals(1, result); } - private static final String STRING_TOP_VALUE = "\"a\""; + private static final String STRING_TOP_VALUE; + + static { + STRING_TOP_VALUE = "\"a\""; + } + @Test public void testParser_string() throws Exception { JsonFactory factory = newFactory(); String result = factory.createJsonParser(STRING_TOP_VALUE).parse(String.class); @@ -1025,8 +1076,13 @@ public void testParser_string() throws Exception { assertEquals("a", result); } - private static final String NULL_TOP_VALUE = "null"; + private static final String NULL_TOP_VALUE; + static { + NULL_TOP_VALUE = "null"; + } + + @Test public void testParser_null() throws Exception { JsonFactory factory = newFactory(); String result = factory.createJsonParser(NULL_TOP_VALUE).parse(String.class); @@ -1035,8 +1091,13 @@ public void testParser_null() throws Exception { assertTrue(Data.isNull(result)); } - private static final String BOOL_TOP_VALUE = "true"; + private static final String BOOL_TOP_VALUE; + static { + BOOL_TOP_VALUE = "true"; + } + + @Test public void testParser_bool() throws Exception { JsonFactory factory = newFactory(); boolean result = factory.createJsonParser(BOOL_TOP_VALUE).parse(boolean.class); @@ -1118,6 +1179,7 @@ public final void testToPrettyString_FeedApproximate() throws Exception { assertEquals(JSON_FEED, factory.toString(factory.fromString(prettyString, Feed.class))); } + @Test public void testParser_nullInputStream() throws Exception { try { newFactory().createJsonParser((InputStream) null, Charsets.UTF_8); @@ -1127,6 +1189,7 @@ public void testParser_nullInputStream() throws Exception { } } + @Test public void testParser_nullString() throws Exception { try { newFactory().createJsonParser((String) null); @@ -1136,6 +1199,7 @@ public void testParser_nullString() throws Exception { } } + @Test public void testParser_nullReader() throws Exception { try { newFactory().createJsonParser((Reader) null); @@ -1145,24 +1209,34 @@ public void testParser_nullReader() throws Exception { } } + @Test public void testObjectParserParse_entry() throws Exception { @SuppressWarnings("serial") - Entry entry = (Entry) newFactory().createJsonObjectParser() - .parseAndClose(new StringReader(JSON_ENTRY), new TypeToken() {}.getType()); + Entry entry = + (Entry) + newFactory() + .createJsonObjectParser() + .parseAndClose(new StringReader(JSON_ENTRY), new TypeToken() {}.getType()); assertEquals("foo", entry.title); } + @Test public void testObjectParserParse_stringList() throws Exception { JsonFactory factory = newFactory(); @SuppressWarnings({"unchecked", "serial"}) - List result = (List) factory.createJsonObjectParser() - .parseAndClose(new StringReader(STRING_ARRAY), new TypeToken>() {}.getType()); + List result = + (List) + factory + .createJsonObjectParser() + .parseAndClose( + new StringReader(STRING_ARRAY), new TypeToken>() {}.getType()); result.get(0); assertEquals(STRING_ARRAY, factory.toString(result)); // check types and values assertTrue(ImmutableList.of("a", "b", "c").equals(result)); } + @Test public void testToString_withFactory() { GenericJson data = new GenericJson(); data.put("a", "b"); @@ -1170,6 +1244,7 @@ public void testToString_withFactory() { assertEquals("{\"a\":\"b\"}", data.toString()); } + @Test public void testFactory() { JsonFactory factory = newFactory(); GenericJson data = new GenericJson(); @@ -1182,6 +1257,7 @@ private JsonParser createParser(String json) throws Exception { return newFactory().createJsonParser(json); } + @Test public void testSkipToKey_firstKey() throws Exception { JsonParser parser = createParser(JSON_THREE_ELEMENTS); assertEquals("one", parser.skipToKey(ImmutableSet.of("one"))); @@ -1189,6 +1265,7 @@ public void testSkipToKey_firstKey() throws Exception { assertEquals(1, parser.getIntValue()); } + @Test public void testSkipToKey_lastKey() throws Exception { JsonParser parser = createParser(JSON_THREE_ELEMENTS); assertEquals("three", parser.skipToKey(ImmutableSet.of("three"))); @@ -1196,6 +1273,7 @@ public void testSkipToKey_lastKey() throws Exception { assertEquals(3, parser.getIntValue()); } + @Test public void testSkipToKey_multipleKeys() throws Exception { JsonParser parser = createParser(JSON_THREE_ELEMENTS); assertEquals("two", parser.skipToKey(ImmutableSet.of("foo", "three", "two"))); @@ -1203,6 +1281,7 @@ public void testSkipToKey_multipleKeys() throws Exception { assertEquals(2, parser.getIntValue()); } + @Test public void testSkipToKey_noMatch() throws Exception { JsonParser parser = createParser(JSON_THREE_ELEMENTS); assertEquals(null, parser.skipToKey(ImmutableSet.of("foo", "bar", "num"))); @@ -1232,8 +1311,7 @@ public final void testParse_array() throws Exception { } public static class TestClass { - public TestClass() { - } + public TestClass() {} @Key("foo") public int foo; @@ -1284,9 +1362,7 @@ public final void testGenerate_infinityOrNanError() throws Exception { } public static class ExtendsGenericJson extends GenericJson { - @Key - @JsonString - Long numAsString; + @Key @JsonString Long numAsString; @Override public ExtendsGenericJson set(String fieldName, Object value) { @@ -1294,8 +1370,13 @@ public ExtendsGenericJson set(String fieldName, Object value) { } } - static final String EXTENDS_JSON = "{\"numAsString\":\"1\",\"num\":1}"; + static final String EXTENDS_JSON; + + static { + EXTENDS_JSON = "{\"numAsString\":\"1\",\"num\":1}"; + } + @Test public void testParser_extendsGenericJson() throws Exception { JsonFactory factory = newFactory(); JsonParser parser; @@ -1307,13 +1388,18 @@ public void testParser_extendsGenericJson() throws Exception { } public static class Simple { - @Key - String a; + @Key String a; } - static final String SIMPLE = "{\"a\":\"b\"}"; - static final String SIMPLE_WRAPPED = "{\"d\":{\"a\":\"b\"}}"; + static final String SIMPLE; + static final String SIMPLE_WRAPPED; + + static { + SIMPLE = "{\"a\":\"b\"}"; + SIMPLE_WRAPPED = "{\"d\":{\"a\":\"b\"}}"; + } + @Test public void testJsonObjectParser_reader() throws Exception { JsonFactory factory = newFactory(); JsonObjectParser parser = new JsonObjectParser(factory); @@ -1321,14 +1407,19 @@ public void testJsonObjectParser_reader() throws Exception { assertEquals("b", simple.a); } + @Test public void testJsonObjectParser_inputStream() throws Exception { JsonFactory factory = newFactory(); JsonObjectParser parser = new JsonObjectParser(factory); - Simple simple = parser.parseAndClose( - new ByteArrayInputStream(StringUtils.getBytesUtf8(SIMPLE)), Charsets.UTF_8, Simple.class); + Simple simple = + parser.parseAndClose( + new ByteArrayInputStream(StringUtils.getBytesUtf8(SIMPLE)), + Charsets.UTF_8, + Simple.class); assertEquals("b", simple.a); } + @Test public void testJsonObjectParser_readerWrapped() throws Exception { JsonFactory factory = newFactory(); JsonObjectParser parser = @@ -1337,16 +1428,20 @@ public void testJsonObjectParser_readerWrapped() throws Exception { assertEquals("b", simple.a); } + @Test public void testJsonObjectParser_inputStreamWrapped() throws Exception { JsonFactory factory = newFactory(); JsonObjectParser parser = new JsonObjectParser.Builder(factory).setWrapperKeys(Collections.singleton("d")).build(); - Simple simple = parser.parseAndClose( - new ByteArrayInputStream(StringUtils.getBytesUtf8(SIMPLE_WRAPPED)), Charsets.UTF_8, - Simple.class); + Simple simple = + parser.parseAndClose( + new ByteArrayInputStream(StringUtils.getBytesUtf8(SIMPLE_WRAPPED)), + Charsets.UTF_8, + Simple.class); assertEquals("b", simple.a); } + @Test public void testJsonHttpContent_simple() throws Exception { JsonFactory factory = newFactory(); Simple simple = new Simple(); @@ -1357,6 +1452,7 @@ public void testJsonHttpContent_simple() throws Exception { assertEquals(SIMPLE, out.toString("UTF-8")); } + @Test public void testJsonHttpContent_wrapped() throws Exception { JsonFactory factory = newFactory(); Simple simple = new Simple(); @@ -1368,12 +1464,11 @@ public void testJsonHttpContent_wrapped() throws Exception { } public static class V { - @Key - Void v; - @Key - String s; + @Key Void v; + @Key String s; } + @Test public void testParse_void() throws Exception { subtestParse_void(null); subtestParse_void("\"a\""); @@ -1396,20 +1491,29 @@ public void subtestParse_void(String value) throws Exception { } public static class BooleanTypes { - @Key - Boolean boolObj; - @Key - boolean bool; + @Key Boolean boolObj; + @Key boolean bool; } - public static final String BOOLEAN_TYPE_EMPTY = "{}"; - public static final String BOOLEAN_TYPE_EMPTY_OUTPUT = "{\"bool\":false}"; - public static final String BOOLEAN_TYPE_TRUE = "{\"bool\":true,\"boolObj\":true}"; - public static final String BOOLEAN_TYPE_FALSE = "{\"bool\":false,\"boolObj\":false}"; - public static final String BOOLEAN_TYPE_NULL = "{\"boolObj\":null}"; - public static final String BOOLEAN_TYPE_NULL_OUTPUT = "{\"bool\":false,\"boolObj\":null}"; - public static final String BOOLEAN_TYPE_WRONG = "{\"boolObj\":{}}"; + public static final String BOOLEAN_TYPE_EMPTY; + public static final String BOOLEAN_TYPE_EMPTY_OUTPUT; + public static final String BOOLEAN_TYPE_TRUE; + public static final String BOOLEAN_TYPE_FALSE; + public static final String BOOLEAN_TYPE_NULL; + public static final String BOOLEAN_TYPE_NULL_OUTPUT; + public static final String BOOLEAN_TYPE_WRONG; + static { + BOOLEAN_TYPE_EMPTY = "{}"; + BOOLEAN_TYPE_EMPTY_OUTPUT = "{\"bool\":false}"; + BOOLEAN_TYPE_TRUE = "{\"bool\":true,\"boolObj\":true}"; + BOOLEAN_TYPE_FALSE = "{\"bool\":false,\"boolObj\":false}"; + BOOLEAN_TYPE_NULL = "{\"boolObj\":null}"; + BOOLEAN_TYPE_NULL_OUTPUT = "{\"bool\":false,\"boolObj\":null}"; + BOOLEAN_TYPE_WRONG = "{\"boolObj\":{}}"; + } + + @Test public void testParse_boolean() throws Exception { JsonFactory factory = newFactory(); BooleanTypes parsed; @@ -1442,21 +1546,25 @@ public void testParse_boolean() throws Exception { } public abstract static class Animal { - @Key - public String name; + @Key public String name; + @Key("legCount") public int numberOfLegs; + @Key - @JsonPolymorphicTypeMap(typeDefinitions = {@TypeDef(key = "dog", ref = Dog.class), - @TypeDef(key = "bug", ref = Centipede.class), @TypeDef(key = "human", ref = Human.class), - @TypeDef(key = "dogwithfamily", ref = DogWithFamily.class), - @TypeDef(key = "human with pets", ref = HumanWithPets.class)}) + @JsonPolymorphicTypeMap( + typeDefinitions = { + @TypeDef(key = "dog", ref = Dog.class), + @TypeDef(key = "bug", ref = Centipede.class), + @TypeDef(key = "human", ref = Human.class), + @TypeDef(key = "dogwithfamily", ref = DogWithFamily.class), + @TypeDef(key = "human with pets", ref = HumanWithPets.class) + }) public String type; } public static class Dog extends Animal { - @Key - public int tricksKnown; + @Key public int tricksKnown; } public static class Centipede extends Animal { @@ -1479,11 +1587,12 @@ public static class Centipede extends Animal { // Test heterogeneous scheme with additional, unused information: public static final String DOG_EXTRA_INFO = "{\"name\":\"Fido\",\"legCount\":4,\"unusedInfo\":\"this is not being used!\"," - + "\"tricksKnown\":3,\"type\":\"dog\",\"unused\":{\"foo\":200}}"; + + "\"tricksKnown\":3,\"type\":\"dog\",\"unused\":{\"foo\":200}}"; public static final String CENTIPEDE_EXTRA_INFO = "{\"unused\":0, \"bodyColor\":\"green\",\"name\":\"Mr. Icky\",\"legCount\":68,\"type\":" - + "\"bug\"}"; + + "\"bug\"}"; + @Test public void testParser_heterogeneousSchemata() throws Exception { testParser_heterogeneousSchemata_Helper(DOG, CENTIPEDE); // TODO(ngmiceli): Test that this uses the optimized flow (once implemented) @@ -1521,6 +1630,7 @@ private void testParser_heterogeneousSchemata_Helper(String dogJson, String cent public static final String ANIMAL_WITHOUT_TYPE = "{\"legCount\":3,\"name\":\"Confused\"}"; + @Test public void testParser_heterogeneousSchema_missingType() throws Exception { JsonFactory factory = newFactory(); JsonParser parser; @@ -1534,14 +1644,14 @@ public void testParser_heterogeneousSchema_missingType() throws Exception { } public static class Human extends Animal { - @Key - public Dog bestFriend; + @Key public Dog bestFriend; } // Test a subclass with an additional object in it. public static final String HUMAN = "{\"bestFriend\":" + DOG + ",\"legCount\":2,\"name\":\"Joe\",\"type\":\"human\"}"; + @Test public void testParser_heterogeneousSchema_withObject() throws Exception { JsonFactory factory = newFactory(); JsonParser parser = factory.createJsonParser(HUMAN); @@ -1560,25 +1670,26 @@ public void testParser_heterogeneousSchema_withObject() throws Exception { } public static class AnimalGenericJson extends GenericJson { - @Key - public String name; + @Key public String name; + @Key("legCount") public int numberOfLegs; + @Key @JsonPolymorphicTypeMap(typeDefinitions = {@TypeDef(key = "dog", ref = DogGenericJson.class)}) public String type; } public static class DogGenericJson extends AnimalGenericJson { - @Key - public int tricksKnown; + @Key public int tricksKnown; } public static final String DOG_EXTRA_INFO_ORDERED = "{\"legCount\":4,\"name\":\"Fido\",\"tricksKnown\":3,\"type\":\"dog\"," - + "\"unusedInfo\":\"this is not being used!\",\"unused\":{\"foo\":200}}"; + + "\"unusedInfo\":\"this is not being used!\",\"unused\":{\"foo\":200}}"; @SuppressWarnings("unchecked") + @Test public void testParser_heterogeneousSchema_genericJson() throws Exception { JsonFactory factory = newFactory(); JsonParser parser = factory.createJsonParser(DOG_EXTRA_INFO); @@ -1594,17 +1705,20 @@ public void testParser_heterogeneousSchema_genericJson() throws Exception { assertEquals(200, foo.intValue()); } - public static final String DOG_WITH_FAMILY = "{\"children\":[" + DOG + "," + CENTIPEDE - + "],\"legCount\":4,\"name\":\"Bob\",\"nicknames\":[\"Fluffy\",\"Hey, you\"]," - + "\"tricksKnown\":10,\"type\":\"dogwithfamily\"}"; + public static final String DOG_WITH_FAMILY = + "{\"children\":[" + + DOG + + "," + + CENTIPEDE + + "],\"legCount\":4,\"name\":\"Bob\",\"nicknames\":[\"Fluffy\",\"Hey, you\"]," + + "\"tricksKnown\":10,\"type\":\"dogwithfamily\"}"; public static class DogWithFamily extends Dog { - @Key - public String[] nicknames; - @Key - public Animal[] children; + @Key public String[] nicknames; + @Key public Animal[] children; } + @Test public void testParser_heterogeneousSchema_withArrays() throws Exception { JsonFactory factory = newFactory(); JsonParser parser = factory.createJsonParser(DOG_WITH_FAMILY); @@ -1629,6 +1743,7 @@ public void testParser_heterogeneousSchema_withArrays() throws Exception { public static final String DOG_WITH_NO_FAMILY_PARSED = "{\"legCount\":4,\"tricksKnown\":0,\"type\":\"dogwithfamily\"}"; + @Test public void testParser_heterogeneousSchema_withNullArrays() throws Exception { JsonFactory factory = newFactory(); JsonParser parser = factory.createJsonParser(DOG_WITH_NO_FAMILY); @@ -1643,13 +1758,14 @@ public void testParser_heterogeneousSchema_withNullArrays() throws Exception { } public static class PolymorphicWithMultipleAnnotations { - @Key - String a; + @Key String a; + @Key @JsonPolymorphicTypeMap(typeDefinitions = {@TypeDef(key = "dog", ref = Dog.class)}) String b; - @Key - String c; + + @Key String c; + @Key @JsonPolymorphicTypeMap(typeDefinitions = {@TypeDef(key = "bug", ref = Centipede.class)}) String d; @@ -1658,6 +1774,7 @@ public static class PolymorphicWithMultipleAnnotations { public static final String MULTIPLE_ANNOTATIONS_JSON = "{\"a\":\"foo\",\"b\":\"dog\",\"c\":\"bar\",\"d\":\"bug\"}"; + @Test public void testParser_polymorphicClass_tooManyAnnotations() throws Exception { JsonFactory factory = newFactory(); JsonParser parser = factory.createJsonParser(MULTIPLE_ANNOTATIONS_JSON); @@ -1666,26 +1783,29 @@ public void testParser_polymorphicClass_tooManyAnnotations() throws Exception { } catch (IllegalArgumentException e) { return; // expected } - fail("Expected IllegalArgumentException on class with multiple @JsonPolymorphicTypeMap" - + " annotations."); + fail( + "Expected IllegalArgumentException on class with multiple @JsonPolymorphicTypeMap" + + " annotations."); } public static class PolymorphicWithNumericType { @Key - @JsonPolymorphicTypeMap(typeDefinitions = { - @TypeDef(key = "1", ref = NumericTypedSubclass1.class), - @TypeDef(key = "2", ref = NumericTypedSubclass2.class)}) + @JsonPolymorphicTypeMap( + typeDefinitions = { + @TypeDef(key = "1", ref = NumericTypedSubclass1.class), + @TypeDef(key = "2", ref = NumericTypedSubclass2.class) + }) Integer type; } - public static class NumericTypedSubclass1 extends PolymorphicWithNumericType { - } - public static class NumericTypedSubclass2 extends PolymorphicWithNumericType { - } + public static class NumericTypedSubclass1 extends PolymorphicWithNumericType {} + + public static class NumericTypedSubclass2 extends PolymorphicWithNumericType {} public static final String POLYMORPHIC_NUMERIC_TYPE_1 = "{\"foo\":\"bar\",\"type\":1}"; public static final String POLYMORPHIC_NUMERIC_TYPE_2 = "{\"foo\":\"bar\",\"type\":2}"; + @Test public void testParser_heterogeneousSchema_numericType() throws Exception { JsonFactory factory = newFactory(); JsonParser parser = factory.createJsonParser(POLYMORPHIC_NUMERIC_TYPE_1); @@ -1700,19 +1820,21 @@ public void testParser_heterogeneousSchema_numericType() throws Exception { public static class PolymorphicWithNumericValueType { @Key - @JsonPolymorphicTypeMap(typeDefinitions = { - @TypeDef(key = "1", ref = NumericValueTypedSubclass1.class), - @TypeDef(key = "2", ref = NumericValueTypedSubclass2.class)}) + @JsonPolymorphicTypeMap( + typeDefinitions = { + @TypeDef(key = "1", ref = NumericValueTypedSubclass1.class), + @TypeDef(key = "2", ref = NumericValueTypedSubclass2.class) + }) int type; } - public static class NumericValueTypedSubclass1 extends PolymorphicWithNumericValueType { - } - public static class NumericValueTypedSubclass2 extends PolymorphicWithNumericValueType { - } + public static class NumericValueTypedSubclass1 extends PolymorphicWithNumericValueType {} + + public static class NumericValueTypedSubclass2 extends PolymorphicWithNumericValueType {} public static final String POLYMORPHIC_NUMERIC_UNSPECIFIED_TYPE = "{\"foo\":\"bar\"}"; + @Test public void testParser_heterogeneousSchema_numericValueType() throws Exception { JsonFactory factory = newFactory(); JsonParser parser = factory.createJsonParser(POLYMORPHIC_NUMERIC_TYPE_1); @@ -1736,11 +1858,15 @@ public void testParser_heterogeneousSchema_numericValueType() throws Exception { public static class PolymorphicWithIllegalValueType { @Key - @JsonPolymorphicTypeMap(typeDefinitions = { - @TypeDef(key = "foo", ref = Object.class), @TypeDef(key = "bar", ref = Object.class)}) + @JsonPolymorphicTypeMap( + typeDefinitions = { + @TypeDef(key = "foo", ref = Object.class), + @TypeDef(key = "bar", ref = Object.class) + }) Object type; } + @Test public void testParser_heterogeneousSchema_illegalValueType() throws Exception { JsonFactory factory = newFactory(); JsonParser parser = factory.createJsonParser(POLYMORPHIC_NUMERIC_TYPE_1); @@ -1752,14 +1878,17 @@ public void testParser_heterogeneousSchema_illegalValueType() throws Exception { fail("Expected IllegalArgumentException on class with illegal @JsonPolymorphicTypeMap type"); } - public static class PolymorphicWithDuplicateTypeKeys { @Key - @JsonPolymorphicTypeMap(typeDefinitions = { - @TypeDef(key = "foo", ref = Object.class), @TypeDef(key = "foo", ref = Object.class)}) + @JsonPolymorphicTypeMap( + typeDefinitions = { + @TypeDef(key = "foo", ref = Object.class), + @TypeDef(key = "foo", ref = Object.class) + }) String type; } + @Test public void testParser_polymorphicClass_duplicateTypeKeys() throws Exception { JsonFactory factory = newFactory(); JsonParser parser = factory.createJsonParser(EMPTY_OBJECT); @@ -1774,6 +1903,7 @@ public void testParser_polymorphicClass_duplicateTypeKeys() throws Exception { public static final String POLYMORPHIC_WITH_UNKNOWN_KEY = "{\"legCount\":4,\"name\":\"Fido\",\"tricksKnown\":3,\"type\":\"unknown\"}"; + @Test public void testParser_polymorphicClass_noMatchingTypeKey() throws Exception { JsonFactory factory = newFactory(); JsonParser parser = factory.createJsonParser(POLYMORPHIC_WITH_UNKNOWN_KEY); @@ -1790,12 +1920,13 @@ public static class PolymorphicSelfReferencing { @JsonPolymorphicTypeMap( typeDefinitions = {@TypeDef(key = "self", ref = PolymorphicSelfReferencing.class)}) String type; - @Key - String info; + + @Key String info; } public static final String POLYMORPHIC_SELF_REFERENCING = "{\"info\":\"blah\",\"type\":\"self\"}"; + @Test public void testParser_polymorphicClass_selfReferencing() throws Exception { JsonFactory factory = newFactory(); JsonParser parser = factory.createJsonParser(POLYMORPHIC_SELF_REFERENCING); @@ -1807,19 +1938,25 @@ public void testParser_polymorphicClass_selfReferencing() throws Exception { } public static class HumanWithPets extends Human { - @Key - Map pets; + @Key Map pets; } - public static final String HUMAN_WITH_PETS = "{\"bestFriend\":" + DOG - + ",\"legCount\":2,\"name\":\"Joe\",\"pets\":{\"first\":" + CENTIPEDE - + ",\"second\":{\"type\":\"dog\"}},\"type\":\"human with pets\",\"unused\":\"foo\"}"; + public static final String HUMAN_WITH_PETS = + "{\"bestFriend\":" + + DOG + + ",\"legCount\":2,\"name\":\"Joe\",\"pets\":{\"first\":" + + CENTIPEDE + + ",\"second\":{\"type\":\"dog\"}},\"type\":\"human with pets\",\"unused\":\"foo\"}"; - public static final String HUMAN_WITH_PETS_PARSED = "{\"bestFriend\":" + DOG - + ",\"legCount\":2,\"name\":\"Joe\",\"pets\":{\"first\":" + CENTIPEDE - + ",\"second\":{\"legCount\":0,\"tricksKnown\":0,\"type\":\"dog\"}}," - + "\"type\":\"human with pets\"}"; + public static final String HUMAN_WITH_PETS_PARSED = + "{\"bestFriend\":" + + DOG + + ",\"legCount\":2,\"name\":\"Joe\",\"pets\":{\"first\":" + + CENTIPEDE + + ",\"second\":{\"legCount\":0,\"tricksKnown\":0,\"type\":\"dog\"}}," + + "\"type\":\"human with pets\"}"; + @Test public void testParser_polymorphicClass_mapOfPolymorphicClasses() throws Exception { JsonFactory factory = newFactory(); JsonParser parser = factory.createJsonParser(HUMAN_WITH_PETS); @@ -1839,6 +1976,4 @@ public void testParser_polymorphicClass_mapOfPolymorphicClasses() throws Excepti assertEquals(0, ((Dog) humanWithPets.pets.get("second")).tricksKnown); assertEquals(2, humanWithPets.pets.size()); } - - } diff --git a/google-http-client-test/src/main/java/com/google/api/client/test/json/AbstractJsonGeneratorTest.java b/google-http-client-test/src/main/java/com/google/api/client/test/json/AbstractJsonGeneratorTest.java new file mode 100644 index 000000000..96db2b1f5 --- /dev/null +++ b/google-http-client-test/src/main/java/com/google/api/client/test/json/AbstractJsonGeneratorTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2018 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.test.json; + +import static org.junit.Assert.assertEquals; + +import com.google.api.client.json.JsonGenerator; +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public abstract class AbstractJsonGeneratorTest { + + protected abstract JsonGenerator newGenerator(Writer writer) throws IOException; + + class IterableMap extends HashMap implements Iterable> { + @Override + public Iterator> iterator() { + return entrySet().iterator(); + } + } + + @Test + public void testSerialize_simpleMap() throws Exception { + StringWriter writer = new StringWriter(); + JsonGenerator generator = newGenerator(writer); + + Map map = new HashMap(); + map.put("a", "b"); + + generator.serialize(map); + generator.close(); + assertEquals("{\"a\":\"b\"}", writer.toString()); + } + + @Test + public void testSerialize_iterableMap() throws Exception { + StringWriter writer = new StringWriter(); + JsonGenerator generator = newGenerator(writer); + + Map map = new IterableMap(); + map.put("a", "b"); + + generator.serialize(map); + generator.close(); + assertEquals("{\"a\":\"b\"}", writer.toString()); + } +} diff --git a/google-http-client-test/src/main/java/com/google/api/client/test/json/AbstractJsonParserTest.java b/google-http-client-test/src/main/java/com/google/api/client/test/json/AbstractJsonParserTest.java new file mode 100644 index 000000000..25bb77bb4 --- /dev/null +++ b/google-http-client-test/src/main/java/com/google/api/client/test/json/AbstractJsonParserTest.java @@ -0,0 +1,95 @@ +/** + * 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 + * + *

    https://www.apache.org/licenses/LICENSE-2.0 + * + *

    Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.api.client.test.json; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.google.api.client.json.GenericJson; +import com.google.api.client.json.JsonFactory; +import com.google.api.client.json.JsonObjectParser; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public abstract class AbstractJsonParserTest { + + protected abstract JsonFactory newJsonFactory(); + + private static final String TEST_JSON; + private static final String TEST_JSON_BIG_DECIMAL; + + static { + TEST_JSON = "{\"strValue\": \"bar\", \"intValue\": 123, \"boolValue\": false}"; + TEST_JSON_BIG_DECIMAL = "{\"bigDecimalValue\": 1559341956102}"; + } + + @Test + public void testParse_basic() throws IOException { + JsonObjectParser parser = new JsonObjectParser(newJsonFactory()); + InputStream inputStream = new ByteArrayInputStream(TEST_JSON.getBytes(StandardCharsets.UTF_8)); + GenericJson json = parser.parseAndClose(inputStream, StandardCharsets.UTF_8, GenericJson.class); + + assertTrue(json.get("strValue") instanceof String); + assertEquals("bar", json.get("strValue")); + assertTrue(json.get("intValue") instanceof BigDecimal); + assertEquals(new BigDecimal(123), json.get("intValue")); + assertTrue(json.get("boolValue") instanceof Boolean); + assertEquals(Boolean.FALSE, json.get("boolValue")); + } + + @Test + public void testGetWrongType() throws IOException { + JsonObjectParser parser = new JsonObjectParser(newJsonFactory()); + InputStream inputStream = new ByteArrayInputStream(TEST_JSON.getBytes(StandardCharsets.UTF_8)); + GenericJson json = parser.parseAndClose(inputStream, StandardCharsets.UTF_8, GenericJson.class); + assertTrue(json.get("strValue") instanceof String); + assertEquals("bar", json.get("strValue")); + assertTrue(json.get("intValue") instanceof BigDecimal); + assertEquals(new BigDecimal(123), json.get("intValue")); + assertTrue(json.get("boolValue") instanceof Boolean); + assertEquals(Boolean.FALSE, json.get("boolValue")); + } + + @Test + public void testParse_badJson() throws IOException { + JsonObjectParser parser = new JsonObjectParser(newJsonFactory()); + InputStream inputStream = new ByteArrayInputStream("not json".getBytes(StandardCharsets.UTF_8)); + try { + parser.parseAndClose(inputStream, StandardCharsets.UTF_8, GenericJson.class); + fail("Malformed JSON not detected"); + } catch (IOException ex) { + Assert.assertNotNull(ex.getMessage()); + } + } + + @Test + public void testParse_bigDecimal() throws IOException { + JsonObjectParser parser = new JsonObjectParser(newJsonFactory()); + InputStream inputStream = + new ByteArrayInputStream(TEST_JSON_BIG_DECIMAL.getBytes(StandardCharsets.UTF_8)); + GenericJson json = parser.parseAndClose(inputStream, StandardCharsets.UTF_8, GenericJson.class); + + assertTrue(json.get("bigDecimalValue") instanceof BigDecimal); + assertEquals(new BigDecimal("1559341956102"), json.get("bigDecimalValue")); + } +} diff --git a/google-http-client-test/src/main/java/com/google/api/client/test/json/package-info.java b/google-http-client-test/src/main/java/com/google/api/client/test/json/package-info.java index 53de2931e..e633d85cf 100644 --- a/google-http-client-test/src/main/java/com/google/api/client/test/json/package-info.java +++ b/google-http-client-test/src/main/java/com/google/api/client/test/json/package-info.java @@ -17,6 +17,4 @@ * * @author Ravi Mistry */ - package com.google.api.client.test.json; - diff --git a/google-http-client-test/src/main/java/com/google/api/client/test/util/store/AbstractDataStoreFactoryTest.java b/google-http-client-test/src/main/java/com/google/api/client/test/util/store/AbstractDataStoreFactoryTest.java index 1986c6204..7a5f464c8 100644 --- a/google-http-client-test/src/main/java/com/google/api/client/test/util/store/AbstractDataStoreFactoryTest.java +++ b/google-http-client-test/src/main/java/com/google/api/client/test/util/store/AbstractDataStoreFactoryTest.java @@ -14,6 +14,12 @@ package com.google.api.client.test.util.store; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import com.google.api.client.util.Beta; import com.google.api.client.util.store.DataStore; import com.google.api.client.util.store.DataStoreFactory; @@ -21,7 +27,11 @@ import java.util.Arrays; import java.util.Collection; import java.util.Set; -import junit.framework.TestCase; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link DataStoreFactory}. @@ -29,10 +39,17 @@ * @author Yaniv Inbar */ @Beta -public abstract class AbstractDataStoreFactoryTest extends TestCase { +@RunWith(JUnit4.class) +public abstract class AbstractDataStoreFactoryTest { + + private static final String STRING_ID; + private static final String BOOLEAN_ID; + + static { + STRING_ID = "String"; + BOOLEAN_ID = "Boolean"; + } - private static final String STRING_ID = "String"; - private static final String BOOLEAN_ID = "Boolean"; DataStoreFactory dataStore; DataStore stringTyped; DataStore boolTyped; @@ -40,14 +57,14 @@ public abstract class AbstractDataStoreFactoryTest extends TestCase { /** Returns a new instance of the data store factory to test. */ protected abstract DataStoreFactory newDataStoreFactory() throws Exception; - @Override + @Before public void setUp() throws Exception { dataStore = newDataStoreFactory(); stringTyped = dataStore.getDataStore(STRING_ID); boolTyped = dataStore.getDataStore(BOOLEAN_ID); } - @Override + @After public void tearDown() throws Exception { stringTyped.clear(); assertTrue(stringTyped.values().isEmpty()); @@ -56,10 +73,10 @@ public void tearDown() throws Exception { } private static void assertContentsAnyOrder(Collection c, Object... elts) { - assertEquals(Sets.newHashSet(c), - Sets.newHashSet(Arrays.asList(elts))); + assertEquals(Sets.newHashSet(c), Sets.newHashSet(Arrays.asList(elts))); } + @Test public void testId() throws Exception { subtestIdNoException("1"); subtestIdNoException("123456789012345678901234567890"); @@ -84,6 +101,7 @@ private void subtestIdNoException(String id) throws Exception { newDataStoreFactory().getDataStore(id); } + @Test public void testSet() throws Exception { // get null assertNull(stringTyped.get(null)); @@ -124,6 +142,7 @@ public void testSet() throws Exception { assertTrue(boolTyped.get("k")); } + @Test public void testValues() throws Exception { // before assertTrue(stringTyped.values().isEmpty()); @@ -145,6 +164,7 @@ public void testValues() throws Exception { assertContentsAnyOrder(boolTyped.values(), true); } + @Test public void testKeySet() throws Exception { // before assertTrue(stringTyped.keySet().isEmpty()); @@ -165,6 +185,7 @@ public void testKeySet() throws Exception { assertTrue(stringTyped.keySet().isEmpty()); } + @Test public void testDelete() throws Exception { // store with basic values stringTyped.set("k", "v").set("k2", "v2"); @@ -187,6 +208,7 @@ public void testDelete() throws Exception { assertEquals(0, stringTyped.size()); } + @Test public void testClear() throws Exception { // store with basic values stringTyped.set("k", "v").set("k2", "v2"); @@ -200,6 +222,7 @@ public void testClear() throws Exception { assertEquals(0, stringTyped.size()); } + @Test public void testLarge() throws Exception { // TODO(yanivi): size = 1000? need to speed up JdoDataStoreTest first int size = 100; @@ -211,6 +234,7 @@ public void testLarge() throws Exception { assertEquals("hello" + mid, stringTyped.get(String.valueOf(mid))); } + @Test public void testContainsKeyAndValue() throws Exception { // before assertFalse(stringTyped.containsKey("k")); diff --git a/google-http-client-test/src/main/java/com/google/api/client/test/util/store/package-info.java b/google-http-client-test/src/main/java/com/google/api/client/test/util/store/package-info.java index ef5f485ec..fb4d33427 100644 --- a/google-http-client-test/src/main/java/com/google/api/client/test/util/store/package-info.java +++ b/google-http-client-test/src/main/java/com/google/api/client/test/util/store/package-info.java @@ -18,4 +18,3 @@ * @author Yaniv Inbar */ package com.google.api.client.test.util.store; - diff --git a/google-http-client-test/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-test/native-image.properties b/google-http-client-test/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-test/native-image.properties new file mode 100644 index 000000000..3cbd2b27f --- /dev/null +++ b/google-http-client-test/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-test/native-image.properties @@ -0,0 +1 @@ +Args=--initialize-at-build-time=org.junit.runner.RunWith,java.lang.annotation.Annotation diff --git a/google-http-client-test/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-test/reflect-config.json b/google-http-client-test/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-test/reflect-config.json new file mode 100644 index 000000000..b86a6e7b3 --- /dev/null +++ b/google-http-client-test/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-test/reflect-config.json @@ -0,0 +1,432 @@ +[ + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$Animal", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$WildCardTypes", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$Simple", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$StringNullValue", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$DogWithFamily", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$ExtendsGenericJson", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$MapOfMapType", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$PolymorphicWithNumericValueType", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$AnimalGenericJson", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$EnumValue", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$NumberTypes", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$Entry", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$TypeVariablesPassedAround", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$FloatMapTypeVariableType", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$Feed", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$V", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$PolymorphicSelfReferencing", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$DoubleListTypeVariableType", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$TypeVariableType", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "java.util.ArrayList", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$TestClass", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$Dog", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$A", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$NumericValueTypedSubclass1", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$DogGenericJson", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$NumberTypesAsString", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$E", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$PolymorphicWithNumericType", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$Centipede", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$AnyType", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$IntArrayTypeVariableType", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$CollectionOfCollectionType", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$ArrayType", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$NumericValueTypedSubclass2", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$NumericTypedSubclass1", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$BooleanTypes", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$Human", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$HumanWithPets", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$NumericTypedSubclass2", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$IntegerTypeVariableType", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$X", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$Y", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.test.json.AbstractJsonFactoryTest$Z", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + } +] \ No newline at end of file diff --git a/google-http-client-test/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-test/serialization-config.json b/google-http-client-test/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-test/serialization-config.json new file mode 100644 index 000000000..03d931127 --- /dev/null +++ b/google-http-client-test/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-test/serialization-config.json @@ -0,0 +1,12 @@ +{ + "types":[ + {"name":"java.lang.String" + }, + {"name":"java.lang.Boolean"}, + {"name":"java.util.HashMap"} + ], + "lambdaCapturingTypes":[ + ], + "proxies":[ + ] +} \ No newline at end of file diff --git a/google-http-client-test/src/test/java/com/google/api/client/test/util/store/FileDataStoreFactoryTest.java b/google-http-client-test/src/test/java/com/google/api/client/test/util/store/FileDataStoreFactoryTest.java index 63685d33e..78282861e 100644 --- a/google-http-client-test/src/test/java/com/google/api/client/test/util/store/FileDataStoreFactoryTest.java +++ b/google-http-client-test/src/test/java/com/google/api/client/test/util/store/FileDataStoreFactoryTest.java @@ -14,34 +14,41 @@ package com.google.api.client.test.util.store; -import com.google.api.client.test.util.store.AbstractDataStoreFactoryTest; +import static java.nio.file.Files.createTempDirectory; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import com.google.api.client.util.store.DataStore; import com.google.api.client.util.store.FileDataStoreFactory; import com.google.common.collect.ImmutableSet; -import com.google.common.io.Files; - import java.io.File; import java.io.IOException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link FileDataStoreFactory}. * * @author Yaniv Inbar */ +@RunWith(JUnit4.class) public class FileDataStoreFactoryTest extends AbstractDataStoreFactoryTest { @Override protected FileDataStoreFactory newDataStoreFactory() throws IOException { - File dataDir = Files.createTempDir(); + File dataDir = createTempDirectory("temp").toFile(); dataDir.deleteOnExit(); return new FileDataStoreFactory(dataDir); } + @Test public void testSave() throws IOException { FileDataStoreFactory factory = newDataStoreFactory(); DataStore store = factory.getDataStore("foo"); store.set("k", "v"); - assertEquals(ImmutableSet.of("k"), + assertEquals( + ImmutableSet.of("k"), new FileDataStoreFactory(factory.getDataDirectory()).getDataStore("foo").keySet()); store.clear(); assertTrue(new FileDataStoreFactory(factory.getDataDirectory()).getDataStore("foo").isEmpty()); diff --git a/google-http-client-test/src/test/java/com/google/api/client/test/util/store/MemoryDataStoreFactoryTest.java b/google-http-client-test/src/test/java/com/google/api/client/test/util/store/MemoryDataStoreFactoryTest.java index bdee074d5..3454f8dba 100644 --- a/google-http-client-test/src/test/java/com/google/api/client/test/util/store/MemoryDataStoreFactoryTest.java +++ b/google-http-client-test/src/test/java/com/google/api/client/test/util/store/MemoryDataStoreFactoryTest.java @@ -14,15 +14,17 @@ package com.google.api.client.test.util.store; -import com.google.api.client.test.util.store.AbstractDataStoreFactoryTest; import com.google.api.client.util.store.DataStoreFactory; import com.google.api.client.util.store.MemoryDataStoreFactory; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link MemoryDataStoreFactory}. * * @author Yaniv Inbar */ +@RunWith(JUnit4.class) public class MemoryDataStoreFactoryTest extends AbstractDataStoreFactoryTest { @Override diff --git a/google-http-client-xml/pom.xml b/google-http-client-xml/pom.xml index 9f7b745cc..31fd2e1c3 100644 --- a/google-http-client-xml/pom.xml +++ b/google-http-client-xml/pom.xml @@ -4,11 +4,11 @@ com.google.http-client google-http-client-parent - 1.26.1-SNAPSHOT + 2.0.3-SNAPSHOT ../pom.xml google-http-client-xml - 1.26.1-SNAPSHOT + 2.0.3-SNAPSHOT XML extensions to the Google HTTP Client Library for Java. @@ -17,7 +17,7 @@ maven-javadoc-plugin - http://download.oracle.com/javase/6/docs/api/ + http://download.oracle.com/javase/7/docs/api/ ${project.name} ${project.version} ${project.artifactId} ${project.version} @@ -25,20 +25,11 @@ maven-source-plugin - - - source-jar - compile - - jar - - - org.codehaus.mojo build-helper-maven-plugin - 1.5 + 3.6.0 add-test-source @@ -54,6 +45,16 @@ + + maven-jar-plugin + + + + com.google.api.client.http.xml + + + + diff --git a/google-http-client-xml/src/main/java/com/google/api/client/http/xml/AbstractXmlHttpContent.java b/google-http-client-xml/src/main/java/com/google/api/client/http/xml/AbstractXmlHttpContent.java index 643fa8369..792138bfe 100644 --- a/google-http-client-xml/src/main/java/com/google/api/client/http/xml/AbstractXmlHttpContent.java +++ b/google-http-client-xml/src/main/java/com/google/api/client/http/xml/AbstractXmlHttpContent.java @@ -25,12 +25,10 @@ import org.xmlpull.v1.XmlSerializer; /** - * {@link Beta}
    + * {@link Beta}
    * Abstract serializer for XML HTTP content based on the data key/value mapping object for an item. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @since 1.0 * @author Yaniv Inbar diff --git a/google-http-client-xml/src/main/java/com/google/api/client/http/xml/XmlHttpContent.java b/google-http-client-xml/src/main/java/com/google/api/client/http/xml/XmlHttpContent.java index 5184aed3e..db3802d84 100644 --- a/google-http-client-xml/src/main/java/com/google/api/client/http/xml/XmlHttpContent.java +++ b/google-http-client-xml/src/main/java/com/google/api/client/http/xml/XmlHttpContent.java @@ -22,23 +22,19 @@ import org.xmlpull.v1.XmlSerializer; /** - * {@link Beta}
    + * {@link Beta}
    * Serializes XML HTTP content based on the data key/value mapping object for an item. * - *

    - * Sample usage: - *

    + *

    Sample usage: * *

    -  static void setContent(HttpRequest request, XmlNamespaceDictionary namespaceDictionary,
    -      String elementName, Object data) {
    -    request.setContent(new XmlHttpContent(namespaceDictionary, elementName, data));
    -  }
    + * static void setContent(HttpRequest request, XmlNamespaceDictionary namespaceDictionary,
    + * String elementName, Object data) {
    + * request.setContent(new XmlHttpContent(namespaceDictionary, elementName, data));
    + * }
      * 
    * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @since 1.0 * @author Yaniv Inbar @@ -51,7 +47,7 @@ public class XmlHttpContent extends AbstractXmlHttpContent { * * @param namespaceDictionary XML namespace dictionary * @param elementName XML element local name, optionally prefixed by its namespace alias, for - * example {@code "atom:entry"} + * example {@code "atom:entry"} * @param data Key/value pair data * @since 1.5 */ @@ -63,8 +59,8 @@ public XmlHttpContent( } /** - * XML element local name, optionally prefixed by its namespace alias, for example - * {@code "atom:entry"}. + * XML element local name, optionally prefixed by its namespace alias, for example {@code + * "atom:entry"}. */ private final String elementName; diff --git a/google-http-client-xml/src/main/java/com/google/api/client/http/xml/atom/AtomContent.java b/google-http-client-xml/src/main/java/com/google/api/client/http/xml/atom/AtomContent.java index 7badc9e62..5f1f5d254 100644 --- a/google-http-client-xml/src/main/java/com/google/api/client/http/xml/atom/AtomContent.java +++ b/google-http-client-xml/src/main/java/com/google/api/client/http/xml/atom/AtomContent.java @@ -24,32 +24,26 @@ import org.xmlpull.v1.XmlSerializer; /** - * {@link Beta}
    + * {@link Beta}
    * Serializes Atom XML HTTP content based on the data key/value mapping object for an Atom entry. * - *

    - * Default value for {@link #getType()} is {@link Atom#MEDIA_TYPE}. - *

    + *

    Default value for {@link #getType()} is {@link Atom#MEDIA_TYPE}. * - *

    - * Sample usages: - *

    + *

    Sample usages: * *

    -  static void setAtomEntryContent(
    -      HttpRequest request, XmlNamespaceDictionary namespaceDictionary, Object entry) {
    -    request.setContent(AtomContent.forEntry(namespaceDictionary, entry));
    -  }
    -
    -  static void setAtomBatchContent(
    -      HttpRequest request, XmlNamespaceDictionary namespaceDictionary, Object batchFeed) {
    -    request.setContent(AtomContent.forFeed(namespaceDictionary, batchFeed));
    -  }
    + * static void setAtomEntryContent(
    + * HttpRequest request, XmlNamespaceDictionary namespaceDictionary, Object entry) {
    + * request.setContent(AtomContent.forEntry(namespaceDictionary, entry));
    + * }
    + *
    + * static void setAtomBatchContent(
    + * HttpRequest request, XmlNamespaceDictionary namespaceDictionary, Object batchFeed) {
    + * request.setContent(AtomContent.forFeed(namespaceDictionary, batchFeed));
    + * }
      * 
    * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @since 1.4 * @author Yaniv Inbar diff --git a/google-http-client-xml/src/main/java/com/google/api/client/http/xml/atom/AtomFeedParser.java b/google-http-client-xml/src/main/java/com/google/api/client/http/xml/atom/AtomFeedParser.java index 71a032429..e3542bdae 100644 --- a/google-http-client-xml/src/main/java/com/google/api/client/http/xml/atom/AtomFeedParser.java +++ b/google-http-client-xml/src/main/java/com/google/api/client/http/xml/atom/AtomFeedParser.java @@ -28,12 +28,10 @@ import org.xmlpull.v1.XmlPullParserException; /** - * {@link Beta}
    + * {@link Beta}
    * Atom feed pull parser when the Atom entry class is known in advance. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @param feed type * @param entry type @@ -53,8 +51,12 @@ public final class AtomFeedParser extends AbstractAtomFeedParser { * @param feedClass feed class to parse * @since 1.5 */ - public AtomFeedParser(XmlNamespaceDictionary namespaceDictionary, XmlPullParser parser, - InputStream inputStream, Class feedClass, Class entryClass) { + public AtomFeedParser( + XmlNamespaceDictionary namespaceDictionary, + XmlPullParser parser, + InputStream inputStream, + Class feedClass, + Class entryClass) { super(namespaceDictionary, parser, inputStream, feedClass); this.entryClass = Preconditions.checkNotNull(entryClass); } @@ -94,8 +96,11 @@ public final Class getEntryClass() { * @throws IOException I/O exception * @throws XmlPullParserException XML pull parser exception */ - public static AtomFeedParser create(HttpResponse response, - XmlNamespaceDictionary namespaceDictionary, Class feedClass, Class entryClass) + public static AtomFeedParser create( + HttpResponse response, + XmlNamespaceDictionary namespaceDictionary, + Class feedClass, + Class entryClass) throws IOException, XmlPullParserException { InputStream content = response.getContent(); try { diff --git a/google-http-client-xml/src/main/java/com/google/api/client/http/xml/atom/package-info.java b/google-http-client-xml/src/main/java/com/google/api/client/http/xml/atom/package-info.java index a94a6c93d..810f86c60 100644 --- a/google-http-client-xml/src/main/java/com/google/api/client/http/xml/atom/package-info.java +++ b/google-http-client-xml/src/main/java/com/google/api/client/http/xml/atom/package-info.java @@ -13,7 +13,7 @@ */ /** - * {@link com.google.api.client.util.Beta}
    + * {@link com.google.api.client.util.Beta}
    * Atom XML HTTP library based on the pluggable HTTP library. * * @since 1.4 @@ -21,4 +21,3 @@ */ @com.google.api.client.util.Beta package com.google.api.client.http.xml.atom; - diff --git a/google-http-client-xml/src/main/java/com/google/api/client/http/xml/package-info.java b/google-http-client-xml/src/main/java/com/google/api/client/http/xml/package-info.java index 975cf8828..abe066191 100644 --- a/google-http-client-xml/src/main/java/com/google/api/client/http/xml/package-info.java +++ b/google-http-client-xml/src/main/java/com/google/api/client/http/xml/package-info.java @@ -13,7 +13,7 @@ */ /** - * {@link com.google.api.client.util.Beta}
    + * {@link com.google.api.client.util.Beta}
    * XML HTTP library based on the pluggable HTTP library. * * @since 1.3 @@ -21,4 +21,3 @@ */ @com.google.api.client.util.Beta package com.google.api.client.http.xml; - diff --git a/google-http-client-xml/src/main/java/com/google/api/client/xml/GenericXml.java b/google-http-client-xml/src/main/java/com/google/api/client/xml/GenericXml.java index 1f4ca3615..a2ffd684b 100644 --- a/google-http-client-xml/src/main/java/com/google/api/client/xml/GenericXml.java +++ b/google-http-client-xml/src/main/java/com/google/api/client/xml/GenericXml.java @@ -17,25 +17,20 @@ import com.google.api.client.util.Beta; import com.google.api.client.util.GenericData; import com.google.api.client.util.Key; - import java.util.concurrent.ConcurrentMap; /** - * {@link Beta}
    + * {@link Beta}
    * Generic XML data that stores all unknown key name/value pairs. * - *

    - * Each data key name maps into the name of the XPath expression value for the XML element, + *

    Each data key name maps into the name of the XPath expression value for the XML element, * attribute, or text content (using {@code "text()"}). Subclasses can declare fields for known XML * content using the {@link Key} annotation. Each field can be of any visibility (private, package * private, protected, or public) and must not be static. {@code null} unknown data key names are * not allowed, but {@code null} data values are allowed. - *

    * - *

    - * Implementation is not thread-safe. For a thread-safe choice instead use an implementation of + *

    Implementation is not thread-safe. For a thread-safe choice instead use an implementation of * {@link ConcurrentMap}. - *

    * * @since 1.0 * @author Yaniv Inbar diff --git a/google-http-client-xml/src/main/java/com/google/api/client/xml/Xml.java b/google-http-client-xml/src/main/java/com/google/api/client/xml/Xml.java index a0f0b95d2..a5132839c 100644 --- a/google-http-client-xml/src/main/java/com/google/api/client/xml/Xml.java +++ b/google-http-client-xml/src/main/java/com/google/api/client/xml/Xml.java @@ -37,7 +37,7 @@ import org.xmlpull.v1.XmlSerializer; /** - * {@link Beta}
    + * {@link Beta}
    * XML utilities. * * @since 1.0 @@ -48,9 +48,7 @@ public class Xml { /** * {@code "application/xml; charset=utf-8"} media type used as a default for XML parsing. * - *

    - * Use {@link HttpMediaType#equalsIgnoreParameters} for comparing media types. - *

    + *

    Use {@link HttpMediaType#equalsIgnoreParameters} for comparing media types. * * @since 1.10 */ @@ -66,12 +64,12 @@ public class Xml { private static synchronized XmlPullParserFactory getParserFactory() throws XmlPullParserException { if (factory == null) { - factory = XmlPullParserFactory.newInstance( - System.getProperty(XmlPullParserFactory.PROPERTY_NAME), null); + factory = + XmlPullParserFactory.newInstance( + System.getProperty(XmlPullParserFactory.PROPERTY_NAME), null); factory.setNamespaceAware(true); } return factory; - } /** @@ -94,12 +92,12 @@ public static XmlPullParser createParser() throws XmlPullParserException { /** * Shows a debug string representation of an element data object of key/value pairs. - *

    - * It will make up something for the element name and XML namespaces. If those are known, it is + * + *

    It will make up something for the element name and XML namespaces. If those are known, it is * better to use {@link XmlNamespaceDictionary#toStringOf(String, Object)}. * * @param element element data object of key/value pairs ({@link GenericXml}, {@link Map}, or any - * object with public fields) + * object with public fields) */ public static String toStringOf(Object element) { return new XmlNamespaceDictionary().toStringOf(null, element); @@ -111,15 +109,16 @@ public static String toStringOf(Object element) { * @param stringValue string value * @param field field to set or {@code null} if not applicable * @param valueType value type (class, parameterized type, or generic array type) or {@code null} - * for none + * for none * @param context context list, going from least specific to most specific type context, for - * example container class and its field + * example container class and its field * @param destination destination object or {@code null} for none * @param genericXml generic XML or {@code null} if not applicable * @param destinationMap destination map or {@code null} if not applicable * @param name key name */ - private static void parseAttributeOrTextContent(String stringValue, + private static void parseAttributeOrTextContent( + String stringValue, Field field, Type valueType, List context, @@ -144,7 +143,8 @@ private static void parseAttributeOrTextContent(String stringValue, * @param destinationMap destination map or {@code null} if not applicable * @param name key name */ - private static void setValue(Object value, + private static void setValue( + Object value, Field field, Object destination, GenericXml genericXml, @@ -163,10 +163,8 @@ private static void setValue(Object value, * Customizes the behavior of XML parsing. Subclasses may override any methods they need to * customize behavior. * - *

    - * Implementation has no fields and therefore thread-safe, but sub-classes are not necessarily + *

    Implementation has no fields and therefore thread-safe, but sub-classes are not necessarily * thread-safe. - *

    */ public static class CustomizeParser { /** @@ -183,8 +181,8 @@ public boolean stopBeforeStartTag(String namespace, String localName) { /** * Returns whether to stop parsing when reaching the end tag of an XML element after it has been - * processed. Only called if the element is actually being processed. By default, returns - * {@code false}, but subclasses may override. + * processed. Only called if the element is actually being processed. By default, returns {@code + * false}, but subclasses may override. * * @param namespace XML element's namespace URI * @param localName XML element's local name @@ -197,21 +195,22 @@ public boolean stopAfterEndTag(String namespace, String localName) { /** * Parses an XML element using the given XML pull parser into the given destination object. * - *

    - * Requires the the current event be {@link XmlPullParser#START_TAG} (skipping any initial - * {@link XmlPullParser#START_DOCUMENT}) of the element being parsed. At normal parsing - * completion, the current event will either be {@link XmlPullParser#END_TAG} of the element being - * parsed, or the {@link XmlPullParser#START_TAG} of the requested {@code atom:entry}. - *

    + *

    Requires the current event be {@link XmlPullParser#START_TAG} (skipping any initial {@link + * XmlPullParser#START_DOCUMENT}) of the element being parsed. At normal parsing completion, the + * current event will either be {@link XmlPullParser#END_TAG} of the element being parsed, or the + * {@link XmlPullParser#START_TAG} of the requested {@code atom:entry}. * * @param parser XML pull parser * @param destination optional destination object to parser into or {@code null} to ignore XML - * content + * content * @param namespaceDictionary XML namespace dictionary to store unknown namespaces * @param customizeParser optional parser customizer or {@code null} for none */ - public static void parseElement(XmlPullParser parser, Object destination, - XmlNamespaceDictionary namespaceDictionary, CustomizeParser customizeParser) + public static void parseElement( + XmlPullParser parser, + Object destination, + XmlNamespaceDictionary namespaceDictionary, + CustomizeParser customizeParser) throws IOException, XmlPullParserException { ArrayList context = new ArrayList(); if (destination != null) { @@ -222,15 +221,17 @@ public static void parseElement(XmlPullParser parser, Object destination, /** * Returns whether the customize parser has requested to stop or reached end of document. - * Otherwise, identical to - * {@link #parseElement(XmlPullParser, Object, XmlNamespaceDictionary, CustomizeParser)} . + * Otherwise, identical to {@link #parseElement(XmlPullParser, Object, XmlNamespaceDictionary, + * CustomizeParser)} . */ - private static boolean parseElementInternal(XmlPullParser parser, + private static boolean parseElementInternal( + XmlPullParser parser, ArrayList context, Object destination, Type valueType, XmlNamespaceDictionary namespaceDictionary, - CustomizeParser customizeParser) throws IOException, XmlPullParserException { + CustomizeParser customizeParser) + throws IOException, XmlPullParserException { // TODO(yanivi): method is too long; needs to be broken down into smaller methods and comment // better GenericXml genericXml = destination instanceof GenericXml ? (GenericXml) destination : null; @@ -260,11 +261,14 @@ private static boolean parseElementInternal(XmlPullParser parser, // TODO(yanivi): can have repeating attribute values, e.g. "@a=value1 @a=value2"? String attributeName = parser.getAttributeName(i); String attributeNamespace = parser.getAttributeNamespace(i); - String attributeAlias = attributeNamespace.length() == 0 - ? "" : namespaceDictionary.getNamespaceAliasForUriErrorOnUnknown(attributeNamespace); + String attributeAlias = + attributeNamespace.length() == 0 + ? "" + : namespaceDictionary.getNamespaceAliasForUriErrorOnUnknown(attributeNamespace); String fieldName = getFieldName(true, attributeAlias, attributeNamespace, attributeName); Field field = classInfo == null ? null : classInfo.getField(fieldName); - parseAttributeOrTextContent(parser.getAttributeValue(i), + parseAttributeOrTextContent( + parser.getAttributeValue(i), field, valueType, context, @@ -278,21 +282,24 @@ private static boolean parseElementInternal(XmlPullParser parser, ArrayValueMap arrayValueMap = new ArrayValueMap(destination); boolean isStopped = false; // TODO(yanivi): support Void type as "ignore" element/attribute - main: while (true) { + main: + while (true) { int event = parser.next(); switch (event) { case XmlPullParser.END_DOCUMENT: isStopped = true; break main; case XmlPullParser.END_TAG: - isStopped = customizeParser != null - && customizeParser.stopAfterEndTag(parser.getNamespace(), parser.getName()); + isStopped = + customizeParser != null + && customizeParser.stopAfterEndTag(parser.getNamespace(), parser.getName()); break main; case XmlPullParser.TEXT: // parse text content if (destination != null) { field = classInfo == null ? null : classInfo.getField(TEXT_CONTENT); - parseAttributeOrTextContent(parser.getText(), + parseAttributeOrTextContent( + parser.getText(), field, valueType, context, @@ -349,7 +356,8 @@ private static boolean parseElementInternal(XmlPullParser parser, break; case XmlPullParser.TEXT: if (!ignore && level == 1) { - parseAttributeOrTextContent(parser.getText(), + parseAttributeOrTextContent( + parser.getText(), field, valueType, context, @@ -363,23 +371,27 @@ private static boolean parseElementInternal(XmlPullParser parser, break; } } - } else if (fieldType == null || fieldClass != null - && Types.isAssignableToOrFrom(fieldClass, Map.class)) { + } else if (fieldType == null + || fieldClass != null && Types.isAssignableToOrFrom(fieldClass, Map.class)) { // store the element as a map Map mapValue = Data.newMapInstance(fieldClass); int contextSize = context.size(); if (fieldType != null) { context.add(fieldType); } - Type subValueType = fieldType != null && Map.class.isAssignableFrom(fieldClass) - ? Types.getMapValueParameter(fieldType) : null; + Type subValueType = + fieldType != null && Map.class.isAssignableFrom(fieldClass) + ? Types.getMapValueParameter(fieldType) + : null; subValueType = Data.resolveWildcardTypeOrTypeVariable(context, subValueType); - isStopped = parseElementInternal(parser, - context, - mapValue, - subValueType, - namespaceDictionary, - customizeParser); + isStopped = + parseElementInternal( + parser, + context, + mapValue, + subValueType, + namespaceDictionary, + customizeParser); if (fieldType != null) { context.remove(contextSize); } @@ -423,8 +435,10 @@ private static boolean parseElementInternal(XmlPullParser parser, // TODO(yanivi): some duplicate code here; isolate into reusable methods FieldInfo fieldInfo = FieldInfo.of(field); Object elementValue = null; - Type subFieldType = isArray - ? Types.getArrayComponentType(fieldType) : Types.getIterableParameter(fieldType); + Type subFieldType = + isArray + ? Types.getArrayComponentType(fieldType) + : Types.getIterableParameter(fieldType); Class rawArrayComponentType = Types.getRawArrayComponentType(context, subFieldType); subFieldType = Data.resolveWildcardTypeOrTypeVariable(context, subFieldType); @@ -436,23 +450,27 @@ private static boolean parseElementInternal(XmlPullParser parser, boolean isSubEnum = subFieldClass != null && subFieldClass.isEnum(); if (Data.isPrimitive(subFieldType) || isSubEnum) { elementValue = parseTextContentForElement(parser, context, false, subFieldType); - } else if (subFieldType == null || subFieldClass != null - && Types.isAssignableToOrFrom(subFieldClass, Map.class)) { + } else if (subFieldType == null + || subFieldClass != null + && Types.isAssignableToOrFrom(subFieldClass, Map.class)) { elementValue = Data.newMapInstance(subFieldClass); int contextSize = context.size(); if (subFieldType != null) { context.add(subFieldType); } - Type subValueType = subFieldType != null - && Map.class.isAssignableFrom(subFieldClass) ? Types.getMapValueParameter( - subFieldType) : null; + Type subValueType = + subFieldType != null && Map.class.isAssignableFrom(subFieldClass) + ? Types.getMapValueParameter(subFieldType) + : null; subValueType = Data.resolveWildcardTypeOrTypeVariable(context, subValueType); - isStopped = parseElementInternal(parser, - context, - elementValue, - subValueType, - namespaceDictionary, - customizeParser); + isStopped = + parseElementInternal( + parser, + context, + elementValue, + subValueType, + namespaceDictionary, + customizeParser); if (subFieldType != null) { context.remove(contextSize); } @@ -460,12 +478,9 @@ private static boolean parseElementInternal(XmlPullParser parser, elementValue = Types.newInstance(rawArrayComponentType); int contextSize = context.size(); context.add(fieldType); - isStopped = parseElementInternal(parser, - context, - elementValue, - null, - namespaceDictionary, - customizeParser); + isStopped = + parseElementInternal( + parser, context, elementValue, null, namespaceDictionary, customizeParser); context.remove(contextSize); } if (isArray) { @@ -478,16 +493,15 @@ private static boolean parseElementInternal(XmlPullParser parser, } else { // collection: add new element to collection @SuppressWarnings("unchecked") - Collection collectionValue = (Collection) (field == null - ? destinationMap.get(fieldName) : fieldInfo.getValue(destination)); + Collection collectionValue = + (Collection) + (field == null + ? destinationMap.get(fieldName) + : fieldInfo.getValue(destination)); if (collectionValue == null) { collectionValue = Data.newCollectionInstance(fieldType); - setValue(collectionValue, - field, - destination, - genericXml, - destinationMap, - fieldName); + setValue( + collectionValue, field, destination, genericXml, destinationMap, fieldName); } collectionValue.add(elementValue); } @@ -496,12 +510,9 @@ private static boolean parseElementInternal(XmlPullParser parser, Object value = Types.newInstance(fieldClass); int contextSize = context.size(); context.add(fieldType); - isStopped = parseElementInternal(parser, - context, - value, - null, - namespaceDictionary, - customizeParser); + isStopped = + parseElementInternal( + parser, context, value, null, namespaceDictionary, customizeParser); context.remove(contextSize); setValue(value, field, destination, genericXml, destinationMap, fieldName); } @@ -564,10 +575,10 @@ private static Object parseValue(Type valueType, List context, String valu valueType = Data.resolveWildcardTypeOrTypeVariable(context, valueType); if (valueType == Double.class || valueType == double.class) { if (value.equals("INF")) { - return new Double(Double.POSITIVE_INFINITY); + return Double.valueOf(Double.POSITIVE_INFINITY); } if (value.equals("-INF")) { - return new Double(Double.NEGATIVE_INFINITY); + return Double.valueOf(Double.NEGATIVE_INFINITY); } } if (valueType == Float.class || valueType == float.class) { @@ -591,8 +602,10 @@ private static void parseNamespacesForElement( XmlPullParser parser, XmlNamespaceDictionary namespaceDictionary) throws XmlPullParserException { int eventType = parser.getEventType(); - Preconditions.checkState(eventType == XmlPullParser.START_TAG, - "expected start of XML element, but got something else (event type %s)", eventType); + Preconditions.checkState( + eventType == XmlPullParser.START_TAG, + "expected start of XML element, but got something else (event type %s)", + eventType); int depth = parser.getDepth(); int nsStart = parser.getNamespaceCount(depth - 1); int nsEnd = parser.getNamespaceCount(depth); @@ -614,6 +627,5 @@ private static void parseNamespacesForElement( } } - private Xml() { - } + private Xml() {} } diff --git a/google-http-client-xml/src/main/java/com/google/api/client/xml/XmlNamespaceDictionary.java b/google-http-client-xml/src/main/java/com/google/api/client/xml/XmlNamespaceDictionary.java index ae32fe5e4..6c04ef00f 100644 --- a/google-http-client-xml/src/main/java/com/google/api/client/xml/XmlNamespaceDictionary.java +++ b/google-http-client-xml/src/main/java/com/google/api/client/xml/XmlNamespaceDictionary.java @@ -32,31 +32,26 @@ import org.xmlpull.v1.XmlSerializer; /** - * {@link Beta}
    + * {@link Beta}
    * Thread-safe XML namespace dictionary that provides a one-to-one map of namespace alias to URI. * - *

    - * Implementation is thread-safe. For maximum efficiency, applications should use a single + *

    Implementation is thread-safe. For maximum efficiency, applications should use a single * globally-shared instance of the XML namespace dictionary. - *

    * - *

    - * A namespace alias is uniquely mapped to a single namespace URI, and a namespace URI is uniquely - * mapped to a single namespace alias. In other words, it is not possible to have duplicates. - *

    + *

    A namespace alias is uniquely mapped to a single namespace URI, and a namespace URI is + * uniquely mapped to a single namespace alias. In other words, it is not possible to have + * duplicates. * - *

    - * Sample usage: - *

    + *

    Sample usage: * *

    {@code
    -  static final XmlNamespaceDictionary DICTIONARY = new XmlNamespaceDictionary()
    -      .set("", "http://www.w3.org/2005/Atom")
    -      .set("activity", "http://activitystrea.ms/spec/1.0/")
    -      .set("georss", "http://www.georss.org/georss")
    -      .set("media", "http://search.yahoo.com/mrss/")
    -      .set("thr", "http://purl.org/syndication/thread/1.0");
    - *}
    + * static final XmlNamespaceDictionary DICTIONARY = new XmlNamespaceDictionary() + * .set("", "http://www.w3.org/2005/Atom") + * .set("activity", "http://activitystrea.ms/spec/1.0/") + * .set("georss", "http://www.georss.org/georss") + * .set("media", "http://search.yahoo.com/mrss/") + * .set("thr", "http://purl.org/syndication/thread/1.0"); + * } * * @since 1.0 * @author Yaniv Inbar @@ -119,12 +114,10 @@ public synchronized Map getUriToAliasMap() { /** * Adds a namespace of the given alias and URI. * - *

    - * If the uri is {@code null}, the namespace alias will be removed. Similarly, if the alias is + *

    If the uri is {@code null}, the namespace alias will be removed. Similarly, if the alias is * {@code null}, the namespace URI will be removed. Otherwise, if the alias is already mapped to a * different URI, it will be remapped to the new URI. Similarly, if a URI is already mapped to a * different alias, it will be remapped to the new alias. - *

    * * @param alias alias or {@code null} to remove the namespace URI * @param uri namespace URI or {@code null} to remove the namespace alias @@ -141,8 +134,9 @@ public synchronized XmlNamespaceDictionary set(String alias, String uri) { } else if (alias == null) { previousAlias = namespaceUriToAliasMap.remove(uri); } else { - previousUri = namespaceAliasToUriMap.put( - Preconditions.checkNotNull(alias), Preconditions.checkNotNull(uri)); + previousUri = + namespaceAliasToUriMap.put( + Preconditions.checkNotNull(alias), Preconditions.checkNotNull(uri)); if (!uri.equals(previousUri)) { previousAlias = namespaceUriToAliasMap.put(uri, alias); } else { @@ -162,9 +156,9 @@ public synchronized XmlNamespaceDictionary set(String alias, String uri) { * Shows a debug string representation of an element data object of key/value pairs. * * @param element element data object ({@link GenericXml}, {@link Map}, or any object with public - * fields) + * fields) * @param elementName optional XML element local name prefixed by its namespace alias -- for - * example {@code "atom:entry"} -- or {@code null} to make up something + * example {@code "atom:entry"} -- or {@code null} to make up something */ public String toStringOf(String elementName, Object element) { try { @@ -182,7 +176,7 @@ public String toStringOf(String elementName, Object element) { * Shows a debug string representation of an element data object of key/value pairs. * * @param element element data object ({@link GenericXml}, {@link Map}, or any object with public - * fields) + * fields) * @param elementNamespaceUri XML namespace URI or {@code null} for no namespace * @param elementLocalName XML local name * @throws IOException I/O exception @@ -197,7 +191,7 @@ public void serialize( * Shows a debug string representation of an element data object of key/value pairs. * * @param element element data object ({@link GenericXml}, {@link Map}, or any object with public - * fields) + * fields) * @param elementName XML element local name prefixed by its namespace alias * @throws IOException I/O exception */ @@ -206,11 +200,16 @@ public void serialize(XmlSerializer serializer, String elementName, Object eleme serialize(serializer, elementName, element, true); } - private void serialize(XmlSerializer serializer, String elementNamespaceUri, - String elementLocalName, Object element, boolean errorOnUnknown) throws IOException { + private void serialize( + XmlSerializer serializer, + String elementNamespaceUri, + String elementLocalName, + Object element, + boolean errorOnUnknown) + throws IOException { String elementAlias = elementNamespaceUri == null ? null : getAliasForUri(elementNamespaceUri); - startDoc(serializer, element, errorOnUnknown, elementAlias).serialize( - serializer, elementNamespaceUri, elementLocalName); + startDoc(serializer, element, errorOnUnknown, elementAlias) + .serialize(serializer, elementNamespaceUri, elementLocalName); serializer.endDocument(); } @@ -257,7 +256,7 @@ private void computeAliases(Object element, SortedSet aliases) { aliases.add(alias); } Class valueClass = value.getClass(); - if (!isAttribute && !Data.isPrimitive(valueClass) && !valueClass.isEnum() ) { + if (!isAttribute && !Data.isPrimitive(valueClass) && !valueClass.isEnum()) { if (value instanceof Iterable || valueClass.isArray()) { for (Object subValue : Types.iterableOf(value)) { computeAliases(subValue, aliases); @@ -275,18 +274,16 @@ private void computeAliases(Object element, SortedSet aliases) { * Returns the namespace URI to use for serialization for a given namespace alias, possibly using * a predictable made-up namespace URI if the alias is not recognized. * - *

    - * Specifically, if the namespace alias is not recognized, the namespace URI returned will be + *

    Specifically, if the namespace alias is not recognized, the namespace URI returned will be * {@code "http://unknown/"} plus the alias, unless {@code errorOnUnknown} is {@code true} in * which case it will throw an {@link IllegalArgumentException}. - *

    * * @param errorOnUnknown whether to thrown an exception if the namespace alias is not recognized * @param alias namespace alias * @return namespace URI, using a predictable made-up namespace URI if the namespace alias is not - * recognized + * recognized * @throws IllegalArgumentException if the namespace alias is not recognized and {@code - * errorOnUnkown} is {@code true} + * errorOnUnknown} is {@code true} */ String getNamespaceUriForAliasHandlingUnknown(boolean errorOnUnknown, String alias) { String result = getUriForAlias(alias); @@ -307,10 +304,12 @@ String getNamespaceUriForAliasHandlingUnknown(boolean errorOnUnknown, String ali */ String getNamespaceAliasForUriErrorOnUnknown(String namespaceUri) { String result = getAliasForUri(namespaceUri); - Preconditions.checkArgument(result != null, + Preconditions.checkArgument( + result != null, "invalid XML: no alias declared for namesapce <%s>; " + "work-around by setting XML namepace directly by calling the set method of %s", - namespaceUri, XmlNamespaceDictionary.class.getName()); + namespaceUri, + XmlNamespaceDictionary.class.getName()); return result; } @@ -328,8 +327,8 @@ class ElementSerializer { Class valueClass = elementValue.getClass(); if (Data.isPrimitive(valueClass) && !Data.isNull(elementValue)) { textValue = elementValue; - } else if (valueClass.isEnum() && !Data.isNull(elementValue)){ - textValue = elementValue; + } else if (valueClass.isEnum() && !Data.isNull(elementValue)) { + textValue = elementValue; } else { for (Map.Entry entry : Data.mapOf(elementValue).entrySet()) { Object fieldValue = entry.getValue(); @@ -377,8 +376,11 @@ void serialize(XmlSerializer serializer, String elementNamespaceUri, String elem String attributeName = attributeNames.get(i); int colon = attributeName.indexOf(':'); String attributeLocalName = attributeName.substring(colon + 1); - String attributeNamespaceUri = colon == -1 ? null : getNamespaceUriForAliasHandlingUnknown( - errorOnUnknown, attributeName.substring(0, colon)); + String attributeNamespaceUri = + colon == -1 + ? null + : getNamespaceUriForAliasHandlingUnknown( + errorOnUnknown, attributeName.substring(0, colon)); serializer.attribute( attributeNamespaceUri, attributeLocalName, toSerializedValue(attributeValues.get(i))); } @@ -395,13 +397,13 @@ void serialize(XmlSerializer serializer, String elementNamespaceUri, String elem if (subElementValue instanceof Iterable || valueClass.isArray()) { for (Object subElement : Types.iterableOf(subElementValue)) { if (subElement != null && !Data.isNull(subElement)) { - new ElementSerializer(subElement, errorOnUnknown).serialize( - serializer, subElementName); + new ElementSerializer(subElement, errorOnUnknown) + .serialize(serializer, subElementName); } } } else { - new ElementSerializer(subElementValue, errorOnUnknown).serialize( - serializer, subElementName); + new ElementSerializer(subElementValue, errorOnUnknown) + .serialize(serializer, subElementName); } } serializer.endTag(elementNamespaceUri, elementLocalName); diff --git a/google-http-client-xml/src/main/java/com/google/api/client/xml/XmlObjectParser.java b/google-http-client-xml/src/main/java/com/google/api/client/xml/XmlObjectParser.java index 2c0a173b5..b29717922 100644 --- a/google-http-client-xml/src/main/java/com/google/api/client/xml/XmlObjectParser.java +++ b/google-http-client-xml/src/main/java/com/google/api/client/xml/XmlObjectParser.java @@ -27,21 +27,17 @@ import org.xmlpull.v1.XmlPullParserException; /** - * {@link Beta}
    + * {@link Beta}
    * XML HTTP parser into an data class of key/value pairs. * - *

    - * Implementation is thread-safe. - *

    + *

    Implementation is thread-safe. * - *

    - * Sample usage: - *

    + *

    Sample usage: * *

    -  static void setParser(HttpRequest request, XmlNamespaceDictionary namespaceDictionary) {
    -    request.setParser(new XmlObjectParser(namespaceDictionary));
    -  }
    + * static void setParser(HttpRequest request, XmlNamespaceDictionary namespaceDictionary) {
    + * request.setParser(new XmlObjectParser(namespaceDictionary));
    + * }
      * 
    * * @since 1.10 @@ -61,9 +57,7 @@ public XmlObjectParser(XmlNamespaceDictionary namespaceDictionary) { this.namespaceDictionary = Preconditions.checkNotNull(namespaceDictionary); } - /** - * Returns the XML namespace dictionary. - */ + /** Returns the XML namespace dictionary. */ public final XmlNamespaceDictionary getNamespaceDictionary() { return namespaceDictionary; } @@ -82,8 +76,7 @@ public T parseAndClose(InputStream in, Charset charset, Class dataClass) return (T) parseAndClose(in, charset, (Type) dataClass); } - public Object parseAndClose(InputStream in, Charset charset, Type dataType) - throws IOException { + public Object parseAndClose(InputStream in, Charset charset, Type dataType) throws IOException { try { // Initialize the parser XmlPullParser parser = Xml.createParser(); diff --git a/google-http-client-xml/src/main/java/com/google/api/client/xml/atom/AbstractAtomFeedParser.java b/google-http-client-xml/src/main/java/com/google/api/client/xml/atom/AbstractAtomFeedParser.java index c68453f57..041f04860 100644 --- a/google-http-client-xml/src/main/java/com/google/api/client/xml/atom/AbstractAtomFeedParser.java +++ b/google-http-client-xml/src/main/java/com/google/api/client/xml/atom/AbstractAtomFeedParser.java @@ -25,12 +25,10 @@ import org.xmlpull.v1.XmlPullParserException; /** - * {@link Beta}
    + * {@link Beta}
    * Abstract base class for an Atom feed parser when the feed type is known in advance. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @param feed type * @since 1.0 @@ -61,8 +59,11 @@ public abstract class AbstractAtomFeedParser { * @param feedClass feed class to parse * @since 1.5 */ - protected AbstractAtomFeedParser(XmlNamespaceDictionary namespaceDictionary, XmlPullParser parser, - InputStream inputStream, Class feedClass) { + protected AbstractAtomFeedParser( + XmlNamespaceDictionary namespaceDictionary, + XmlPullParser parser, + InputStream inputStream, + Class feedClass) { this.namespaceDictionary = Preconditions.checkNotNull(namespaceDictionary); this.parser = Preconditions.checkNotNull(parser); this.inputStream = Preconditions.checkNotNull(inputStream); diff --git a/google-http-client-xml/src/main/java/com/google/api/client/xml/atom/Atom.java b/google-http-client-xml/src/main/java/com/google/api/client/xml/atom/Atom.java index fe5575c84..c948bd36d 100644 --- a/google-http-client-xml/src/main/java/com/google/api/client/xml/atom/Atom.java +++ b/google-http-client-xml/src/main/java/com/google/api/client/xml/atom/Atom.java @@ -22,11 +22,10 @@ import com.google.api.client.util.Preconditions; import com.google.api.client.util.escape.PercentEscaper; import com.google.api.client.xml.Xml; - import java.util.Arrays; /** - * {@link Beta}
    + * {@link Beta}
    * Atom Utilities. * * @since 1.0 @@ -41,9 +40,7 @@ public final class Atom { /** * {@code "application/atom+xml; charset=utf-8"} media type used as a default for Atom parsing. * - *

    - * Use {@link HttpMediaType#equalsIgnoreParameters} for comparing media types. - *

    + *

    Use {@link HttpMediaType#equalsIgnoreParameters} for comparing media types. * * @since 1.10 */ @@ -52,7 +49,7 @@ public final class Atom { /** Escaper for the {@code Slug} header. */ private static final PercentEscaper SLUG_ESCAPER = - new PercentEscaper(" !\"#$&'()*+,-./:;<=>?@[\\]^_`{|}~", false); + new PercentEscaper(" !\"#$&'()*+,-./:;<=>?@[\\]^_`{|}~"); static final class StopAtAtomEntry extends Xml.CustomizeParser { @@ -64,8 +61,7 @@ public boolean stopBeforeStartTag(String namespace, String localName) { } } - private Atom() { - } + private Atom() {} /** * Checks the given content type matches the Atom content type specified in {@link #MEDIA_TYPE}. @@ -74,8 +70,10 @@ private Atom() { */ public static void checkContentType(String contentType) { Preconditions.checkArgument(contentType != null); // for backwards compatibility - Preconditions.checkArgument(HttpMediaType.equalsIgnoreParameters(MEDIA_TYPE, contentType), - "Wrong content type: expected <" + MEDIA_TYPE + "> but got <%s>", contentType); + Preconditions.checkArgument( + HttpMediaType.equalsIgnoreParameters(MEDIA_TYPE, contentType), + "Wrong content type: expected <" + MEDIA_TYPE + "> but got <%s>", + contentType); } /** diff --git a/google-http-client-xml/src/main/java/com/google/api/client/xml/atom/package-info.java b/google-http-client-xml/src/main/java/com/google/api/client/xml/atom/package-info.java index e210800fa..afb992a0b 100644 --- a/google-http-client-xml/src/main/java/com/google/api/client/xml/atom/package-info.java +++ b/google-http-client-xml/src/main/java/com/google/api/client/xml/atom/package-info.java @@ -13,7 +13,7 @@ */ /** - * {@link com.google.api.client.util.Beta}
    + * {@link com.google.api.client.util.Beta}
    * Utilities for Atom XML. * * @since 1.0 @@ -21,4 +21,3 @@ */ @com.google.api.client.util.Beta package com.google.api.client.xml.atom; - diff --git a/google-http-client-xml/src/main/java/com/google/api/client/xml/package-info.java b/google-http-client-xml/src/main/java/com/google/api/client/xml/package-info.java index 4076d8288..a5f0bed0a 100644 --- a/google-http-client-xml/src/main/java/com/google/api/client/xml/package-info.java +++ b/google-http-client-xml/src/main/java/com/google/api/client/xml/package-info.java @@ -13,7 +13,7 @@ */ /** - * {@link com.google.api.client.util.Beta}
    + * {@link com.google.api.client.util.Beta}
    * Utilities for XML. * * @since 1.0 @@ -21,4 +21,3 @@ */ @com.google.api.client.util.Beta package com.google.api.client.xml; - diff --git a/google-http-client-xml/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-xml/reflect-config.json b/google-http-client-xml/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-xml/reflect-config.json new file mode 100644 index 000000000..52ad6a97b --- /dev/null +++ b/google-http-client-xml/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client-xml/reflect-config.json @@ -0,0 +1,12 @@ +[ + { + "name": "com.google.api.client.xml.GenericXml", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + } +] \ No newline at end of file diff --git a/google-http-client-xml/src/test/java/com/google/api/client/xml/AtomTest.java b/google-http-client-xml/src/test/java/com/google/api/client/xml/AtomTest.java index 65515f321..b33196b0f 100644 --- a/google-http-client-xml/src/test/java/com/google/api/client/xml/AtomTest.java +++ b/google-http-client-xml/src/test/java/com/google/api/client/xml/AtomTest.java @@ -14,26 +14,58 @@ package com.google.api.client.xml; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + import com.google.api.client.http.HttpHeaders; +import com.google.api.client.http.xml.atom.AtomFeedParser; +import com.google.api.client.util.Charsets; +import com.google.api.client.util.Key; +import com.google.api.client.xml.atom.AbstractAtomFeedParser; import com.google.api.client.xml.atom.Atom; +import com.google.common.io.Resources; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.StringReader; +import java.net.URL; import java.util.List; -import junit.framework.TestCase; import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.xmlpull.v1.XmlPullParser; /** * Tests {@link Atom}. * * @author Yaniv Inbar + * @author Gerald Madlmayr */ -public class AtomTest extends TestCase { +@RunWith(JUnit4.class) +public class AtomTest { - @SuppressWarnings("unchecked") + private static final String SAMPLE_FEED = + " Example Feed 2003-12-13T18:31:02Z " + + "John Doe urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6" + + " Atom-Powered Robots Run Amok urn:uuid:1225c695-cfb8-4ebb-aaaa" + + "-80da344efa6a 2003-12-13T18:30:02Z

    Some text" + + ". Atom-Powered Robots Run Amok! urn:uuid:1225c695-cfb8-4ebb" + + "-aaaa-80da344efa62 2003-12-13T18:32:02Z Some " + + "other text. "; + + /** Test for checking the Slug Header */ + @Test public void testSetSlugHeader() { HttpHeaders headers = new HttpHeaders(); assertNull(headers.get("Slug")); subtestSetSlugHeader(headers, "value", "value"); subtestSetSlugHeader( - headers, " !\"#$&'()*+,-./:;<=>?@[\\]^_`{|}~", " !\"#$&'()*+,-./:;<=>?@[\\]^_`{|}~"); + headers, " !\"#$&'()*+,-./:;<=>?@[\\]^_`{|}~", " !\"#$&'()*+,-./:;" + "<=>?@[\\]^_`{|}~"); subtestSetSlugHeader(headers, "%D7%99%D7%A0%D7%99%D7%91", "יניב"); subtestSetSlugHeader(headers, null, null); } @@ -48,4 +80,218 @@ public void subtestSetSlugHeader(HttpHeaders headers, String expectedValue, Stri new String[] {expectedValue}, ((List) headers.get("Slug")).toArray()); } } + + /** + * This tests parses a simple Atom Feed given as a constant. All elements are asserted, to see if + * everything works fine. For parsing a dedicated {@link AtomFeedParser} is used. + * + *

    The purpose of this test is to test the {@link AtomFeedParser#parseFeed} and {@link + * AtomFeedParser#parseNextEntry} and see if the mapping of the XML element to the entity classes + * is done correctly. + */ + @Test + public void testAtomFeedUsingCustomizedParser() throws Exception { + XmlPullParser parser = Xml.createParser(); + // Wired. Both, the InputStream for the FeedParser and the XPP need to be set (?) + parser.setInput(new StringReader(SAMPLE_FEED)); + InputStream stream = new ByteArrayInputStream(SAMPLE_FEED.getBytes()); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + AbstractAtomFeedParser atomParser = + new AtomFeedParser( + namespaceDictionary, parser, stream, Feed.class, FeedEntry.class); + + Feed feed = (Feed) atomParser.parseFeed(); + assertEquals("John Doe", feed.author.name); + assertEquals("urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6", feed.id); + assertEquals("2003-12-13T18:31:02Z", feed.updated); + assertEquals("Example Feed", feed.title); + assertEquals("http://example.org/", feed.link.href); + + FeedEntry entry1 = (FeedEntry) atomParser.parseNextEntry(); + // assertNotNull(feed.entry); + assertEquals("urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a", entry1.id); + assertEquals("2003-12-13T18:30:02Z", entry1.updated); + assertEquals("Some text.", entry1.summary); + assertEquals("Atom-Powered Robots Run Amok", entry1.title); + assertEquals("http://example.org/2003/12/13/atom03", entry1.link.href); + + FeedEntry entry2 = (FeedEntry) atomParser.parseNextEntry(); + assertEquals("urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa62", entry2.id); + assertEquals("2003-12-13T18:32:02Z", entry2.updated); + assertEquals("Some other text.", entry2.summary); + assertEquals("Atom-Powered Robots Run Amok!", entry2.title); + assertEquals("http://example.org/2003/12/13/atom02", entry2.link.href); + + FeedEntry entry3 = (FeedEntry) atomParser.parseNextEntry(); + assertNull(entry3); + + atomParser.close(); + } + + /** + * Tests of a constant string to see if the data structure can be parsed using the standard method + * {@link Xml#parseElement} + * + *

    The purpose of this test is to assert, if the parsed elements are correctly parsed using a + * {@link AtomFeedParser}. + */ + @Test + public void testAtomFeedUsingStandardParser() throws Exception { + Feed feed = new Feed(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(SAMPLE_FEED)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, feed, namespaceDictionary, null); + assertNotNull(feed); + assertEquals(2, feed.entry.length); + + assertEquals("John Doe", feed.author.name); + assertEquals("urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6", feed.id); + assertEquals("2003-12-13T18:31:02Z", feed.updated); + assertEquals("Example Feed", feed.title); + assertEquals("http://example.org/", feed.link.href); + + FeedEntry entry1 = feed.entry[0]; + // assertNotNull(feed.entry); + assertEquals("urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a", entry1.id); + assertEquals("2003-12-13T18:30:02Z", entry1.updated); + assertEquals("Some text.", entry1.summary); + assertEquals("Atom-Powered Robots Run Amok", entry1.title); + assertEquals("http://example.org/2003/12/13/atom03", entry1.link.href); + + FeedEntry entry2 = feed.entry[1]; + assertEquals("urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa62", entry2.id); + assertEquals("2003-12-13T18:32:02Z", entry2.updated); + assertEquals("Some other text.", entry2.summary); + assertEquals("Atom-Powered Robots Run Amok!", entry2.title); + assertEquals("http://example.org/2003/12/13/atom02", entry2.link.href); + } + + /** + * Read an XML ATOM Feed from a file to a string and assert if all the {@link FeedEntry}s are + * present. No detailed assertion of each element + * + *

    The purpose of this test is to read a bunch of elements which contain additional elements + * (HTML in this case), that are not part of the {@link FeedEntry} and to see if there is an issue + * if we parse some more entries. + */ + @Test + public void testSampleFeedParser() throws Exception { + XmlPullParser parser = Xml.createParser(); + URL url = Resources.getResource("sample-atom.xml"); + String read = Resources.toString(url, Charsets.UTF_8); + parser.setInput(new StringReader(read)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + AbstractAtomFeedParser atomParser = + new AtomFeedParser( + namespaceDictionary, + parser, + new ByteArrayInputStream(read.getBytes()), + Feed.class, + FeedEntry.class); + Feed feed = (Feed) atomParser.parseFeed(); + assertNotNull(feed); + + // validate feed 1 -- Long Content + FeedEntry entry = (FeedEntry) atomParser.parseNextEntry(); + assertNotNull(entry); + assertNotNull(entry.id); + assertNotNull(entry.title); + assertNotNull(entry.summary); + assertNotNull(entry.link); + assertNotNull(entry.updated); + assertNotNull(entry.content); + assertEquals(5000, entry.content.length()); + + // validate feed 2 -- Special Charts + entry = (FeedEntry) atomParser.parseNextEntry(); + assertNotNull(entry); + assertNotNull(entry.id); + assertNotNull(entry.title); + assertNotNull(entry.summary); + assertNotNull(entry.link); + assertNotNull(entry.updated); + assertNotNull(entry.content); + assertEquals( + "aäb cde fgh ijk lmn oöpoöp tuü vwx yz AÄBC DEF GHI JKL MNO ÖPQ RST UÜV WXYZ " + + "!\"§ $%& /() =?* '<> #|; ²³~ @`´ ©«» ¼× {} aäb cde fgh ijk lmn oöp qrsß tuü vwx yz " + + "AÄBC DEF GHI JKL MNO", + entry.content); + + // validate feed 3 -- Missing Content + entry = (FeedEntry) atomParser.parseNextEntry(); + assertNotNull(entry); + assertNotNull(entry.id); + assertNotNull(entry.title); + assertNotNull(entry.summary); + assertNotNull(entry.link); + assertNotNull(entry.updated); + assertNull(entry.content); + + // validate feed 4 -- Missing Updated + entry = (FeedEntry) atomParser.parseNextEntry(); + assertNotNull(entry); + assertNotNull(entry.id); + assertNotNull(entry.title); + assertNotNull(entry.summary); + assertNotNull(entry.link); + assertNull(entry.updated); + assertNotNull(entry.content); + + // validate feed 5 + entry = (FeedEntry) atomParser.parseNextEntry(); + assertNotNull(entry); + assertNotNull(entry.id); + assertNotNull(entry.title); + assertNull(entry.summary); + assertNotNull(entry.link); + assertNotNull(entry.updated); + assertNotNull(entry.content); + + // validate feed 6 + entry = (FeedEntry) atomParser.parseNextEntry(); + assertNull(entry); + + atomParser.close(); + } + + /** Feed Element to map the XML to */ + public static class Feed { + @Key private String title; + @Key private Link link; + @Key private String updated; + @Key private Author author; + @Key private String id; + @Key private FeedEntry[] entry; + } + + /** + * Author Element as part of the {@link Feed} Element to map the XML to. As this is sub-element, + * this needs to be public. + */ + public static class Author { + @Key private String name; + } + + /** + * Link Element as part of the {@link Feed} Element to map the XML to. As this is sub-element, + * this needs to be public. + */ + public static class Link { + @Key("@href") + private String href; + } + + /** + * Entry Element to cover the Entries of a Atom {@link Feed}. As this is sub-element, this needs + * to be public. + */ + public static class FeedEntry { + @Key private String title; + @Key private Link link; + @Key private String updated; + @Key private String summary; + @Key private String id; + @Key private String content; + } } diff --git a/google-http-client-xml/src/test/java/com/google/api/client/xml/GenericXmlListTest.java b/google-http-client-xml/src/test/java/com/google/api/client/xml/GenericXmlListTest.java new file mode 100644 index 000000000..e40f0658d --- /dev/null +++ b/google-http-client-xml/src/test/java/com/google/api/client/xml/GenericXmlListTest.java @@ -0,0 +1,464 @@ +/* + * Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.xml; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import com.google.api.client.util.ArrayMap; +import com.google.api.client.util.Key; +import java.io.ByteArrayOutputStream; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Collection; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlSerializer; + +/** + * Tests List and Arrays of {@link GenericXml}. + * + *

    Tests are copies from {@link XmlListTest}, but the dedicated classes are derived from {@link + * GenericXml} + * + * @author Gerald Madlmayr + */ +@RunWith(JUnit4.class) +public class GenericXmlListTest { + + private static final String MULTI_TYPE_WITH_CLASS_TYPE = + "content1rep10" + + "rep11value1content2rep20rep21" + + "value2content3rep30rep31" + + "value3"; + private static final String MULTIPLE_STRING_ELEMENT = + "rep1rep2"; + private static final String MULTIPLE_INTEGER_ELEMENT = + "12"; + private static final String ARRAY_TYPE_WITH_PRIMITIVE_ADDED_NESTED = + "1something2"; + private static final String MULTIPLE_ENUM_ELEMENT = + "ENUM_1ENUM_2"; + private static final String COLLECTION_OF_ARRAY = + "abcd"; + + /** The purpose of this test is to map an XML with an Array of {@link XmlTest.AnyType} objects. */ + @SuppressWarnings("unchecked") + @Test + public void testParseArrayTypeWithClassType() throws Exception { + ArrayWithClassTypeGeneric xml = new ArrayWithClassTypeGeneric(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(MULTI_TYPE_WITH_CLASS_TYPE)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + assertNotNull(xml.rep); + XmlTest.AnyType[] rep = xml.rep; + assertNotNull(rep); + assertEquals(3, rep.length); + ArrayList> elem0 = (ArrayList>) rep[0].elem; + assertEquals(1, elem0.size()); + assertEquals("content1", elem0.get(0).get("text()")); + ArrayList> elem1 = (ArrayList>) rep[1].elem; + assertEquals(1, elem1.size()); + assertEquals("content2", elem1.get(0).get("text()")); + ArrayList> elem2 = (ArrayList>) rep[2].elem; + assertEquals(1, elem2.size()); + assertEquals("content3", elem2.get(0).get("text()")); + + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(MULTI_TYPE_WITH_CLASS_TYPE, out.toString()); + } + + /** + * The purpose of this test is to map an XML with a {@link Collection} of {@link XmlTest.AnyType} + * objects. + */ + @Test + public void testParseCollectionWithClassType() throws Exception { + CollectionWithClassTypeGeneric xml = new CollectionWithClassTypeGeneric(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(MULTI_TYPE_WITH_CLASS_TYPE)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + assertNotNull(xml.rep); + Collection rep = xml.rep; + assertNotNull(rep); + assertEquals(3, rep.size()); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(MULTI_TYPE_WITH_CLASS_TYPE, out.toString()); + } + + /** The purpose of this test is to map an XML with an Array of {@link XmlTest.AnyType} objects. */ + @SuppressWarnings("unchecked") + @Test + public void testParseMultiGenericWithClassType() throws Exception { + MultiGenericWithClassType xml = new MultiGenericWithClassType(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(MULTI_TYPE_WITH_CLASS_TYPE)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + GenericXml[] rep = xml.rep; + assertNotNull(rep); + assertEquals(3, rep.length); + assertEquals( + "text()", + ((ArrayMap) (rep[0].values().toArray(new ArrayList[] {})[0].get(0))) + .getKey(0)); + assertEquals( + "content1", + ((ArrayMap) (rep[0].values().toArray(new ArrayList[] {})[0].get(0))) + .getValue(0)); + assertEquals( + "text()", + ((ArrayMap) (rep[1].values().toArray(new ArrayList[] {})[0].get(0))) + .getKey(0)); + assertEquals( + "content2", + ((ArrayMap) (rep[1].values().toArray(new ArrayList[] {})[0].get(0))) + .getValue(0)); + assertEquals( + "text()", + ((ArrayMap) (rep[2].values().toArray(new ArrayList[] {})[0].get(0))) + .getKey(0)); + assertEquals( + "content3", + ((ArrayMap) (rep[2].values().toArray(new ArrayList[] {})[0].get(0))) + .getValue(0)); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(MULTI_TYPE_WITH_CLASS_TYPE, out.toString()); + } + + /** The purpose of this test is to map an XML with an Array of {@link XmlTest.AnyType} objects. */ + @SuppressWarnings("unchecked") + @Test + public void testParseMultiGenericWithClassTypeGeneric() throws Exception { + MultiGenericWithClassTypeGeneric xml = new MultiGenericWithClassTypeGeneric(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(MULTI_TYPE_WITH_CLASS_TYPE)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + GenericXml[] rep = xml.rep; + assertNotNull(rep); + assertEquals(3, rep.length); + assertEquals( + "text()", + ((ArrayMap) (rep[0].values().toArray(new ArrayList[] {})[0].get(0))) + .getKey(0)); + assertEquals( + "content1", + ((ArrayMap) (rep[0].values().toArray(new ArrayList[] {})[0].get(0))) + .getValue(0)); + assertEquals( + "text()", + ((ArrayMap) (rep[1].values().toArray(new ArrayList[] {})[0].get(0))) + .getKey(0)); + assertEquals( + "content2", + ((ArrayMap) (rep[1].values().toArray(new ArrayList[] {})[0].get(0))) + .getValue(0)); + assertEquals( + "text()", + ((ArrayMap) (rep[2].values().toArray(new ArrayList[] {})[0].get(0))) + .getKey(0)); + assertEquals( + "content3", + ((ArrayMap) (rep[2].values().toArray(new ArrayList[] {})[0].get(0))) + .getValue(0)); + + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(MULTI_TYPE_WITH_CLASS_TYPE, out.toString()); + } + + /** The purpose of this test is to map an XML with a {@link Collection} of {@link String}. */ + @Test + public void testParseCollectionTypeString() throws Exception { + CollectionTypeStringGeneric xml = new CollectionTypeStringGeneric(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(MULTIPLE_STRING_ELEMENT)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + assertEquals(2, xml.rep.size()); + assertEquals("rep1", xml.rep.toArray(new String[] {})[0]); + assertEquals("rep2", xml.rep.toArray(new String[] {})[1]); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(MULTIPLE_STRING_ELEMENT, out.toString()); + } + + /** The purpose of this test is to map an XML with an Array of {@link String} objects. */ + @Test + public void testParseArrayTypeString() throws Exception { + ArrayTypeStringGeneric xml = new ArrayTypeStringGeneric(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(MULTIPLE_STRING_ELEMENT)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + assertEquals(2, xml.rep.length); + assertEquals("rep1", xml.rep[0]); + assertEquals("rep2", xml.rep[1]); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(MULTIPLE_STRING_ELEMENT, out.toString()); + } + + /** + * The purpose of this test is to map an XML with a {@link Collection} of {@link Integer} objects. + */ + @Test + public void testParseCollectionTypeInteger() throws Exception { + CollectionTypeIntegerGeneric xml = new CollectionTypeIntegerGeneric(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(MULTIPLE_INTEGER_ELEMENT)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + assertEquals(2, xml.rep.size()); + assertEquals(1, xml.rep.toArray(new Integer[] {})[0].intValue()); + assertEquals(2, xml.rep.toArray(new Integer[] {})[1].intValue()); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(MULTIPLE_INTEGER_ELEMENT, out.toString()); + } + + /** The purpose of this test is to map an XML with an Array of {@link Integer} objects. */ + @Test + public void testParseArrayTypeInteger() throws Exception { + ArrayTypeIntegerGeneric xml = new ArrayTypeIntegerGeneric(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(MULTIPLE_INTEGER_ELEMENT)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + assertEquals(2, xml.rep.length); + assertEquals(1, xml.rep[0].intValue()); + assertEquals(2, xml.rep[1].intValue()); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(MULTIPLE_INTEGER_ELEMENT, out.toString()); + } + + /** The purpose of this test is to map an XML with an Array of {@code int} types. */ + @Test + public void testParseArrayTypeInt() throws Exception { + ArrayTypeIntGeneric xml = new ArrayTypeIntGeneric(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(MULTIPLE_INTEGER_ELEMENT)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + assertEquals(2, xml.rep.length); + assertEquals(1, xml.rep[0]); + assertEquals(2, xml.rep[1]); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(MULTIPLE_INTEGER_ELEMENT, out.toString()); + } + + /** + * The purpose of this test is to map an XML with a {@link Collection} of {@link Enum} objects. + */ + @Test + public void testParseCollectionTypeWithEnum() throws Exception { + CollectionTypeEnumGeneric xml = new CollectionTypeEnumGeneric(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(MULTIPLE_ENUM_ELEMENT)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + assertEquals(2, xml.rep.size()); + assertEquals(XmlEnumTest.AnyEnum.ENUM_1, xml.rep.toArray(new XmlEnumTest.AnyEnum[] {})[0]); + assertEquals(XmlEnumTest.AnyEnum.ENUM_2, xml.rep.toArray(new XmlEnumTest.AnyEnum[] {})[1]); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(MULTIPLE_ENUM_ELEMENT, out.toString()); + } + + /** + * The purpose of this test is to map an XML with a {@link Collection} of {@link Enum} objects. + */ + @Test + public void testParseArrayTypeWithEnum() throws Exception { + ArrayTypeEnumGeneric xml = new ArrayTypeEnumGeneric(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(MULTIPLE_ENUM_ELEMENT)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + assertEquals(2, xml.rep.length); + assertEquals(XmlEnumTest.AnyEnum.ENUM_1, xml.rep[0]); + assertEquals(XmlEnumTest.AnyEnum.ENUM_2, xml.rep[1]); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(MULTIPLE_ENUM_ELEMENT, out.toString()); + } + + /** The purpose is to have an Array of {@link java.lang.reflect.ParameterizedType} elements. */ + @Test + public void testParseToArrayOfArrayMaps() throws Exception { + ArrayOfArrayMapsTypeGeneric xml = new ArrayOfArrayMapsTypeGeneric(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(COLLECTION_OF_ARRAY)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + assertEquals(2, xml.rep.length); + assertEquals("a", xml.rep[0].getValue(0)); + assertEquals("a", xml.rep[0].getKey(0)); + assertEquals("b", xml.rep[0].getValue(1)); + assertEquals("b", xml.rep[0].getKey(1)); + assertEquals("c", xml.rep[1].getValue(0)); + assertEquals("c", xml.rep[1].getKey(0)); + assertEquals("d", xml.rep[1].getValue(1)); + assertEquals("d", xml.rep[1].getKey(1)); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(COLLECTION_OF_ARRAY, out.toString()); + } + + /** + * The purpose is to have a Collection of {@link java.lang.reflect.ParameterizedType} elements. + */ + @Test + public void testParseToCollectionOfArrayMaps() throws Exception { + CollectionOfArrayMapsTypeGeneric xml = new CollectionOfArrayMapsTypeGeneric(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(COLLECTION_OF_ARRAY)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + assertEquals(2, xml.rep.size()); + assertEquals("a", xml.rep.toArray(new ArrayMap[] {})[0].getValue(0)); + assertEquals("a", xml.rep.toArray(new ArrayMap[] {})[0].getKey(0)); + assertEquals("b", xml.rep.toArray(new ArrayMap[] {})[0].getValue(1)); + assertEquals("b", xml.rep.toArray(new ArrayMap[] {})[0].getKey(1)); + assertEquals("c", xml.rep.toArray(new ArrayMap[] {})[1].getValue(0)); + assertEquals("c", xml.rep.toArray(new ArrayMap[] {})[1].getKey(0)); + assertEquals("d", xml.rep.toArray(new ArrayMap[] {})[1].getValue(1)); + assertEquals("d", xml.rep.toArray(new ArrayMap[] {})[1].getKey(1)); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(COLLECTION_OF_ARRAY, out.toString()); + } + + private static class CollectionOfArrayMapsTypeGeneric extends GenericXml { + @Key public Collection> rep; + } + + private static class ArrayOfArrayMapsTypeGeneric extends GenericXml { + @Key public ArrayMap[] rep; + } + + private static class ArrayWithClassTypeGeneric extends GenericXml { + @Key public XmlTest.AnyType[] rep; + } + + private static class CollectionWithClassTypeGeneric extends GenericXml { + @Key public Collection rep; + } + + private static class MultiGenericWithClassType { + @Key public GenericXml[] rep; + } + + private static class MultiGenericWithClassTypeGeneric extends GenericXml { + @Key public GenericXml[] rep; + } + + private static class CollectionTypeStringGeneric extends GenericXml { + @Key public Collection rep; + } + + private static class ArrayTypeStringGeneric extends GenericXml { + @Key public String[] rep; + } + + private static class CollectionTypeIntegerGeneric extends GenericXml { + @Key public Collection rep; + } + + private static class ArrayTypeIntegerGeneric extends GenericXml { + @Key public Integer[] rep; + } + + private static class ArrayTypeIntGeneric extends GenericXml { + @Key public int[] rep; + } + + private static class CollectionTypeEnumGeneric extends GenericXml { + @Key public Collection rep; + } + + private static class ArrayTypeEnumGeneric extends GenericXml { + @Key public XmlEnumTest.AnyEnum[] rep; + } +} diff --git a/google-http-client-xml/src/test/java/com/google/api/client/xml/GenericXmlTest.java b/google-http-client-xml/src/test/java/com/google/api/client/xml/GenericXmlTest.java index d8c57918e..abc083fcc 100644 --- a/google-http-client-xml/src/test/java/com/google/api/client/xml/GenericXmlTest.java +++ b/google-http-client-xml/src/test/java/com/google/api/client/xml/GenericXmlTest.java @@ -14,36 +14,79 @@ package com.google.api.client.xml; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + import com.google.api.client.util.ArrayMap; +import com.google.api.client.util.Key; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.io.StringReader; +import java.util.ArrayList; import java.util.Collection; -import junit.framework.TestCase; +import java.util.List; +import java.util.Map; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; /** * Tests {@link GenericXml}. * + *

    Tests are copies from {@link XmlTest}, but the dedicated Objects are derived from {@link + * GenericXml} + * * @author Yaniv Inbar + * @author Gerald Madlmayr */ -public class GenericXmlTest extends TestCase { - - public GenericXmlTest() { - } - - public GenericXmlTest(String name) { - super(name); - } +@RunWith(JUnit4.class) +public class GenericXmlTest { private static final String XML = - "One" - + "Two"; + "OneTwo"; + private static final String ANY_GENERIC_TYPE_XML = + "rep1rep2content"; + private static final String SIMPLE_XML = "test"; + private static final String SIMPLE_XML_NUMERIC = "1"; + private static final String ANY_TYPE_XML = + "contentrep1rep2content"; + private static final String ANY_TYPE_XML_PRIMITIVE_INT = + "112" + + ""; + private static final String ANY_TYPE_XML_PRIMITIVE_STR = + "1+11+12" + + "+1"; + private static final String ALL_TYPE = + ""; + private static final String ANY_TYPE_XML_NESTED_ARRAY = + "content

    rep1

    rep2

    rep3

    rep4

    content"; + + public GenericXmlTest() {} + /** + * The purpose of this test is to parse the given XML into a {@link GenericXml} Object that has no + * fixed structure. + */ @SuppressWarnings("unchecked") - public void testParse() throws Exception { + @Test + public void testParseToGenericXml() throws Exception { GenericXml xml = new GenericXml(); XmlPullParser parser = Xml.createParser(); parser.setInput(new StringReader(XML)); @@ -54,7 +97,344 @@ public void testParse() throws Exception { assertEquals(expected, namespaceDictionary.getAliasToUriMap()); assertEquals("feed", xml.name); Collection foo = (Collection) xml.get("entry"); - // TODO(yanivi): check contents of foo assertEquals(2, foo.size()); + ArrayMap singleElementOne = ArrayMap.of("text()", "One"); + List> testOne = new ArrayList>(); + testOne.add(singleElementOne); + assertEquals("abc", foo.toArray(new ArrayMap[] {})[0].get("@gd:etag")); + assertEquals(testOne, foo.toArray(new ArrayMap[] {})[0].get("title")); + ArrayMap singleElementTwoAttrib = + ArrayMap.of("@attribute", "someattribute", "text()", "Two"); + // ArrayMap singleElementTwoValue =ArrayMap.of(); + List> testTwo = new ArrayList>(); + testTwo.add(singleElementTwoAttrib); + // testTwo.add(singleElementTwoValue); + assertEquals("def", foo.toArray(new ArrayMap[] {})[1].get("@gd:etag")); + assertEquals(testTwo, foo.toArray(new ArrayMap[] {})[1].get("title")); + } + + /** The purpose of this test is map a generic XML to an element inside a dedicated element. */ + @SuppressWarnings("unchecked") + @Test + public void testParseAnyGenericType() throws Exception { + AnyGenericType xml = new AnyGenericType(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(ANY_GENERIC_TYPE_XML)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + assertTrue(xml.attr instanceof String); + Collection repList = (Collection) xml.elem.get("rep"); + assertEquals(2, repList.size()); + Collection repValue = (Collection) xml.elem.get("value"); + assertEquals(1, repValue.size()); + // 1st rep element + assertEquals( + "@attr", + ((Map.Entry) + repList.toArray(new ArrayMap[] {})[0].entrySet().toArray(new Map.Entry[] {})[0]) + .getKey()); + assertEquals( + "param1", + ((Map.Entry) + repList.toArray(new ArrayMap[] {})[0].entrySet().toArray(new Map.Entry[] {})[0]) + .getValue()); + assertEquals( + "text()", + ((Map.Entry) + repList.toArray(new ArrayMap[] {})[0].entrySet().toArray(new Map.Entry[] {})[1]) + .getKey()); + assertEquals( + "rep1", + ((Map.Entry) + repList.toArray(new ArrayMap[] {})[0].entrySet().toArray(new Map.Entry[] {})[1]) + .getValue()); + // 2nd rep element + assertEquals( + "@attr", + ((Map.Entry) + repList.toArray(new ArrayMap[] {})[1].entrySet().toArray(new Map.Entry[] {})[0]) + .getKey()); + assertEquals( + "param2", + ((Map.Entry) + repList.toArray(new ArrayMap[] {})[1].entrySet().toArray(new Map.Entry[] {})[0]) + .getValue()); + assertEquals( + "text()", + ((Map.Entry) + repList.toArray(new ArrayMap[] {})[1].entrySet().toArray(new Map.Entry[] {})[1]) + .getKey()); + assertEquals( + "rep2", + ((Map.Entry) + repList.toArray(new ArrayMap[] {})[1].entrySet().toArray(new Map.Entry[] {})[1]) + .getValue()); + // value element + assertEquals( + "text()", + ((Map.Entry) + repValue.toArray(new ArrayMap[] {})[0].entrySet().toArray(new Map.Entry[] {})[0]) + .getKey()); + assertEquals( + "content", + ((Map.Entry) + repValue.toArray(new ArrayMap[] {})[0].entrySet().toArray(new Map.Entry[] {})[0]) + .getValue()); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(ANY_GENERIC_TYPE_XML, out.toString()); + } + + /** + * The purpose of this test is to map a {@link GenericXml} to the String element in the object. + */ + @Test + public void testParseSimpleTypeAsValueString() throws Exception { + SimpleTypeStringGeneric xml = new SimpleTypeStringGeneric(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(SIMPLE_XML)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary().set("", ""); + Xml.parseElement(parser, xml, namespaceDictionary, null); + assertNotNull(xml); + assertEquals(1, xml.values().size()); + assertEquals("test", xml.values().toArray()[0]); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals("test", out.toString()); + } + + /** + * The purpose of this test is to map a {@link GenericXml} to the String element in the object. + */ + @Test + public void testParseSimpleTypeAsValueInteger() throws Exception { + SimpleTypeNumericGeneric xml = new SimpleTypeNumericGeneric(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(SIMPLE_XML_NUMERIC)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary().set("", ""); + Xml.parseElement(parser, xml, namespaceDictionary, null); + assertNotNull(xml); + assertEquals(1, xml.values().size()); + assertEquals(1, xml.values().toArray()[0]); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals("1", out.toString()); + } + + /** + * The purpose of this test is to map a {@link GenericXml} to the String element in the object. + */ + @Test + public void testParseToAnyType() throws Exception { + processAnyTypeGeneric(ANY_TYPE_XML); + } + + /** + * The purpose of this test is to map a {@link GenericXml} to the String element in the object. + */ + @Test + public void testParseToAnyTypeMissingField() throws Exception { + AnyTypeMissingFieldGeneric xml = new AnyTypeMissingFieldGeneric(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(ANY_TYPE_XML)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + assertNotNull(xml); + assertEquals(4, xml.values().size()); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals( + "contentcontentrep1rep2", + out.toString()); + } + + /** The purpose of this test isto map a {@link GenericXml} to the String element in the object. */ + @Test + public void testParseToAnyTypeAdditionalField() throws Exception { + AnyTypeAdditionalFieldGeneric xml = new AnyTypeAdditionalFieldGeneric(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(ANY_TYPE_XML)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + assertNotNull(xml); + assertEquals(4, xml.values().size()); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(ANY_TYPE_XML, out.toString()); + } + + /** + * The purpose of this test is to map a {@link GenericXml} to the String element in the object. + */ + @Test + public void testParseToAnyTypePrimitiveInt() throws Exception { + AnyTypePrimitiveIntGeneric xml = new AnyTypePrimitiveIntGeneric(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(ANY_TYPE_XML_PRIMITIVE_INT)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + assertNotNull(xml); + assertEquals(3, xml.values().size()); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(ANY_TYPE_XML_PRIMITIVE_INT, out.toString()); + } + + /** + * The purpose of this test is to map a {@link GenericXml} to the String element in the object. + */ + @Test + public void testParseToAnyTypeStringOnly() throws Exception { + AnyTypePrimitiveStringGeneric xml = new AnyTypePrimitiveStringGeneric(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(ANY_TYPE_XML_PRIMITIVE_STR)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + assertNotNull(xml); + assertEquals(3, xml.values().size()); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(ANY_TYPE_XML_PRIMITIVE_STR, out.toString()); + } + + /** + * The purpose of this tests is to test the mapping of an XML to an object without any overlap. In + * this case all the objects are stored in the {@link GenericXml#values} field. + */ + @Test + public void testParseIncorrectMapping() throws Exception { + AnyTypeGeneric xml = new AnyTypeGeneric(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(ALL_TYPE)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary().set("", ""); + Xml.parseElement(parser, xml, namespaceDictionary, null); + assertNotNull(xml); + assertEquals(6, xml.values().size()); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals( + "", + out.toString()); + } + + /** + * The purpose of this test is to map a {@link GenericXml} to the String element in the object. + */ + @Test + public void testParseAnyTypeWithNestedElementArrayMap() throws Exception { + processAnyTypeGeneric(ANY_TYPE_XML_NESTED_ARRAY); + } + + private void processAnyTypeGeneric(final String anyTypeXmlNestedArray) + throws XmlPullParserException, IOException { + AnyTypeGeneric xml = new AnyTypeGeneric(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(anyTypeXmlNestedArray)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + assertNotNull(xml); + assertEquals(4, xml.values().size()); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(anyTypeXmlNestedArray, out.toString()); + } + + private static class AnyGenericType { + @Key("@attr") + public Object attr; + + @Key public GenericXml elem; + } + + private static class SimpleTypeStringGeneric extends GenericXml { + @Key("text()") + public String value; + } + + private static class SimpleTypeNumericGeneric extends GenericXml { + @Key("text()") + public int value; + } + + private static class AnyTypeGeneric extends GenericXml { + @Key("@attr") + public Object attr; + + @Key public Object elem; + @Key public Object rep; + @Key public ValueTypeGeneric value; + } + + private static class AnyTypeMissingFieldGeneric extends GenericXml { + @Key("@attr") + public Object attr; + + @Key public Object elem; + @Key public ValueTypeGeneric value; + } + + private static class AnyTypeAdditionalFieldGeneric extends GenericXml { + @Key("@attr") + public Object attr; + + @Key public Object elem; + @Key public Object rep; + @Key public Object additionalField; + @Key public ValueTypeGeneric value; + } + + public static class ValueTypeGeneric extends GenericXml { + @Key("text()") + public Object content; + } + + private static class AnyTypePrimitiveIntGeneric extends GenericXml { + @Key("text()") + public int value; + + @Key("@attr") + public int attr; + + @Key public int[] intArray; + } + + private static class AnyTypePrimitiveStringGeneric extends GenericXml { + @Key("text()") + public String value; + + @Key("@attr") + public String attr; + + @Key public String[] strArray; } } diff --git a/google-http-client-xml/src/test/java/com/google/api/client/xml/XmlEnumTest.java b/google-http-client-xml/src/test/java/com/google/api/client/xml/XmlEnumTest.java index 1e0d79182..b199cb2db 100644 --- a/google-http-client-xml/src/test/java/com/google/api/client/xml/XmlEnumTest.java +++ b/google-http-client-xml/src/test/java/com/google/api/client/xml/XmlEnumTest.java @@ -1,64 +1,63 @@ +/* + * Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + package com.google.api.client.xml; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.google.api.client.util.Key; +import com.google.api.client.util.Value; import java.io.ByteArrayOutputStream; import java.io.StringReader; import java.util.ArrayList; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; -import com.google.api.client.util.Key; -import com.google.api.client.util.Value; -import junit.framework.TestCase; - -public class XmlEnumTest extends TestCase { - public enum AnyEnum { - @Value ENUM_1, - @Value ENUM_2 - } - - public static class AnyType { - @Key("@attr") - public Object attr; - @Key - public Object elem; - @Key - public Object rep; - @Key("@anyEnum") - public XmlEnumTest.AnyEnum anyEnum; - @Key - public XmlEnumTest.AnyEnum anotherEnum; - @Key - public ValueType value; - } - - public static class AnyTypeEnumElementOnly { - @Key - public XmlEnumTest.AnyEnum elementEnum; - } - - public static class AnyTypeEnumAttributeOnly { - @Key("@attributeEnum") - public XmlEnumTest.AnyEnum attributeEnum; - } - - public static class ValueType { - @Key("text()") - public XmlEnumTest.AnyEnum content; - } +/** + * Tests {@link Xml}. + * + * @author Gerald Madlmayr + */ +@RunWith(JUnit4.class) +public class XmlEnumTest { private static final String XML = - "" - + "ENUM_2contentrep1rep2ENUM_1"; - - private static final String XML_ENUM_ELEMENT_ONLY = "ENUM_2"; - - private static final String XML_ENUM_ATTRIBUTE_ONLY = ""; - - private static final String XML_ENUM_INCORRECT = "ENUM_3"; - - - @SuppressWarnings("cast") - public void testParse_anyType() throws Exception { + "ENUM_2contentrep1rep2ENUM_1"; + private static final String XML_ENUM_ELEMENT_ONLY = + "ENUM_2"; + private static final String XML_ENUM_ATTRIBUTE_ONLY = + ""; + private static final String XML_ENUM_INCORRECT = + "ENUM_3"; + private static final String XML_ENUM_ELEMENT_ONLY_NESTED = + "ENUM_2something"; + + @Test + public void testParseAnyType() throws Exception { AnyType xml = new AnyType(); XmlPullParser parser = Xml.createParser(); parser.setInput(new StringReader(XML)); @@ -67,13 +66,13 @@ public void testParse_anyType() throws Exception { assertTrue(xml.attr instanceof String); assertTrue(xml.elem.toString(), xml.elem instanceof ArrayList); assertTrue(xml.rep.toString(), xml.rep instanceof ArrayList); - assertTrue(xml.value instanceof ValueType); - assertTrue(xml.value.content instanceof XmlEnumTest.AnyEnum); - assertTrue(xml.anyEnum instanceof XmlEnumTest.AnyEnum); - assertTrue(xml.anotherEnum instanceof XmlEnumTest.AnyEnum); - assertTrue(xml.anyEnum.equals(AnyEnum.ENUM_1)); - assertTrue(xml.anotherEnum.equals(AnyEnum.ENUM_2)); - assertTrue(xml.value.content.equals(AnyEnum.ENUM_1)); + assertNotNull(xml.value); + assertNotNull(xml.value.content); + assertNotNull(xml.anyEnum); + assertNotNull(xml.anotherEnum); + assertEquals(xml.anyEnum, AnyEnum.ENUM_1); + assertEquals(xml.anotherEnum, AnyEnum.ENUM_2); + assertEquals(xml.value.content, AnyEnum.ENUM_1); // serialize XmlSerializer serializer = Xml.createSerializer(); ByteArrayOutputStream out = new ByteArrayOutputStream(); @@ -82,30 +81,54 @@ public void testParse_anyType() throws Exception { assertEquals(XML, out.toString()); } - public void testParse_enumElementType() throws Exception { - XmlEnumTest.AnyTypeEnumElementOnly xml = new XmlEnumTest.AnyTypeEnumElementOnly(); + /** The purpose of this test is to parse an XML element to an objects's field. */ + @Test + public void testParseToEnumElementType() throws Exception { + assertEquals(XML_ENUM_ELEMENT_ONLY, testStandardXml(XML_ENUM_ELEMENT_ONLY)); + } + + /** + * The purpose of this test is to parse an XML element to an objects's field, whereas there are + * additional nested elements in the tag. + */ + @Test + public void testParseToEnumElementTypeWithNestedElement() throws Exception { + assertEquals(XML_ENUM_ELEMENT_ONLY, testStandardXml(XML_ENUM_ELEMENT_ONLY_NESTED)); + } + + /** + * Private Method to handle standard parsing and mapping to {@link AnyTypeEnumElementOnly}. + * + * @param xmlString XML String that needs to be mapped to {@link AnyTypeEnumElementOnly}. + * @return returns the serialized string of the XML object. + * @throws Exception thrown if there is an issue processing the XML. + */ + private String testStandardXml(final String xmlString) throws Exception { + AnyTypeEnumElementOnly xml = new AnyTypeEnumElementOnly(); XmlPullParser parser = Xml.createParser(); - parser.setInput(new StringReader(XML_ENUM_ELEMENT_ONLY)); + parser.setInput(new StringReader(xmlString)); XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); Xml.parseElement(parser, xml, namespaceDictionary, null); - assertTrue(xml.elementEnum instanceof XmlEnumTest.AnyEnum); - assertTrue(xml.elementEnum.equals(AnyEnum.ENUM_2)); + assertNotNull(xml.elementEnum); + assertEquals(xml.elementEnum, AnyEnum.ENUM_2); // serialize XmlSerializer serializer = Xml.createSerializer(); ByteArrayOutputStream out = new ByteArrayOutputStream(); serializer.setOutput(out, "UTF-8"); namespaceDictionary.serialize(serializer, "any", xml); - assertEquals(XML_ENUM_ELEMENT_ONLY, out.toString()); + return out.toString(); } + /** The purpose of this test is to parse an XML attribute to an object's field. */ + @Test public void testParse_enumAttributeType() throws Exception { XmlEnumTest.AnyTypeEnumAttributeOnly xml = new XmlEnumTest.AnyTypeEnumAttributeOnly(); XmlPullParser parser = Xml.createParser(); parser.setInput(new StringReader(XML_ENUM_ATTRIBUTE_ONLY)); XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); Xml.parseElement(parser, xml, namespaceDictionary, null); - assertTrue(xml.attributeEnum instanceof XmlEnumTest.AnyEnum); - assertTrue(xml.attributeEnum.equals(AnyEnum.ENUM_1)); + assertNotNull(xml.attributeEnum); + assertEquals(xml.attributeEnum, AnyEnum.ENUM_1); // serialize XmlSerializer serializer = Xml.createSerializer(); ByteArrayOutputStream out = new ByteArrayOutputStream(); @@ -114,19 +137,58 @@ public void testParse_enumAttributeType() throws Exception { assertEquals(XML_ENUM_ATTRIBUTE_ONLY, out.toString()); } + /** + * The purpose of this test is to parse an XML element to an object's field which is an + * enumeration, whereas the enumeration element does not exist. + */ + @Test public void testParse_enumElementTypeIncorrect() throws Exception { XmlEnumTest.AnyTypeEnumElementOnly xml = new XmlEnumTest.AnyTypeEnumElementOnly(); XmlPullParser parser = Xml.createParser(); parser.setInput(new StringReader(XML_ENUM_INCORRECT)); XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); - try{ + try { Xml.parseElement(parser, xml, namespaceDictionary, null); // fail test, if there is no exception fail(); - } catch (final IllegalArgumentException e){ + } catch (final IllegalArgumentException e) { assertEquals("given enum name ENUM_3 not part of enumeration", e.getMessage()); } + } + + public enum AnyEnum { + @Value + ENUM_1, + @Value + ENUM_2 + } + + private static class AnyType { + @Key("@attr") + private Object attr; + + @Key private Object elem; + @Key private Object rep; + + @Key("@anyEnum") + private XmlEnumTest.AnyEnum anyEnum; + + @Key private XmlEnumTest.AnyEnum anotherEnum; + @Key private ValueType value; + } + private static class AnyTypeEnumElementOnly { + @Key private XmlEnumTest.AnyEnum elementEnum; } + private static class AnyTypeEnumAttributeOnly { + @Key("@attributeEnum") + private AnyEnum attributeEnum; + } + + /** Needs to be public, this is referenced in another element. */ + public static class ValueType { + @Key("text()") + private XmlEnumTest.AnyEnum content; + } } diff --git a/google-http-client-xml/src/test/java/com/google/api/client/xml/XmlListTest.java b/google-http-client-xml/src/test/java/com/google/api/client/xml/XmlListTest.java new file mode 100644 index 000000000..c12904faa --- /dev/null +++ b/google-http-client-xml/src/test/java/com/google/api/client/xml/XmlListTest.java @@ -0,0 +1,386 @@ +/* + * Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.xml; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import com.google.api.client.util.ArrayMap; +import com.google.api.client.util.Key; +import java.io.ByteArrayOutputStream; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Collection; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlSerializer; + +/** + * Tests Lists of various data types parsed in {@link Xml}. + * + * @author Gerald Madlmayr + */ +@RunWith(JUnit4.class) +public class XmlListTest { + + private static final String MULTI_TYPE_WITH_CLASS_TYPE = + "content1rep10rep11value1content2rep20rep21value2content3rep30rep31value3"; + private static final String MULTIPLE_STRING_ELEMENT = + "rep1rep2"; + private static final String MULTIPLE_STRING_ELEMENT_IN_COLLECTION = + "rep1rep2"; + private static final String MULTIPLE_INTEGER_ELEMENT = + "12"; + private static final String MULTIPLE_ENUM_ELEMENT = + "ENUM_1ENUM_2"; + private static final String COLLECTION_OF_ARRAY = + "abcd"; + + /** The purpose of this test is to map an XML with an Array of {@link XmlTest.AnyType} objects. */ + @SuppressWarnings("unchecked") + @Test + public void testParseArrayTypeWithClassType() throws Exception { + ArrayWithClassType xml = new ArrayWithClassType(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(MULTI_TYPE_WITH_CLASS_TYPE)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + assertNotNull(xml.rep); + XmlTest.AnyType[] rep = xml.rep; + assertNotNull(rep); + assertEquals(3, rep.length); + ArrayList> elem0 = (ArrayList>) rep[0].elem; + assertEquals(1, elem0.size()); + assertEquals("content1", elem0.get(0).get("text()")); + ArrayList> elem1 = (ArrayList>) rep[1].elem; + assertEquals(1, elem1.size()); + assertEquals("content2", elem1.get(0).get("text()")); + ArrayList> elem2 = (ArrayList>) rep[2].elem; + assertEquals(1, elem2.size()); + assertEquals("content3", elem2.get(0).get("text()")); + + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(MULTI_TYPE_WITH_CLASS_TYPE, out.toString()); + } + + /** + * The purpose of this test is to map an XML with a {@link Collection} of {@link XmlTest.AnyType} + * objects. + */ + @Test + public void testParseCollectionWithClassType() throws Exception { + CollectionWithClassType xml = new CollectionWithClassType(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(MULTI_TYPE_WITH_CLASS_TYPE)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + assertNotNull(xml.rep); + Collection rep = xml.rep; + assertNotNull(rep); + assertEquals(3, rep.size()); + + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(MULTI_TYPE_WITH_CLASS_TYPE, out.toString()); + } + + /** The purpose of this test is to map an XML with a {@link Collection} of {@link String}. */ + @Test + public void testParseCollectionTypeString() throws Exception { + CollectionTypeString xml = new CollectionTypeString(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(MULTIPLE_STRING_ELEMENT)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + assertEquals(2, xml.rep.size()); + assertEquals("rep1", xml.rep.toArray(new String[] {})[0]); + assertEquals("rep2", xml.rep.toArray(new String[] {})[1]); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(MULTIPLE_STRING_ELEMENT, out.toString()); + } + + /** The purpose of this test is to map an XML with an Array of {@link String} objects. */ + @Test + public void testParseArrayTypeString() throws Exception { + ArrayTypeString xml = new ArrayTypeString(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(MULTIPLE_STRING_ELEMENT)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + assertEquals(2, xml.rep.length); + assertEquals("rep1", xml.rep[0]); + assertEquals("rep2", xml.rep[1]); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(MULTIPLE_STRING_ELEMENT, out.toString()); + } + /** + * The purpose of this test is to map an XML with a sub element of a {@link Collection} of {@link + * String} objects. + */ + @Test + public void testParseAnyTypeWithACollectionString() throws Exception { + AnyTypeWithCollectionString xml = new AnyTypeWithCollectionString(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(MULTIPLE_STRING_ELEMENT_IN_COLLECTION)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + assertNotNull(xml.coll); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(MULTIPLE_STRING_ELEMENT_IN_COLLECTION, out.toString()); + } + + /** + * The purpose of this test is to map an XML with a {@link Collection} of {@link Integer} objects. + */ + @Test + public void testParseCollectionTypeInteger() throws Exception { + CollectionTypeInteger xml = new CollectionTypeInteger(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(MULTIPLE_INTEGER_ELEMENT)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + assertEquals(2, xml.rep.size()); + assertEquals(1, xml.rep.toArray(new Integer[] {})[0].intValue()); + assertEquals(2, xml.rep.toArray(new Integer[] {})[1].intValue()); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(MULTIPLE_INTEGER_ELEMENT, out.toString()); + } + + /** The purpose of this test is to map an XML with an Array of {@link Integer} objects. */ + @Test + public void testParseArrayTypeInteger() throws Exception { + ArrayTypeInteger xml = new ArrayTypeInteger(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(MULTIPLE_INTEGER_ELEMENT)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + assertEquals(2, xml.rep.length); + assertEquals(1, xml.rep[0].intValue()); + assertEquals(2, xml.rep[1].intValue()); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(MULTIPLE_INTEGER_ELEMENT, out.toString()); + } + + /** The purpose of this test is to map an XML with an Array of {@code int} types. */ + @Test + public void testParseArrayTypeInt() throws Exception { + ArrayTypeInt xml = new ArrayTypeInt(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(MULTIPLE_INTEGER_ELEMENT)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + assertEquals(2, xml.rep.length); + assertEquals(1, xml.rep[0]); + assertEquals(2, xml.rep[1]); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(MULTIPLE_INTEGER_ELEMENT, out.toString()); + } + + /** + * The purpose of this test is to map an XML with a {@link Collection} of {@link Enum} objects. + */ + @Test + public void testParseCollectionTypeWithEnum() throws Exception { + CollectionTypeEnum xml = new CollectionTypeEnum(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(MULTIPLE_ENUM_ELEMENT)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + assertEquals(2, xml.rep.size()); + assertEquals(XmlEnumTest.AnyEnum.ENUM_1, xml.rep.toArray(new XmlEnumTest.AnyEnum[] {})[0]); + assertEquals(XmlEnumTest.AnyEnum.ENUM_2, xml.rep.toArray(new XmlEnumTest.AnyEnum[] {})[1]); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(MULTIPLE_ENUM_ELEMENT, out.toString()); + } + + /** The purpose of this test is to map an XML with an Array of {@link Enum} objects. */ + @Test + public void testParseArrayTypeWithEnum() throws Exception { + ArrayTypeEnum xml = new ArrayTypeEnum(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(MULTIPLE_ENUM_ELEMENT)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + assertEquals(2, xml.rep.length); + assertEquals(XmlEnumTest.AnyEnum.ENUM_1, xml.rep[0]); + assertEquals(XmlEnumTest.AnyEnum.ENUM_2, xml.rep[1]); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(MULTIPLE_ENUM_ELEMENT, out.toString()); + } + + /** The purpose is to have an Array of {@link java.lang.reflect.ParameterizedType} elements. */ + @Test + public void testParseToArrayOfArrayMaps() throws Exception { + ArrayOfArrayMapsType xml = new ArrayOfArrayMapsType(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(COLLECTION_OF_ARRAY)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + assertEquals(2, xml.rep.length); + assertEquals("a", xml.rep[0].getValue(0)); + assertEquals("a", xml.rep[0].getKey(0)); + assertEquals("b", xml.rep[0].getValue(1)); + assertEquals("b", xml.rep[0].getKey(1)); + assertEquals("c", xml.rep[1].getValue(0)); + assertEquals("c", xml.rep[1].getKey(0)); + assertEquals("d", xml.rep[1].getValue(1)); + assertEquals("d", xml.rep[1].getKey(1)); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(COLLECTION_OF_ARRAY, out.toString()); + } + + /** + * The purpose is to have an Collection of {@link java.lang.reflect.ParameterizedType} elements. + */ + @Test + public void testParseToCollectionOfArrayMaps() throws Exception { + CollectionOfArrayMapsType xml = new CollectionOfArrayMapsType(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(COLLECTION_OF_ARRAY)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + assertEquals(2, xml.rep.size()); + assertEquals("a", xml.rep.toArray(new ArrayMap[] {})[0].getValue(0)); + assertEquals("a", xml.rep.toArray(new ArrayMap[] {})[0].getKey(0)); + assertEquals("b", xml.rep.toArray(new ArrayMap[] {})[0].getValue(1)); + assertEquals("b", xml.rep.toArray(new ArrayMap[] {})[0].getKey(1)); + assertEquals("c", xml.rep.toArray(new ArrayMap[] {})[1].getValue(0)); + assertEquals("c", xml.rep.toArray(new ArrayMap[] {})[1].getKey(0)); + assertEquals("d", xml.rep.toArray(new ArrayMap[] {})[1].getValue(1)); + assertEquals("d", xml.rep.toArray(new ArrayMap[] {})[1].getKey(1)); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(COLLECTION_OF_ARRAY, out.toString()); + } + + private static class CollectionOfArrayMapsType { + @Key public Collection> rep; + } + + private static class ArrayOfArrayMapsType { + @Key public ArrayMap[] rep; + } + + private static class ArrayWithClassType { + @Key public XmlTest.AnyType[] rep; + } + + private static class CollectionWithClassType { + @Key public Collection rep; + } + + /** Needs to be public, this is referenced in another element. */ + public static class CollectionTypeString { + @Key public Collection rep; + } + + private static class ArrayTypeString { + @Key public String[] rep; + } + + private static class AnyTypeWithCollectionString { + @Key public CollectionTypeString coll; + } + + private static class CollectionTypeInteger { + @Key public Collection rep; + } + + private static class ArrayTypeInteger { + @Key public Integer[] rep; + } + + private static class ArrayTypeInt { + @Key public int[] rep; + } + + private static class CollectionTypeEnum { + @Key public Collection rep; + } + + private static class ArrayTypeEnum { + @Key public XmlEnumTest.AnyEnum[] rep; + } +} diff --git a/google-http-client-xml/src/test/java/com/google/api/client/xml/XmlNamespaceDictionaryTest.java b/google-http-client-xml/src/test/java/com/google/api/client/xml/XmlNamespaceDictionaryTest.java index 051b6e5ec..562bdb224 100644 --- a/google-http-client-xml/src/test/java/com/google/api/client/xml/XmlNamespaceDictionaryTest.java +++ b/google-http-client-xml/src/test/java/com/google/api/client/xml/XmlNamespaceDictionaryTest.java @@ -14,13 +14,20 @@ package com.google.api.client.xml; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import com.google.api.client.util.Key; import com.google.api.client.xml.atom.Atom; import com.google.common.collect.ImmutableMap; import java.io.StringWriter; import java.util.Collection; import java.util.TreeSet; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; import org.xmlpull.v1.XmlSerializer; /** @@ -28,35 +35,25 @@ * * @author Yaniv Inbar */ -public class XmlNamespaceDictionaryTest extends TestCase { - - public XmlNamespaceDictionaryTest() { - } - - public XmlNamespaceDictionaryTest(String name) { - super(name); - } +@RunWith(JUnit4.class) +public class XmlNamespaceDictionaryTest { private static final String EXPECTED = - "" + "" - + "One" - + "Two"; - + "OneTwo"; private static final String EXPECTED_EMPTY_MAP = - "" + ""; - + ""; private static final String EXPECTED_EMPTY_MAP_NS_UNDECLARED = - "" + ""; - + ""; private static final String EXPECTED_EMPTY_MAP_ATOM_NS = - "" + ""; - + ""; private static final String EXPECTED_UNKNOWN_NS = - "" + "" + "One" - + "Two"; + "One" + + "Two"; + @Test public void testSet() { XmlNamespaceDictionary dictionary = new XmlNamespaceDictionary(); dictionary.set("", "http://www.w3.org/2005/Atom").set("gd", "http://schemas.google.com/g/2005"); @@ -78,11 +75,13 @@ public void testSet() { assertEquals("http://schemas.google.com/g/2005", dictionary.getUriForAlias("foo")); dictionary.set("foo", null); assertTrue(dictionary.getAliasToUriMap().isEmpty()); - dictionary.set("foo", "http://schemas.google.com/g/2005").set( - null, "http://schemas.google.com/g/2005"); + dictionary + .set("foo", "http://schemas.google.com/g/2005") + .set(null, "http://schemas.google.com/g/2005"); assertTrue(dictionary.getAliasToUriMap().isEmpty()); } + @Test public void testSerialize() throws Exception { Feed feed = new Feed(); feed.entries = new TreeSet(); @@ -98,6 +97,7 @@ public void testSerialize() throws Exception { assertEquals(EXPECTED, writer.toString()); } + @Test public void testSerializeByName() throws Exception { Feed feed = new Feed(); feed.entries = new TreeSet(); @@ -113,6 +113,7 @@ public void testSerializeByName() throws Exception { assertEquals(EXPECTED, writer.toString()); } + @Test public void testSerialize_emptyMap() throws Exception { ImmutableMap map = ImmutableMap.of(); StringWriter writer = new StringWriter(); @@ -124,6 +125,7 @@ public void testSerialize_emptyMap() throws Exception { assertEquals(EXPECTED_EMPTY_MAP, writer.toString()); } + @Test public void testSerializeByName_emptyMap() throws Exception { ImmutableMap map = ImmutableMap.of(); StringWriter writer = new StringWriter(); @@ -135,6 +137,7 @@ public void testSerializeByName_emptyMap() throws Exception { assertEquals(EXPECTED_EMPTY_MAP, writer.toString()); } + @Test public void testSerializeByName_emptyMapAtomNs() throws Exception { ImmutableMap map = ImmutableMap.of(); StringWriter writer = new StringWriter(); @@ -146,6 +149,7 @@ public void testSerializeByName_emptyMapAtomNs() throws Exception { assertEquals(EXPECTED_EMPTY_MAP_ATOM_NS, writer.toString()); } + @Test public void testSerialize_emptyMapNsUndeclared() throws Exception { ImmutableMap map = ImmutableMap.of(); StringWriter writer = new StringWriter(); @@ -156,30 +160,7 @@ public void testSerialize_emptyMapNsUndeclared() throws Exception { assertEquals(EXPECTED_EMPTY_MAP_NS_UNDECLARED, writer.toString()); } - public static class Entry implements Comparable { - @Key - public String title; - - @Key("@gd:etag") - public String etag; - - public Entry(String title, String etag) { - super(); - this.title = title; - this.etag = etag; - } - - public int compareTo(Entry other) { - return title.compareTo(other.title); - } - } - - public static class Feed { - @Key("entry") - public Collection entries; - - } - + @Test public void testSerialize_errorOnUnknown() throws Exception { Entry entry = new Entry("One", "abc"); StringWriter writer = new StringWriter(); @@ -195,6 +176,7 @@ public void testSerialize_errorOnUnknown() throws Exception { } } + @Test public void testSerializeByName_errorOnUnknown() throws Exception { Entry entry = new Entry("One", "abc"); StringWriter writer = new StringWriter(); @@ -210,6 +192,7 @@ public void testSerializeByName_errorOnUnknown() throws Exception { } } + @Test public void testSerialize_unknown() throws Exception { Feed feed = new Feed(); feed.entries = new TreeSet(); @@ -222,4 +205,25 @@ public void testSerialize_unknown() throws Exception { assertEquals(EXPECTED_UNKNOWN_NS, namespaceDictionary.toStringOf("feed", feed)); } + public static class Entry implements Comparable { + @Key public String title; + + @Key("@gd:etag") + public String etag; + + public Entry(String title, String etag) { + super(); + this.title = title; + this.etag = etag; + } + + public int compareTo(Entry other) { + return title.compareTo(other.title); + } + } + + public static class Feed { + @Key("entry") + public Collection entries; + } } diff --git a/google-http-client-xml/src/test/java/com/google/api/client/xml/XmlTest.java b/google-http-client-xml/src/test/java/com/google/api/client/xml/XmlTest.java index 033d3e1ba..d9a381424 100644 --- a/google-http-client-xml/src/test/java/com/google/api/client/xml/XmlTest.java +++ b/google-http-client-xml/src/test/java/com/google/api/client/xml/XmlTest.java @@ -14,13 +14,22 @@ package com.google.api.client.xml; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import com.google.api.client.util.ArrayMap; import com.google.api.client.util.Key; import java.io.ByteArrayOutputStream; import java.io.StringReader; import java.util.ArrayList; -import java.util.Map; -import junit.framework.TestCase; +import java.util.Collection; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; @@ -28,92 +37,356 @@ * Tests {@link Xml}. * * @author Yaniv Inbar + * @author Gerald Madlmayr */ -public class XmlTest extends TestCase { +@RunWith(JUnit4.class) +public class XmlTest { - public static class AnyType { - @Key("@attr") - public Object attr; - @Key - public Object elem; - @Key - public Object rep; - @Key - public ValueType value; + private static final String SIMPLE_XML = "test"; + private static final String SIMPLE_XML_NUMERIC = "1"; + private static final String START_WITH_TEXT = "start_with_text"; + private static final String MISSING_END_ELEMENT = + "" + "missing_end_element"; + private static final String START_WITH_END_ELEMENT = + "

    start_with_end_elemtn"; + private static final String START_WITH_END_ELEMENT_NESTED = + "

    start_with_end_element_nested
    "; + private static final String ANY_TYPE_XML = + "contentrep1rep2" + + "content"; + private static final String ANY_TYPE_MISSING_XML = + "contentcontent"; + private static final String ANY_TYPE_XML_PRIMITIVE_INT = + "112" + + ""; + private static final String ANY_TYPE_XML_PRIMITIVE_STR = + "1+11+12" + + "+1"; + private static final String NESTED_NS = + "2011-08-09T04:38" + + ":14.017Z"; + private static final String NESTED_NS_SERIALIZED = + "2011-08-09T04:38:14.017Z"; + private static final String INF_TEST = + "-INFINF-INFINF" + + ""; + private static final String ALL_TYPE = + ""; + private static final String ALL_TYPE_WITH_DATA = + "" + + "ENUM_1ENUM_2Title

    Test

    112str1arr1arr2
    "; + private static final String ANY_TYPE_XML_NESTED_ARRAY = + "content

    rep1

    rep2

    rep3

    rep4

    content
    "; + + /** + * The purpose of this test is to map a single element to a single field of a destination object. + * In this case the object mapped is a {@link String}; no namespace used. + */ + @Test + public void testParseSimpleTypeAsValueString() throws Exception { + SimpleTypeString xml = new SimpleTypeString(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(SIMPLE_XML)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary().set("", ""); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + assertEquals("test", xml.value); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals("test", out.toString()); } - public static class ValueType { - @Key("text()") - public Object content; + /** + * The purpose of this test is to map a single element to a single field of a destination object. + * In this is it is not an object but a {@code int}. no namespace used. + */ + @Test + public void testParseSimpleTypeAsValueInteger() throws Exception { + SimpleTypeNumeric xml = new SimpleTypeNumeric(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(SIMPLE_XML_NUMERIC)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary().set("", ""); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + assertEquals(1, xml.value); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals("1", out.toString()); + } + + /** Negative test to check for text without a start-element. */ + @Test + public void testWithTextFail() throws Exception { + SimpleTypeString xml = new SimpleTypeString(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(START_WITH_TEXT)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary().set("", ""); + try { + Xml.parseElement(parser, xml, namespaceDictionary, null); + fail(); + } catch (final Exception e) { + assertEquals( + "only whitespace content allowed before start tag and not s (position: " + + "START_DOCUMENT seen s... @1:22)", + e.getMessage().trim()); + } + } + + /** Negative test to check for missing end-element. */ + @Test + public void testWithMissingEndElementFail() throws Exception { + SimpleTypeString xml = new SimpleTypeString(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(MISSING_END_ELEMENT)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary().set("", ""); + try { + Xml.parseElement(parser, xml, namespaceDictionary, null); + fail(); + } catch (final Exception e) { + assertEquals( + "no more data available - expected end tag
    to close start tag from line 1, parser stopped on START_TAG seen ...missing_end_element... @1:54", + e.getMessage().trim()); + } + } + + /** Negative test with that start with a end-element. */ + @Test + public void testWithEndElementStarting() throws Exception { + SimpleTypeString xml = new SimpleTypeString(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(START_WITH_END_ELEMENT)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary().set("", ""); + try { + Xml.parseElement(parser, xml, namespaceDictionary, null); + fail(); + } catch (final Exception e) { + assertEquals( + "expected start tag name and not / (position: START_DOCUMENT seen must match start tag name from line 1 (position:" + + " START_TAG seen ...

    ... @1:39)", + e.getMessage().trim()); + } } - private static final String XML = - "" - + "contentrep1rep2content"; + /** Negative test that maps a string to an integer and causes an exception. */ + @Test + public void testFailMappingOfDataType() throws Exception { + SimpleTypeNumeric xml = new SimpleTypeNumeric(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(SIMPLE_XML)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary().set("", ""); + try { + Xml.parseElement(parser, xml, namespaceDictionary, null); + fail(); + } catch (final Exception e) { + assertEquals("For input string: \"test\"", e.getMessage().trim()); + } + } - @SuppressWarnings("cast") - public void testParse_anyType() throws Exception { + /** + * The purpose of this tests it to test the {@link Key} Annotation for mapping of elements and + * attributes. All elements/attributes are matched. + */ + @Test + public void testParseToAnyType() throws Exception { AnyType xml = new AnyType(); XmlPullParser parser = Xml.createParser(); - parser.setInput(new StringReader(XML)); + parser.setInput(new StringReader(ANY_TYPE_XML)); XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); Xml.parseElement(parser, xml, namespaceDictionary, null); assertTrue(xml.attr instanceof String); assertTrue(xml.elem.toString(), xml.elem instanceof ArrayList); assertTrue(xml.rep.toString(), xml.rep instanceof ArrayList); - assertTrue(xml.value instanceof ValueType); + assertNotNull(xml.value); assertTrue(xml.value.content instanceof String); // serialize XmlSerializer serializer = Xml.createSerializer(); ByteArrayOutputStream out = new ByteArrayOutputStream(); serializer.setOutput(out, "UTF-8"); namespaceDictionary.serialize(serializer, "any", xml); - assertEquals(XML, out.toString()); + assertEquals(ANY_TYPE_XML, out.toString()); } - public static class ArrayType extends GenericXml { - @Key - public Map[] rep; + /** + * The purpose of this tests it to test the {@link Key} annotation for mapping of elements and + * attributes. The matched object misses some field that are present int the XML ('elem' is + * missing and therefore ignored). + */ + @Test + public void testParseToAnyTypeMissingField() throws Exception { + AnyTypeMissingField xml = new AnyTypeMissingField(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(ANY_TYPE_XML)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + assertTrue(xml.attr instanceof String); + assertTrue(xml.elem.toString(), xml.elem instanceof ArrayList); + assertNotNull(xml.value); + assertTrue(xml.value.content instanceof String); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(ANY_TYPE_MISSING_XML, out.toString()); } - private static final String ARRAY_TYPE = - "" - + "rep1rep2"; - - public void testParse_arrayType() throws Exception { - ArrayType xml = new ArrayType(); + /** + * The purpose of this tests it to test the {@link Key} Annotation for mapping of elements and + * attributes. The matched object has an additional field, that will not be used and stays {@code + * null}. + */ + @Test + public void testParseToAnyTypeAdditionalField() throws Exception { + AnyTypeAdditionalField xml = new AnyTypeAdditionalField(); XmlPullParser parser = Xml.createParser(); - parser.setInput(new StringReader(ARRAY_TYPE)); + parser.setInput(new StringReader(ANY_TYPE_XML)); XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); Xml.parseElement(parser, xml, namespaceDictionary, null); - // check type - Map[] rep = xml.rep; - assertEquals(2, rep.length); - ArrayMap map0 = (ArrayMap) rep[0]; - assertEquals(1, map0.size()); - assertEquals("rep1", map0.get("text()")); - ArrayMap map1 = (ArrayMap) rep[1]; - assertEquals(1, map1.size()); - assertEquals("rep2", map1.get("text()")); + assertTrue(xml.attr instanceof String); + assertTrue(xml.elem.toString(), xml.elem instanceof ArrayList); + assertNotNull(xml.value); + assertNull(xml.additionalField); + assertTrue(xml.rep.toString(), xml.rep instanceof ArrayList); + assertTrue(xml.value.content instanceof String); // serialize XmlSerializer serializer = Xml.createSerializer(); ByteArrayOutputStream out = new ByteArrayOutputStream(); serializer.setOutput(out, "UTF-8"); namespaceDictionary.serialize(serializer, "any", xml); - assertEquals(ARRAY_TYPE, out.toString()); + assertEquals(ANY_TYPE_XML, out.toString()); } - private static final String NESTED_NS = - "" - + "2011-08-09T04:38:14.017Z" - + ""; + /** + * The purpose of this test is to see, if there is an exception of the parameter 'destination' in + * {@link Xml#parseElement} is {@code null}. The parser internally will skip mapping of the XML + * structure, but will parse it anyway. + */ + @Test + public void testParseToAnyTypeWithNullDestination() throws Exception { + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(ANY_TYPE_XML)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, null, namespaceDictionary, null); + } - private static final String NESTED_NS_SERIALIZED = - "" + "2011-08-09T04:38:14.017Z" - + ""; + /** + * The purpose of this test is to see, if parsing works with a {@link Xml.CustomizeParser}. The + * XML will be mapped to {@link AnyType}. + */ + @Test + public void testParseAnyTypeWithCustomParser() throws Exception { + AnyType xml = new AnyType(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(ANY_TYPE_XML)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, new Xml.CustomizeParser()); + assertTrue(xml.attr instanceof String); + assertTrue(xml.elem.toString(), xml.elem instanceof ArrayList); + assertTrue(xml.rep.toString(), xml.rep instanceof ArrayList); + assertNotNull(xml.value); + assertTrue(xml.value.content instanceof String); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(ANY_TYPE_XML, out.toString()); + } + + /** + * The purpose of this test it to parse elements which will be mapped to a {@link + * javax.lang.model.type.PrimitiveType}. Therefore {@code int}s are mapped to attributes, elements + * and element arrays. + */ + @Test + public void testParseToAnyTypePrimitiveInt() throws Exception { + AnyTypePrimitiveInt xml = new AnyTypePrimitiveInt(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(ANY_TYPE_XML_PRIMITIVE_INT)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, new Xml.CustomizeParser()); + assertEquals(1, xml.value); + assertEquals(2, xml.attr); + assertEquals(2, xml.intArray.length); + assertEquals(1, xml.intArray[0]); + assertEquals(2, xml.intArray[1]); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(ANY_TYPE_XML_PRIMITIVE_INT, out.toString()); + } + + /** + * The purpose of this test it to parse elements which will be mapped to a Java {@link + * javax.lang.model.type.PrimitiveType}. Therefore {@code int}s are mapped to attributes, elements + * and element arrays. + */ + @Test + public void testParseToAnyTypeStringOnly() throws Exception { + AnyTypePrimitiveString xml = new AnyTypePrimitiveString(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(ANY_TYPE_XML_PRIMITIVE_STR)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, new Xml.CustomizeParser()); + assertEquals("1+1", xml.value); + assertEquals("2+1", xml.attr); + assertEquals(2, xml.strArray.length); + assertEquals("1+1", xml.strArray[0]); + assertEquals("2+1", xml.strArray[1]); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(ANY_TYPE_XML_PRIMITIVE_STR, out.toString()); + } - public void testParse_nestedNs() throws Exception { + /** The purpose of this test is to map nested elements with a namespace attribute. */ + @Test + public void testParseOfNestedNs() throws Exception { XmlPullParser parser = Xml.createParser(); parser.setInput(new StringReader(NESTED_NS)); XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); @@ -127,4 +400,225 @@ public void testParse_nestedNs() throws Exception { namespaceDictionary.serialize(serializer, "any", xml); assertEquals(NESTED_NS_SERIALIZED, out.toString()); } + + /** + * The purpose of this test is to map the infinity values of both {@code doubles} and {@code + * floats}. + */ + @Test + public void testParseInfiniteValues() throws Exception { + AnyTypeInf xml = new AnyTypeInf(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(INF_TEST)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary().set("", ""); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + assertEquals(Double.NEGATIVE_INFINITY, xml.dblInfNeg, 0.0001); + assertEquals(Double.POSITIVE_INFINITY, xml.dblInfPos, 0.0001); + assertEquals(Float.NEGATIVE_INFINITY, xml.fltInfNeg, 0.0001); + assertEquals(Float.POSITIVE_INFINITY, xml.fltInfPos, 0.0001); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(INF_TEST, out.toString()); + } + + /** + * The purpose of this test is to map multiple different data types in a single test, without + * data. (explorative) + */ + @Test + public void testParseEmptyElements() throws Exception { + AllType xml = new AllType(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(ALL_TYPE)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary().set("", ""); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + assertEquals(0, xml.integer); + // TODO: Shouldn't array size == 0? (currently generated via a = new x[1]). + assertEquals(1, xml.stringArray.length); + assertEquals(1, xml.anyEnum.length); + assertNotNull(xml.genericXml); + assertNotNull(xml.integerCollection); + assertNull(xml.str); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals( + "0", + out.toString()); + } + + /** + * The purpose of this test is to map multiple different data types in a single test, with data. + * (explorative) + */ + @Test + public void testParseAllElements() throws Exception { + AllType xml = new AllType(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(ALL_TYPE_WITH_DATA)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary().set("", ""); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(ALL_TYPE_WITH_DATA, out.toString()); + } + + /** + * The purpose of this tests is to map a completely unrelated XML to a given destination object. + * (explorative) + */ + @Test + public void testParseIncorrectMapping() throws Exception { + AnyType xml = new AnyType(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(ALL_TYPE)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary().set("", ""); + Xml.parseElement(parser, xml, namespaceDictionary, null); + // check type + assertNull(xml.elem); + assertNull(xml.value); + assertNull(xml.rep); + assertNull(xml.rep); + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals("", out.toString()); + } + + /** + * The purpose of this test is to map the sub elements of an {@link ArrayMap} again to an {@link + * ArrayMap}. + */ + @Test + public void testParseAnyTypeWithNestedElementArrayMap() throws Exception { + AnyType xml = new AnyType(); + XmlPullParser parser = Xml.createParser(); + parser.setInput(new StringReader(ANY_TYPE_XML_NESTED_ARRAY)); + XmlNamespaceDictionary namespaceDictionary = new XmlNamespaceDictionary(); + Xml.parseElement(parser, xml, namespaceDictionary, null); + assertTrue(xml.attr instanceof String); + assertTrue(xml.elem.toString(), xml.elem instanceof ArrayList); + assertTrue(xml.rep.toString(), xml.rep instanceof ArrayList); + assertNotNull(xml.value); + assertTrue(xml.value.content instanceof String); + assertEquals(1, ((Collection) xml.elem).size()); + assertEquals(2, ((Collection) xml.rep).size()); + assertEquals(1, ((Collection) xml.rep).toArray(new ArrayMap[] {})[0].size()); + assertEquals(1, ((Collection) xml.rep).toArray(new ArrayMap[] {})[1].size()); + assertEquals( + "rep1", + ((ArrayList) ((ArrayList) xml.rep).toArray(new ArrayMap[] {})[0].get("p")) + .toArray(new ArrayMap[] {})[0].getValue(0)); + assertEquals( + "rep2", + ((ArrayList) ((ArrayList) xml.rep).toArray(new ArrayMap[] {})[0].get("p")) + .toArray(new ArrayMap[] {})[1].getValue(0)); + assertEquals( + "rep3", + ((ArrayList) ((ArrayList) xml.rep).toArray(new ArrayMap[] {})[1].get("p")) + .toArray(new ArrayMap[] {})[0].getValue(0)); + assertEquals( + "rep4", + ((ArrayList) ((ArrayList) xml.rep).toArray(new ArrayMap[] {})[1].get("p")) + .toArray(new ArrayMap[] {})[1].getValue(0)); + + // serialize + XmlSerializer serializer = Xml.createSerializer(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.setOutput(out, "UTF-8"); + namespaceDictionary.serialize(serializer, "any", xml); + assertEquals(ANY_TYPE_XML_NESTED_ARRAY, out.toString()); + } + + public static class SimpleTypeString { + @Key("text()") + public String value; + } + + public static class SimpleTypeNumeric { + @Key("text()") + public int value; + } + + public static class AnyType { + @Key("@attr") + public Object attr; + + @Key public Object elem; + @Key public Object rep; + @Key public ValueType value; + } + + public static class AnyTypeMissingField { + @Key("@attr") + public Object attr; + + @Key public Object elem; + @Key public ValueType value; + } + + public static class AnyTypeAdditionalField { + @Key("@attr") + public Object attr; + + @Key public Object elem; + @Key public Object rep; + @Key public Object additionalField; + @Key public ValueType value; + } + + public static class ValueType { + @Key("text()") + public Object content; + } + + public static class AnyTypePrimitiveInt { + @Key("text()") + public int value; + + @Key("@attr") + public int attr; + + @Key public int[] intArray; + } + + public static class AnyTypePrimitiveString { + @Key("text()") + public String value; + + @Key("@attr") + public String attr; + + @Key public String[] strArray; + } + + private static class AnyTypeInf { + @Key public double dblInfNeg; + @Key public double dblInfPos; + @Key public float fltInfNeg; + @Key public float fltInfPos; + } + + private static class AllType { + @Key public int integer; + @Key public String str; + @Key public GenericXml genericXml; + @Key public XmlEnumTest.AnyEnum[] anyEnum; + @Key public String[] stringArray; + @Key public List integerCollection; + } } diff --git a/google-http-client-xml/src/test/resources/META-INF/native-image/com.google.http-client/google-http-client-xml/native-image.properties b/google-http-client-xml/src/test/resources/META-INF/native-image/com.google.http-client/google-http-client-xml/native-image.properties new file mode 100644 index 000000000..3cbd2b27f --- /dev/null +++ b/google-http-client-xml/src/test/resources/META-INF/native-image/com.google.http-client/google-http-client-xml/native-image.properties @@ -0,0 +1 @@ +Args=--initialize-at-build-time=org.junit.runner.RunWith,java.lang.annotation.Annotation diff --git a/google-http-client-xml/src/test/resources/META-INF/native-image/com.google.http-client/google-http-client-xml/reflect-config.json b/google-http-client-xml/src/test/resources/META-INF/native-image/com.google.http-client/google-http-client-xml/reflect-config.json new file mode 100644 index 000000000..ce1fa5588 --- /dev/null +++ b/google-http-client-xml/src/test/resources/META-INF/native-image/com.google.http-client/google-http-client-xml/reflect-config.json @@ -0,0 +1,674 @@ +[ + { + "name": "com.google.api.client.xml.GenericXmlListTest$CollectionOfArrayMapsTypeGeneric", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.GenericXmlListTest$ArrayOfArrayMapsTypeGeneric", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.GenericXmlListTest$ArrayWithClassTypeGeneric", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.GenericXmlListTest$CollectionWithClassTypeGeneric", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.GenericXmlListTest$MultiGenericWithClassType", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.GenericXmlListTest$MultiGenericWithClassTypeGeneric", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + + { + "name": "com.google.api.client.xml.GenericXmlListTest$CollectionTypeStringGeneric", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.GenericXmlListTest$ArrayTypeStringGeneric", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.GenericXmlListTest$CollectionTypeIntegerGeneric", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + + { + "name": "com.google.api.client.xml.GenericXmlListTest$ArrayTypeIntegerGeneric", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.GenericXmlListTest$ArrayTypeIntGeneric", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.GenericXmlListTest$CollectionTypeEnumGeneric", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.GenericXmlListTest$ArrayTypeEnumGeneric", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlNamespaceDictionaryTest$Entry", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlNamespaceDictionaryTest$Feed", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.AtomTest$Feed", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.AtomTest$Author", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.AtomTest$Link", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.AtomTest$FeedEntry", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlTest$AnyType", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlEnumTest$AnyEnum", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlEnumTest$AnyType", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlEnumTest$AnyTypeEnumElementOnly", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlEnumTest$AnyTypeEnumAttributeOnly", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlEnumTest$ValueType", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlEnumTest$CollectionOfArrayMapsType", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlEnumTest$ArrayOfArrayMapsType", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlEnumTest$ArrayWithClassType", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlEnumTest$CollectionTypeString", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlEnumTest$ArrayTypeString", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlEnumTest$AnyTypeWithCollectionString", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlEnumTest$CollectionTypeInteger", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlEnumTest$ArrayTypeInteger", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlEnumTest$ArrayTypeInt", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlEnumTest$CollectionTypeEnum", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlEnumTest$ArrayTypeEnum", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlTest$SimpleTypeString", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlTest$SimpleTypeNumeric", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlTest$AnyType", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlTest$AnyTypeMissingField", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlTest$AnyTypeAdditionalField", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlTest$ValueType", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlTest$AnyTypePrimitiveInt", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlTest$AnyTypePrimitiveString", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlTest$AnyTypeInf", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlTest$AllType", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlListTest$CollectionOfArrayMapsType", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlListTest$ArrayOfArrayMapsType", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlListTest$ArrayWithClassType", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlListTest$CollectionWithClassType", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlListTest$CollectionTypeString", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlListTest$ArrayTypeString", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.xml.XmlListTest$AnyTypeWithCollectionString", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name":"com.google.api.client.xml.XmlListTest$CollectionTypeInteger", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name":"com.google.api.client.xml.XmlListTest$ArrayTypeInteger", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name":"com.google.api.client.xml.XmlListTest$ArrayTypeInt", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name":"com.google.api.client.xml.XmlListTest$CollectionTypeEnum", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name":"com.google.api.client.xml.XmlListTest$ArrayTypeEnum", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name":"com.google.api.client.xml.GenericXmlTest$AnyGenericType", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name":"com.google.api.client.xml.GenericXmlTest$SimpleTypeStringGeneric", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name":"com.google.api.client.xml.GenericXmlTest$SimpleTypeNumericGeneric", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name":"com.google.api.client.xml.GenericXmlTest$AnyTypeGeneric", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name":"com.google.api.client.xml.GenericXmlTest$AnyTypeMissingFieldGeneric", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name":"com.google.api.client.xml.GenericXmlTest$AnyTypeAdditionalFieldGeneric", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name":"com.google.api.client.xml.GenericXmlTest$ValueTypeGeneric", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name":"com.google.api.client.xml.GenericXmlTest$AnyTypePrimitiveIntGeneric", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name":"com.google.api.client.xml.GenericXmlTest$AnyTypePrimitiveStringGeneric", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allPublicMethods": true, + "allDeclaredFields": true + } +] \ No newline at end of file diff --git a/google-http-client-xml/src/test/resources/META-INF/native-image/com.google.http-client/google-http-client-xml/resource-config.json b/google-http-client-xml/src/test/resources/META-INF/native-image/com.google.http-client/google-http-client-xml/resource-config.json new file mode 100644 index 000000000..29b0c76b1 --- /dev/null +++ b/google-http-client-xml/src/test/resources/META-INF/native-image/com.google.http-client/google-http-client-xml/resource-config.json @@ -0,0 +1,5 @@ +{ + "resources":[ + {"pattern":"\\Qsample-atom.xml\\E"}], + "bundles":[] +} \ No newline at end of file diff --git a/google-http-client-xml/src/test/resources/sample-atom.xml b/google-http-client-xml/src/test/resources/sample-atom.xml new file mode 100644 index 000000000..1d370a4dd --- /dev/null +++ b/google-http-client-xml/src/test/resources/sample-atom.xml @@ -0,0 +1 @@ +World Of Sample News online NewsEverything about Computers and ATOM Parsers2018-10-14T10:00:00+02:00https://www.world-of-sample-news.com/newsticker/https://www.world-of-sample-news.com/icons/svg/logos/svg/World Of Sample Newsonline.svgCopyright (c) 2018 World Of Sample News MedienWorld Of Sample News onlineTitle 01: Standard Blind Text with 5000 chars Contenthttps://world-of-sample-news.com/-41704982018-10-14T10:00:00+02:002018-10-14T10:00:00+02:00Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequatLorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut eros et nisl sagittis vestibulum. Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede. Sed lectus. Donec mollis hendrerit risus. Phasellus nec sem in justo pellentesque facilisis. Etiam imperdiet imperdiet orci. Nunc nec neque. Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi. Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo. Maecenas malesuada. Praesent congue erat at massa. Sed cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis orci. Phasellus consectetuer vestibulum elit. Aenean tellus metus, bibendum sed, posuere ac, mattis non, nunc. Vestibulum fringilla pede sit amet augue. In turpis. Pellentesque posuere. Praesent turpis. Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales nec, volutpat a, suscipit non, turpis. Nullam sagittis. Suspendisse pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id purus. Ut varius tincidunt libero. Phasellus dolor. Maecenas vestibulum mollis diam. Pellentesque ut neque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In dui magna, posuere eget, vestibulum et, tempor auctor, justo. In ac felis quis tortor malesuada pretium. Pellentesque auctor neque nec urna. Proin sapien ipsum, porta a, auctor quis, euismod ut, mi. Aenean viverra rhoncus pede. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut non enim eleifend felis pretium feugiat. Vivamus quis mi. Phasellus a est. Phasellus magna. In hac habitasse platea dictumst. Curabitur at lacus ac velit ornare lobortis. Curabitur a felis in nunc fringilla tristique. Morbi mattis ullamcorper velit. Phasellus gravida semper nisi. Nullam vel sem. Pellentesque libero tortor, tincidunt et, tincidunt eget, semper nec, quam. Sed hendrerit. Morbi ac felis. Nunc egestas, augue at pellentesque laoreet, felis eros vehicula leo, at malesuada velit leo quis pede. Donec interdum, metus et hendrerit aliquet, dolor diam sagittis ligula, eget egestas libero turpis vel mi. Nunc nulla. Fusce risus nisl, viverra et, tempor et, pretium in, sapien. Donec venenatis vulputate lorem. Morbi nec metus. Phasellus blandit leo ut odio. Maecenas ullamcorper, dui et placerat feugiat, eros pede varius nisi, condimentum viverra felis nunc et lorem. Sed magna purus, fermentum eu, tincidunt eu, varius ut, felis. In auctor lobortis lacus. Quisque libero metus, condimentum nec, tempor a, commodo mollis, magna. Vestibulum ullamcorper mauris at ligulTitle 02: Blind Text with oöpoöp Charshttps://world-of-sample-news.com/-41881152018-10-14T09:00:00+02:002018-10-14T09:00:00+02:00aäb cde fgh ijk lmn oöpoöp tuü vwx yz AÄBC DEF GHI JKL MNO ÖPQ RST UÜV WXYZ !"§ $%& /() =?* '<> #|; ²³~ @`´ ©«» ¼× {} aäb cde fgh ijk lmn oöp qrsß tuü vwx yz AÄBC DEF GHI JKL MNOaäb cde fgh ijk lmn oöpoöp tuü vwx yz AÄBC DEF GHI JKL MNO ÖPQ RST UÜV WXYZ !"§ $%& /() =?* '<> #|; ²³~ @`´ ©«» ¼× {} aäb cde fgh ijk lmn oöp qrsß tuü vwx yz AÄBC DEF GHI JKL MNOTitle 03: Atom-Powered Robots Run Amok, No Content!urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa622003-12-13T18:32:02ZSummary 03: Some other text.Title 04: Atom-Powered Robots Run Amok!urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa62Summary 04: Some other text.Content 04: Some more ContentTitle 05: Atom-Powered Robots Run Amok, No Summary!urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa622003-12-13T18:32:02ZContent 05: Some more Content \ No newline at end of file diff --git a/google-http-client/pom.xml b/google-http-client/pom.xml index 0a5996d24..cc35550de 100644 --- a/google-http-client/pom.xml +++ b/google-http-client/pom.xml @@ -4,26 +4,67 @@ com.google.http-client google-http-client-parent - 1.26.1-SNAPSHOT + 2.0.3-SNAPSHOT ../pom.xml google-http-client - 1.26.1-SNAPSHOT + 2.0.3-SNAPSHOT Google HTTP Client Library for Java Google HTTP Client Library for Java. Functionality that works on all supported Java platforms, - including Java 6 (or higher) desktop (SE) and web (EE), Android, and Google App Engine. + including Java 7 (or higher) desktop (SE) and web (EE), Android, and Google App Engine. + + + + org.apache.maven.plugins + maven-dependency-plugin + + + io.opencensus:opencensus-impl + io.grpc:grpc-context + + + + + org.codehaus.mojo + animal-sniffer-maven-plugin + + + android + + check + + + + java.nio.file.* + + + + + + + + + org.apache.maven.plugins + maven-resources-plugin + + + + resources + + + + maven-javadoc-plugin - http://download.oracle.com/javase/6/docs/api/ + http://download.oracle.com/javase/7/docs/api/ https://google.github.io/guava/releases/${project.guava.version}/api/docs/ - https://commons.apache.org/proper/commons-codec/archives/${project.commons-codec.version}/apidocs/ ${project.name} ${project.version} ${project.artifactId} ${project.version} @@ -31,80 +72,14 @@ maven-source-plugin - - - source-jar - compile - - jar - - - - - - - org.sonatype.plugins - jarjar-maven-plugin - - - package - - jarjar - - - - commons-codec:commons-codec - com.google.guava:guava - - - - org.apache.commons.codec.** - com.google.api.client.repackaged.org.apache.commons.codec.@1 - - - com.google.common.** - com.google.api.client.repackaged.com.google.common.@1 - - - com.google.api.client.** - - - - - - - - maven-antrun-plugin - - - - scrub - package - - run - - - - - - - - - - - - - - - maven-jar-plugin - + + + true + ${project.build.outputDirectory}/META-INF/MANIFEST.MF com.google.api.client @@ -115,7 +90,7 @@ org.apache.felix maven-bundle-plugin - 2.5.4 + 5.1.9 bundle-manifest @@ -125,25 +100,70 @@ + + + javax.annotation;resolution:=optional,* + + + + + + src/main/resources + true + + - com.google.android - android - 1.5_r4 - provided + org.apache.httpcomponents + httpclient + + + commons-logging + commons-logging + + + + + org.apache.httpcomponents + httpcore com.google.code.findbugs jsr305 + + com.google.errorprone + error_prone_annotations + com.google.guava guava - provided + + com.google.j2objc + j2objc-annotations + + + + io.grpc + grpc-context + + + io.opencensus + opencensus-api + + + io.opencensus + opencensus-contrib-http-util + + com.google.guava guava-testlib @@ -160,23 +180,14 @@ test - org.mockito - mockito-all + io.opencensus + opencensus-impl test - org.apache.httpcomponents - httpclient - - - commons-codec - commons-codec - provided - - - com.google.j2objc - j2objc-annotations - 1.1 + io.opencensus + opencensus-testing + test diff --git a/google-http-client/src/main/java/com/google/api/client/http/AbstractHttpContent.java b/google-http-client/src/main/java/com/google/api/client/http/AbstractHttpContent.java index d288db1fd..9384c4470 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/AbstractHttpContent.java +++ b/google-http-client/src/main/java/com/google/api/client/http/AbstractHttpContent.java @@ -14,19 +14,16 @@ package com.google.api.client.http; -import com.google.api.client.util.Charsets; import com.google.api.client.util.IOUtils; import com.google.api.client.util.StreamingContent; - import java.io.IOException; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; /** * Abstract implementation of an HTTP content with typical options. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @since 1.5 * @author Yaniv Inbar @@ -41,7 +38,7 @@ public abstract class AbstractHttpContent implements HttpContent { /** * @param mediaType Media type string (for example "type/subtype") this content represents or - * {@code null} to leave out. Can also contain parameters like {@code "charset=utf-8"} + * {@code null} to leave out. Can also contain parameters like {@code "charset=utf-8"} * @since 1.10 */ protected AbstractHttpContent(String mediaType) { @@ -79,10 +76,8 @@ public final HttpMediaType getMediaType() { /** * Sets the media type to use for the Content-Type header, or {@code null} if unspecified. * - *

    - * This will also overwrite any previously set parameter of the media type (for example - * {@code "charset"}), and therefore might change other properties as well. - *

    + *

    This will also overwrite any previously set parameter of the media type (for example {@code + * "charset"}), and therefore might change other properties as well. * * @since 1.10 */ @@ -92,13 +87,14 @@ public AbstractHttpContent setMediaType(HttpMediaType mediaType) { } /** - * Returns the charset specified in the media type or {@code Charsets#UTF_8} if not specified. + * Returns the charset specified in the media type or ISO_8859_1 if not specified. * * @since 1.10 */ protected final Charset getCharset() { return mediaType == null || mediaType.getCharsetParameter() == null - ? Charsets.UTF_8 : mediaType.getCharsetParameter(); + ? StandardCharsets.ISO_8859_1 + : mediaType.getCharsetParameter(); } public String getType() { @@ -108,10 +104,8 @@ public String getType() { /** * Computes and returns the content length or less than zero if not known. * - *

    - * Subclasses may override, but by default this computes the length by calling - * {@link #computeLength(HttpContent)}. - *

    + *

    Subclasses may override, but by default this computes the length by calling {@link + * #computeLength(HttpContent)}. */ protected long computeLength() throws IOException { return computeLength(this); @@ -129,7 +123,6 @@ public boolean retrySupported() { * * @param content HTTP content * @return computed content length or {@code -1} if retry is not supported - * * @since 1.14 */ public static long computeLength(HttpContent content) throws IOException { diff --git a/google-http-client/src/main/java/com/google/api/client/http/AbstractInputStreamContent.java b/google-http-client/src/main/java/com/google/api/client/http/AbstractInputStreamContent.java index 29a37d984..96ed50193 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/AbstractInputStreamContent.java +++ b/google-http-client/src/main/java/com/google/api/client/http/AbstractInputStreamContent.java @@ -16,7 +16,6 @@ import com.google.api.client.util.ByteStreams; import com.google.api.client.util.IOUtils; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -24,17 +23,13 @@ /** * Serializes HTTP request content from an input stream into an output stream. * - *

    - * The {@link #type} field is required. Subclasses should implement the {@link #getLength()}, + *

    The {@link #type} field is required. Subclasses should implement the {@link #getLength()}, * {@link #getInputStream()}, and {@link #retrySupported()} for their specific type of input stream. * By default, all content is read from the input stream. If instead you want to limit the maximum - * amount of content read from the input stream, you may use - * {@link ByteStreams#limit(InputStream, long)}. - *

    + * amount of content read from the input stream, you may use {@link ByteStreams#limit(InputStream, + * long)}. * - *

    - * Implementations don't need to be thread-safe. - *

    + *

    Implementations don't need to be thread-safe. * * @since 1.4 * @author moshenko@google.com (Jacob Moshenko) @@ -45,8 +40,8 @@ public abstract class AbstractInputStreamContent implements HttpContent { private String type; /** - * Whether the input stream should be closed at the end of {@link #writeTo}. Default is - * {@code true}. + * Whether the input stream should be closed at the end of {@link #writeTo}. Default is {@code + * true}. */ private boolean closeInputStream = true; @@ -59,10 +54,10 @@ public AbstractInputStreamContent(String type) { } /** - * Return an input stream for the specific implementation type of - * {@link AbstractInputStreamContent}. If the specific implementation will return {@code true} for - * {@link #retrySupported()} this should be a factory function which will create a new - * {@link InputStream} from the source data whenever invoked. + * Return an input stream for the specific implementation type of {@link + * AbstractInputStreamContent}. If the specific implementation will return {@code true} for {@link + * #retrySupported()} this should be a factory function which will create a new {@link + * InputStream} from the source data whenever invoked. * * @since 1.7 */ diff --git a/google-http-client/src/main/java/com/google/api/client/http/BackOffPolicy.java b/google-http-client/src/main/java/com/google/api/client/http/BackOffPolicy.java index d06304011..704c9f85a 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/BackOffPolicy.java +++ b/google-http-client/src/main/java/com/google/api/client/http/BackOffPolicy.java @@ -15,48 +15,41 @@ package com.google.api.client.http; import com.google.api.client.util.Beta; - import java.io.IOException; /** - * {@link Beta}
    + * {@link Beta}
    * Strategy interface to control back off between retry attempts. * * @since 1.7 * @author Ravi Mistry * @deprecated (scheduled to be removed in 1.18) Use {@link HttpBackOffUnsuccessfulResponseHandler} - * instead. + * instead. */ @Deprecated @Beta public interface BackOffPolicy { - /** - * Value indicating that no more retries should be made, see {@link #getNextBackOffMillis()}. - */ + /** Value indicating that no more retries should be made, see {@link #getNextBackOffMillis()}. */ public static final long STOP = -1L; /** * Determines if back off is required based on the specified status code. * - *

    - * Implementations may want to back off on server or product-specific errors. - *

    + *

    Implementations may want to back off on server or product-specific errors. * * @param statusCode HTTP status code */ public boolean isBackOffRequired(int statusCode); - /** - * Reset Back off counters (if any) in an implementation-specific fashion. - */ + /** Reset Back off counters (if any) in an implementation-specific fashion. */ public void reset(); /** * Gets the number of milliseconds to wait before retrying an HTTP request. If {@link #STOP} is * returned, no retries should be made. * - * This method should be used as follows: + *

    This method should be used as follows: * *

        *  long backoffTime = backoffPolicy.getNextBackoffMs();
    @@ -65,10 +58,10 @@ public interface BackOffPolicy {
        *  } else {
        *    // Retry after backoffTime.
        *  }
    -   *
    + * * * @return the number of milliseconds to wait when backing off requests, or {@link #STOP} if no - * more retries should be made + * more retries should be made */ public long getNextBackOffMillis() throws IOException; } diff --git a/google-http-client/src/main/java/com/google/api/client/http/BasicAuthentication.java b/google-http-client/src/main/java/com/google/api/client/http/BasicAuthentication.java index 8da4bd985..0e55cea4c 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/BasicAuthentication.java +++ b/google-http-client/src/main/java/com/google/api/client/http/BasicAuthentication.java @@ -15,19 +15,16 @@ package com.google.api.client.http; import com.google.api.client.util.Preconditions; - import java.io.IOException; /** * Basic authentication HTTP request initializer as specified in Basic Authentication Scheme * - *

    - * Implementation is immutable and thread-safe. It can be used as either an HTTP request initializer - * or an HTTP request execute interceptor. {@link #initialize(HttpRequest)} only sets itself as the - * interceptor. Authentication is actually done in {@link #intercept(HttpRequest)}, which is - * implemented using {@link HttpHeaders#setBasicAuthentication(String, String)}. - *

    + *

    Implementation is immutable and thread-safe. It can be used as either an HTTP request + * initializer or an HTTP request execute interceptor. {@link #initialize(HttpRequest)} only sets + * itself as the interceptor. Authentication is actually done in {@link #intercept(HttpRequest)}, + * which is implemented using {@link HttpHeaders#setBasicAuthentication(String, String)}. * * @since 1.7 * @author Yaniv Inbar diff --git a/google-http-client/src/main/java/com/google/api/client/http/ByteArrayContent.java b/google-http-client/src/main/java/com/google/api/client/http/ByteArrayContent.java index cf4166934..bd934fb5d 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/ByteArrayContent.java +++ b/google-http-client/src/main/java/com/google/api/client/http/ByteArrayContent.java @@ -16,7 +16,6 @@ import com.google.api.client.util.Preconditions; import com.google.api.client.util.StringUtils; - import java.io.ByteArrayInputStream; import java.io.InputStream; @@ -24,21 +23,17 @@ * Concrete implementation of {@link AbstractInputStreamContent} that generates repeatable input * streams based on the contents of byte array. * - *

    - * Sample use: - *

    + *

    Sample use: * *

      * 
    -  static void setJsonContent(HttpRequest request, byte[] json) {
    -    request.setContent(new ByteArrayContent("application/json", json));
    -  }
    + * static void setJsonContent(HttpRequest request, byte[] json) {
    + * request.setContent(new ByteArrayContent("application/json", json));
    + * }
      * 
      * 
    * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @since 1.4 * @author moshenko@google.com (Jacob Moshenko) @@ -78,8 +73,12 @@ public ByteArrayContent(String type, byte[] array) { public ByteArrayContent(String type, byte[] array, int offset, int length) { super(type); this.byteArray = Preconditions.checkNotNull(array); - Preconditions.checkArgument(offset >= 0 && length >= 0 && offset + length <= array.length, - "offset %s, length %s, array length %s", offset, length, array.length); + Preconditions.checkArgument( + offset >= 0 && length >= 0 && offset + length <= array.length, + "offset %s, length %s, array length %s", + offset, + length, + array.length); this.offset = offset; this.length = length; } @@ -87,15 +86,14 @@ public ByteArrayContent(String type, byte[] array, int offset, int length) { /** * Returns a new instance with the UTF-8 encoding (using {@link StringUtils#getBytesUtf8(String)}) * of the given content string. - *

    - * Sample use: - *

    + * + *

    Sample use: * *

        * 
    -  static void setJsonContent(HttpRequest request, String json) {
    -    request.setContent(ByteArrayContent.fromString("application/json", json));
    -  }
    +   * static void setJsonContent(HttpRequest request, String json) {
    +   * request.setContent(ByteArrayContent.fromString("application/json", json));
    +   * }
        * 
        * 
    * diff --git a/google-http-client/src/main/java/com/google/api/client/http/ConsumingInputStream.java b/google-http-client/src/main/java/com/google/api/client/http/ConsumingInputStream.java new file mode 100644 index 000000000..f0170a61b --- /dev/null +++ b/google-http-client/src/main/java/com/google/api/client/http/ConsumingInputStream.java @@ -0,0 +1,47 @@ +/* + * 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 + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.api.client.http; + +import com.google.common.io.ByteStreams; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * This class in meant to wrap an {@link InputStream} so that all bytes in the steam are read and + * discarded on {@link InputStream#close()}. This ensures that the underlying connection has the + * option to be reused. + */ +final class ConsumingInputStream extends FilterInputStream { + private boolean closed = false; + + ConsumingInputStream(InputStream inputStream) { + super(inputStream); + } + + @Override + public void close() throws IOException { + if (!closed && in != null) { + try { + ByteStreams.exhaust(this); + super.in.close(); + } finally { + this.closed = true; + } + } + } +} diff --git a/google-http-client/src/main/java/com/google/api/client/http/EmptyContent.java b/google-http-client/src/main/java/com/google/api/client/http/EmptyContent.java index 7dad5881d..492c4cb2a 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/EmptyContent.java +++ b/google-http-client/src/main/java/com/google/api/client/http/EmptyContent.java @@ -21,12 +21,10 @@ * Empty HTTP content of length zero just to force {@link HttpRequest#execute()} to add the header * {@code Content-Length: 0}. * - *

    - * Note that there is no {@code Content-Length} header if the HTTP request content is {@code null} . - * However, when making a request like PUT or POST without a {@code Content-Length} header, some - * servers may respond with a {@code 411 Length Required} error. Specifying the - * {@code Content-Length: 0} header may work around that problem. - *

    + *

    Note that there is no {@code Content-Length} header if the HTTP request content is {@code + * null} . However, when making a request like PUT or POST without a {@code Content-Length} header, + * some servers may respond with a {@code 411 Length Required} error. Specifying the {@code + * Content-Length: 0} header may work around that problem. * * @since 1.11 * @author Yaniv Inbar diff --git a/google-http-client/src/main/java/com/google/api/client/http/ExponentialBackOffPolicy.java b/google-http-client/src/main/java/com/google/api/client/http/ExponentialBackOffPolicy.java index 90e8d0058..20d8859bf 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/ExponentialBackOffPolicy.java +++ b/google-http-client/src/main/java/com/google/api/client/http/ExponentialBackOffPolicy.java @@ -17,41 +17,34 @@ import com.google.api.client.util.Beta; import com.google.api.client.util.ExponentialBackOff; import com.google.api.client.util.NanoClock; - import java.io.IOException; /** - * {@link Beta}
    + * {@link Beta}
    * Implementation of {@link BackOffPolicy} that increases the back off period for each retry attempt * using a randomization function that grows exponentially. * - *

    - * {@link #getNextBackOffMillis()} is calculated using the following formula: + *

    {@link #getNextBackOffMillis()} is calculated using the following formula: * *

      * randomized_interval =
      *     retry_interval * (random value in range [1 - randomization_factor, 1 + randomization_factor])
      * 
    - * In other words {@link #getNextBackOffMillis()} will range between the randomization factor + * + *

    In other words {@link #getNextBackOffMillis()} will range between the randomization factor * percentage below and above the retry interval. For example, using 2 seconds as the base retry * interval and 0.5 as the randomization factor, the actual back off period used in the next retry * attempt will be between 1 and 3 seconds. - *

    * - *

    - * Note: max_interval caps the retry_interval and not the randomized_interval. - *

    + *

    Note: max_interval caps the retry_interval and not the randomized_interval. * - *

    - * If the time elapsed since an {@link ExponentialBackOffPolicy} instance is created goes past the - * max_elapsed_time then the method {@link #getNextBackOffMillis()} starts returning - * {@link BackOffPolicy#STOP}. The elapsed time can be reset by calling {@link #reset()}. - *

    + *

    If the time elapsed since an {@link ExponentialBackOffPolicy} instance is created goes past + * the max_elapsed_time then the method {@link #getNextBackOffMillis()} starts returning {@link + * BackOffPolicy#STOP}. The elapsed time can be reset by calling {@link #reset()}. * - *

    - * Example: The default retry_interval is .5 seconds, default randomization_factor is 0.5, default - * multiplier is 1.5 and the default max_interval is 1 minute. For 10 requests the sequence will be - * (values in seconds) and assuming we go over the max_elapsed_time on the 10th request: + *

    Example: The default retry_interval is .5 seconds, default randomization_factor is 0.5, + * default multiplier is 1.5 and the default max_interval is 1 minute. For 10 requests the sequence + * will be (values in seconds) and assuming we go over the max_elapsed_time on the 10th request: * *

      * request#     retry_interval     randomized_interval
    @@ -67,24 +60,19 @@
      * 9            12.807              [6.403, 19.210]
      * 10           19.210              {@link BackOffPolicy#STOP}
      * 
    - *

    * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @since 1.7 * @author Ravi Mistry * @deprecated (scheduled to be removed in 1.18). Use {@link HttpBackOffUnsuccessfulResponseHandler} - * with {@link ExponentialBackOff} instead. + * with {@link ExponentialBackOff} instead. */ @Beta @Deprecated public class ExponentialBackOffPolicy implements BackOffPolicy { - /** - * The default initial interval value in milliseconds (0.5 seconds). - */ + /** The default initial interval value in milliseconds (0.5 seconds). */ public static final int DEFAULT_INITIAL_INTERVAL_MILLIS = ExponentialBackOff.DEFAULT_INITIAL_INTERVAL_MILLIS; @@ -95,20 +83,14 @@ public class ExponentialBackOffPolicy implements BackOffPolicy { public static final double DEFAULT_RANDOMIZATION_FACTOR = ExponentialBackOff.DEFAULT_RANDOMIZATION_FACTOR; - /** - * The default multiplier value (1.5 which is 50% increase per back off). - */ + /** The default multiplier value (1.5 which is 50% increase per back off). */ public static final double DEFAULT_MULTIPLIER = ExponentialBackOff.DEFAULT_MULTIPLIER; - /** - * The default maximum back off time in milliseconds (1 minute). - */ + /** The default maximum back off time in milliseconds (1 minute). */ public static final int DEFAULT_MAX_INTERVAL_MILLIS = ExponentialBackOff.DEFAULT_MAX_INTERVAL_MILLIS; - /** - * The default maximum elapsed time in milliseconds (15 minutes). - */ + /** The default maximum elapsed time in milliseconds (15 minutes). */ public static final int DEFAULT_MAX_ELAPSED_TIME_MILLIS = ExponentialBackOff.DEFAULT_MAX_ELAPSED_TIME_MILLIS; @@ -118,12 +100,13 @@ public class ExponentialBackOffPolicy implements BackOffPolicy { /** * Creates an instance of ExponentialBackOffPolicy using default values. To override the defaults * use {@link #builder}. + * *

      - *
    • {@code initialIntervalMillis} is defaulted to {@link #DEFAULT_INITIAL_INTERVAL_MILLIS}
    • - *
    • {@code randomizationFactor} is defaulted to {@link #DEFAULT_RANDOMIZATION_FACTOR}
    • - *
    • {@code multiplier} is defaulted to {@link #DEFAULT_MULTIPLIER}
    • - *
    • {@code maxIntervalMillis} is defaulted to {@link #DEFAULT_MAX_INTERVAL_MILLIS}
    • - *
    • {@code maxElapsedTimeMillis} is defaulted in {@link #DEFAULT_MAX_ELAPSED_TIME_MILLIS}
    • + *
    • {@code initialIntervalMillis} is defaulted to {@link #DEFAULT_INITIAL_INTERVAL_MILLIS} + *
    • {@code randomizationFactor} is defaulted to {@link #DEFAULT_RANDOMIZATION_FACTOR} + *
    • {@code multiplier} is defaulted to {@link #DEFAULT_MULTIPLIER} + *
    • {@code maxIntervalMillis} is defaulted to {@link #DEFAULT_MAX_INTERVAL_MILLIS} + *
    • {@code maxElapsedTimeMillis} is defaulted in {@link #DEFAULT_MAX_ELAPSED_TIME_MILLIS} *
    */ public ExponentialBackOffPolicy() { @@ -132,7 +115,6 @@ public ExponentialBackOffPolicy() { /** * @param builder builder - * * @since 1.14 */ protected ExponentialBackOffPolicy(Builder builder) { @@ -142,15 +124,11 @@ protected ExponentialBackOffPolicy(Builder builder) { /** * Determines if back off is required based on the specified status code. * - *

    - * The idea is that the servers are only temporarily unavailable, and they should not be + *

    The idea is that the servers are only temporarily unavailable, and they should not be * overwhelmed when they are trying to get back up. - *

    * - *

    - * The default implementation requires back off for 500 and 503 status codes. Subclasses may + *

    The default implementation requires back off for 500 and 503 status codes. Subclasses may * override if different status codes are required. - *

    */ public boolean isBackOffRequired(int statusCode) { switch (statusCode) { @@ -162,9 +140,7 @@ public boolean isBackOffRequired(int statusCode) { } } - /** - * Sets the interval back to the initial retry interval and restarts the timer. - */ + /** Sets the interval back to the initial retry interval and restarts the timer. */ public final void reset() { exponentialBackOff.reset(); } @@ -173,25 +149,19 @@ public final void reset() { * Gets the number of milliseconds to wait before retrying an HTTP request. If {@link #STOP} is * returned, no retries should be made. * - *

    - * This method calculates the next back off interval using the formula: randomized_interval = + *

    This method calculates the next back off interval using the formula: randomized_interval = * retry_interval +/- (randomization_factor * retry_interval) - *

    * - *

    - * Subclasses may override if a different algorithm is required. - *

    + *

    Subclasses may override if a different algorithm is required. * * @return the number of milliseconds to wait when backing off requests, or {@link #STOP} if no - * more retries should be made + * more retries should be made */ public long getNextBackOffMillis() throws IOException { return exponentialBackOff.nextBackOffMillis(); } - /** - * Returns the initial retry interval in milliseconds. - */ + /** Returns the initial retry interval in milliseconds. */ public final int getInitialIntervalMillis() { return exponentialBackOff.getInitialIntervalMillis(); } @@ -199,25 +169,19 @@ public final int getInitialIntervalMillis() { /** * Returns the randomization factor to use for creating a range around the retry interval. * - *

    - * A randomization factor of 0.5 results in a random period ranging between 50% below and 50% + *

    A randomization factor of 0.5 results in a random period ranging between 50% below and 50% * above the retry interval. - *

    */ public final double getRandomizationFactor() { return exponentialBackOff.getRandomizationFactor(); } - /** - * Returns the current retry interval in milliseconds. - */ + /** Returns the current retry interval in milliseconds. */ public final int getCurrentIntervalMillis() { return exponentialBackOff.getCurrentIntervalMillis(); } - /** - * Returns the value to multiply the current interval with for each retry attempt. - */ + /** Returns the value to multiply the current interval with for each retry attempt. */ public final double getMultiplier() { return exponentialBackOff.getMultiplier(); } @@ -233,11 +197,9 @@ public final int getMaxIntervalMillis() { /** * Returns the maximum elapsed time in milliseconds. * - *

    - * If the time elapsed since an {@link ExponentialBackOffPolicy} instance is created goes past the - * max_elapsed_time then the method {@link #getNextBackOffMillis()} starts returning - * {@link BackOffPolicy#STOP}. The elapsed time can be reset by calling {@link #reset()}. - *

    + *

    If the time elapsed since an {@link ExponentialBackOffPolicy} instance is created goes past + * the max_elapsed_time then the method {@link #getNextBackOffMillis()} starts returning {@link + * BackOffPolicy#STOP}. The elapsed time can be reset by calling {@link #reset()}. */ public final int getMaxElapsedTimeMillis() { return exponentialBackOff.getMaxElapsedTimeMillis(); @@ -247,28 +209,22 @@ public final int getMaxElapsedTimeMillis() { * Returns the elapsed time in milliseconds since an {@link ExponentialBackOffPolicy} instance is * created and is reset when {@link #reset()} is called. * - *

    - * The elapsed time is computed using {@link System#nanoTime()}. - *

    + *

    The elapsed time is computed using {@link System#nanoTime()}. */ public final long getElapsedTimeMillis() { return exponentialBackOff.getElapsedTimeMillis(); } - /** - * Returns an instance of a new builder. - */ + /** Returns an instance of a new builder. */ public static Builder builder() { return new Builder(); } /** - * {@link Beta}
    + * {@link Beta}
    * Builder for {@link ExponentialBackOffPolicy}. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @since 1.7 */ @@ -279,8 +235,7 @@ public static class Builder { /** Exponential back-off builder. */ final ExponentialBackOff.Builder exponentialBackOffBuilder = new ExponentialBackOff.Builder(); - protected Builder() { - } + protected Builder() {} /** Builds a new instance of {@link ExponentialBackOffPolicy}. */ public ExponentialBackOffPolicy build() { @@ -288,21 +243,19 @@ public ExponentialBackOffPolicy build() { } /** - * Returns the initial retry interval in milliseconds. The default value is - * {@link #DEFAULT_INITIAL_INTERVAL_MILLIS}. + * Returns the initial retry interval in milliseconds. The default value is {@link + * #DEFAULT_INITIAL_INTERVAL_MILLIS}. */ public final int getInitialIntervalMillis() { return exponentialBackOffBuilder.getInitialIntervalMillis(); } /** - * Sets the initial retry interval in milliseconds. The default value is - * {@link #DEFAULT_INITIAL_INTERVAL_MILLIS}. Must be {@code > 0}. + * Sets the initial retry interval in milliseconds. The default value is {@link + * #DEFAULT_INITIAL_INTERVAL_MILLIS}. Must be {@code > 0}. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Builder setInitialIntervalMillis(int initialIntervalMillis) { exponentialBackOffBuilder.setInitialIntervalMillis(initialIntervalMillis); @@ -313,15 +266,11 @@ public Builder setInitialIntervalMillis(int initialIntervalMillis) { * Returns the randomization factor to use for creating a range around the retry interval. The * default value is {@link #DEFAULT_RANDOMIZATION_FACTOR}. * - *

    - * A randomization factor of 0.5 results in a random period ranging between 50% below and 50% + *

    A randomization factor of 0.5 results in a random period ranging between 50% below and 50% * above the retry interval. - *

    * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public final double getRandomizationFactor() { return exponentialBackOffBuilder.getRandomizationFactor(); @@ -329,18 +278,14 @@ public final double getRandomizationFactor() { /** * Sets the randomization factor to use for creating a range around the retry interval. The - * default value is {@link #DEFAULT_RANDOMIZATION_FACTOR}. Must fall in the range - * {@code 0 <= randomizationFactor < 1}. + * default value is {@link #DEFAULT_RANDOMIZATION_FACTOR}. Must fall in the range {@code 0 <= + * randomizationFactor < 1}. * - *

    - * A randomization factor of 0.5 results in a random period ranging between 50% below and 50% + *

    A randomization factor of 0.5 results in a random period ranging between 50% below and 50% * above the retry interval. - *

    * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Builder setRandomizationFactor(double randomizationFactor) { exponentialBackOffBuilder.setRandomizationFactor(randomizationFactor); @@ -359,10 +304,8 @@ public final double getMultiplier() { * Sets the value to multiply the current interval with for each retry attempt. The default * value is {@link #DEFAULT_MULTIPLIER}. Must be {@code >= 1}. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Builder setMultiplier(double multiplier) { exponentialBackOffBuilder.setMultiplier(multiplier); @@ -371,8 +314,8 @@ public Builder setMultiplier(double multiplier) { /** * Returns the maximum value of the back off period in milliseconds. Once the current interval - * reaches this value it stops increasing. The default value is - * {@link #DEFAULT_MAX_INTERVAL_MILLIS}. Must be {@code >= initialInterval}. + * reaches this value it stops increasing. The default value is {@link + * #DEFAULT_MAX_INTERVAL_MILLIS}. Must be {@code >= initialInterval}. */ public final int getMaxIntervalMillis() { return exponentialBackOffBuilder.getMaxIntervalMillis(); @@ -380,13 +323,11 @@ public final int getMaxIntervalMillis() { /** * Sets the maximum value of the back off period in milliseconds. Once the current interval - * reaches this value it stops increasing. The default value is - * {@link #DEFAULT_MAX_INTERVAL_MILLIS}. + * reaches this value it stops increasing. The default value is {@link + * #DEFAULT_MAX_INTERVAL_MILLIS}. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Builder setMaxIntervalMillis(int maxIntervalMillis) { exponentialBackOffBuilder.setMaxIntervalMillis(maxIntervalMillis); @@ -394,33 +335,27 @@ public Builder setMaxIntervalMillis(int maxIntervalMillis) { } /** - * Returns the maximum elapsed time in milliseconds. The default value is - * {@link #DEFAULT_MAX_ELAPSED_TIME_MILLIS}. + * Returns the maximum elapsed time in milliseconds. The default value is {@link + * #DEFAULT_MAX_ELAPSED_TIME_MILLIS}. * - *

    - * If the time elapsed since an {@link ExponentialBackOffPolicy} instance is created goes past - * the max_elapsed_time then the method {@link #getNextBackOffMillis()} starts returning + *

    If the time elapsed since an {@link ExponentialBackOffPolicy} instance is created goes + * past the max_elapsed_time then the method {@link #getNextBackOffMillis()} starts returning * {@link BackOffPolicy#STOP}. The elapsed time can be reset by calling {@link #reset()}. - *

    */ public final int getMaxElapsedTimeMillis() { return exponentialBackOffBuilder.getMaxElapsedTimeMillis(); } /** - * Sets the maximum elapsed time in milliseconds. The default value is - * {@link #DEFAULT_MAX_ELAPSED_TIME_MILLIS}. Must be {@code > 0}. + * Sets the maximum elapsed time in milliseconds. The default value is {@link + * #DEFAULT_MAX_ELAPSED_TIME_MILLIS}. Must be {@code > 0}. * - *

    - * If the time elapsed since an {@link ExponentialBackOffPolicy} instance is created goes past - * the max_elapsed_time then the method {@link #getNextBackOffMillis()} starts returning + *

    If the time elapsed since an {@link ExponentialBackOffPolicy} instance is created goes + * past the max_elapsed_time then the method {@link #getNextBackOffMillis()} starts returning * {@link BackOffPolicy#STOP}. The elapsed time can be reset by calling {@link #reset()}. - *

    * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Builder setMaxElapsedTimeMillis(int maxElapsedTimeMillis) { exponentialBackOffBuilder.setMaxElapsedTimeMillis(maxElapsedTimeMillis); @@ -439,10 +374,8 @@ public final NanoClock getNanoClock() { /** * Sets the nano clock ({@link NanoClock#SYSTEM} by default). * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.14 */ diff --git a/google-http-client/src/main/java/com/google/api/client/http/FileContent.java b/google-http-client/src/main/java/com/google/api/client/http/FileContent.java index 9f1f22075..5d951c520 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/FileContent.java +++ b/google-http-client/src/main/java/com/google/api/client/http/FileContent.java @@ -15,7 +15,6 @@ package com.google.api.client.http; import com.google.api.client.util.Preconditions; - import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -25,21 +24,17 @@ * Concrete implementation of {@link AbstractInputStreamContent} that generates repeatable input * streams based on the contents of a file. * - *

    - * Sample use: - *

    + *

    Sample use: * *

      * 
    -  private static void setRequestJpegContent(HttpRequest request, File jpegFile) {
    -    request.setContent(new FileContent("image/jpeg", jpegFile));
    -  }
    + * private static void setRequestJpegContent(HttpRequest request, File jpegFile) {
    + * request.setContent(new FileContent("image/jpeg", jpegFile));
    + * }
      * 
      * 
    * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @since 1.4 * @author moshenko@google.com (Jacob Moshenko) diff --git a/google-http-client/src/main/java/com/google/api/client/http/GZipEncoding.java b/google-http-client/src/main/java/com/google/api/client/http/GZipEncoding.java index 07e17b3db..fce2289da 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/GZipEncoding.java +++ b/google-http-client/src/main/java/com/google/api/client/http/GZipEncoding.java @@ -15,7 +15,6 @@ package com.google.api.client.http; import com.google.api.client.util.StreamingContent; - import java.io.BufferedOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -29,22 +28,27 @@ */ public class GZipEncoding implements HttpEncoding { + @Override public String getName() { return "gzip"; } + @Override public void encode(StreamingContent content, OutputStream out) throws IOException { // must not close the underlying output stream - OutputStream out2 = new BufferedOutputStream(out) { - @Override - public void close() throws IOException { - // copy implementation of super.close(), except do not close the underlying output stream - try { - flush(); - } catch (IOException ignored) { - } - } - }; + OutputStream out2 = + new BufferedOutputStream(out) { + @Override + public void close() throws IOException { + // copy implementation of super.close(), except do not close the underlying output + // stream + try { + flush(); + } catch (IOException ignored) { + // fall through + } + } + }; GZIPOutputStream zipper = new GZIPOutputStream(out2); content.writeTo(zipper); // cannot call just zipper.finish() because that would cause a severe memory leak diff --git a/google-http-client/src/main/java/com/google/api/client/http/GenericUrl.java b/google-http-client/src/main/java/com/google/api/client/http/GenericUrl.java index 9f70ac120..3204f695e 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/GenericUrl.java +++ b/google-http-client/src/main/java/com/google/api/client/http/GenericUrl.java @@ -20,7 +20,6 @@ import com.google.api.client.util.escape.CharEscapers; import com.google.api.client.util.escape.Escaper; import com.google.api.client.util.escape.PercentEscaper; - import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; @@ -39,28 +38,21 @@ * the specification RFC 3986: Uniform Resource * Identifier (URI). * - *

    - * The query parameters are specified with the data key name as the parameter name, and the data + *

    The query parameters are specified with the data key name as the parameter name, and the data * value as the parameter value. Subclasses can declare fields for known query parameters using the * {@link Key} annotation. {@code null} parameter names are not allowed, but {@code null} query * values are allowed. - *

    * - *

    - * Query parameter values are parsed using {@link UrlEncodedParser#parse(String, Object)}. - *

    + *

    Query parameter values are parsed using {@link UrlEncodedParser#parse(String, Object)}. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @since 1.0 * @author Yaniv Inbar */ public class GenericUrl extends GenericData { - private static final Escaper URI_FRAGMENT_ESCAPER = - new PercentEscaper("=&-_.!~*'()@:$,;/?:", false); + private static final Escaper URI_FRAGMENT_ESCAPER = new PercentEscaper("=&-_.!~*'()@:$,;/?:"); /** Scheme (lowercase), for example {@code "https"}. */ private String scheme; @@ -77,99 +69,152 @@ public class GenericUrl extends GenericData { /** * Decoded path component by parts with each part separated by a {@code '/'} or {@code null} for * none, for example {@code "/m8/feeds/contacts/default/full"} is represented by {@code "", "m8", - *"feeds", "contacts", "default", "full"}. - *

    - * Use {@link #appendRawPath(String)} to append to the path, which ensures that no extra slash is - * added. - *

    + * "feeds", "contacts", "default", "full"}. + * + *

    Use {@link #appendRawPath(String)} to append to the path, which ensures that no extra slash + * is added. */ private List pathParts; /** Fragment component or {@code null} for none. */ private String fragment; - public GenericUrl() { - } + /** + * If true, the URL string originally given is used as is (without encoding, decoding and + * escaping) whenever referenced; otherwise, part of the URL string may be encoded or decoded as + * deemed appropriate or necessary. + */ + private boolean verbatim; + + public GenericUrl() {} /** - * Constructs from an encoded URL. + * Constructs a GenericUrl from a URL encoded string. * - *

    - * Any known query parameters with pre-defined fields as data keys will be parsed based on their - * data type. Any unrecognized query parameter will always be parsed as a string. - *

    + *

    Any known query parameters with pre-defined fields as data keys will be parsed based on + * their data type. Any unrecognized query parameter will always be parsed as a string. * - *

    - * Any {@link MalformedURLException} is wrapped in an {@link IllegalArgumentException}. - *

    + *

    Any {@link MalformedURLException} is wrapped in an {@link IllegalArgumentException}. * - *

    Upgrade warning: starting in version 1.18 this parses the encodedUrl using - * new URL(encodedUrl). In previous versions it used new URI(encodedUrl). - * In particular, this means that only a limited set of schemes are allowed such as "http" and - * "https", but that parsing is compliant with, at least, RFC 3986.

    + *

    Upgrade warning: starting in version 1.18 this parses the encodedUrl using new + * URL(encodedUrl). In previous versions it used new URI(encodedUrl). In particular, this means + * that only a limited set of schemes are allowed such as "http" and "https", but that parsing is + * compliant with, at least, RFC 3986. * * @param encodedUrl encoded URL, including any existing query parameters that should be parsed - * @throws IllegalArgumentException if URL has a syntax error + * @throws IllegalArgumentException if the URL has a syntax error */ public GenericUrl(String encodedUrl) { - this(parseURL(encodedUrl)); + this(encodedUrl, false); + } + + /** + * Constructs a GenericUrl from a string. + * + *

    Any known query parameters with pre-defined fields as data keys will be parsed based on + * their data type. Any unrecognized query parameter will always be parsed as a string. + * + *

    Any {@link MalformedURLException} is wrapped in an {@link IllegalArgumentException}. + * + * @param encodedUrl encoded URL, including any existing query parameters that should be parsed + * @param verbatim flag, to specify if URL should be used as is (without encoding, decoding and + * escaping) + * @throws IllegalArgumentException if URL has a syntax error + */ + public GenericUrl(String encodedUrl, boolean verbatim) { + this(parseURL(encodedUrl), verbatim); } /** * Constructs from a URI. * * @param uri URI - * * @since 1.14 */ public GenericUrl(URI uri) { - this(uri.getScheme(), + this(uri, false); + } + + /** + * Constructs from a URI. + * + * @param uri URI + * @param verbatim flag, to specify if URL should be used as is (without encoding, decoding and + * escaping) + */ + public GenericUrl(URI uri, boolean verbatim) { + this( + uri.getScheme(), uri.getHost(), uri.getPort(), uri.getRawPath(), uri.getRawFragment(), uri.getRawQuery(), - uri.getRawUserInfo()); + uri.getRawUserInfo(), + verbatim); } /** * Constructs from a URL. * * @param url URL - * * @since 1.14 */ public GenericUrl(URL url) { - this(url.getProtocol(), + this(url, false); + } + + /** + * Constructs from a URL. + * + * @param url URL + * @param verbatim flag, to specify if URL should be used as is (without encoding, decoding and + * escaping) + * @since 1.14 + */ + public GenericUrl(URL url, boolean verbatim) { + this( + url.getProtocol(), url.getHost(), url.getPort(), url.getPath(), url.getRef(), url.getQuery(), - url.getUserInfo()); + url.getUserInfo(), + verbatim); } - private GenericUrl(String scheme, + private GenericUrl( + String scheme, String host, int port, String path, String fragment, String query, - String userInfo) { + String userInfo, + boolean verbatim) { this.scheme = scheme.toLowerCase(Locale.US); this.host = host; this.port = port; - this.pathParts = toPathParts(path); - this.fragment = fragment != null ? CharEscapers.decodeUri(fragment) : null; - if (query != null) { - UrlEncodedParser.parse(query, this); + this.pathParts = toPathParts(path, verbatim); + this.verbatim = verbatim; + if (verbatim) { + this.fragment = fragment; + if (query != null) { + UrlEncodedParser.parse(query, this, false); + } + this.userInfo = userInfo; + } else { + this.fragment = fragment != null ? CharEscapers.decodeUri(fragment) : null; + if (query != null) { + UrlEncodedParser.parse(query, this); + } + this.userInfo = userInfo != null ? CharEscapers.decodeUri(userInfo) : null; } - this.userInfo = userInfo != null ? CharEscapers.decodeUri(userInfo) : null; } @Override public int hashCode() { - // TODO(yanivi): optimize? return build().hashCode(); } @@ -182,7 +227,6 @@ public boolean equals(Object obj) { return false; } GenericUrl other = (GenericUrl) obj; - // TODO(yanivi): optimize? return build().equals(other.build()); } @@ -279,8 +323,8 @@ public final void setPort(int port) { } /** - * Returns the decoded path component by parts with each part separated by a {@code '/'} or - * {@code null} for none. + * Returns the decoded path component by parts with each part separated by a {@code '/'} or {@code + * null} for none. * * @since 1.5 */ @@ -289,18 +333,14 @@ public List getPathParts() { } /** - * Sets the decoded path component by parts with each part separated by a {@code '/'} or - * {@code null} for none. + * Sets the decoded path component by parts with each part separated by a {@code '/'} or {@code + * null} for none. * - *

    - * For example {@code "/m8/feeds/contacts/default/full"} is represented by {@code "", "m8", - *"feeds", "contacts", "default", "full"}. - *

    + *

    For example {@code "/m8/feeds/contacts/default/full"} is represented by {@code "", "m8", + * "feeds", "contacts", "default", "full"}. * - *

    - * Use {@link #appendRawPath(String)} to append to the path, which ensures that no extra slash is - * added. - *

    + *

    Use {@link #appendRawPath(String)} to append to the path, which ensures that no extra slash + * is added. * * @since 1.5 */ @@ -327,8 +367,8 @@ public final void setFragment(String fragment) { } /** - * Constructs the string representation of the URL, including the path specified by - * {@link #pathParts} and the query parameters specified by this generic URL. + * Constructs the string representation of the URL, including the path specified by {@link + * #pathParts} and the query parameters specified by this generic URL. */ public final String build() { return buildAuthority() + buildRelativeUrl(); @@ -337,10 +377,8 @@ public final String build() { /** * Constructs the portion of the URL containing the scheme, host and port. * - *

    - * For the URL {@code "http://example.com/something?action=add"} this method would return + *

    For the URL {@code "http://example.com/something?action=add"} this method would return * {@code "http://example.com"}. - *

    * * @return scheme://[user-info@]host[:port] * @since 1.9 @@ -351,7 +389,7 @@ public final String buildAuthority() { buf.append(Preconditions.checkNotNull(scheme)); buf.append("://"); if (userInfo != null) { - buf.append(CharEscapers.escapeUriUserInfo(userInfo)).append('@'); + buf.append(verbatim ? userInfo : CharEscapers.escapeUriUserInfo(userInfo)).append('@'); } buf.append(Preconditions.checkNotNull(host)); int port = this.port; @@ -364,10 +402,8 @@ public final String buildAuthority() { /** * Constructs the portion of the URL beginning at the rooted path. * - *

    - * For the URL {@code "http://example.com/something?action=add"} this method would return + *

    For the URL {@code "http://example.com/something?action=add"} this method would return * {@code "/something?action=add"}. - *

    * * @return path with with leading '/' if the path is non-empty, query parameters and fragment * @since 1.9 @@ -377,12 +413,12 @@ public final String buildRelativeUrl() { if (pathParts != null) { appendRawPathFromParts(buf); } - addQueryParams(entrySet(), buf); + addQueryParams(entrySet(), buf, verbatim); // URL fragment String fragment = this.fragment; if (fragment != null) { - buf.append('#').append(URI_FRAGMENT_ESCAPER.escape(fragment)); + buf.append('#').append(verbatim ? fragment : URI_FRAGMENT_ESCAPER.escape(fragment)); } return buf.toString(); } @@ -390,12 +426,9 @@ public final String buildRelativeUrl() { /** * Constructs the URI based on the string representation of the URL from {@link #build()}. * - *

    - * Any {@link URISyntaxException} is wrapped in an {@link IllegalArgumentException}. - *

    + *

    Any {@link URISyntaxException} is wrapped in an {@link IllegalArgumentException}. * * @return new URI instance - * * @since 1.14 */ public final URI toURI() { @@ -405,12 +438,9 @@ public final URI toURI() { /** * Constructs the URL based on the string representation of the URL from {@link #build()}. * - *

    - * Any {@link MalformedURLException} is wrapped in an {@link IllegalArgumentException}. - *

    + *

    Any {@link MalformedURLException} is wrapped in an {@link IllegalArgumentException}. * * @return new URL instance - * * @since 1.14 */ public final URL toURL() { @@ -421,12 +451,9 @@ public final URL toURL() { * Constructs the URL based on {@link URL#URL(URL, String)} with this URL representation from * {@link #toURL()} and a relative url. * - *

    - * Any {@link MalformedURLException} is wrapped in an {@link IllegalArgumentException}. - *

    + *

    Any {@link MalformedURLException} is wrapped in an {@link IllegalArgumentException}. * * @return new URL instance - * * @since 1.14 */ public final URL toURL(String relativeUrl) { @@ -477,8 +504,8 @@ public Collection getAll(String name) { /** * Returns the raw encoded path computed from the {@link #pathParts}. * - * @return raw encoded path computed from the {@link #pathParts} or {@code null} if - * {@link #pathParts} is {@code null} + * @return raw encoded path computed from the {@link #pathParts} or {@code null} if {@link + * #pathParts} is {@code null} */ public String getRawPath() { List pathParts = this.pathParts; @@ -496,23 +523,22 @@ public String getRawPath() { * @param encodedPath raw encoded path or {@code null} to set {@link #pathParts} to {@code null} */ public void setRawPath(String encodedPath) { - pathParts = toPathParts(encodedPath); + pathParts = toPathParts(encodedPath, verbatim); } /** * Appends the given raw encoded path to the current {@link #pathParts}, setting field only if it * is {@code null} or empty. - *

    - * The last part of the {@link #pathParts} is merged with the first part of the path parts + * + *

    The last part of the {@link #pathParts} is merged with the first part of the path parts * computed from the given encoded path. Thus, if the current raw encoded path is {@code "a"}, and * the given encoded path is {@code "b"}, then the resulting raw encoded path is {@code "ab"}. - *

    * * @param encodedPath raw encoded path or {@code null} to ignore */ public void appendRawPath(String encodedPath) { if (encodedPath != null && encodedPath.length() != 0) { - List appendedPathParts = toPathParts(encodedPath); + List appendedPathParts = toPathParts(encodedPath, verbatim); if (pathParts == null || pathParts.isEmpty()) { this.pathParts = appendedPathParts; } else { @@ -522,17 +548,31 @@ public void appendRawPath(String encodedPath) { } } } - /** * Returns the decoded path parts for the given encoded path. * - * @param encodedPath slash-prefixed encoded path, for example - * {@code "/m8/feeds/contacts/default/full"} + * @param encodedPath slash-prefixed encoded path, for example {@code + * "/m8/feeds/contacts/default/full"} * @return decoded path parts, with each part assumed to be preceded by a {@code '/'}, for example - * {@code "", "m8", "feeds", "contacts", "default", "full"}, or {@code null} for - * {@code null} or {@code ""} input + * {@code "", "m8", "feeds", "contacts", "default", "full"}, or {@code null} for {@code null} + * or {@code ""} input */ public static List toPathParts(String encodedPath) { + return toPathParts(encodedPath, false); + } + + /** + * Returns the path parts (decoded if not {@code verbatim}). + * + * @param encodedPath slash-prefixed encoded path, for example {@code + * "/m8/feeds/contacts/default/full"} + * @param verbatim flag, to specify if URL should be used as is (without encoding, decoding and + * escaping) + * @return path parts (decoded if not {@code verbatim}), with each part assumed to be preceded by + * a {@code '/'}, for example {@code "", "m8", "feeds", "contacts", "default", "full"}, or + * {@code null} for {@code null} or {@code ""} input + */ + public static List toPathParts(String encodedPath, boolean verbatim) { if (encodedPath == null || encodedPath.length() == 0) { return null; } @@ -548,7 +588,7 @@ public static List toPathParts(String encodedPath) { } else { sub = encodedPath.substring(cur); } - result.add(CharEscapers.decodeUri(sub)); + result.add(verbatim ? sub : CharEscapers.decodeUriPath(sub)); cur = slash + 1; } return result; @@ -562,34 +602,37 @@ private void appendRawPathFromParts(StringBuilder buf) { buf.append('/'); } if (pathPart.length() != 0) { - buf.append(CharEscapers.escapeUriPath(pathPart)); + buf.append(verbatim ? pathPart : CharEscapers.escapeUriPath(pathPart)); } } } - /** - * Adds query parameters from the provided entrySet into the buffer. - */ - static void addQueryParams(Set> entrySet, StringBuilder buf) { + /** Adds query parameters from the provided entrySet into the buffer. */ + static void addQueryParams( + Set> entrySet, StringBuilder buf, boolean verbatim) { // (similar to UrlEncodedContent) boolean first = true; for (Map.Entry nameValueEntry : entrySet) { Object value = nameValueEntry.getValue(); if (value != null) { - String name = CharEscapers.escapeUriQuery(nameValueEntry.getKey()); + String name = + verbatim + ? nameValueEntry.getKey() + : CharEscapers.escapeUriQuery(nameValueEntry.getKey()); if (value instanceof Collection) { Collection collectionValue = (Collection) value; for (Object repeatedValue : collectionValue) { - first = appendParam(first, buf, name, repeatedValue); + first = appendParam(first, buf, name, repeatedValue, verbatim); } } else { - first = appendParam(first, buf, name, value); + first = appendParam(first, buf, name, value, verbatim); } } } } - private static boolean appendParam(boolean first, StringBuilder buf, String name, Object value) { + private static boolean appendParam( + boolean first, StringBuilder buf, String name, Object value, boolean verbatim) { if (first) { first = false; buf.append('?'); @@ -597,7 +640,8 @@ private static boolean appendParam(boolean first, StringBuilder buf, String name buf.append('&'); } buf.append(name); - String stringValue = CharEscapers.escapeUriQuery(value.toString()); + String stringValue = + verbatim ? value.toString() : CharEscapers.escapeUriQuery(value.toString()); if (stringValue.length() != 0) { buf.append('=').append(stringValue); } @@ -607,9 +651,7 @@ private static boolean appendParam(boolean first, StringBuilder buf, String name /** * Returns the URI for the given encoded URL. * - *

    - * Any {@link URISyntaxException} is wrapped in an {@link IllegalArgumentException}. - *

    + *

    Any {@link URISyntaxException} is wrapped in an {@link IllegalArgumentException}. * * @param encodedUrl encoded URL * @return URI @@ -625,9 +667,7 @@ private static URI toURI(String encodedUrl) { /** * Returns the URI for the given encoded URL. * - *

    - * Any {@link MalformedURLException} is wrapped in an {@link IllegalArgumentException}. - *

    + *

    Any {@link MalformedURLException} is wrapped in an {@link IllegalArgumentException}. * * @param encodedUrl encoded URL * @return URL diff --git a/google-http-client/src/main/java/com/google/api/client/http/GzipSupport.java b/google-http-client/src/main/java/com/google/api/client/http/GzipSupport.java new file mode 100644 index 000000000..6dc5df304 --- /dev/null +++ b/google-http-client/src/main/java/com/google/api/client/http/GzipSupport.java @@ -0,0 +1,90 @@ +package com.google.api.client.http; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.zip.GZIPInputStream; + +final class GzipSupport { + + private GzipSupport() {} + + static GZIPInputStream newGzipInputStream(InputStream in) throws IOException { + return new GZIPInputStream(new OptimisticAvailabilityInputStream(in)); + } + + /** + * When {@link GZIPInputStream} completes processing an individual member it will call {@link + * InputStream#available()} to determine if there is more stream to try and process. If the call + * to {@code available()} returns 0 {@code GZIPInputStream} will determine it has processed the + * entirety of the underlying stream. This is spurious, as {@link InputStream#available()} is + * allowed to return 0 if it would require blocking in order for more bytes to be available. When + * {@code GZIPInputStream} is reading from a {@code Transfer-Encoding: chunked} response, if the + * chunk boundary happens to align closely enough to the member boundary {@code GZIPInputStream} + * won't consume the whole response. + * + *

    This class, provides an optimistic "estimate" (in actuality, a lie) of the number of {@code + * available()} bytes in the underlying stream. It does this by tracking the last number of bytes + * read. If the last number of bytes read is grater than -1, we return {@link Integer#MAX_VALUE} + * to any call of {@link #available()}. + * + *

    We're breaking the contract of available() in that we're lying about how much data we have + * accessible without blocking, however in the case where we're weaving {@link GZIPInputStream} + * into response processing we already know there are going to be blocking calls to read before + * the stream is exhausted. + * + *

    This scenario isn't unique to processing of chunked responses, and can be replicated + * reliably using a {@link java.io.SequenceInputStream} with two underlying {@link + * java.io.ByteArrayInputStream}. See the corresponding test class for a reproduction. + * + *

    The need for this class has been verified for the following JVMs: + * + *

      + *
    1. + *
      +   * openjdk version "1.8.0_292"
      +   * OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_292-b10)
      +   * OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.292-b10, mixed mode)
      +   *   
      + *
    2. + *
      +   * openjdk version "11.0.14.1" 2022-02-08
      +   * OpenJDK Runtime Environment Temurin-11.0.14.1+1 (build 11.0.14.1+1)
      +   * OpenJDK 64-Bit Server VM Temurin-11.0.14.1+1 (build 11.0.14.1+1, mixed mode)
      +   *   
      + *
    3. + *
      +   * openjdk version "17" 2021-09-14
      +   * OpenJDK Runtime Environment Temurin-17+35 (build 17+35)
      +   * OpenJDK 64-Bit Server VM Temurin-17+35 (build 17+35, mixed mode, sharing)
      +   *   
      + *
    + */ + private static final class OptimisticAvailabilityInputStream extends FilterInputStream { + private int lastRead = 0; + + OptimisticAvailabilityInputStream(InputStream delegate) { + super(delegate); + } + + @Override + public int available() throws IOException { + return lastRead > -1 ? Integer.MAX_VALUE : 0; + } + + @Override + public int read() throws IOException { + return lastRead = super.read(); + } + + @Override + public int read(byte[] b) throws IOException { + return lastRead = super.read(b); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + return lastRead = super.read(b, off, len); + } + } +} diff --git a/google-http-client/src/main/java/com/google/api/client/http/HttpBackOffIOExceptionHandler.java b/google-http-client/src/main/java/com/google/api/client/http/HttpBackOffIOExceptionHandler.java index 35fca7346..a0874841f 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/HttpBackOffIOExceptionHandler.java +++ b/google-http-client/src/main/java/com/google/api/client/http/HttpBackOffIOExceptionHandler.java @@ -19,35 +19,26 @@ import com.google.api.client.util.Beta; import com.google.api.client.util.Preconditions; import com.google.api.client.util.Sleeper; - import java.io.IOException; /** - * {@link Beta}
    + * {@link Beta}
    * {@link HttpIOExceptionHandler} implementation with {@link BackOff}. * - *

    - * It is designed to work with only one {@link HttpRequest} at a time. As a result you MUST create a - * new instance of {@link HttpBackOffIOExceptionHandler} with a new instance of {@link BackOff} for - * each instance of {@link HttpRequest}. - *

    + *

    It is designed to work with only one {@link HttpRequest} at a time. As a result you MUST + * create a new instance of {@link HttpBackOffIOExceptionHandler} with a new instance of {@link + * BackOff} for each instance of {@link HttpRequest}. * - *

    - * Sample usage: - *

    + *

    Sample usage: * *

    -  request.setIOExceptionHandler(new HttpBackOffIOExceptionHandler(new ExponentialBackOff());
    + * request.setIOExceptionHandler(new HttpBackOffIOExceptionHandler(new ExponentialBackOff());
      * 
    * - *

    - * Note: Implementation doesn't call {@link BackOff#reset} at all, since it expects a new - * {@link BackOff} instance. - *

    + *

    Note: Implementation doesn't call {@link BackOff#reset} at all, since it expects a new {@link + * BackOff} instance. * - *

    - * Implementation is not thread-safe - *

    + *

    Implementation is not thread-safe * * @author Eyal Peled * @since 1.15 @@ -83,14 +74,10 @@ public final Sleeper getSleeper() { /** * Sets the sleeper. * - *

    - * The default value is {@link Sleeper#DEFAULT}. - *

    + *

    The default value is {@link Sleeper#DEFAULT}. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public HttpBackOffIOExceptionHandler setSleeper(Sleeper sleeper) { this.sleeper = Preconditions.checkNotNull(sleeper); @@ -100,10 +87,8 @@ public HttpBackOffIOExceptionHandler setSleeper(Sleeper sleeper) { /** * {@inheritDoc} * - *

    - * Handles the request with {@link BackOff}. That means that if back-off is required a call to + *

    Handles the request with {@link BackOff}. That means that if back-off is required a call to * {@link Sleeper#sleep(long)} will be made. - *

    */ public boolean handleIOException(HttpRequest request, boolean supportsRetry) throws IOException { if (!supportsRetry) { @@ -112,6 +97,8 @@ public boolean handleIOException(HttpRequest request, boolean supportsRetry) thr try { return BackOffUtils.next(sleeper, backOff); } catch (InterruptedException exception) { + // Mark thread as interrupted since we cannot throw InterruptedException here. + Thread.currentThread().interrupt(); return false; } } diff --git a/google-http-client/src/main/java/com/google/api/client/http/HttpBackOffUnsuccessfulResponseHandler.java b/google-http-client/src/main/java/com/google/api/client/http/HttpBackOffUnsuccessfulResponseHandler.java index 23039ff16..cc44c6136 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/HttpBackOffUnsuccessfulResponseHandler.java +++ b/google-http-client/src/main/java/com/google/api/client/http/HttpBackOffUnsuccessfulResponseHandler.java @@ -19,36 +19,27 @@ import com.google.api.client.util.Beta; import com.google.api.client.util.Preconditions; import com.google.api.client.util.Sleeper; - import java.io.IOException; /** - * {@link Beta}
    + * {@link Beta}
    * Back-off handler which handles an abnormal HTTP response with {@link BackOff}. * - *

    - * It is designed to work with only one {@link HttpRequest} at a time. As a result you MUST create a - * new instance of {@link HttpBackOffUnsuccessfulResponseHandler} with a new instance of + *

    It is designed to work with only one {@link HttpRequest} at a time. As a result you MUST + * create a new instance of {@link HttpBackOffUnsuccessfulResponseHandler} with a new instance of * {@link BackOff} for each instance of {@link HttpRequest}. - *

    * - *

    - * Sample usage: - *

    + *

    Sample usage: * *

    -  request.setUnsuccessfulResponseHandler(
    -    new HttpBackOffUnsuccessfulResponseHandler(new ExponentialBackOff()));
    + * request.setUnsuccessfulResponseHandler(
    + * new HttpBackOffUnsuccessfulResponseHandler(new ExponentialBackOff()));
      * 
    * - *

    - * Note: Implementation doesn't call {@link BackOff#reset} at all, since it expects a new - * {@link BackOff} instance. - *

    + *

    Note: Implementation doesn't call {@link BackOff#reset} at all, since it expects a new {@link + * BackOff} instance. * - *

    - * Implementation is not thread-safe - *

    + *

    Implementation is not thread-safe * * @author Eyal Peled * @since 1.15 @@ -91,14 +82,10 @@ public final BackOffRequired getBackOffRequired() { * Sets the {@link BackOffRequired} instance which determines if back-off is required based on an * abnormal HTTP response. * - *

    - * The default value is {@link BackOffRequired#ON_SERVER_ERROR}. - *

    + *

    The default value is {@link BackOffRequired#ON_SERVER_ERROR}. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public HttpBackOffUnsuccessfulResponseHandler setBackOffRequired( BackOffRequired backOffRequired) { @@ -114,14 +101,10 @@ public final Sleeper getSleeper() { /** * Sets the sleeper. * - *

    - * The default value is {@link Sleeper#DEFAULT}. - *

    + *

    The default value is {@link Sleeper#DEFAULT}. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public HttpBackOffUnsuccessfulResponseHandler setSleeper(Sleeper sleeper) { this.sleeper = Preconditions.checkNotNull(sleeper); @@ -131,13 +114,12 @@ public HttpBackOffUnsuccessfulResponseHandler setSleeper(Sleeper sleeper) { /** * {@inheritDoc} * - *

    - * Handles the request with {@link BackOff}. That means that if back-off is required a call to + *

    Handles the request with {@link BackOff}. That means that if back-off is required a call to * {@link Sleeper#sleep(long)} will be made. - *

    */ - public final boolean handleResponse( - HttpRequest request, HttpResponse response, boolean supportsRetry) throws IOException { + @Override + public boolean handleResponse(HttpRequest request, HttpResponse response, boolean supportsRetry) + throws IOException { if (!supportsRetry) { return false; } @@ -146,14 +128,15 @@ public final boolean handleResponse( try { return BackOffUtils.next(sleeper, backOff); } catch (InterruptedException exception) { - // ignore + // Mark thread as interrupted since we cannot throw InterruptedException here. + Thread.currentThread().interrupt(); } } return false; } /** - * {@link Beta}
    + * {@link Beta}
    * Interface which defines if back-off is required based on an abnormal {@link HttpResponse}. * * @author Eyal Peled @@ -165,23 +148,25 @@ public interface BackOffRequired { boolean isRequired(HttpResponse response); /** - * Back-off required implementation which returns {@code true} to every - * {@link #isRequired(HttpResponse)} call. + * Back-off required implementation which returns {@code true} to every {@link + * #isRequired(HttpResponse)} call. */ - BackOffRequired ALWAYS = new BackOffRequired() { - public boolean isRequired(HttpResponse response) { - return true; - } - }; + BackOffRequired ALWAYS = + new BackOffRequired() { + public boolean isRequired(HttpResponse response) { + return true; + } + }; /** - * Back-off required implementation which its {@link #isRequired(HttpResponse)} returns - * {@code true} if a server error occurred (5xx). + * Back-off required implementation which its {@link #isRequired(HttpResponse)} returns {@code + * true} if a server error occurred (5xx). */ - BackOffRequired ON_SERVER_ERROR = new BackOffRequired() { - public boolean isRequired(HttpResponse response) { - return response.getStatusCode() / 100 == 5; - } - }; + BackOffRequired ON_SERVER_ERROR = + new BackOffRequired() { + public boolean isRequired(HttpResponse response) { + return response.getStatusCode() / 100 == 5; + } + }; } } diff --git a/google-http-client/src/main/java/com/google/api/client/http/HttpContent.java b/google-http-client/src/main/java/com/google/api/client/http/HttpContent.java index e3ef37c2c..ca9c362b5 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/HttpContent.java +++ b/google-http-client/src/main/java/com/google/api/client/http/HttpContent.java @@ -15,16 +15,13 @@ package com.google.api.client.http; import com.google.api.client.util.StreamingContent; - import java.io.IOException; import java.io.OutputStream; /** * Serializes HTTP request content into an output stream. * - *

    - * Implementations don't need to be thread-safe. - *

    + *

    Implementations don't need to be thread-safe. * * @since 1.0 * @author Yaniv Inbar diff --git a/google-http-client/src/main/java/com/google/api/client/http/HttpEncoding.java b/google-http-client/src/main/java/com/google/api/client/http/HttpEncoding.java index 154132d28..2720310f8 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/HttpEncoding.java +++ b/google-http-client/src/main/java/com/google/api/client/http/HttpEncoding.java @@ -15,16 +15,13 @@ package com.google.api.client.http; import com.google.api.client.util.StreamingContent; - import java.io.IOException; import java.io.OutputStream; /** * HTTP content encoding. * - *

    - * Implementations don't need to be thread-safe. - *

    + *

    Implementations don't need to be thread-safe. * * @since 1.14 * @author Yaniv Inbar @@ -37,11 +34,9 @@ public interface HttpEncoding { /** * Encodes the streaming content into the output stream. * - *

    - * Implementations must not close the output stream, and instead should flush the output stream. - * Some callers may assume that the the output stream has not been closed, and will fail to work - * if it has been closed. - *

    + *

    Implementations must not close the output stream, and instead should flush the output + * stream. Some callers may assume that the output stream has not been closed, and will fail to + * work if it has been closed. * * @param content streaming content * @param out output stream diff --git a/google-http-client/src/main/java/com/google/api/client/http/HttpEncodingStreamingContent.java b/google-http-client/src/main/java/com/google/api/client/http/HttpEncodingStreamingContent.java index 6da9a6848..b8a83dfb4 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/HttpEncodingStreamingContent.java +++ b/google-http-client/src/main/java/com/google/api/client/http/HttpEncodingStreamingContent.java @@ -16,16 +16,13 @@ import com.google.api.client.util.Preconditions; import com.google.api.client.util.StreamingContent; - import java.io.IOException; import java.io.OutputStream; /** * Streaming content based on an HTTP encoding. * - *

    - * Implementation is thread-safe only if the streaming content and HTTP encoding are thread-safe. - *

    + *

    Implementation is thread-safe only if the streaming content and HTTP encoding are thread-safe. * * @since 1.14 * @author Yaniv Inbar diff --git a/google-http-client/src/main/java/com/google/api/client/http/HttpExecuteInterceptor.java b/google-http-client/src/main/java/com/google/api/client/http/HttpExecuteInterceptor.java index 514961784..21fc247a9 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/HttpExecuteInterceptor.java +++ b/google-http-client/src/main/java/com/google/api/client/http/HttpExecuteInterceptor.java @@ -20,55 +20,47 @@ * HTTP request execute interceptor to intercept the start of {@link HttpRequest#execute()} before * executing the HTTP request. * - *

    - * For example, this might be used to sign a request for OAuth: - *

    + *

    For example, this might be used to sign a request for OAuth: * *

    -  public class OAuthSigner implements HttpExecuteInterceptor {
    -    public void intercept(HttpRequest request) throws IOException {
    -      // sign request...
    -    }
    -  }
    + * public class OAuthSigner implements HttpExecuteInterceptor {
    + * public void intercept(HttpRequest request) throws IOException {
    + * // sign request...
    + * }
    + * }
      * 
    * - *

    - * Sample usage with a request factory: - *

    + *

    Sample usage with a request factory: * *

    -  public static HttpRequestFactory createRequestFactory(HttpTransport transport) {
    -    final OAuthSigner signer = new OAuthSigner(...);
    -    return transport.createRequestFactory(new HttpRequestInitializer() {
    -      public void initialize(HttpRequest request) {
    -        request.setInterceptor(signer);
    -      }
    -    });
    -  }
    + * public static HttpRequestFactory createRequestFactory(HttpTransport transport) {
    + * final OAuthSigner signer = new OAuthSigner(...);
    + * return transport.createRequestFactory(new HttpRequestInitializer() {
    + * public void initialize(HttpRequest request) {
    + * request.setInterceptor(signer);
    + * }
    + * });
    + * }
      * 
    * - *

    - * More complex usage example: - *

    + *

    More complex usage example: * *

    -  public static HttpRequestFactory createRequestFactory2(HttpTransport transport) {
    -    final OAuthSigner signer = new OAuthSigner(...);
    -    return transport.createRequestFactory(new HttpRequestInitializer() {
    -      public void initialize(HttpRequest request) {
    -        request.setInterceptor(new HttpExecuteInterceptor() {
    -          public void intercept(HttpRequest request) throws IOException {
    -            signer.intercept(request);
    -          }
    -        });
    -      }
    -    });
    -  }
    + * public static HttpRequestFactory createRequestFactory2(HttpTransport transport) {
    + * final OAuthSigner signer = new OAuthSigner(...);
    + * return transport.createRequestFactory(new HttpRequestInitializer() {
    + * public void initialize(HttpRequest request) {
    + * request.setInterceptor(new HttpExecuteInterceptor() {
    + * public void intercept(HttpRequest request) throws IOException {
    + * signer.intercept(request);
    + * }
    + * });
    + * }
    + * });
    + * }
      * 
    * - *

    - * Implementations should normally be thread-safe. - *

    + *

    Implementations should normally be thread-safe. * * @since 1.0 * @author Yaniv Inbar diff --git a/google-http-client/src/main/java/com/google/api/client/http/HttpHeaders.java b/google-http-client/src/main/java/com/google/api/client/http/HttpHeaders.java index 87560573f..e43f09b0f 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/HttpHeaders.java +++ b/google-http-client/src/main/java/com/google/api/client/http/HttpHeaders.java @@ -25,7 +25,6 @@ import com.google.api.client.util.StringUtils; import com.google.api.client.util.Throwables; import com.google.api.client.util.Types; - import java.io.IOException; import java.io.Writer; import java.lang.reflect.Type; @@ -45,13 +44,9 @@ * Stores HTTP headers used in an HTTP request or response, as defined in Header Field Definitions. * - *

    - * {@code null} is not allowed as a name or value of a header. Names are case-insensitive. - *

    + *

    {@code null} is not allowed as a name or value of a header. Names are case-insensitive. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @since 1.0 * @author Yaniv Inbar @@ -158,6 +153,10 @@ public HttpHeaders() { @Key("User-Agent") private List userAgent; + /** {@code "Warning"} header. */ + @Key("Warning") + private List warning; + /** {@code "WWW-Authenticate"} header. */ @Key("WWW-Authenticate") private List authenticate; @@ -188,10 +187,8 @@ public final String getAccept() { /** * Sets the {@code "Accept"} header or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.5 */ @@ -212,9 +209,7 @@ public final String getAcceptEncoding() { /** * Sets the {@code "Accept-Encoding"} header or {@code null} for none. * - *

    - * By default, this is {@code "gzip"}. - *

    + *

    By default, this is {@code "gzip"}. * * @since 1.5 */ @@ -244,10 +239,8 @@ public final List getAuthorizationAsList() { /** * Sets the {@code "Authorization"} header or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.5 */ @@ -258,10 +251,8 @@ public HttpHeaders setAuthorization(String authorization) { /** * Sets the {@code "Authorization"} header or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.13 */ @@ -282,10 +273,8 @@ public final String getCacheControl() { /** * Sets the {@code "Cache-Control"} header or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.5 */ @@ -306,10 +295,8 @@ public final String getContentEncoding() { /** * Sets the {@code "Content-Encoding"} header or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.5 */ @@ -330,10 +317,8 @@ public final Long getContentLength() { /** * Sets the {@code "Content-Length"} header or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.5 */ @@ -354,10 +339,8 @@ public final String getContentMD5() { /** * Sets the {@code "Content-MD5"} header or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.5 */ @@ -378,10 +361,8 @@ public final String getContentRange() { /** * Sets the {@code "Content-Range"} header or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.5 */ @@ -402,10 +383,8 @@ public final String getContentType() { /** * Sets the {@code "Content-Type"} header or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.5 */ @@ -417,9 +396,7 @@ public HttpHeaders setContentType(String contentType) { /** * Returns the first {@code "Cookie"} header or {@code null} for none. * - *

    - * See Cookie Specification. - *

    + *

    See Cookie Specification. * * @since 1.6 */ @@ -430,10 +407,8 @@ public final String getCookie() { /** * Sets the {@code "Cookie"} header or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.6 */ @@ -454,10 +429,8 @@ public final String getDate() { /** * Sets the {@code "Date"} header or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.5 */ @@ -478,10 +451,8 @@ public final String getETag() { /** * Sets the {@code "ETag"} header or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.5 */ @@ -502,10 +473,8 @@ public final String getExpires() { /** * Sets the {@code "Expires"} header or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.5 */ @@ -526,10 +495,8 @@ public final String getIfModifiedSince() { /** * Sets the {@code "If-Modified-Since"} header or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.5 */ @@ -550,10 +517,8 @@ public final String getIfMatch() { /** * Sets the {@code "If-Match"} header or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.5 */ @@ -574,10 +539,8 @@ public final String getIfNoneMatch() { /** * Sets the {@code "If-None-Match"} header or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.5 */ @@ -598,10 +561,8 @@ public final String getIfUnmodifiedSince() { /** * Sets the {@code "If-Unmodified-Since"} header or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.5 */ @@ -622,10 +583,8 @@ public final String getIfRange() { /** * Sets the {@code "If-Range"} header or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.14 */ @@ -646,10 +605,8 @@ public final String getLastModified() { /** * Sets the {@code "Last-Modified"} header or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.5 */ @@ -670,10 +627,8 @@ public final String getLocation() { /** * Sets the {@code "Location"} header or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.5 */ @@ -694,10 +649,8 @@ public final String getMimeVersion() { /** * Sets the {@code "MIME-Version"} header or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.5 */ @@ -718,10 +671,8 @@ public final String getRange() { /** * Sets the {@code "Range"} header or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.5 */ @@ -742,10 +693,8 @@ public final String getRetryAfter() { /** * Sets the {@code "Retry-After"} header or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.5 */ @@ -766,10 +715,8 @@ public final String getUserAgent() { /** * Sets the {@code "User-Agent"} header or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.5 */ @@ -799,10 +746,8 @@ public final List getAuthenticateAsList() { /** * Sets the {@code "WWW-Authenticate"} header or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.5 */ @@ -811,6 +756,35 @@ public HttpHeaders setAuthenticate(String authenticate) { return this; } + /** + * Adds the {@code "Warning"} header or {@code null} for none. + * + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. + * + * @since 1.28 + */ + public HttpHeaders addWarning(String warning) { + if (warning == null) { + return this; + } + if (this.warning == null) { + this.warning = getAsList(warning); + } else { + this.warning.add(warning); + } + return this; + } + + /** + * Returns all {@code "Warning"} headers or {@code null} for none. + * + * @since 1.28 + */ + public final List getWarning() { + return warning == null ? null : new ArrayList<>(warning); + } + /** * Returns the first {@code "Age"} header or {@code null} for none. * @@ -823,10 +797,8 @@ public final Long getAge() { /** * Sets the {@code "Age"} header or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.14 */ @@ -839,10 +811,8 @@ public HttpHeaders setAge(Long age) { * Sets the {@link #authorization} header as specified in Basic Authentication Scheme. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.2 */ @@ -853,13 +823,15 @@ public HttpHeaders setBasicAuthentication(String username, String password) { return setAuthorization("Basic " + encoded); } - private static void addHeader(Logger logger, + private static void addHeader( + Logger logger, StringBuilder logbuf, StringBuilder curlbuf, LowLevelHttpRequest lowLevelHttpRequest, String name, Object value, - Writer writer) throws IOException { + Writer writer) + throws IOException { // ignore nulls if (value == null || Data.isNull(value)) { return; @@ -893,12 +865,11 @@ private static void addHeader(Logger logger, } } - /** - * Returns the string header value for the given header value as an object. - */ + /** Returns the string header value for the given header value as an object. */ private static String toStringValue(Object headerValue) { return headerValue instanceof Enum - ? FieldInfo.of((Enum) headerValue).getName() : headerValue.toString(); + ? FieldInfo.of((Enum) headerValue).getName() + : headerValue.toString(); } /** @@ -908,42 +879,35 @@ private static String toStringValue(Object headerValue) { * @param logbuf log buffer or {@code null} for none * @param curlbuf log buffer for logging curl requests or {@code null} for none * @param logger logger or {@code null} for none. Logger must be specified if log buffer is - * specified + * specified * @param lowLevelHttpRequest low level HTTP request where HTTP headers will be serialized to or - * {@code null} for none + * {@code null} for none */ - static void serializeHeaders(HttpHeaders headers, StringBuilder logbuf, StringBuilder curlbuf, - Logger logger, LowLevelHttpRequest lowLevelHttpRequest) throws IOException { + static void serializeHeaders( + HttpHeaders headers, + StringBuilder logbuf, + StringBuilder curlbuf, + Logger logger, + LowLevelHttpRequest lowLevelHttpRequest) + throws IOException { serializeHeaders(headers, logbuf, curlbuf, logger, lowLevelHttpRequest, null); } - /** - * Serializes headers to an {@link Writer} for Multi-part requests. - * - * @param headers HTTP headers - * @param logbuf log buffer or {@code null} for none - * @param logger logger or {@code null} for none. Logger must be specified if log buffer is - * specified - * @param writer Writer where HTTP headers will be serialized to or {@code null} for none - * - * @since 1.9 - */ - public static void serializeHeadersForMultipartRequests( - HttpHeaders headers, StringBuilder logbuf, Logger logger, Writer writer) throws IOException { - serializeHeaders(headers, logbuf, null, logger, null, writer); - } - - static void serializeHeaders(HttpHeaders headers, + static void serializeHeaders( + HttpHeaders headers, StringBuilder logbuf, StringBuilder curlbuf, Logger logger, LowLevelHttpRequest lowLevelHttpRequest, - Writer writer) throws IOException { + Writer writer) + throws IOException { HashSet headerNames = new HashSet(); for (Map.Entry headerEntry : headers.entrySet()) { String name = headerEntry.getKey(); - Preconditions.checkArgument(headerNames.add(name), - "multiple headers of the same name (headers are case insensitive): %s", name); + Preconditions.checkArgument( + headerNames.add(name), + "multiple headers of the same name (headers are case insensitive): %s", + name); Object value = headerEntry.getValue(); if (value != null) { // compute the display name from the declared field name to fix capitalization @@ -955,13 +919,8 @@ static void serializeHeaders(HttpHeaders headers, Class valueClass = value.getClass(); if (value instanceof Iterable || valueClass.isArray()) { for (Object repeatedValue : Types.iterableOf(value)) { - addHeader(logger, - logbuf, - curlbuf, - lowLevelHttpRequest, - displayName, - repeatedValue, - writer); + addHeader( + logger, logbuf, curlbuf, lowLevelHttpRequest, displayName, repeatedValue, writer); } } else { addHeader(logger, logbuf, curlbuf, lowLevelHttpRequest, displayName, value, writer); @@ -973,12 +932,27 @@ static void serializeHeaders(HttpHeaders headers, } } + /** + * Serializes headers to an {@link Writer} for Multi-part requests. + * + * @param headers HTTP headers + * @param logbuf log buffer or {@code null} for none + * @param logger logger or {@code null} for none. Logger must be specified if log buffer is + * specified + * @param writer Writer where HTTP headers will be serialized to or {@code null} for none + * @since 1.9 + */ + public static void serializeHeadersForMultipartRequests( + HttpHeaders headers, StringBuilder logbuf, Logger logger, Writer writer) throws IOException { + serializeHeaders(headers, logbuf, null, logger, null, writer); + } + /** * Puts all headers of the {@link LowLevelHttpResponse} into this {@link HttpHeaders} object. * * @param response Response from which the headers are copied * @param logger {@link StringBuilder} to which logging output is added or {@code null} to disable - * logging + * logging * @since 1.10 */ public final void fromHttpResponse(LowLevelHttpResponse response, StringBuilder logger) @@ -1118,9 +1092,7 @@ public ParseHeaderState(HttpHeaders headers, StringBuilder logger) { this.arrayValueMap = new ArrayValueMap(headers); } - /** - * Finishes the parsing-process by setting all array-values. - */ + /** Finishes the parsing-process by setting all array-values. */ void finish() { arrayValueMap.setValues(); } @@ -1145,7 +1117,9 @@ void parseHeader(String headerName, String headerValue, ParseHeaderState state) // array that can handle repeating values Class rawArrayComponentType = Types.getRawArrayComponentType(context, Types.getArrayComponentType(type)); - arrayValueMap.put(fieldInfo.getField(), rawArrayComponentType, + arrayValueMap.put( + fieldInfo.getField(), + rawArrayComponentType, parseValue(rawArrayComponentType, context, headerValue)); } else if (Types.isAssignableToOrFrom( Types.getRawArrayComponentType(context, type), Iterable.class)) { diff --git a/google-http-client/src/main/java/com/google/api/client/http/HttpIOExceptionHandler.java b/google-http-client/src/main/java/com/google/api/client/http/HttpIOExceptionHandler.java index 4020a136e..0b0b877d0 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/HttpIOExceptionHandler.java +++ b/google-http-client/src/main/java/com/google/api/client/http/HttpIOExceptionHandler.java @@ -16,32 +16,30 @@ import com.google.api.client.util.BackOff; import com.google.api.client.util.Beta; - import java.io.IOException; /** - * {@link Beta}
    + * {@link Beta}
    * Handles an {@link IOException} in an HTTP request. * - *

    - * For example, this might be used to handle an {@link IOException} with {@link BackOff} policy. - *

    + *

    For example, this might be used to handle an {@link IOException} with {@link BackOff} policy. * *

    -  public static class HttpBackOffIOExceptionHandler implements HttpIOExceptionHandler {
    -    BackOff backOff;
    -    Sleeper sleeper;
    -    public boolean handle(HttpRequest request, boolean supportsRetry) throws IOException {
    -      if (!supportsRetry) {
    -        return false;
    -      }
    -      try {
    -        return BackOffUtils.next(sleeper, backOff);
    -      } catch (InterruptedException exception) {
    -        return false;
    -      }
    -    }
    -  }
    + * public static class HttpBackOffIOExceptionHandler implements HttpIOExceptionHandler {
    + * BackOff backOff;
    + * Sleeper sleeper;
    + * public boolean handle(HttpRequest request, boolean supportsRetry) throws IOException {
    + * if (!supportsRetry) {
    + * return false;
    + * }
    + * try {
    + * return BackOffUtils.next(sleeper, backOff);
    + * } catch (InterruptedException exception) {
    + * Thread.currentThread().interrupt();
    + * return false;
    + * }
    + * }
    + * }
      * 
    * * @author Eyal Peled @@ -53,18 +51,16 @@ public interface HttpIOExceptionHandler { /** * Invoked when an {@link IOException} is thrown during an HTTP request. * - *

    - * There is a simple rule that one must follow: If you modify the request object or modify its + *

    There is a simple rule that one must follow: If you modify the request object or modify its * execute interceptors in a way that should resolve the error, you must return {@code true} to * issue a retry. - *

    * * @param request request object that can be read from for context or modified before retry * @param supportsRetry whether there will actually be a retry if this handler return {@code true} - * . Some handlers may want to have an effect only when there will actually be a retry - * after they handle their event (e.g. a handler that implements backoff policy). + * . Some handlers may want to have an effect only when there will actually be a retry after + * they handle their event (e.g. a handler that implements backoff policy). * @return whether or not this handler has made a change that will require the request to be - * re-sent. + * re-sent. */ boolean handleIOException(HttpRequest request, boolean supportsRetry) throws IOException; } diff --git a/google-http-client/src/main/java/com/google/api/client/http/HttpMediaType.java b/google-http-client/src/main/java/com/google/api/client/http/HttpMediaType.java index 3dc9c8245..f39499328 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/HttpMediaType.java +++ b/google-http-client/src/main/java/com/google/api/client/http/HttpMediaType.java @@ -15,7 +15,6 @@ package com.google.api.client.http; import com.google.api.client.util.Preconditions; - import java.nio.charset.Charset; import java.util.Collections; import java.util.Locale; @@ -27,12 +26,10 @@ import java.util.regex.Pattern; /** - * HTTP Media-type as specified in the HTTP RFC ( - * {@link "http://tools.ietf.org/html/rfc2616#section-3.7"}). + * HTTP Media-type as specified in the HTTP RFC. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @author Matthias Linder (mlinder) * @since 1.10 @@ -79,22 +76,39 @@ public final class HttpMediaType { // detection can be done on a per-type/parameter basis. String typeOrKey = "[^\\s/=;\"]+"; // only disallow separators String wholeParameterSection = ";.*"; - FULL_MEDIA_TYPE_REGEX = Pattern.compile( - "\\s*(" + typeOrKey + ")/(" + typeOrKey + ")" + // main type (G1)/sub type (G2) - "\\s*(" + wholeParameterSection + ")?", Pattern.DOTALL); // parameters (G3) or null + FULL_MEDIA_TYPE_REGEX = + Pattern.compile( + "\\s*(" + + typeOrKey + + ")/(" + + typeOrKey + + ")" + + // main type (G1)/sub type (G2) + "\\s*(" + + wholeParameterSection + + ")?", + Pattern.DOTALL); // parameters (G3) or null // PARAMETER_REGEX: Semi-restrictive regex matching each parameter in the parameter section. // We also allow multipart values here (http://www.w3.org/Protocols/rfc1341/7_2_Multipart.html) // although those do not fully conform to the HTTP spec. String quotedParameterValue = "\"([^\"]*)\""; String unquotedParameterValue = "[^\\s;\"]*"; - String parameterValue = quotedParameterValue + "|" + unquotedParameterValue; - PARAMETER_REGEX = Pattern.compile("\\s*;\\s*(" + typeOrKey + ")" + // parameter key (G1) - "=(" + parameterValue + ")"); // G2 (if quoted) and else G3 + String parameterValue = quotedParameterValue + "|" + unquotedParameterValue; + PARAMETER_REGEX = + Pattern.compile( + "\\s*;\\s*(" + + typeOrKey + + ")" + + // parameter key (G1) + "=(" + + parameterValue + + ")"); // G2 (if quoted) and else G3 } /** * Initializes the {@link HttpMediaType} by setting the specified media type. + * * @param type main media type, for example {@code "text"} * @param subType sub media type, for example {@code "plain"} */ @@ -125,9 +139,7 @@ public HttpMediaType setType(String type) { return this; } - /** - * Returns the main media type, for example {@code "text"}, or {@code null} for '*'. - */ + /** Returns the main media type, for example {@code "text"}, or {@code null} for '*'. */ public String getType() { return type; } @@ -145,21 +157,17 @@ public HttpMediaType setSubType(String subType) { return this; } - /** - * Returns the sub media type, for example {@code "plain"} when using {@code "text"}. - */ + /** Returns the sub media type, for example {@code "plain"} when using {@code "text"}. */ public String getSubType() { return subType; } /** - * Sets the full media type by parsing a full content-type string, for example - * {@code "text/plain; foo=bar"}. + * Sets the full media type by parsing a full content-type string, for example {@code "text/plain; + * foo=bar"}. * - *

    - * This method will not clear existing parameters. Use {@link #clearParameters()} if this behavior - * is required. - *

    + *

    This method will not clear existing parameters. Use {@link #clearParameters()} if this + * behavior is required. * * @param combinedType full media type in the {@code "maintype/subtype; key=value"} format. */ @@ -225,9 +233,7 @@ public HttpMediaType removeParameter(String name) { return this; } - /** - * Removes all set parameters from this media type. - */ + /** Removes all set parameters from this media type. */ public void clearParameters() { cachedBuildResult = null; parameters.clear(); @@ -255,9 +261,7 @@ private static String quoteString(String unquotedString) { return "\"" + escapedString + "\""; } - /** - * Builds the full media type string which can be passed in the Content-Type header. - */ + /** Builds the full media type string which can be passed in the Content-Type header. */ public String build() { if (cachedBuildResult != null) { return cachedBuildResult; @@ -286,11 +290,12 @@ public String toString() { } /** - * Returns {@code true} if the specified media type has both the same type and subtype, or - * {@code false} if they don't match or the media type is {@code null}. + * Returns {@code true} if the specified media type has both the same type and subtype, or {@code + * false} if they don't match or the media type is {@code null}. */ public boolean equalsIgnoreParameters(HttpMediaType mediaType) { - return mediaType != null && getType().equalsIgnoreCase(mediaType.getType()) + return mediaType != null + && getType().equalsIgnoreCase(mediaType.getType()) && getSubType().equalsIgnoreCase(mediaType.getSubType()); } @@ -300,8 +305,10 @@ public boolean equalsIgnoreParameters(HttpMediaType mediaType) { */ public static boolean equalsIgnoreParameters(String mediaTypeA, String mediaTypeB) { // TODO(mlinder): Make the HttpMediaType.isSameType implementation more performant. - return (mediaTypeA == null && mediaTypeB == null) || mediaTypeA != null && mediaTypeB != null - && new HttpMediaType(mediaTypeA).equalsIgnoreParameters(new HttpMediaType(mediaTypeB)); + return (mediaTypeA == null && mediaTypeB == null) + || mediaTypeA != null + && mediaTypeB != null + && new HttpMediaType(mediaTypeA).equalsIgnoreParameters(new HttpMediaType(mediaTypeB)); } /** @@ -314,9 +321,7 @@ public HttpMediaType setCharsetParameter(Charset charset) { return this; } - /** - * Returns the specified charset or {@code null} if unset. - */ + /** Returns the specified charset or {@code null} if unset. */ public Charset getCharsetParameter() { String value = getParameter("charset"); return value == null ? null : Charset.forName(value); diff --git a/google-http-client/src/main/java/com/google/api/client/http/HttpMethods.java b/google-http-client/src/main/java/com/google/api/client/http/HttpMethods.java index 0ca55d325..34726362e 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/HttpMethods.java +++ b/google-http-client/src/main/java/com/google/api/client/http/HttpMethods.java @@ -52,6 +52,5 @@ public final class HttpMethods { /** HTTP TRACE method. */ public static final String TRACE = "TRACE"; - private HttpMethods() { - } + private HttpMethods() {} } diff --git a/google-http-client/src/main/java/com/google/api/client/http/HttpRequest.java b/google-http-client/src/main/java/com/google/api/client/http/HttpRequest.java index d75b7fa2f..78f15d868 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/HttpRequest.java +++ b/google-http-client/src/main/java/com/google/api/client/http/HttpRequest.java @@ -15,16 +15,21 @@ package com.google.api.client.http; import com.google.api.client.util.Beta; -import com.google.api.client.util.IOUtils; import com.google.api.client.util.LoggingStreamingContent; import com.google.api.client.util.ObjectParser; import com.google.api.client.util.Preconditions; import com.google.api.client.util.Sleeper; import com.google.api.client.util.StreamingContent; import com.google.api.client.util.StringUtils; - +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import io.opencensus.common.Scope; +import io.opencensus.contrib.http.util.HttpTraceAttributeConstants; +import io.opencensus.trace.AttributeValue; +import io.opencensus.trace.Span; +import io.opencensus.trace.Tracer; import java.io.IOException; import java.io.InputStream; +import java.util.Properties; import java.util.concurrent.Callable; import java.util.concurrent.Executor; import java.util.concurrent.Executors; @@ -36,9 +41,7 @@ /** * HTTP request. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @since 1.0 * @author Yaniv Inbar @@ -50,15 +53,14 @@ public final class HttpRequest { * * @since 1.8 */ - public static final String VERSION = "1.26.0"; + public static final String VERSION = getVersion(); /** * User agent suffix for all requests. * - *

    - * Includes a {@code "(gzip)"} suffix in case the server -- as Google's servers may do -- checks - * the {@code User-Agent} header to try to detect if the client accepts gzip-encoded responses. - *

    + *

    Includes a {@code "(gzip)"} suffix in case the server -- as Google's servers may do -- + * checks the {@code User-Agent} header to try to detect if the client accepts gzip-encoded + * responses. * * @since 1.4 */ @@ -85,49 +87,39 @@ public final class HttpRequest { /** * HTTP response headers. * - *

    - * For example, this can be used if you want to use a subclass of {@link HttpHeaders} called + *

    For example, this can be used if you want to use a subclass of {@link HttpHeaders} called * MyHeaders to process the response: - *

    * *
    -  static String executeAndGetValueOfSomeCustomHeader(HttpRequest request) {
    -    MyHeaders responseHeaders = new MyHeaders();
    -    request.responseHeaders = responseHeaders;
    -    HttpResponse response = request.execute();
    -    return responseHeaders.someCustomHeader;
    -  }
    +   * static String executeAndGetValueOfSomeCustomHeader(HttpRequest request) {
    +   * MyHeaders responseHeaders = new MyHeaders();
    +   * request.responseHeaders = responseHeaders;
    +   * HttpResponse response = request.execute();
    +   * return responseHeaders.someCustomHeader;
    +   * }
        * 
    */ private HttpHeaders responseHeaders = new HttpHeaders(); /** * The number of retries that will be allowed to execute before the request will be terminated or - * {@code 0} to not retry requests. Retries occur as a result of either - * {@link HttpUnsuccessfulResponseHandler} or {@link HttpIOExceptionHandler} which handles - * abnormal HTTP response or the I/O exception. + * {@code 0} to not retry requests. Retries occur as a result of either {@link + * HttpUnsuccessfulResponseHandler} or {@link HttpIOExceptionHandler} which handles abnormal HTTP + * response or the I/O exception. */ private int numRetries = DEFAULT_NUMBER_OF_RETRIES; /** * Determines the limit to the content size that will be logged during {@link #execute()}. * - *

    - * Content will only be logged if {@link #isLoggingEnabled} is {@code true}. - *

    + *

    Content will only be logged if {@link #isLoggingEnabled} is {@code true}. * - *

    - * If the content size is greater than this limit then it will not be logged. - *

    + *

    If the content size is greater than this limit then it will not be logged. * - *

    - * Can be set to {@code 0} to disable content logging. This is useful for example if content has - * sensitive data such as authentication information. - *

    + *

    Can be set to {@code 0} to disable content logging. This is useful for example if content + * has sensitive data such as authentication information. * - *

    - * Defaults to 16KB. - *

    + *

    Defaults to 16KB. */ private int contentLoggingLimit = 0x4000; @@ -158,12 +150,14 @@ static String executeAndGetValueOfSomeCustomHeader(HttpRequest request) { */ private int readTimeout = 20 * 1000; + /** Timeout in milliseconds to set POST/PUT data or {@code 0} for an infinite timeout. */ + private int writeTimeout = 0; + /** HTTP unsuccessful (non-2XX) response handler or {@code null} for none. */ private HttpUnsuccessfulResponseHandler unsuccessfulResponseHandler; /** HTTP I/O exception handler or {@code null} for none. */ - @Beta - private HttpIOExceptionHandler ioExceptionHandler; + @Beta private HttpIOExceptionHandler ioExceptionHandler; /** HTTP response interceptor or {@code null} for none. */ private HttpResponseInterceptor responseInterceptor; @@ -174,16 +168,15 @@ static String executeAndGetValueOfSomeCustomHeader(HttpRequest request) { /** HTTP content encoding or {@code null} for none. */ private HttpEncoding encoding; - /** - * The {@link BackOffPolicy} to use between retry attempts or {@code null} for none. - */ - @Deprecated - @Beta - private BackOffPolicy backOffPolicy; + /** The {@link BackOffPolicy} to use between retry attempts or {@code null} for none. */ + @Deprecated @Beta private BackOffPolicy backOffPolicy; /** Whether to automatically follow redirects ({@code true} by default). */ private boolean followRedirects = true; + /** Whether to use raw redirect URLs ({@code false} by default). */ + private boolean useRawRedirectUrls = false; + /** * Whether to throw an exception at the end of {@link #execute()} on an HTTP error code (non-2XX) * after all retries and response handlers have been exhausted ({@code true} by default). @@ -191,25 +184,32 @@ static String executeAndGetValueOfSomeCustomHeader(HttpRequest request) { private boolean throwExceptionOnExecuteError = true; /** - * Whether to retry the request if an {@link IOException} is encountered in - * {@link LowLevelHttpRequest#execute()}. + * Whether to retry the request if an {@link IOException} is encountered in {@link + * LowLevelHttpRequest#execute()}. */ - @Deprecated - @Beta - private boolean retryOnExecuteIOException = false; + @Deprecated @Beta private boolean retryOnExecuteIOException = false; /** * Whether to not add the suffix {@link #USER_AGENT_SUFFIX} to the User-Agent header. * - *

    - * It is {@code false} by default. - *

    + *

    It is {@code false} by default. */ private boolean suppressUserAgentSuffix; /** Sleeper. */ private Sleeper sleeper = Sleeper.DEFAULT; + /** OpenCensus tracing component. */ + private final Tracer tracer = OpenCensusUtils.getTracer(); + + /** + * Determines whether {@link HttpResponse#getContent()} of this request should return raw input + * stream or not. + * + *

    It is {@code false} by default. + */ + private boolean responseReturnRawInputStream = false; + /** * @param transport HTTP transport * @param requestMethod HTTP request method or {@code null} for none @@ -306,13 +306,13 @@ public HttpRequest setEncoding(HttpEncoding encoding) { } /** - * {@link Beta}
    + * {@link Beta}
    * Returns the {@link BackOffPolicy} to use between retry attempts or {@code null} for none. * * @since 1.7 - * @deprecated (scheduled to be removed in 1.18). - * {@link #setUnsuccessfulResponseHandler(HttpUnsuccessfulResponseHandler)} with a new - * {@link HttpBackOffUnsuccessfulResponseHandler} instead. + * @deprecated (scheduled to be removed in 1.18). {@link + * #setUnsuccessfulResponseHandler(HttpUnsuccessfulResponseHandler)} with a new {@link + * HttpBackOffUnsuccessfulResponseHandler} instead. */ @Deprecated @Beta @@ -321,13 +321,13 @@ public BackOffPolicy getBackOffPolicy() { } /** - * {@link Beta}
    + * {@link Beta}
    * Sets the {@link BackOffPolicy} to use between retry attempts or {@code null} for none. * * @since 1.7 - * @deprecated (scheduled to be removed in 1.18). Use - * {@link #setUnsuccessfulResponseHandler(HttpUnsuccessfulResponseHandler)} with a new - * {@link HttpBackOffUnsuccessfulResponseHandler} instead. + * @deprecated (scheduled to be removed in 1.18). Use {@link + * #setUnsuccessfulResponseHandler(HttpUnsuccessfulResponseHandler)} with a new {@link + * HttpBackOffUnsuccessfulResponseHandler} instead. */ @Deprecated @Beta @@ -339,22 +339,14 @@ public HttpRequest setBackOffPolicy(BackOffPolicy backOffPolicy) { /** * Returns the limit to the content size that will be logged during {@link #execute()}. * - *

    - * If the content size is greater than this limit then it will not be logged. - *

    + *

    If the content size is greater than this limit then it will not be logged. * - *

    - * Content will only be logged if {@link #isLoggingEnabled} is {@code true}. - *

    + *

    Content will only be logged if {@link #isLoggingEnabled} is {@code true}. * - *

    - * Can be set to {@code 0} to disable content logging. This is useful for example if content has - * sensitive data such as authentication information. - *

    + *

    Can be set to {@code 0} to disable content logging. This is useful for example if content + * has sensitive data such as authentication information. * - *

    - * Defaults to 16KB. - *

    + *

    Defaults to 16KB. * * @since 1.7 */ @@ -365,22 +357,14 @@ public int getContentLoggingLimit() { /** * Set the limit to the content size that will be logged during {@link #execute()}. * - *

    - * If the content size is greater than this limit then it will not be logged. - *

    + *

    If the content size is greater than this limit then it will not be logged. * - *

    - * Content will only be logged if {@link #isLoggingEnabled} is {@code true}. - *

    + *

    Content will only be logged if {@link #isLoggingEnabled} is {@code true}. * - *

    - * Can be set to {@code 0} to disable content logging. This is useful for example if content has - * sensitive data such as authentication information. - *

    + *

    Can be set to {@code 0} to disable content logging. This is useful for example if content + * has sensitive data such as authentication information. * - *

    - * Defaults to 16KB. - *

    + *

    Defaults to 16KB. * * @since 1.7 */ @@ -394,9 +378,7 @@ public HttpRequest setContentLoggingLimit(int contentLoggingLimit) { /** * Returns whether logging should be enabled for this request. * - *

    - * Defaults to {@code true}. - *

    + *

    Defaults to {@code true}. * * @since 1.9 */ @@ -407,9 +389,7 @@ public boolean isLoggingEnabled() { /** * Sets whether logging should be enabled for this request. * - *

    - * Defaults to {@code true}. - *

    + *

    Defaults to {@code true}. * * @since 1.9 */ @@ -430,9 +410,7 @@ public boolean isCurlLoggingEnabled() { /** * Sets whether logging in form of curl commands should be enabled for this request. * - *

    - * Defaults to {@code true}. - *

    + *

    Defaults to {@code true}. * * @since 1.11 */ @@ -455,9 +433,7 @@ public int getConnectTimeout() { * Sets the timeout in milliseconds to establish a connection or {@code 0} for an infinite * timeout. * - *

    - * By default it is 20000 (20 seconds). - *

    + *

    By default it is 20000 (20 seconds). * * @since 1.5 */ @@ -471,9 +447,7 @@ public HttpRequest setConnectTimeout(int connectTimeout) { * Returns the timeout in milliseconds to read data from an established connection or {@code 0} * for an infinite timeout. * - *

    - * By default it is 20000 (20 seconds). - *

    + *

    By default it is 20000 (20 seconds). * * @since 1.5 */ @@ -493,6 +467,28 @@ public HttpRequest setReadTimeout(int readTimeout) { return this; } + /** + * Returns the timeout in milliseconds to send POST/PUT data or {@code 0} for an infinite timeout. + * + *

    By default it is 0 (infinite). + * + * @since 1.27 + */ + public int getWriteTimeout() { + return writeTimeout; + } + + /** + * Sets the timeout in milliseconds to send POST/PUT data or {@code 0} for an infinite timeout. + * + * @since 1.27 + */ + public HttpRequest setWriteTimeout(int writeTimeout) { + Preconditions.checkArgument(writeTimeout >= 0); + this.writeTimeout = writeTimeout; + return this; + } + /** * Returns the HTTP request headers. * @@ -505,9 +501,7 @@ public HttpHeaders getHeaders() { /** * Sets the HTTP request headers. * - *

    - * By default, this is a new unmodified instance of {@link HttpHeaders}. - *

    + *

    By default, this is a new unmodified instance of {@link HttpHeaders}. * * @since 1.5 */ @@ -528,22 +522,18 @@ public HttpHeaders getResponseHeaders() { /** * Sets the HTTP response headers. * - *

    - * By default, this is a new unmodified instance of {@link HttpHeaders}. - *

    + *

    By default, this is a new unmodified instance of {@link HttpHeaders}. * - *

    - * For example, this can be used if you want to use a subclass of {@link HttpHeaders} called + *

    For example, this can be used if you want to use a subclass of {@link HttpHeaders} called * MyHeaders to process the response: - *

    * *
    -  static String executeAndGetValueOfSomeCustomHeader(HttpRequest request) {
    -    MyHeaders responseHeaders = new MyHeaders();
    -    request.responseHeaders = responseHeaders;
    -    HttpResponse response = request.execute();
    -    return responseHeaders.someCustomHeader;
    -  }
    +   * static String executeAndGetValueOfSomeCustomHeader(HttpRequest request) {
    +   * MyHeaders responseHeaders = new MyHeaders();
    +   * request.responseHeaders = responseHeaders;
    +   * HttpResponse response = request.execute();
    +   * return responseHeaders.someCustomHeader;
    +   * }
        * 
    * * @since 1.5 @@ -595,7 +585,7 @@ public HttpRequest setUnsuccessfulResponseHandler( } /** - * {@link Beta}
    + * {@link Beta}
    * Returns the HTTP I/O exception handler or {@code null} for none. * * @since 1.15 @@ -606,7 +596,7 @@ public HttpIOExceptionHandler getIOExceptionHandler() { } /** - * {@link Beta}
    + * {@link Beta}
    * Sets the HTTP I/O exception handler or {@code null} for none. * * @since 1.15 @@ -638,10 +628,9 @@ public HttpRequest setResponseInterceptor(HttpResponseInterceptor responseInterc /** * Returns the number of retries that will be allowed to execute before the request will be - * terminated or {@code 0} to not retry requests. Retries occur as a result of either - * {@link HttpUnsuccessfulResponseHandler} or {@link HttpIOExceptionHandler} which handles - * abnormal HTTP response or the I/O exception. - * + * terminated or {@code 0} to not retry requests. Retries occur as a result of either {@link + * HttpUnsuccessfulResponseHandler} or {@link HttpIOExceptionHandler} which handles abnormal HTTP + * response or the I/O exception. * * @since 1.5 */ @@ -651,13 +640,11 @@ public int getNumberOfRetries() { /** * Sets the number of retries that will be allowed to execute before the request will be - * terminated or {@code 0} to not retry requests. Retries occur as a result of either - * {@link HttpUnsuccessfulResponseHandler} or {@link HttpIOExceptionHandler} which handles - * abnormal HTTP response or the I/O exception. + * terminated or {@code 0} to not retry requests. Retries occur as a result of either {@link + * HttpUnsuccessfulResponseHandler} or {@link HttpIOExceptionHandler} which handles abnormal HTTP + * response or the I/O exception. * - *

    - * The default value is {@link #DEFAULT_NUMBER_OF_RETRIES}. - *

    + *

    The default value is {@link #DEFAULT_NUMBER_OF_RETRIES}. * * @since 1.5 */ @@ -671,9 +658,7 @@ public HttpRequest setNumberOfRetries(int numRetries) { * Sets the {@link ObjectParser} used to parse the response to this request or {@code null} for * none. * - *

    - * This parser will be preferred over any registered HttpParser. - *

    + *

    This parser will be preferred over any registered HttpParser. * * @since 1.10 */ @@ -703,9 +688,7 @@ public boolean getFollowRedirects() { /** * Sets whether to follow redirects automatically. * - *

    - * The default value is {@code true}. - *

    + *

    The default value is {@code true}. * * @since 1.6 */ @@ -714,6 +697,21 @@ public HttpRequest setFollowRedirects(boolean followRedirects) { return this; } + /** Return whether to use raw redirect URLs. */ + public boolean getUseRawRedirectUrls() { + return useRawRedirectUrls; + } + + /** + * Sets whether to use raw redirect URLs. + * + *

    The default value is {@code false}. + */ + public HttpRequest setUseRawRedirectUrls(boolean useRawRedirectUrls) { + this.useRawRedirectUrls = useRawRedirectUrls; + return this; + } + /** * Returns whether to throw an exception at the end of {@link #execute()} on an HTTP error code * (non-2XX) after all retries and response handlers have been exhausted. @@ -728,9 +726,7 @@ public boolean getThrowExceptionOnExecuteError() { * Sets whether to throw an exception at the end of {@link #execute()} on a HTTP error code * (non-2XX) after all retries and response handlers have been exhausted. * - *

    - * The default value is {@code true}. - *

    + *

    The default value is {@code true}. * * @since 1.7 */ @@ -740,13 +736,13 @@ public HttpRequest setThrowExceptionOnExecuteError(boolean throwExceptionOnExecu } /** - * {@link Beta}
    - * Returns whether to retry the request if an {@link IOException} is encountered in - * {@link LowLevelHttpRequest#execute()}. + * {@link Beta}
    + * Returns whether to retry the request if an {@link IOException} is encountered in {@link + * LowLevelHttpRequest#execute()}. * * @since 1.9 - * @deprecated (scheduled to be removed in 1.18) Use - * {@link #setIOExceptionHandler(HttpIOExceptionHandler)} instead. + * @deprecated (scheduled to be removed in 1.18) Use {@link + * #setIOExceptionHandler(HttpIOExceptionHandler)} instead. */ @Deprecated @Beta @@ -755,17 +751,15 @@ public boolean getRetryOnExecuteIOException() { } /** - * {@link Beta}
    - * Sets whether to retry the request if an {@link IOException} is encountered in - * {@link LowLevelHttpRequest#execute()}. + * {@link Beta}
    + * Sets whether to retry the request if an {@link IOException} is encountered in {@link + * LowLevelHttpRequest#execute()}. * - *

    - * The default value is {@code false}. - *

    + *

    The default value is {@code false}. * * @since 1.9 - * @deprecated (scheduled to be removed in 1.18) Use - * {@link #setIOExceptionHandler(HttpIOExceptionHandler)} instead. + * @deprecated (scheduled to be removed in 1.18) Use {@link + * #setIOExceptionHandler(HttpIOExceptionHandler)} instead. */ @Deprecated @Beta @@ -786,9 +780,7 @@ public boolean getSuppressUserAgentSuffix() { /** * Sets whether to not add the suffix {@link #USER_AGENT_SUFFIX} to the User-Agent header. * - *

    - * The default value is {@code false}. - *

    + *

    The default value is {@code false}. * * @since 1.11 */ @@ -797,46 +789,60 @@ public HttpRequest setSuppressUserAgentSuffix(boolean suppressUserAgentSuffix) { return this; } + /** + * Returns whether {@link HttpResponse#getContent()} should return raw input stream for this + * request. + * + * @since 1.29 + */ + public boolean getResponseReturnRawInputStream() { + return responseReturnRawInputStream; + } + + /** + * Sets whether {@link HttpResponse#getContent()} should return raw input stream for this request. + * + *

    The default value is {@code false}. + * + * @since 1.29 + */ + public HttpRequest setResponseReturnRawInputStream(boolean responseReturnRawInputStream) { + this.responseReturnRawInputStream = responseReturnRawInputStream; + return this; + } + /** * Execute the HTTP request and returns the HTTP response. * - *

    - * Note that regardless of the returned status code, the HTTP response content has not been parsed - * yet, and must be parsed by the calling code. - *

    + *

    Note that regardless of the returned status code, the HTTP response content has not been + * parsed yet, and must be parsed by the calling code. * - *

    - * Note that when calling to this method twice or more, the state of this HTTP request object + *

    Note that when calling to this method twice or more, the state of this HTTP request object * isn't cleared, so the request will continue where it was left. For example, the state of the * {@link HttpUnsuccessfulResponseHandler} attached to this HTTP request will remain the same as * it was left after last execute. - *

    * - *

    - * Almost all details of the request and response are logged if {@link Level#CONFIG} is loggable. - * The only exception is the value of the {@code Authorization} header which is only logged if - * {@link Level#ALL} is loggable. - *

    + *

    Almost all details of the request and response are logged if {@link Level#CONFIG} is + * loggable. The only exception is the value of the {@code Authorization} header which is only + * logged if {@link Level#ALL} is loggable. * - *

    - * Callers should call {@link HttpResponse#disconnect} when the returned HTTP response object is - * no longer needed. However, {@link HttpResponse#disconnect} does not have to be called if the + *

    Callers should call {@link HttpResponse#disconnect} when the returned HTTP response object + * is no longer needed. However, {@link HttpResponse#disconnect} does not have to be called if the * response stream is properly closed. Example usage: - *

    * *
    -     HttpResponse response = request.execute();
    -     try {
    -       // process the HTTP response object
    -     } finally {
    -       response.disconnect();
    -     }
    +   * HttpResponse response = request.execute();
    +   * try {
    +   * // process the HTTP response object
    +   * } finally {
    +   * response.disconnect();
    +   * }
        * 
    * - * @return HTTP response for an HTTP success response (or HTTP error response if - * {@link #getThrowExceptionOnExecuteError()} is {@code false}) - * @throws HttpResponseException for an HTTP error response (only if - * {@link #getThrowExceptionOnExecuteError()} is {@code true}) + * @return HTTP response for an HTTP success response (or HTTP error response if {@link + * #getThrowExceptionOnExecuteError()} is {@code false}) + * @throws HttpResponseException for an HTTP error response (only if {@link + * #getThrowExceptionOnExecuteError()} is {@code true}) * @see HttpResponse#isSuccessStatusCode() */ @SuppressWarnings("deprecation") @@ -854,7 +860,13 @@ public HttpResponse execute() throws IOException { Preconditions.checkNotNull(requestMethod); Preconditions.checkNotNull(url); + Span span = + tracer + .spanBuilder(OpenCensusUtils.SPAN_NAME_HTTP_REQUEST_EXECUTE) + .setRecordEvents(OpenCensusUtils.isRecordEvent()) + .startSpan(); do { + span.addAnnotation("retry #" + (numRetries - retriesRemaining)); // Cleanup any unneeded response from a previous iteration if (response != null) { response.ignore(); @@ -869,6 +881,11 @@ public HttpResponse execute() throws IOException { } // build low-level HTTP request String urlString = url.build(); + addSpanAttribute(span, HttpTraceAttributeConstants.HTTP_METHOD, requestMethod); + addSpanAttribute(span, HttpTraceAttributeConstants.HTTP_HOST, url.getHost()); + addSpanAttribute(span, HttpTraceAttributeConstants.HTTP_PATH, url.getRawPath()); + addSpanAttribute(span, HttpTraceAttributeConstants.HTTP_URL, urlString); + LowLevelHttpRequest lowLevelHttpRequest = transport.buildRequest(requestMethod, urlString); Logger logger = HttpTransport.LOGGER; boolean loggable = loggingEnabled && logger.isLoggable(Level.CONFIG); @@ -878,8 +895,11 @@ public HttpResponse execute() throws IOException { if (loggable) { logbuf = new StringBuilder(); logbuf.append("-------------- REQUEST --------------").append(StringUtils.LINE_SEPARATOR); - logbuf.append(requestMethod) - .append(' ').append(urlString).append(StringUtils.LINE_SEPARATOR); + logbuf + .append(requestMethod) + .append(' ') + .append(urlString) + .append(StringUtils.LINE_SEPARATOR); // setup curl logging if (curlLoggingEnabled) { @@ -894,10 +914,15 @@ public HttpResponse execute() throws IOException { if (!suppressUserAgentSuffix) { if (originalUserAgent == null) { headers.setUserAgent(USER_AGENT_SUFFIX); + addSpanAttribute(span, HttpTraceAttributeConstants.HTTP_USER_AGENT, USER_AGENT_SUFFIX); } else { - headers.setUserAgent(originalUserAgent + " " + USER_AGENT_SUFFIX); + String newUserAgent = originalUserAgent + " " + USER_AGENT_SUFFIX; + headers.setUserAgent(newUserAgent); + addSpanAttribute(span, HttpTraceAttributeConstants.HTTP_USER_AGENT, newUserAgent); } } + OpenCensusUtils.propagateTracingContext(span, headers); + // headers HttpHeaders.serializeHeaders(headers, logbuf, curlbuf, logger, lowLevelHttpRequest); if (!suppressUserAgentSuffix) { @@ -910,12 +935,13 @@ public HttpResponse execute() throws IOException { final boolean contentRetrySupported = streamingContent == null || content.retrySupported(); if (streamingContent != null) { final String contentEncoding; - final long contentLength; + long contentLength = -1; final String contentType = content.getType(); // log content if (loggable) { - streamingContent = new LoggingStreamingContent( - streamingContent, HttpTransport.LOGGER, Level.CONFIG, contentLoggingLimit); + streamingContent = + new LoggingStreamingContent( + streamingContent, HttpTransport.LOGGER, Level.CONFIG, contentLoggingLimit); } // encoding if (encoding == null) { @@ -924,7 +950,6 @@ public HttpResponse execute() throws IOException { } else { contentEncoding = encoding.getName(); streamingContent = new HttpEncodingStreamingContent(streamingContent, encoding); - contentLength = contentRetrySupported ? IOUtils.computeLength(streamingContent) : -1; } // append content headers to log buffer if (loggable) { @@ -977,8 +1002,20 @@ public HttpResponse execute() throws IOException { // execute lowLevelHttpRequest.setTimeout(connectTimeout, readTimeout); + lowLevelHttpRequest.setWriteTimeout(writeTimeout); + + // switch tracing scope to current span + @SuppressWarnings("MustBeClosedChecker") + Scope ws = tracer.withSpan(span); + OpenCensusUtils.recordSentMessageEvent(span, lowLevelHttpRequest.getContentLength()); try { LowLevelHttpResponse lowLevelHttpResponse = lowLevelHttpRequest.execute(); + if (lowLevelHttpResponse != null) { + OpenCensusUtils.recordReceivedMessageEvent(span, lowLevelHttpResponse.getContentLength()); + span.putAttribute( + HttpTraceAttributeConstants.HTTP_STATUS_CODE, + AttributeValue.longAttributeValue(lowLevelHttpResponse.getStatusCode())); + } // Flag used to indicate if an exception is thrown before the response is constructed. boolean responseConstructed = false; try { @@ -993,8 +1030,11 @@ public HttpResponse execute() throws IOException { } } } catch (IOException e) { - if (!retryOnExecuteIOException && (ioExceptionHandler == null - || !ioExceptionHandler.handleIOException(this, retryRequest))) { + if (!retryOnExecuteIOException + && (ioExceptionHandler == null + || !ioExceptionHandler.handleIOException(this, retryRequest))) { + // static analysis shows response is always null here + span.end(OpenCensusUtils.getEndSpanOptions(null)); throw e; } // Save the exception in case the retries do not work and we need to re-throw it later. @@ -1002,6 +1042,8 @@ public HttpResponse execute() throws IOException { if (loggable) { logger.log(Level.WARNING, "exception thrown while executing request", e); } + } finally { + ws.close(); } // Flag used to indicate if an exception is thrown before the response has completed @@ -1020,7 +1062,8 @@ public HttpResponse execute() throws IOException { if (handleRedirect(response.getStatusCode(), response.getHeaders())) { // The unsuccessful request's error could not be handled and it is a redirect request. errorHandled = true; - } else if (retryRequest && backOffPolicy != null + } else if (retryRequest + && backOffPolicy != null && backOffPolicy.isBackOffRequired(response.getStatusCode())) { // The unsuccessful request's error could not be handled and should be backed off // before retrying @@ -1057,6 +1100,7 @@ public HttpResponse execute() throws IOException { } } } while (retryRequest); + span.end(OpenCensusUtils.getEndSpanOptions(response == null ? null : response.getStatusCode())); if (response == null) { // Retries did not help resolve the execute exception, re-throw it. @@ -1069,7 +1113,9 @@ public HttpResponse execute() throws IOException { // throw an exception if unsuccessful response if (throwExceptionOnExecuteError && !response.isSuccessStatusCode()) { try { - throw new HttpResponseException(response); + throw new HttpResponseException.Builder(response) + .setAttemptCount(numRetries - retriesRemaining) + .build(); } finally { response.disconnect(); } @@ -1078,7 +1124,7 @@ public HttpResponse execute() throws IOException { } /** - * {@link Beta}
    + * {@link Beta}
    * Executes this request asynchronously in a single separate thread using the supplied executor. * * @param executor executor to run the asynchronous request @@ -1087,54 +1133,54 @@ public HttpResponse execute() throws IOException { */ @Beta public Future executeAsync(Executor executor) { - FutureTask future = new FutureTask(new Callable() { + FutureTask future = + new FutureTask( + new Callable() { - public HttpResponse call() throws Exception { - return execute(); - } - }); + public HttpResponse call() throws Exception { + return execute(); + } + }); executor.execute(future); return future; } /** - * {@link Beta}
    + * {@link Beta}
    * Executes this request asynchronously using {@link #executeAsync(Executor)} in a single separate - * thread using {@link Executors#newSingleThreadExecutor()}. + * thread using {@link Executors#newFixedThreadPool(int)}. * * @return A future for accessing the results of the asynchronous request. * @since 1.13 */ @Beta public Future executeAsync() { - return executeAsync(Executors.newSingleThreadExecutor()); + return executeAsync( + Executors.newFixedThreadPool(1, new ThreadFactoryBuilder().setDaemon(true).build())); } /** * Sets up this request object to handle the necessary redirect if redirects are turned on, it is * a redirect status code and the header has a location. * - *

    - * When the status code is {@code 303} the method on the request is changed to a GET as per the + *

    When the status code is {@code 303} the method on the request is changed to a GET as per the * RFC2616 specification. On a redirect, it also removes the {@code "Authorization"} and all * {@code "If-*"} request headers. - *

    * - *

    - * Upgrade warning: When handling a status code of 303, {@link #handleRedirect(int, HttpHeaders)} - * now correctly removes any content from the body of the new request, as GET requests should not - * have content. It did not do this in prior version 1.16. - *

    + *

    Upgrade warning: When handling a status code of 303, {@link #handleRedirect(int, + * HttpHeaders)} now correctly removes any content from the body of the new request, as GET + * requests should not have content. It did not do this in prior version 1.16. * * @return whether the redirect was successful * @since 1.11 */ public boolean handleRedirect(int statusCode, HttpHeaders responseHeaders) { String redirectLocation = responseHeaders.getLocation(); - if (getFollowRedirects() && HttpStatusCodes.isRedirect(statusCode) + if (getFollowRedirects() + && HttpStatusCodes.isRedirect(statusCode) && redirectLocation != null) { // resolve the redirect location relative to the current location - setUrl(new GenericUrl(url.toURL(redirectLocation))); + setUrl(new GenericUrl(url.toURL(redirectLocation), useRawRedirectUrls)); // on 303 change method to GET if (statusCode == HttpStatusCodes.STATUS_CODE_SEE_OTHER) { setRequestMethod(HttpMethods.GET); @@ -1171,4 +1217,28 @@ public HttpRequest setSleeper(Sleeper sleeper) { this.sleeper = Preconditions.checkNotNull(sleeper); return this; } + + private static void addSpanAttribute(Span span, String key, String value) { + if (value != null) { + span.putAttribute(key, AttributeValue.stringAttributeValue(value)); + } + } + + private static String getVersion() { + // attempt to read the library's version from a properties file generated during the build + // this value should be read and cached for later use + String version = "unknown-version"; + try (InputStream inputStream = + HttpRequest.class.getResourceAsStream( + "/com/google/api/client/http/google-http-client.properties")) { + if (inputStream != null) { + final Properties properties = new Properties(); + properties.load(inputStream); + version = properties.getProperty("google-http-client.version"); + } + } catch (IOException e) { + // ignore + } + return version; + } } diff --git a/google-http-client/src/main/java/com/google/api/client/http/HttpRequestFactory.java b/google-http-client/src/main/java/com/google/api/client/http/HttpRequestFactory.java index 8f5ec2550..7a92a03bf 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/HttpRequestFactory.java +++ b/google-http-client/src/main/java/com/google/api/client/http/HttpRequestFactory.java @@ -16,23 +16,20 @@ import java.io.IOException; - /** * Thread-safe light-weight HTTP request factory layer on top of the HTTP transport that has an * optional {@link HttpRequestInitializer HTTP request initializer} for initializing requests. * - *

    - * For example, to use a particular authorization header across all requests, use: - *

    + *

    For example, to use a particular authorization header across all requests, use: * *

    -  public static HttpRequestFactory createRequestFactory(HttpTransport transport) {
    -    return transport.createRequestFactory(new HttpRequestInitializer() {
    -      public void initialize(HttpRequest request) throws IOException {
    -        request.getHeaders().setAuthorization("...");
    -      }
    -    });
    -  }
    + * public static HttpRequestFactory createRequestFactory(HttpTransport transport) {
    + * return transport.createRequestFactory(new HttpRequestInitializer() {
    + * public void initialize(HttpRequest request) throws IOException {
    + * request.getHeaders().setAuthorization("...");
    + * }
    + * });
    + * }
      * 
    * * @since 1.4 @@ -67,9 +64,7 @@ public HttpTransport getTransport() { /** * Returns the HTTP request initializer or {@code null} for none. * - *

    - * This initializer is invoked before setting its method, URL, or content. - *

    + *

    This initializer is invoked before setting its method, URL, or content. * * @since 1.5 */ @@ -89,13 +84,13 @@ public HttpRequestInitializer getInitializer() { public HttpRequest buildRequest(String requestMethod, GenericUrl url, HttpContent content) throws IOException { HttpRequest request = transport.buildRequest(); + if (url != null) { + request.setUrl(url); + } if (initializer != null) { initializer.initialize(request); } request.setRequestMethod(requestMethod); - if (url != null) { - request.setUrl(url); - } if (content != null) { request.setContent(content); } diff --git a/google-http-client/src/main/java/com/google/api/client/http/HttpRequestInitializer.java b/google-http-client/src/main/java/com/google/api/client/http/HttpRequestInitializer.java index c0f854558..4ee946907 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/HttpRequestInitializer.java +++ b/google-http-client/src/main/java/com/google/api/client/http/HttpRequestInitializer.java @@ -19,47 +19,39 @@ /** * HTTP request initializer. * - *

    - * For example, this might be used to disable request timeouts: - *

    + *

    For example, this might be used to disable request timeouts: * *

    -  public class DisableTimeout implements HttpRequestInitializer {
    -    public void initialize(HttpRequest request) {
    -      request.setConnectTimeout(0);
    -      request.setReadTimeout(0);
    -    }
    -  }
    + * public class DisableTimeout implements HttpRequestInitializer {
    + * public void initialize(HttpRequest request) {
    + * request.setConnectTimeout(0);
    + * request.setReadTimeout(0);
    + * }
    + * }
      * 
    * - *

    - * Sample usage with a request factory: - *

    + *

    Sample usage with a request factory: * *

    -  public static HttpRequestFactory createRequestFactory(HttpTransport transport) {
    -    return transport.createRequestFactory(new DisableTimeout());
    -  }
    + * public static HttpRequestFactory createRequestFactory(HttpTransport transport) {
    + * return transport.createRequestFactory(new DisableTimeout());
    + * }
      * 
    * - *

    - * More complex usage example: - *

    + *

    More complex usage example: * *

    -  public static HttpRequestFactory createRequestFactory2(HttpTransport transport) {
    -    final DisableTimeout disableTimeout = new DisableTimeout();
    -    return transport.createRequestFactory(new HttpRequestInitializer() {
    -      public void initialize(HttpRequest request) {
    -        disableTimeout.initialize(request);
    -      }
    -    });
    -  }
    + * public static HttpRequestFactory createRequestFactory2(HttpTransport transport) {
    + * final DisableTimeout disableTimeout = new DisableTimeout();
    + * return transport.createRequestFactory(new HttpRequestInitializer() {
    + * public void initialize(HttpRequest request) {
    + * disableTimeout.initialize(request);
    + * }
    + * });
    + * }
      * 
    * - *

    - * Implementations should normally be thread-safe. - *

    + *

    Implementations should normally be thread-safe. * * @since 1.4 * @author Yaniv Inbar diff --git a/google-http-client/src/main/java/com/google/api/client/http/HttpResponse.java b/google-http-client/src/main/java/com/google/api/client/http/HttpResponse.java index 64e8986b1..130208671 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/HttpResponse.java +++ b/google-http-client/src/main/java/com/google/api/client/http/HttpResponse.java @@ -14,12 +14,11 @@ package com.google.api.client.http; -import com.google.api.client.util.Charsets; import com.google.api.client.util.IOUtils; import com.google.api.client.util.LoggingInputStream; import com.google.api.client.util.Preconditions; import com.google.api.client.util.StringUtils; - +import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.EOFException; import java.io.IOException; @@ -27,31 +26,28 @@ import java.io.OutputStream; import java.lang.reflect.Type; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Locale; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.zip.GZIPInputStream; /** * HTTP response. * - *

    - * Callers should call {@link #disconnect} when the HTTP response object is no longer needed. + *

    Callers should call {@link #disconnect} when the HTTP response object is no longer needed. * However, {@link #disconnect} does not have to be called if the response stream is properly * closed. Example usage: - *

    * *
    -   HttpResponse response = request.execute();
    -   try {
    -     // process the HTTP response object
    -   } finally {
    -     response.disconnect();
    -   }
    + * HttpResponse response = request.execute();
    + * try {
    + * // process the HTTP response object
    + * } finally {
    + * response.disconnect();
    + * }
      * 
    * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @since 1.0 * @author Yaniv Inbar @@ -82,34 +78,33 @@ public final class HttpResponse { /** HTTP request. */ private final HttpRequest request; + /** Whether {@link #getContent()} should return raw input stream. */ + private final boolean returnRawInputStream; + + /** Content encoding for GZip */ + private static final String CONTENT_ENCODING_GZIP = "gzip"; + + /** Content encoding for GZip (legacy) */ + private static final String CONTENT_ENCODING_XGZIP = "x-gzip"; + /** * Determines the limit to the content size that will be logged during {@link #getContent()}. * - *

    - * Content will only be logged if {@link #isLoggingEnabled} is {@code true}. - *

    + *

    Content will only be logged if {@link #isLoggingEnabled} is {@code true}. * - *

    - * If the content size is greater than this limit then it will not be logged. - *

    + *

    If the content size is greater than this limit then it will not be logged. * - *

    - * Can be set to {@code 0} to disable content logging. This is useful for example if content has - * sensitive data such as authentication information. - *

    + *

    Can be set to {@code 0} to disable content logging. This is useful for example if content + * has sensitive data such as authentication information. * - *

    - * Defaults to {@link HttpRequest#getContentLoggingLimit()}. - *

    + *

    Defaults to {@link HttpRequest#getContentLoggingLimit()}. */ private int contentLoggingLimit; /** * Determines whether logging should be enabled on this response. * - *

    - * Defaults to {@link HttpRequest#isLoggingEnabled()}. - *

    + *

    Defaults to {@link HttpRequest#isLoggingEnabled()}. */ private boolean loggingEnabled; @@ -118,6 +113,7 @@ public final class HttpResponse { HttpResponse(HttpRequest request, LowLevelHttpResponse response) throws IOException { this.request = request; + this.returnRawInputStream = request.getResponseReturnRawInputStream(); contentLoggingLimit = request.getContentLoggingLimit(); loggingEnabled = request.isLoggingEnabled(); this.response = response; @@ -154,7 +150,7 @@ public final class HttpResponse { contentType = request.getResponseHeaders().getContentType(); } this.contentType = contentType; - mediaType = contentType == null ? null : new HttpMediaType(contentType); + this.mediaType = parseMediaType(contentType); // log from buffer if (loggable) { @@ -162,25 +158,33 @@ public final class HttpResponse { } } + /** + * Returns an {@link HttpMediaType} object parsed from {@link #contentType}, or {@code null} if if + * {@link #contentType} cannot be parsed or {@link #contentType} is {@code null}. + */ + private static HttpMediaType parseMediaType(String contentType) { + if (contentType == null) { + return null; + } + try { + return new HttpMediaType(contentType); + } catch (IllegalArgumentException e) { + // contentType is invalid and cannot be parsed. + return null; + } + } + /** * Returns the limit to the content size that will be logged during {@link #getContent()}. * - *

    - * Content will only be logged if {@link #isLoggingEnabled} is {@code true}. - *

    + *

    Content will only be logged if {@link #isLoggingEnabled} is {@code true}. * - *

    - * If the content size is greater than this limit then it will not be logged. - *

    + *

    If the content size is greater than this limit then it will not be logged. * - *

    - * Can be set to {@code 0} to disable content logging. This is useful for example if content has - * sensitive data such as authentication information. - *

    + *

    Can be set to {@code 0} to disable content logging. This is useful for example if content + * has sensitive data such as authentication information. * - *

    - * Defaults to {@link HttpRequest#getContentLoggingLimit()}. - *

    + *

    Defaults to {@link HttpRequest#getContentLoggingLimit()}. * * @since 1.7 */ @@ -191,22 +195,14 @@ public int getContentLoggingLimit() { /** * Set the limit to the content size that will be logged during {@link #getContent()}. * - *

    - * Content will only be logged if {@link #isLoggingEnabled} is {@code true}. - *

    + *

    Content will only be logged if {@link #isLoggingEnabled} is {@code true}. * - *

    - * If the content size is greater than this limit then it will not be logged. - *

    + *

    If the content size is greater than this limit then it will not be logged. * - *

    - * Can be set to {@code 0} to disable content logging. This is useful for example if content has - * sensitive data such as authentication information. - *

    + *

    Can be set to {@code 0} to disable content logging. This is useful for example if content + * has sensitive data such as authentication information. * - *

    - * Defaults to {@link HttpRequest#getContentLoggingLimit()}. - *

    + *

    Defaults to {@link HttpRequest#getContentLoggingLimit()}. * * @since 1.7 */ @@ -220,9 +216,7 @@ public HttpResponse setContentLoggingLimit(int contentLoggingLimit) { /** * Returns whether logging should be enabled on this response. * - *

    - * Defaults to {@link HttpRequest#isLoggingEnabled()}. - *

    + *

    Defaults to {@link HttpRequest#isLoggingEnabled()}. * * @since 1.9 */ @@ -233,9 +227,7 @@ public boolean isLoggingEnabled() { /** * Sets whether logging should be enabled on this response. * - *

    - * Defaults to {@link HttpRequest#isLoggingEnabled()}. - *

    + *

    Defaults to {@link HttpRequest#isLoggingEnabled()}. * * @since 1.9 */ @@ -282,8 +274,8 @@ public HttpHeaders getHeaders() { } /** - * Returns whether received a successful HTTP status code {@code >= 200 && < 300} (see - * {@link #getStatusCode()}). + * Returns whether received a successful HTTP status code {@code >= 200 && < 300} (see {@link + * #getStatusCode()}). * * @since 1.5 */ @@ -329,22 +321,22 @@ public HttpRequest getRequest() { /** * Returns the content of the HTTP response. - *

    - * The result is cached, so subsequent calls will be fast. - *

    - * Callers should call {@link InputStream#close} after the returned {@link InputStream} is no + * + *

    The result is cached, so subsequent calls will be fast. + * + *

    Callers should call {@link InputStream#close} after the returned {@link InputStream} is no * longer needed. Example usage: * *

    -     InputStream is = response.getContent();
    -     try {
    -       // Process the input stream..
    -     } finally {
    -       is.close();
    -     }
    +   * InputStream is = response.getContent();
    +   * try {
    +   * // Process the input stream..
    +   * } finally {
    +   * is.close();
    +   * }
        * 
    - *

    - * {@link HttpResponse#disconnect} does not have to be called if the content is closed. + * + *

    {@link HttpResponse#disconnect} does not have to be called if the content is closed. * * @return input stream content of the HTTP response or {@code null} for none * @throws IOException I/O exception @@ -358,17 +350,34 @@ public InputStream getContent() throws IOException { boolean contentProcessed = false; try { // gzip encoding (wrap content with GZipInputStream) - String contentEncoding = this.contentEncoding; - if (contentEncoding != null && contentEncoding.contains("gzip")) { - lowLevelResponseContent = new GZIPInputStream(lowLevelResponseContent); + if (!returnRawInputStream && this.contentEncoding != null) { + String contentEncoding = this.contentEncoding.trim().toLowerCase(Locale.ENGLISH); + if (CONTENT_ENCODING_GZIP.equals(contentEncoding) + || CONTENT_ENCODING_XGZIP.equals(contentEncoding)) { + // Wrap the original stream in a ConsumingInputStream before passing it to + // GZIPInputStream. The GZIPInputStream leaves content unconsumed in the original + // stream (it almost always leaves the last chunk unconsumed in chunked responses). + // ConsumingInputStream ensures that any unconsumed bytes are read at close. + // GZIPInputStream.close() --> ConsumingInputStream.close() --> + // exhaust(ConsumingInputStream) + lowLevelResponseContent = + GzipSupport.newGzipInputStream(new ConsumingInputStream(lowLevelResponseContent)); + } } // logging (wrap content with LoggingInputStream) Logger logger = HttpTransport.LOGGER; if (loggingEnabled && logger.isLoggable(Level.CONFIG)) { - lowLevelResponseContent = new LoggingInputStream( - lowLevelResponseContent, logger, Level.CONFIG, contentLoggingLimit); + lowLevelResponseContent = + new LoggingInputStream( + lowLevelResponseContent, logger, Level.CONFIG, contentLoggingLimit); + } + if (returnRawInputStream) { + content = lowLevelResponseContent; + } else { + // wrap the content with BufferedInputStream to support + // mark()/reset() while error checking in error handlers + content = new BufferedInputStream(lowLevelResponseContent); } - content = lowLevelResponseContent; contentProcessed = true; } catch (EOFException e) { // this may happen for example on a HEAD request since there no actual response data read @@ -387,30 +396,23 @@ public InputStream getContent() throws IOException { /** * Writes the content of the HTTP response into the given destination output stream. * - *

    - * Sample usage: + *

    Sample usage: * *

    -     HttpRequest request = requestFactory.buildGetRequest(
    -         new GenericUrl("https://www.google.com/images/srpr/logo3w.png"));
    -     OutputStream outputStream = new FileOutputStream(new File("/tmp/logo3w.png"));
    -     try {
    -       HttpResponse response = request.execute();
    -       response.download(outputStream);
    -     } finally {
    -       outputStream.close();
    -     }
    -    
    - * - *

    - * - *

    - * This method closes the content of the HTTP response from {@link #getContent()}. - *

    - * - *

    - * This method does not close the given output stream. - *

    + * HttpRequest request = requestFactory.buildGetRequest( + * new GenericUrl("https://www.google.com/images/srpr/logo3w.png")); + * OutputStream outputStream = new FileOutputStream(new File("/tmp/logo3w.png")); + * try { + * HttpResponse response = request.execute(); + * response.download(outputStream); + * } finally { + * outputStream.close(); + * } + * + * + *

    This method closes the content of the HTTP response from {@link #getContent()}. + * + *

    This method does not close the given output stream. * * @param outputStream destination output stream * @throws IOException I/O exception @@ -421,19 +423,20 @@ public void download(OutputStream outputStream) throws IOException { IOUtils.copy(inputStream, outputStream); } - /** - * Closes the content of the HTTP response from {@link #getContent()}, ignoring any content. - */ + /** Closes the content of the HTTP response from {@link #getContent()}, ignoring any content. */ public void ignore() throws IOException { - InputStream content = getContent(); - if (content != null) { - content.close(); + if (this.response == null) { + return; + } + InputStream lowLevelResponseContent = this.response.getContent(); + if (lowLevelResponseContent != null) { + lowLevelResponseContent.close(); } } /** - * Close the HTTP response content using {@link #ignore}, and disconnect using - * {@link LowLevelHttpResponse#disconnect()}. + * Disconnect using {@link LowLevelHttpResponse#disconnect()}, then close the HTTP response + * content using {@link #ignore}. * * @since 1.4 */ @@ -446,9 +449,7 @@ public void disconnect() throws IOException { * Parses the content of the HTTP response from {@link #getContent()} and reads it into a data * class of key/value pairs using the parser returned by {@link HttpRequest#getParser()}. * - *

    - * Reference: http://tools.ietf.org/html/rfc2616#section-4.3 - *

    + *

    Reference: http://tools.ietf.org/html/rfc2616#section-4.3 * * @return parsed data class or {@code null} for no content */ @@ -465,7 +466,8 @@ public T parseAs(Class dataClass) throws IOException { */ private boolean hasMessageBody() throws IOException { int statusCode = getStatusCode(); - if (getRequest().getRequestMethod().equals(HttpMethods.HEAD) || statusCode / 100 == 1 + if (getRequest().getRequestMethod().equals(HttpMethods.HEAD) + || statusCode / 100 == 1 || statusCode == HttpStatusCodes.STATUS_CODE_NO_CONTENT || statusCode == HttpStatusCodes.STATUS_CODE_NOT_MODIFIED) { ignore(); @@ -491,17 +493,13 @@ public Object parseAs(Type dataType) throws IOException { /** * Parses the content of the HTTP response from {@link #getContent()} and reads it into a string. * - *

    - * Since this method returns {@code ""} for no content, a simpler check for no content is to check - * if {@link #getContent()} is {@code null}. - *

    + *

    Since this method returns {@code ""} for no content, a simpler check for no content is to + * check if {@link #getContent()} is {@code null}. * - *

    - * All content is read from the input content stream rather than being limited by the + *

    All content is read from the input content stream rather than being limited by the * Content-Length. For the character set, it follows the specification by parsing the "charset" * parameter of the Content-Type header or by default {@code "ISO-8859-1"} if the parameter is * missing. - *

    * * @return parsed string or {@code ""} for no content * @throws IOException I/O exception @@ -517,13 +515,28 @@ public String parseAsString() throws IOException { } /** - * Returns the {@link Charset} specified in the Content-Type of this response or the - * {@code "ISO-8859-1"} charset as a default. + * Returns the {@link Charset} specified in the Content-Type of this response or the ISO-8859-1 + * charset as a default. * * @since 1.10 - * */ + */ public Charset getContentCharset() { - return mediaType == null || mediaType.getCharsetParameter() == null - ? Charsets.ISO_8859_1 : mediaType.getCharsetParameter(); + if (mediaType != null) { + // use specified charset parameter from content/type header if available + if (mediaType.getCharsetParameter() != null) { + return mediaType.getCharsetParameter(); + } + // fallback to well-known charsets + if ("application".equals(mediaType.getType()) && "json".equals(mediaType.getSubType())) { + // https://tools.ietf.org/html/rfc4627 - JSON must be encoded with UTF-8 + return StandardCharsets.UTF_8; + } + // fallback to well-kown charset for text/csv + if ("text".equals(mediaType.getType()) && "csv".equals(mediaType.getSubType())) { + // https://www.iana.org/assignments/media-types/text/csv - CSV must be encoded with UTF-8 + return StandardCharsets.UTF_8; + } + } + return StandardCharsets.ISO_8859_1; } } diff --git a/google-http-client/src/main/java/com/google/api/client/http/HttpResponseException.java b/google-http-client/src/main/java/com/google/api/client/http/HttpResponseException.java index 139773a89..63bf6fe4c 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/HttpResponseException.java +++ b/google-http-client/src/main/java/com/google/api/client/http/HttpResponseException.java @@ -16,15 +16,12 @@ import com.google.api.client.util.Preconditions; import com.google.api.client.util.StringUtils; - import java.io.IOException; /** * Exception thrown when an error status code is detected in an HTTP response. * - *

    - * Implementation is not thread safe. - *

    + *

    Implementation is not thread safe. * * @since 1.0 * @author Yaniv Inbar @@ -45,21 +42,22 @@ public class HttpResponseException extends IOException { /** HTTP response content or {@code null} for none. */ private final String content; + /** Number of attempts performed */ + private final int attemptCount; + /** * Constructor that constructs a detail message from the given HTTP response that includes the * status code, status message and HTTP response content. * - *

    - * Callers of this constructor should call {@link HttpResponse#disconnect} after - * {@link HttpResponseException} is instantiated. Example usage: - *

    + *

    Callers of this constructor should call {@link HttpResponse#disconnect} after {@link + * HttpResponseException} is instantiated. Example usage: * *

    -     try {
    -       throw new HttpResponseException(response);
    -     } finally {
    -       response.disconnect();
    -     }
    +   * try {
    +   * throw new HttpResponseException(response);
    +   * } finally {
    +   * response.disconnect();
    +   * }
        * 
    * * @param response HTTP response @@ -70,7 +68,6 @@ public HttpResponseException(HttpResponse response) { /** * @param builder builder - * * @since 1.14 */ protected HttpResponseException(Builder builder) { @@ -79,11 +76,12 @@ protected HttpResponseException(Builder builder) { statusMessage = builder.statusMessage; headers = builder.headers; content = builder.content; + attemptCount = builder.attemptCount; } /** - * Returns whether received a successful HTTP status code {@code >= 200 && < 300} (see - * {@link #getStatusCode()}). + * Returns whether received a successful HTTP status code {@code >= 200 && < 300} (see {@link + * #getStatusCode()}). * * @since 1.7 */ @@ -128,12 +126,18 @@ public final String getContent() { } /** - * Builder. + * Returns the attempt count * - *

    - * Implementation is not thread safe. - *

    + * @since 1.41 + */ + public final int getAttemptCount() { + return attemptCount; + } + + /** + * Builder. * + *

    Implementation is not thread safe. * * @since 1.14 */ @@ -154,6 +158,9 @@ public static class Builder { /** Detail message to use or {@code null} for none. */ String message; + /** Number of attempts performed */ + int attemptCount; + /** * @param statusCode HTTP status code * @param statusMessage status message or {@code null} @@ -165,9 +172,7 @@ public Builder(int statusCode, String statusMessage, HttpHeaders headers) { setHeaders(headers); } - /** - * @param response HTTP response - */ + /** @param response HTTP response */ public Builder(HttpResponse response) { this(response.getStatusCode(), response.getStatusMessage(), response.getHeaders()); // content @@ -179,6 +184,8 @@ public Builder(HttpResponse response) { } catch (IOException exception) { // it would be bad to throw an exception while throwing an exception exception.printStackTrace(); + } catch (IllegalArgumentException exception) { + exception.printStackTrace(); } // message StringBuilder builder = computeMessageBuffer(response); @@ -196,10 +203,8 @@ public final String getMessage() { /** * Sets the detail message to use or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Builder setMessage(String message) { this.message = message; @@ -214,10 +219,8 @@ public final int getStatusCode() { /** * Sets the HTTP status code or {@code 0} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Builder setStatusCode(int statusCode) { Preconditions.checkArgument(statusCode >= 0); @@ -233,10 +236,8 @@ public final String getStatusMessage() { /** * Sets the HTTP status message or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Builder setStatusMessage(String statusMessage) { this.statusMessage = statusMessage; @@ -251,10 +252,8 @@ public HttpHeaders getHeaders() { /** * Sets the HTTP response headers. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Builder setHeaders(HttpHeaders headers) { this.headers = Preconditions.checkNotNull(headers); @@ -269,16 +268,26 @@ public final String getContent() { /** * Sets the HTTP response content or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Builder setContent(String content) { this.content = content; return this; } + /** Returns the request attempt count */ + public final int getAttemptCount() { + return attemptCount; + } + + /** Sets the attempt count for the related HTTP request execution. */ + public Builder setAttemptCount(int attemptCount) { + Preconditions.checkArgument(attemptCount >= 0); + this.attemptCount = attemptCount; + return this; + } + /** Returns a new instance of {@link HttpResponseException} based on this builder. */ public HttpResponseException build() { return new HttpResponseException(this); @@ -303,6 +312,17 @@ public static StringBuilder computeMessageBuffer(HttpResponse response) { } builder.append(statusMessage); } + HttpRequest request = response.getRequest(); + if (request != null) { + if (builder.length() > 0) { + builder.append('\n'); + } + String requestMethod = request.getRequestMethod(); + if (requestMethod != null) { + builder.append(requestMethod).append(' '); + } + builder.append(request.getUrl()); + } return builder; } } diff --git a/google-http-client/src/main/java/com/google/api/client/http/HttpResponseInterceptor.java b/google-http-client/src/main/java/com/google/api/client/http/HttpResponseInterceptor.java index 0873399b1..a37b0b1b1 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/HttpResponseInterceptor.java +++ b/google-http-client/src/main/java/com/google/api/client/http/HttpResponseInterceptor.java @@ -20,62 +20,54 @@ * HTTP response interceptor to intercept the end of {@link HttpRequest#execute()} before returning * a successful response or throwing an exception for an unsuccessful response. * - *

    - * For example, this might be used to add a simple timer on requests: - *

    + *

    For example, this might be used to add a simple timer on requests: * *

    -  public static class TimerResponseInterceptor implements HttpResponseInterceptor {
    -
    -    private final long startTime = System.nanoTime();
    -
    -    public void interceptResponse(HttpResponse response) {
    -      long elapsedNanos = System.nanoTime() - startTime;
    -      System.out.println("elapsed seconds: " + TimeUnit.NANOSECONDS.toSeconds(elapsedNanos) + "s");
    -    }
    -  }
    + * public static class TimerResponseInterceptor implements HttpResponseInterceptor {
    + *
    + * private final long startTime = System.nanoTime();
    + *
    + * public void interceptResponse(HttpResponse response) {
    + * long elapsedNanos = System.nanoTime() - startTime;
    + * System.out.println("elapsed seconds: " + TimeUnit.NANOSECONDS.toSeconds(elapsedNanos) + "s");
    + * }
    + * }
      * 
    * - *

    - * Sample usage with a request factory: - *

    + *

    Sample usage with a request factory: * *

    -  public static HttpRequestFactory createRequestFactory(HttpTransport transport) {
    -    return transport.createRequestFactory(new HttpRequestInitializer() {
    -
    -      {@literal @}Override
    -      public void initialize(HttpRequest request) {
    -        request.setResponseInterceptor(new TimerResponseInterceptor());
    -      }
    -    });
    -  }
    + * public static HttpRequestFactory createRequestFactory(HttpTransport transport) {
    + * return transport.createRequestFactory(new HttpRequestInitializer() {
    + *
    + * {@literal @}Override
    + * public void initialize(HttpRequest request) {
    + * request.setResponseInterceptor(new TimerResponseInterceptor());
    + * }
    + * });
    + * }
      * 
    * - *

    - * More complex usage example: - *

    + *

    More complex usage example: * *

    -  public static HttpRequestFactory createRequestFactory2(HttpTransport transport) {
    -    final HttpResponseInterceptor responseInterceptor = new TimerResponseInterceptor();
    -    return transport.createRequestFactory(new HttpRequestInitializer() {
    -
    -      public void initialize(HttpRequest request) {
    -        request.setResponseInterceptor(new HttpResponseInterceptor() {
    -
    -          public void interceptResponse(HttpResponse response) throws IOException {
    -            responseInterceptor.interceptResponse(response);
    -          }
    -        });
    -      }
    -    });
    -  }
    + * public static HttpRequestFactory createRequestFactory2(HttpTransport transport) {
    + * final HttpResponseInterceptor responseInterceptor = new TimerResponseInterceptor();
    + * return transport.createRequestFactory(new HttpRequestInitializer() {
    + *
    + * public void initialize(HttpRequest request) {
    + * request.setResponseInterceptor(new HttpResponseInterceptor() {
    + *
    + * public void interceptResponse(HttpResponse response) throws IOException {
    + * responseInterceptor.interceptResponse(response);
    + * }
    + * });
    + * }
    + * });
    + * }
      * 
    * - *

    - * Implementations should normally be thread-safe. - *

    + *

    Implementations should normally be thread-safe. * * @author Yaniv Inbar * @since 1.13 @@ -86,12 +78,10 @@ public interface HttpResponseInterceptor { * Invoked at the end of {@link HttpRequest#execute()} before returning a successful response or * throwing an exception for an unsuccessful response. * - *

    - * Do not read from the content stream unless you intend to throw an exception. Otherwise, it + *

    Do not read from the content stream unless you intend to throw an exception. Otherwise, it * would prevent the caller of {@link HttpRequest#execute()} to be able to read the stream from * {@link HttpResponse#getContent()}. If you intend to throw an exception, you should parse the * response, or alternatively pass the response as part of the exception. - *

    * * @param response HTTP response */ diff --git a/google-http-client/src/main/java/com/google/api/client/http/HttpStatusCodes.java b/google-http-client/src/main/java/com/google/api/client/http/HttpStatusCodes.java index 0dd63ca2b..4f7e18be3 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/HttpStatusCodes.java +++ b/google-http-client/src/main/java/com/google/api/client/http/HttpStatusCodes.java @@ -26,16 +26,10 @@ public class HttpStatusCodes { /** Status code for a successful request. */ public static final int STATUS_CODE_OK = 200; - /** - * Status code for a successful request that has been fulfilled - * to create a new resource. - */ + /** Status code for a successful request that has been fulfilled to create a new resource. */ public static final int STATUS_CODE_CREATED = 201; - /** - * Status code for a successful request that has been received - * but not yet acted upon. - */ + /** Status code for a successful request that has been received but not yet acted upon. */ public static final int STATUS_CODE_ACCEPTED = 202; /** @@ -63,6 +57,9 @@ public class HttpStatusCodes { /** Status code for a resource that has temporarily moved to a new URI. */ public static final int STATUS_CODE_TEMPORARY_REDIRECT = 307; + /** Status code for a resource that has permanently moved to a new URI. */ + private static final int STATUS_CODE_PERMANENT_REDIRECT = 308; + /** Status code for a request that could not be understood by the server. */ public static final int STATUS_CODE_BAD_REQUEST = 400; @@ -75,15 +72,15 @@ public class HttpStatusCodes { /** Status code for a server that has not found anything matching the Request-URI. */ public static final int STATUS_CODE_NOT_FOUND = 404; - /** - * Status code for a method specified in the Request-Line is not allowed for the resource - * identified by the Request-URI. - * */ + /** + * Status code for a method specified in the Request-Line is not allowed for the resource + * identified by the Request-URI. + */ public static final int STATUS_CODE_METHOD_NOT_ALLOWED = 405; - + /** Status code for a request that could not be completed due to a resource conflict. */ public static final int STATUS_CODE_CONFLICT = 409; - + /** Status code for a request for which one of the conditions it was made under has failed. */ public static final int STATUS_CODE_PRECONDITION_FAILED = 412; @@ -114,8 +111,8 @@ public static boolean isSuccess(int statusCode) { } /** - * Returns whether the given HTTP response status code is a redirect code - * {@code 301, 302, 303, 307}. + * Returns whether the given HTTP response status code is a redirect code {@code 301, 302, 303, + * 307, 308}. * * @since 1.11 */ @@ -125,6 +122,7 @@ public static boolean isRedirect(int statusCode) { case HttpStatusCodes.STATUS_CODE_FOUND: // 302 case HttpStatusCodes.STATUS_CODE_SEE_OTHER: // 303 case HttpStatusCodes.STATUS_CODE_TEMPORARY_REDIRECT: // 307 + case HttpStatusCodes.STATUS_CODE_PERMANENT_REDIRECT: // 308 return true; default: return false; diff --git a/google-http-client/src/main/java/com/google/api/client/http/HttpTransport.java b/google-http-client/src/main/java/com/google/api/client/http/HttpTransport.java index 5fa036f7b..9a2d04220 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/HttpTransport.java +++ b/google-http-client/src/main/java/com/google/api/client/http/HttpTransport.java @@ -21,59 +21,49 @@ /** * Thread-safe abstract HTTP transport. * - *

    - * Implementation is thread-safe, and sub-classes must be thread-safe. For maximum efficiency, + *

    Implementation is thread-safe, and subclasses must be thread-safe. For maximum efficiency, * applications should use a single globally-shared instance of the HTTP transport. - *

    * - *

    - * The recommended concrete implementation HTTP transport library to use depends on what environment - * you are running in: - *

    - *
      - *
    • Google App Engine: use - * {@code com.google.api.client.extensions.appengine.http.UrlFetchTransport}. - *
        - *
      • {@code com.google.api.client.apache.ApacheHttpTransport} doesn't work on App Engine because - * the Apache HTTP Client opens its own sockets (though in theory there are ways to hack it to work - * on App Engine that might work).
      • - *
      • {@code com.google.api.client.javanet.NetHttpTransport} is discouraged due to a bug in the App - * Engine SDK itself in how it parses HTTP headers in the response.
      • - *
      - *
    • - *
    • Android: - *
        - *
      • For maximum backwards compatibility with older SDK's use {@code newCompatibleTransport} from - * {@code com.google.api.client.extensions.android.http.AndroidHttp} (read its JavaDoc for details). - *
      • - *
      • If your application is targeting Gingerbread (SDK 2.3) or higher, simply use - * {@code com.google.api.client.javanet.NetHttpTransport}.
      • - *
      - *
    • - *
    • Other Java environments + *

      The recommended concrete implementation HTTP transport library to use depends on what + * environment you are running in: + * *

        - *
      • {@code com.google.api.client.googleapis.javanet.GoogleNetHttpTransport} is included in - * google-api-cient 1.22.0, so easy to include.
      • - *
      • {@code com.google.api.client.javanet.NetHttpTransport} is based on the HttpURLConnection - * built into the Java SDK, so it used to be the preferred choice.
      • - *
      • {@code com.google.api.client.apache.ApacheHttpTransport} is a good choice for users of the - * Apache HTTP Client, especially if you need some of the configuration options available in that - * library.
      • - *
      - *
    • + *
    • Google App Engine: use {@code + * com.google.api.client.extensions.appengine.http.UrlFetchTransport}. + *
        + *
      • {@code com.google.api.client.apache.ApacheHttpTransport} doesn't work on App Engine + * because the Apache HTTP Client opens its own sockets (though in theory there are ways + * to hack it to work on App Engine that might work). + *
      • {@code com.google.api.client.javanet.NetHttpTransport} is discouraged due to a bug in + * the App Engine SDK itself in how it parses HTTP headers in the response. + *
      + *
    • Android: + *
        + *
      • For maximum backwards compatibility with older SDK's use {@code + * newCompatibleTransport} from {@code + * com.google.api.client.extensions.android.http.AndroidHttp} (read its JavaDoc for + * details). + *
      • If your application is targeting Gingerbread (SDK 2.3) or higher, simply use {@code + * com.google.api.client.javanet.NetHttpTransport}. + *
      + *
    • Other Java environments + *
        + *
      • {@code com.google.api.client.googleapis.javanet.GoogleNetHttpTransport} is included + * in google-api-cient 1.22.0, so easy to include. + *
      • {@code com.google.api.client.javanet.NetHttpTransport} is based on the + * HttpURLConnection built into the Java SDK, so it used to be the preferred choice. + *
      • {@code com.google.api.client.apache.ApacheHttpTransport} is a good choice for users + * of the Apache HTTP Client, especially if you need some of the configuration options + * available in that library. + *
      *
    * - *

    - * Some HTTP transports do not support all HTTP methods. Use {@link #supportsMethod(String)} to + *

    Some HTTP transports do not support all HTTP methods. Use {@link #supportsMethod(String)} to * check if a certain HTTP method is supported. Calling {@link #buildRequest()} on a method that is * not supported will result in an {@link IllegalArgumentException}. - *

    * - *

    - * Subclasses should override {@link #supportsMethod(String)} and - * {@link #buildRequest(String, String)} to build requests and specify which HTTP methods are - * supported. - *

    + *

    Subclasses should override {@link #supportsMethod(String)} and {@link #buildRequest(String, + * String)} to build requests and specify which HTTP methods are supported. * * @since 1.0 * @author Yaniv Inbar @@ -86,8 +76,10 @@ public abstract class HttpTransport { * All valid request methods as specified in {@link #supportsMethod(String)}, sorted in ascending * alphabetical order. */ - private static final String[] SUPPORTED_METHODS = - {HttpMethods.DELETE, HttpMethods.GET, HttpMethods.POST, HttpMethods.PUT}; + private static final String[] SUPPORTED_METHODS = { + HttpMethods.DELETE, HttpMethods.GET, HttpMethods.POST, HttpMethods.PUT + }; + static { Arrays.sort(SUPPORTED_METHODS); } @@ -126,10 +118,8 @@ HttpRequest buildRequest() { /** * Returns whether a specified HTTP method is supported by this transport. * - *

    - * Default implementation returns true if and only if the request method is {@code "DELETE"}, + *

    Default implementation returns true if and only if the request method is {@code "DELETE"}, * {@code "GET"}, {@code "POST"}, or {@code "PUT"}. Subclasses should override. - *

    * * @param method HTTP method * @throws IOException I/O exception @@ -139,6 +129,16 @@ public boolean supportsMethod(String method) throws IOException { return Arrays.binarySearch(SUPPORTED_METHODS, method) >= 0; } + /** + * Returns whether the transport is mTLS. + * + * @return boolean indicating if the transport is mTLS. + * @since 1.38 + */ + public boolean isMtls() { + return false; + } + /** * Builds a low level HTTP request for the given HTTP method. * @@ -157,6 +157,15 @@ public boolean supportsMethod(String method) throws IOException { * @throws IOException I/O exception * @since 1.4 */ - public void shutdown() throws IOException { + public void shutdown() throws IOException {} + + /** + * Returns whether the transport is shutdown or not. + * + * @return true if the transport is shutdown. + * @since 1.44.0 + */ + public boolean isShutdown() { + return true; } } diff --git a/google-http-client/src/main/java/com/google/api/client/http/HttpUnsuccessfulResponseHandler.java b/google-http-client/src/main/java/com/google/api/client/http/HttpUnsuccessfulResponseHandler.java index ec0eb4b1b..0a6fc6f7c 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/HttpUnsuccessfulResponseHandler.java +++ b/google-http-client/src/main/java/com/google/api/client/http/HttpUnsuccessfulResponseHandler.java @@ -19,56 +19,50 @@ /** * Interface which handles abnormal HTTP responses (in other words not 2XX). * - *

    - * For example, this might be used to refresh an OAuth 2 token: - *

    + *

    For example, this might be used to refresh an OAuth 2 token: * *

    -  public static class RefreshTokenHandler implements HttpUnsuccessfulResponseHandler {
    -    public boolean handleResponse(
    -        HttpRequest request, HttpResponse response, boolean retrySupported) throws IOException {
    -      if (response.getStatusCode() == HttpStatusCodes.STATUS_CODE_UNAUTHORIZED) {
    -        refreshToken();
    -      }
    -      return false;
    -    }
    -  }
    + * public static class RefreshTokenHandler implements HttpUnsuccessfulResponseHandler {
    + * public boolean handleResponse(
    + * HttpRequest request, HttpResponse response, boolean retrySupported) throws IOException {
    + * if (response.getStatusCode() == HttpStatusCodes.STATUS_CODE_UNAUTHORIZED) {
    + * refreshToken();
    + * }
    + * return false;
    + * }
    + * }
      * 
    * - *

    - * Sample usage with a request factory: - *

    + *

    Sample usage with a request factory: * *

    -  public static HttpRequestFactory createRequestFactory(HttpTransport transport) {
    -    final RefreshTokenHandler handler = new RefreshTokenHandler();
    -    return transport.createRequestFactory(new HttpRequestInitializer() {
    -      public void initialize(HttpRequest request) {
    -        request.setUnsuccessfulResponseHandler(handler);
    -      }
    -    });
    -  }
    + * public static HttpRequestFactory createRequestFactory(HttpTransport transport) {
    + * final RefreshTokenHandler handler = new RefreshTokenHandler();
    + * return transport.createRequestFactory(new HttpRequestInitializer() {
    + * public void initialize(HttpRequest request) {
    + * request.setUnsuccessfulResponseHandler(handler);
    + * }
    + * });
    + * }
      * 
    * - *

    - * More complex usage example: - *

    + *

    More complex usage example: * *

    -  public static HttpRequestFactory createRequestFactory2(HttpTransport transport) {
    -    final RefreshTokenHandler handler = new RefreshTokenHandler();
    -    return transport.createRequestFactory(new HttpRequestInitializer() {
    -      public void initialize(HttpRequest request) {
    -        request.setUnsuccessfulResponseHandler(new HttpUnsuccessfulResponseHandler() {
    -          public boolean handleResponse(
    -              HttpRequest request, HttpResponse response, boolean retrySupported)
    -              throws IOException {
    -            return handler.handleResponse(request, response, retrySupported);
    -          }
    -        });
    -      }
    -    });
    -  }
    + * public static HttpRequestFactory createRequestFactory2(HttpTransport transport) {
    + * final RefreshTokenHandler handler = new RefreshTokenHandler();
    + * return transport.createRequestFactory(new HttpRequestInitializer() {
    + * public void initialize(HttpRequest request) {
    + * request.setUnsuccessfulResponseHandler(new HttpUnsuccessfulResponseHandler() {
    + * public boolean handleResponse(
    + * HttpRequest request, HttpResponse response, boolean retrySupported)
    + * throws IOException {
    + * return handler.handleResponse(request, response, retrySupported);
    + * }
    + * });
    + * }
    + * });
    + * }
      * 
    * * @author moshenko@google.com (Jacob Moshenko) @@ -79,20 +73,21 @@ public interface HttpUnsuccessfulResponseHandler { /** * Handler that will be invoked when an abnormal response is received. There are a few simple * rules that one must follow: + * *
      - *
    • If you modify the request object or modify its execute interceptors in a way that should - * resolve the error, you must return true to issue a retry.
    • - *
    • Do not read from the content stream, this will prevent the eventual end user from having - * access to it.
    • + *
    • If you modify the request object or modify its execute interceptors in a way that should + * resolve the error, you must return true to issue a retry. + *
    • Do not read from the content stream, this will prevent the eventual end user from having + * access to it. *
    * * @param request Request object that can be read from for context or modified before retry * @param response Response to process * @param supportsRetry Whether there will actually be a retry if this handler return {@code - * true}. Some handlers may want to have an effect only when there will actually be a retry - * after they handle their event (e.g. a handler that implements exponential backoff). + * true}. Some handlers may want to have an effect only when there will actually be a retry + * after they handle their event (e.g. a handler that implements exponential backoff). * @return Whether or not this handler has made a change that will require the request to be - * re-sent. + * re-sent. */ boolean handleResponse(HttpRequest request, HttpResponse response, boolean supportsRetry) throws IOException; diff --git a/google-http-client/src/main/java/com/google/api/client/http/InputStreamContent.java b/google-http-client/src/main/java/com/google/api/client/http/InputStreamContent.java index e026e3349..14ee7d259 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/InputStreamContent.java +++ b/google-http-client/src/main/java/com/google/api/client/http/InputStreamContent.java @@ -15,7 +15,6 @@ package com.google.api.client.http; import com.google.api.client.util.Preconditions; - import java.io.InputStream; import java.io.OutputStream; @@ -25,24 +24,19 @@ * be re-opened and retried. If you have a stream that it is possible to recreate please create a * new subclass of {@link AbstractInputStreamContent}. * - *

    - * The input stream is guaranteed to be closed at the end of {@link #writeTo(OutputStream)}. - *

    + *

    The input stream is guaranteed to be closed at the end of {@link #writeTo(OutputStream)}. * - *

    - * Sample use with a URL: + *

    Sample use with a URL: * *

      * 
    -  private static void setRequestJpegContent(HttpRequest request, URL jpegUrl) throws IOException {
    -    request.setContent(new InputStreamContent("image/jpeg", jpegUrl.openStream()));
    -  }
    + * private static void setRequestJpegContent(HttpRequest request, URL jpegUrl) throws IOException {
    + * request.setContent(new InputStreamContent("image/jpeg", jpegUrl.openStream()));
    + * }
      * 
      * 
    * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @since 1.0 * @author Yaniv Inbar @@ -79,10 +73,8 @@ public boolean retrySupported() { /** * Sets whether or not retry is supported. Defaults to {@code false}. * - *

    - * Should be set to {@code true} if {@link #getInputStream} is called to reset to the original + *

    Should be set to {@code true} if {@link #getInputStream} is called to reset to the original * position of the input stream. - *

    * * @since 1.7 */ @@ -109,9 +101,7 @@ public InputStreamContent setCloseInputStream(boolean closeInputStream) { /** * Sets the content length or less than zero if not known. * - *

    - * Defaults to {@code -1}. - *

    + *

    Defaults to {@code -1}. * * @since 1.5 */ diff --git a/google-http-client/src/main/java/com/google/api/client/http/LowLevelHttpRequest.java b/google-http-client/src/main/java/com/google/api/client/http/LowLevelHttpRequest.java index a3f489efc..b13e2a040 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/LowLevelHttpRequest.java +++ b/google-http-client/src/main/java/com/google/api/client/http/LowLevelHttpRequest.java @@ -15,21 +15,16 @@ package com.google.api.client.http; import com.google.api.client.util.StreamingContent; - import java.io.IOException; /** * Low-level HTTP request. * - *

    - * This allows providing a different implementation of the HTTP request that is more compatible with - * the Java environment used. - *

    + *

    This allows providing a different implementation of the HTTP request that is more compatible + * with the Java environment used. * - *

    - * Implementation has no fields and therefore thread-safe, but sub-classes are not necessarily + *

    Implementation has no fields and therefore thread-safe, but sub-classes are not necessarily * thread-safe. - *

    * * @since 1.0 * @author Yaniv Inbar @@ -51,10 +46,8 @@ public abstract class LowLevelHttpRequest { /** * Adds a header to the HTTP request. * - *

    - * Note that multiple headers of the same name need to be supported, in which case - * {@link #addHeader} will be called for each instance of the header. - *

    + *

    Note that multiple headers of the same name need to be supported, in which case {@link + * #addHeader} will be called for each instance of the header. * * @param name header name * @param value header value @@ -64,9 +57,7 @@ public abstract class LowLevelHttpRequest { /** * Sets the content length or less than zero if not known. * - *

    - * Default value is {@code -1}. - *

    + *

    Default value is {@code -1}. * * @throws IOException I/O exception * @since 1.14 @@ -128,8 +119,7 @@ public final String getContentType() { * @throws IOException I/O exception * @since 1.14 */ - public final void setStreamingContent(StreamingContent streamingContent) - throws IOException { + public final void setStreamingContent(StreamingContent streamingContent) throws IOException { this.streamingContent = streamingContent; } @@ -145,19 +135,28 @@ public final StreamingContent getStreamingContent() { /** * Sets the connection and read timeouts. * - *

    - * Default implementation does nothing, but subclasses should normally override. - *

    + *

    Default implementation does nothing, but subclasses should normally override. * * @param connectTimeout timeout in milliseconds to establish a connection or {@code 0} for an - * infinite timeout + * infinite timeout * @param readTimeout Timeout in milliseconds to read data from an established connection or - * {@code 0} for an infinite timeout + * {@code 0} for an infinite timeout * @throws IOException I/O exception * @since 1.4 */ - public void setTimeout(int connectTimeout, int readTimeout) throws IOException { - } + public void setTimeout(int connectTimeout, int readTimeout) throws IOException {} + + /** + * Sets the write timeout for POST/PUT requests. + * + *

    Default implementation does nothing, but subclasses should normally override. + * + * @param writeTimeout timeout in milliseconds to establish a connection or {@code 0} for an + * infinite timeout + * @throws IOException I/O exception + * @since 1.27 + */ + public void setWriteTimeout(int writeTimeout) throws IOException {} /** Executes the request and returns a low-level HTTP response object. */ public abstract LowLevelHttpResponse execute() throws IOException; diff --git a/google-http-client/src/main/java/com/google/api/client/http/LowLevelHttpResponse.java b/google-http-client/src/main/java/com/google/api/client/http/LowLevelHttpResponse.java index 94e444ebd..c4e86ffec 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/LowLevelHttpResponse.java +++ b/google-http-client/src/main/java/com/google/api/client/http/LowLevelHttpResponse.java @@ -20,15 +20,11 @@ /** * Low-level HTTP response. * - *

    - * This allows providing a different implementation of the HTTP response that is more compatible + *

    This allows providing a different implementation of the HTTP response that is more compatible * with the Java environment used. - *

    * - *

    - * Implementation has no fields and therefore thread-safe, but sub-classes are not necessarily + *

    Implementation has no fields and therefore thread-safe, but sub-classes are not necessarily * thread-safe. - *

    * * @since 1.0 * @author Yaniv Inbar @@ -63,10 +59,8 @@ public abstract class LowLevelHttpResponse { /** * Returns the number of HTTP response headers. * - *

    - * Note that multiple headers of the same name need to be supported, in which case each header + *

    Note that multiple headers of the same name need to be supported, in which case each header * value is treated as a separate header. - *

    */ public abstract int getHeaderCount() throws IOException; @@ -83,6 +77,5 @@ public abstract class LowLevelHttpResponse { * @throws IOException I/O exception * @since 1.4 */ - public void disconnect() throws IOException { - } + public void disconnect() throws IOException {} } diff --git a/google-http-client/src/main/java/com/google/api/client/http/MultipartContent.java b/google-http-client/src/main/java/com/google/api/client/http/MultipartContent.java index 7108ebeee..35a59cf6f 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/MultipartContent.java +++ b/google-http-client/src/main/java/com/google/api/client/http/MultipartContent.java @@ -16,7 +16,6 @@ import com.google.api.client.util.Preconditions; import com.google.api.client.util.StreamingContent; - import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; @@ -25,6 +24,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.UUID; /** * Serializes MIME multipart content as specified by RFC 2046: Multipurpose Internet * Mail Extensions: The Multipart/mixed (primary) subtype. * - *

    - * By default the media type is {@code "multipart/related; boundary=__END_OF_PART__"}, but this may - * be customized by calling {@link #setMediaType(HttpMediaType)}, {@link #getMediaType()}, or - * {@link #setBoundary(String)}. - *

    + *

    By default the media type is {@code "multipart/related; boundary=__END_OF_PART____"}, but this may be customized by calling {@link #setMediaType(HttpMediaType)}, {@link + * #getMediaType()}, or {@link #setBoundary(String)}. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @since 1.14 * @author Yaniv Inbar @@ -52,10 +48,14 @@ public class MultipartContent extends AbstractHttpContent { private static final String TWO_DASHES = "--"; /** Parts of the HTTP multipart request. */ - private ArrayList parts = new ArrayList(); + private ArrayList parts = new ArrayList<>(); public MultipartContent() { - super(new HttpMediaType("multipart/related").setParameter("boundary", "__END_OF_PART__")); + this("__END_OF_PART__" + UUID.randomUUID().toString() + "__"); + } + + public MultipartContent(String boundary) { + super(new HttpMediaType("multipart/related").setParameter("boundary", boundary)); } public void writeTo(OutputStream out) throws IOException { @@ -66,7 +66,8 @@ public void writeTo(OutputStream out) throws IOException { if (part.headers != null) { headers.fromHttpHeaders(part.headers); } - headers.setContentEncoding(null) + headers + .setContentEncoding(null) .setUserAgent(null) .setContentType(null) .setContentLength(null) @@ -141,10 +142,8 @@ public final Collection getParts() { /** * Adds an HTTP multipart part. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public MultipartContent addPart(Part part) { parts.add(Preconditions.checkNotNull(part)); @@ -154,13 +153,11 @@ public MultipartContent addPart(Part part) { /** * Sets the parts of the HTTP multipart request. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public MultipartContent setParts(Collection parts) { - this.parts = new ArrayList(parts); + this.parts = new ArrayList<>(parts); return this; } @@ -168,13 +165,11 @@ public MultipartContent setParts(Collection parts) { * Sets the HTTP content parts of the HTTP multipart request, where each part is assumed to have * no HTTP headers and no encoding. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public MultipartContent setContentParts(Collection contentParts) { - this.parts = new ArrayList(contentParts.size()); + this.parts = new ArrayList<>(contentParts.size()); for (HttpContent contentPart : contentParts) { addPart(new Part(contentPart)); } @@ -189,14 +184,10 @@ public final String getBoundary() { /** * Sets the boundary string to use. * - *

    - * Defaults to {@code "END_OF_PART"}. - *

    + *

    Defaults to {@code "END_OF_PART"}. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public MultipartContent setBoundary(String boundary) { getMediaType().setParameter("boundary", Preconditions.checkNotNull(boundary)); @@ -206,9 +197,7 @@ public MultipartContent setBoundary(String boundary) { /** * Single part of a multi-part request. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. */ public static final class Part { @@ -225,9 +214,7 @@ public Part() { this(null); } - /** - * @param content HTTP content or {@code null} for none - */ + /** @param content HTTP content or {@code null} for none */ public Part(HttpContent content) { this(null, content); } diff --git a/google-http-client/src/main/java/com/google/api/client/http/OpenCensusUtils.java b/google-http-client/src/main/java/com/google/api/client/http/OpenCensusUtils.java new file mode 100644 index 000000000..5b2c9d41e --- /dev/null +++ b/google-http-client/src/main/java/com/google/api/client/http/OpenCensusUtils.java @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2018 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.http; + +import com.google.api.client.util.Beta; +import com.google.api.client.util.Preconditions; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableList; +import io.opencensus.contrib.http.util.HttpPropagationUtil; +import io.opencensus.trace.BlankSpan; +import io.opencensus.trace.EndSpanOptions; +import io.opencensus.trace.MessageEvent; +import io.opencensus.trace.MessageEvent.Type; +import io.opencensus.trace.Span; +import io.opencensus.trace.Status; +import io.opencensus.trace.Tracer; +import io.opencensus.trace.Tracing; +import io.opencensus.trace.propagation.TextFormat; +import java.util.concurrent.atomic.AtomicLong; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.annotation.Nullable; + +/** + * {@link Beta}
    + * Utilities for Census monitoring and tracing. + * + * @author Hailong Wen + * @since 1.28 + */ +@Beta +public class OpenCensusUtils { + + private static final Logger logger = Logger.getLogger(OpenCensusUtils.class.getName()); + + /** Span name for tracing {@link HttpRequest#execute()}. */ + public static final String SPAN_NAME_HTTP_REQUEST_EXECUTE = + "Sent." + HttpRequest.class.getName() + ".execute"; + + /** + * OpenCensus tracing component. When no OpenCensus implementation is provided, it will return a + * no-op tracer. + */ + private static final Tracer tracer = Tracing.getTracer(); + + /** Sequence id generator for message event. */ + private static final AtomicLong idGenerator = new AtomicLong(); + + /** Whether spans should be recorded locally. Defaults to true. */ + private static volatile boolean isRecordEvent = true; + + /** {@link TextFormat} used in tracing context propagation. */ + @Nullable @VisibleForTesting static volatile TextFormat propagationTextFormat = null; + + /** {@link TextFormat.Setter} for {@link #propagationTextFormat}. */ + @Nullable @VisibleForTesting static volatile TextFormat.Setter propagationTextFormatSetter = null; + + /** + * Sets the {@link TextFormat} used in context propagation. + * + *

    This API allows users of google-http-client to specify other text format, or disable context + * propagation by setting it to {@code null}. It should be used along with {@link + * #setPropagationTextFormatSetter} for setting purpose. + * + * @param textFormat the text format. + */ + public static void setPropagationTextFormat(@Nullable TextFormat textFormat) { + propagationTextFormat = textFormat; + } + + /** + * Sets the {@link io.opencensus.trace.propagation.TextFormat.Setter} used in context propagation. + * + *

    This API allows users of google-http-client to specify other text format setter, or disable + * context propagation by setting it to {@code null}. It should be used along with {@link + * #setPropagationTextFormat} for setting purpose. + * + * @param textFormatSetter the {@code TextFormat.Setter} for the text format. + */ + public static void setPropagationTextFormatSetter(@Nullable TextFormat.Setter textFormatSetter) { + propagationTextFormatSetter = textFormatSetter; + } + + /** + * Sets whether spans should be recorded locally. + * + *

    This API allows users of google-http-client to turn on/off local span collection. + * + * @param recordEvent record span locally if true. + */ + public static void setIsRecordEvent(boolean recordEvent) { + isRecordEvent = recordEvent; + } + + /** + * Returns the tracing component of OpenCensus. + * + * @return the tracing component of OpenCensus. + */ + public static Tracer getTracer() { + return tracer; + } + + /** + * Returns whether spans should be recorded locally. + * + * @return whether spans should be recorded locally. + */ + public static boolean isRecordEvent() { + return isRecordEvent; + } + + /** + * Propagate information of current tracing context. This information will be injected into HTTP + * header. + * + * @param span the span to be propagated. + * @param headers the headers used in propagation. + */ + public static void propagateTracingContext(Span span, HttpHeaders headers) { + Preconditions.checkArgument(span != null, "span should not be null."); + Preconditions.checkArgument(headers != null, "headers should not be null."); + if (propagationTextFormat != null && propagationTextFormatSetter != null) { + if (!span.equals(BlankSpan.INSTANCE)) { + propagationTextFormat.inject(span.getContext(), headers, propagationTextFormatSetter); + } + } + } + + /** + * Returns an {@link EndSpanOptions} to end a http span according to the status code. + * + * @param statusCode the status code, can be null to represent no valid response is returned. + * @return an {@code EndSpanOptions} that best suits the status code. + */ + public static EndSpanOptions getEndSpanOptions(@Nullable Integer statusCode) { + // Always sample the span, but optionally export it. + EndSpanOptions.Builder builder = EndSpanOptions.builder(); + if (statusCode == null) { + builder.setStatus(Status.UNKNOWN); + } else if (!HttpStatusCodes.isSuccess(statusCode)) { + switch (statusCode) { + case HttpStatusCodes.STATUS_CODE_BAD_REQUEST: + builder.setStatus(Status.INVALID_ARGUMENT); + break; + case HttpStatusCodes.STATUS_CODE_UNAUTHORIZED: + builder.setStatus(Status.UNAUTHENTICATED); + break; + case HttpStatusCodes.STATUS_CODE_FORBIDDEN: + builder.setStatus(Status.PERMISSION_DENIED); + break; + case HttpStatusCodes.STATUS_CODE_NOT_FOUND: + builder.setStatus(Status.NOT_FOUND); + break; + case HttpStatusCodes.STATUS_CODE_PRECONDITION_FAILED: + builder.setStatus(Status.FAILED_PRECONDITION); + break; + case HttpStatusCodes.STATUS_CODE_SERVER_ERROR: + builder.setStatus(Status.UNAVAILABLE); + break; + default: + builder.setStatus(Status.UNKNOWN); + } + } else { + builder.setStatus(Status.OK); + } + return builder.build(); + } + + /** + * Records a new message event which contains the size of the request content. Note that the size + * represents the message size in application layer, i.e., content-length. + * + * @param span The {@code span} in which the send event occurs. + * @param size Size of the request. + */ + public static void recordSentMessageEvent(Span span, long size) { + recordMessageEvent(span, size, Type.SENT); + } + + /** + * Records a new message event which contains the size of the response content. Note that the size + * represents the message size in application layer, i.e., content-length. + * + * @param span The {@code span} in which the receive event occurs. + * @param size Size of the response. + */ + public static void recordReceivedMessageEvent(Span span, long size) { + recordMessageEvent(span, size, Type.RECEIVED); + } + + /** + * Records a message event of a certain {@link MessageEvent.Type}. This method is package + * protected since {@link MessageEvent} might be deprecated in future releases. + * + * @param span The {@code span} in which the event occurs. + * @param size Size of the message. + * @param eventType The {@code NetworkEvent.Type} of the message event. + */ + @VisibleForTesting + static void recordMessageEvent(Span span, long size, Type eventType) { + Preconditions.checkArgument(span != null, "span should not be null."); + if (size < 0) { + size = 0; + } + MessageEvent event = + MessageEvent.builder(eventType, idGenerator.getAndIncrement()) + .setUncompressedMessageSize(size) + .build(); + span.addMessageEvent(event); + } + + static { + try { + propagationTextFormat = HttpPropagationUtil.getCloudTraceFormat(); + propagationTextFormatSetter = + new TextFormat.Setter() { + @Override + public void put(HttpHeaders carrier, String key, String value) { + carrier.set(key, value); + } + }; + } catch (Exception e) { + logger.log( + Level.WARNING, "Cannot initialize default OpenCensus HTTP propagation text format.", e); + } + + try { + Tracing.getExportComponent() + .getSampledSpanStore() + .registerSpanNamesForCollection(ImmutableList.of(SPAN_NAME_HTTP_REQUEST_EXECUTE)); + } catch (Exception e) { + logger.log(Level.WARNING, "Cannot register default OpenCensus span names for collection.", e); + } + } + + private OpenCensusUtils() {} +} diff --git a/google-http-client/src/main/java/com/google/api/client/http/UriTemplate.java b/google-http-client/src/main/java/com/google/api/client/http/UriTemplate.java index e5c4a40cb..d6095c4b4 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/UriTemplate.java +++ b/google-http-client/src/main/java/com/google/api/client/http/UriTemplate.java @@ -20,7 +20,6 @@ import com.google.api.client.util.Types; import com.google.api.client.util.escape.CharEscapers; import com.google.common.base.Splitter; - import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; @@ -30,56 +29,33 @@ /** * Expands URI Templates. * - * This Class supports Level 1 templates and all Level 4 composite templates as described in: - * RFC 6570. + *

    This class supports URI Template Level 1, partial support for Levels 2 and 3, and Level 4 + * composite templates as described in: RFC 6570. * - * Specifically, for the variables: - * var := "value" - * list := ["red", "green", "blue"] - * keys := [("semi", ";"),("dot", "."),("comma", ",")] + *

    Specifically, for the variables: var := "value" list := ["red", "green", "blue"] keys := + * [("semi", ";"),("dot", "."),("comma", ",")] * - * The following templates results in the following expansions: - * {var} -> value - * {list} -> red,green,blue - * {list*} -> red,green,blue - * {keys} -> semi,%3B,dot,.,comma,%2C - * {keys*} -> semi=%3B,dot=.,comma=%2C - * {+list} -> red,green,blue - * {+list*} -> red,green,blue - * {+keys} -> semi,;,dot,.,comma,, - * {+keys*} -> semi=;,dot=.,comma=, - * {#list} -> #red,green,blue - * {#list*} -> #red,green,blue - * {#keys} -> #semi,;,dot,.,comma,, - * {#keys*} -> #semi=;,dot=.,comma=, - * X{.list} -> X.red,green,blue - * X{.list*} -> X.red.green.blue - * X{.keys} -> X.semi,%3B,dot,.,comma,%2C - * X{.keys*} -> X.semi=%3B.dot=..comma=%2C - * {/list} -> /red,green,blue - * {/list*} -> /red/green/blue - * {/keys} -> /semi,%3B,dot,.,comma,%2C - * {/keys*} -> /semi=%3B/dot=./comma=%2C - * {;list} -> ;list=red,green,blue - * {;list*} -> ;list=red;list=green;list=blue - * {;keys} -> ;keys=semi,%3B,dot,.,comma,%2C - * {;keys*} -> ;semi=%3B;dot=.;comma=%2C - * {?list} -> ?list=red,green,blue - * {?list*} -> ?list=red&list=green&list=blue - * {?keys} -> ?keys=semi,%3B,dot,.,comma,%2C - * {?keys*} -> ?semi=%3B&dot=.&comma=%2C - * {&list} -> &list=red,green,blue - * {&list*} -> &list=red&list=green&list=blue - * {&keys} -> &keys=semi,%3B,dot,.,comma,%2C - * {&keys*} -> &semi=%3B&dot=.&comma=%2C - * {?var,list} -> ?var=value&list=red,green,blue + *

    The following templates results in the following expansions: {var} -> value {list} -> + * red,green,blue {list*} -> red,green,blue {keys} -> semi,%3B,dot,.,comma,%2C {keys*} -> + * semi=%3B,dot=.,comma=%2C {+list} -> red,green,blue {+list*} -> red,green,blue {+keys} -> + * semi,;,dot,.,comma,, {+keys*} -> semi=;,dot=.,comma=, {#list} -> #red,green,blue {#list*} -> + * #red,green,blue {#keys} -> #semi,;,dot,.,comma,, {#keys*} -> #semi=;,dot=.,comma=, X{.list} -> + * X.red,green,blue X{.list*} -> X.red.green.blue X{.keys} -> X.semi,%3B,dot,.,comma,%2C X{.keys*} + * -> X.semi=%3B.dot=..comma=%2C {/list} -> /red,green,blue {/list*} -> /red/green/blue {/keys} -> + * /semi,%3B,dot,.,comma,%2C {/keys*} -> /semi=%3B/dot=./comma=%2C {;list} -> ;list=red,green,blue + * {;list*} -> ;list=red;list=green;list=blue {;keys} -> ;keys=semi,%3B,dot,.,comma,%2C {;keys*} -> + * ;semi=%3B;dot=.;comma=%2C {?list} -> ?list=red,green,blue {?list*} -> + * ?list=red&list=green&list=blue {?keys} -> ?keys=semi,%3B,dot,.,comma,%2C {?keys*} -> + * ?semi=%3B&dot=.&comma=%2C {&list} -> &list=red,green,blue {&list*} -> + * &list=red&list=green&list=blue {&keys} -> &keys=semi,%3B,dot,.,comma,%2C {&keys*} -> + * &semi=%3B&dot=.&comma=%2C {?var,list} -> ?var=value&list=red,green,blue * * @since 1.6 * @author Ravi Mistry */ public class UriTemplate { - static final Map COMPOSITE_PREFIXES = + private static final Map COMPOSITE_PREFIXES = new HashMap(); static { @@ -88,9 +64,7 @@ public class UriTemplate { private static final String COMPOSITE_NON_EXPLODE_JOINER = ","; - /** - * Contains information on how to output a composite value. - */ + /** Contains information on how to output a composite value. */ private enum CompositeOutput { /** Reserved expansion. */ @@ -124,17 +98,21 @@ private enum CompositeOutput { private final boolean reservedExpansion; /** - * @param propertyPrefix The prefix of a parameter or {@code null} for none. In {+var} the - * prefix is '+' - * @param outputPrefix The string that should be prefixed to the expanded template. - * @param explodeJoiner The delimiter used to join composite values. - * @param requiresVarAssignment Denotes whether or not the expanded template should contain - * an assignment with the variable. - * @param reservedExpansion Reserved expansion allows pct-encoded triplets and characters in - * the reserved set. + * @param propertyPrefix the prefix of a parameter or {@code null} for none. In {+var} the + * prefix is '+' + * @param outputPrefix the string that should be prefixed to the expanded template. + * @param explodeJoiner the delimiter used to join composite values. + * @param requiresVarAssignment denotes whether or not the expanded template should contain an + * assignment with the variable + * @param reservedExpansion reserved expansion allows percent-encoded triplets and characters in + * the reserved set */ - CompositeOutput(Character propertyPrefix, String outputPrefix, String explodeJoiner, - boolean requiresVarAssignment, boolean reservedExpansion) { + CompositeOutput( + Character propertyPrefix, + String outputPrefix, + String explodeJoiner, + boolean requiresVarAssignment, + boolean reservedExpansion) { this.propertyPrefix = propertyPrefix; this.outputPrefix = Preconditions.checkNotNull(outputPrefix); this.explodeJoiner = Preconditions.checkNotNull(explodeJoiner); @@ -145,22 +123,18 @@ private enum CompositeOutput { } } - /** - * Returns the string that should be prefixed to the expanded template. - */ + /** Returns the string that should be prefixed to the expanded template. */ String getOutputPrefix() { return outputPrefix; } - /** - * Returns the delimiter used to join composite values. - */ + /** Returns the delimiter used to join composite values. */ String getExplodeJoiner() { return explodeJoiner; } /** - * Returns whether or not the expanded template should contain an assignment with the variable. + * Returns whether or not the expanded template should contain an assignment with the variable. */ boolean requiresVarAssignment() { return requiresVarAssignment; @@ -175,27 +149,22 @@ int getVarNameStartIndex() { } /** - * Encodes the specified value. If reserved expansion is turned on then - * pct-encoded triplets and characters are allowed in the reserved set. - * - * @param value The string to be encoded. + * Encodes the specified value. If reserved expansion is turned on, then percent-encoded + * triplets and characters are allowed in the reserved set. * - * @return The encoded string. + * @param value the string to be encoded + * @return the encoded string */ - String getEncodedValue(String value) { + private String getEncodedValue(String value) { String encodedValue; if (reservedExpansion) { - // Reserved expansion allows pct-encoded triplets and characters in the reserved set. - encodedValue = CharEscapers.escapeUriPath(value); + // Reserved expansion allows percent-encoded triplets and characters in the reserved set. + encodedValue = CharEscapers.escapeUriPathWithoutReservedAndPercentEncoded(value); } else { - encodedValue = CharEscapers.escapeUri(value); + encodedValue = CharEscapers.escapeUriConformant(value); } return encodedValue; } - - boolean getReservedExpansion() { - return reservedExpansion; - } } static CompositeOutput getCompositeOutput(String propertyName) { @@ -206,9 +175,7 @@ static CompositeOutput getCompositeOutput(String propertyName) { /** * Constructs a new {@code Map} from an {@code Object}. * - *

    - * There are no null values in the returned map. - *

    + *

    There are no null values in the returned map. */ private static Map getMap(Object obj) { // Using a LinkedHashMap to maintain the original order of insertions. This is done to help @@ -226,28 +193,24 @@ private static Map getMap(Object obj) { /** * Expands templates in a URI template that is relative to a base URL. * - *

    - * If the URI template starts with a "/" the raw path from the base URL is stripped out. If the + *

    If the URI template starts with a "/" the raw path from the base URL is stripped out. If the * URI template is a full URL then it is used instead of the base URL. - *

    * - *

    - * Supports Level 1 templates and all Level 4 composite templates as described in: - * RFC 6570. - *

    + *

    Supports Level 1 templates and all Level 4 composite templates as described in: RFC 6570. * * @param baseUrl The base URL which the URI component is relative to. * @param uriTemplate URI component. It may contain one or more sequences of the form "{name}", - * where "name" must be a key in variableMap. + * where "name" must be a key in variableMap. * @param parameters an object with parameters designated by Key annotations. If the template has - * no variable references, parameters may be {@code null}. + * no variable references, parameters may be {@code null}. * @param addUnusedParamsAsQueryParams If true then parameters that do not match the template are - * appended to the expanded template as query parameters. + * appended to the expanded template as query parameters. * @return The expanded template * @since 1.7 */ - public static String expand(String baseUrl, String uriTemplate, Object parameters, - boolean addUnusedParamsAsQueryParams) { + public static String expand( + String baseUrl, String uriTemplate, Object parameters, boolean addUnusedParamsAsQueryParams) { String pathUri; if (uriTemplate.startsWith("/")) { // Remove the base path from the base URL. @@ -265,22 +228,20 @@ public static String expand(String baseUrl, String uriTemplate, Object parameter /** * Expands templates in a URI. * - *

    - * Supports Level 1 templates and all Level 4 composite templates as described in: - * RFC 6570. - *

    + *

    Supports Level 1 templates and all Level 4 composite templates as described in: RFC 6570. * * @param pathUri URI component. It may contain one or more sequences of the form "{name}", where - * "name" must be a key in variableMap + * "name" must be a key in variableMap * @param parameters an object with parameters designated by Key annotations. If the template has - * no variable references, parameters may be {@code null}. + * no variable references, parameters may be {@code null}. * @param addUnusedParamsAsQueryParams If true then parameters that do not match the template are - * appended to the expanded template as query parameters. + * appended to the expanded template as query parameters. * @return The expanded template * @since 1.6 */ - public static String expand(String pathUri, Object parameters, - boolean addUnusedParamsAsQueryParams) { + public static String expand( + String pathUri, Object parameters, boolean addUnusedParamsAsQueryParams) { Map variableMap = getMap(parameters); StringBuilder pathBuf = new StringBuilder(); int cur = 0; @@ -308,8 +269,8 @@ public static String expand(String pathUri, Object parameters, String template = templateIterator.next(); boolean containsExplodeModifier = template.endsWith("*"); - int varNameStartIndex = templateIterator.nextIndex() == 1 - ? compositeOutput.getVarNameStartIndex() : 0; + int varNameStartIndex = + templateIterator.nextIndex() == 1 ? compositeOutput.getVarNameStartIndex() : 0; int varNameEndIndex = template.length(); if (containsExplodeModifier) { // The expression contains an explode modifier '*' at the end, update end index. @@ -339,52 +300,49 @@ public static String expand(String pathUri, Object parameters, value = getListPropertyValue(varName, iterator, containsExplodeModifier, compositeOutput); } else if (value.getClass().isEnum()) { String name = FieldInfo.of((Enum) value).getName(); - if (name != null) { - if (compositeOutput.requiresVarAssignment()) { - value = String.format("%s=%s", varName, value); - } - value = CharEscapers.escapeUriPath(value.toString()); - } + value = getSimpleValue(varName, name != null ? name : value.toString(), compositeOutput); } else if (!Data.isValueOfPrimitiveType(value)) { // Parse the value as a key/value map. Map map = getMap(value); value = getMapPropertyValue(varName, map, containsExplodeModifier, compositeOutput); } else { // For everything else... - if (compositeOutput.requiresVarAssignment()) { - value = String.format("%s=%s", varName, value); - } - if (compositeOutput.getReservedExpansion()) { - value = CharEscapers.escapeUriPathWithoutReserved(value.toString()); - } else { - value = CharEscapers.escapeUriPath(value.toString()); - } + value = getSimpleValue(varName, value.toString(), compositeOutput); } pathBuf.append(value); } } if (addUnusedParamsAsQueryParams) { // Add the parameters remaining in the variableMap as query parameters. - GenericUrl.addQueryParams(variableMap.entrySet(), pathBuf); + GenericUrl.addQueryParams(variableMap.entrySet(), pathBuf, false); } return pathBuf.toString(); } + private static String getSimpleValue(String name, String value, CompositeOutput compositeOutput) { + if (compositeOutput.requiresVarAssignment()) { + return String.format("%s=%s", name, compositeOutput.getEncodedValue(value)); + } + return compositeOutput.getEncodedValue(value); + } + /** - * Expand the template of a composite list property. - * Eg: If d := ["red", "green", "blue"] - * then {/d*} is expanded to "/red/green/blue" + * Expand the template of a composite list property. Eg: If d := ["red", "green", "blue"] then + * {/d*} is expanded to "/red/green/blue" * - * @param varName The name of the variable the value corresponds to. Eg: "d" - * @param iterator The iterator over list values. Eg: ["red", "green", "blue"] - * @param containsExplodeModifier Set to true if the template contains the explode modifier "*" - * @param compositeOutput An instance of CompositeOutput. Contains information on how the + * @param varName the name of the variable the value corresponds to. E.g. "d" + * @param iterator the iterator over list values. E.g. ["red", "green", "blue"] + * @param containsExplodeModifiersSet to true if the template contains the explode modifier "*" + * @param compositeOutput an instance of CompositeOutput. Contains information on how the * expansion should be done - * @return The expanded list template + * @return the expanded list template * @throws IllegalArgumentException if the required list path parameter is empty */ - private static String getListPropertyValue(String varName, Iterator iterator, - boolean containsExplodeModifier, CompositeOutput compositeOutput) { + private static String getListPropertyValue( + String varName, + Iterator iterator, + boolean containsExplodeModifier, + CompositeOutput compositeOutput) { if (!iterator.hasNext()) { return ""; } @@ -413,20 +371,21 @@ private static String getListPropertyValue(String varName, Iterator iterator, } /** - * Expand the template of a composite map property. - * Eg: If d := [("semi", ";"),("dot", "."),("comma", ",")] - * then {/d*} is expanded to "/semi=%3B/dot=./comma=%2C" + * Expand the template of a composite map property. Eg: If d := [("semi", ";"),("dot", + * "."),("comma", ",")] then {/d*} is expanded to "/semi=%3B/dot=./comma=%2C" * - * @param varName The name of the variable the value corresponds to. Eg: "d" - * @param map The map property value. Eg: [("semi", ";"),("dot", "."),("comma", ",")] + * @param varName the name of the variable the value corresponds to. Eg: "d" + * @param map the map property value. Eg: [("semi", ";"),("dot", "."),("comma", ",")] * @param containsExplodeModifier Set to true if the template contains the explode modifier "*" - * @param compositeOutput An instance of CompositeOutput. Contains information on how the - * expansion should be done - * @return The expanded map template + * @param compositeOutput contains information on how the expansion should be done + * @return the expanded map template * @throws IllegalArgumentException if the required list path parameter is map */ - private static String getMapPropertyValue(String varName, Map map, - boolean containsExplodeModifier, CompositeOutput compositeOutput) { + private static String getMapPropertyValue( + String varName, + Map map, + boolean containsExplodeModifier, + CompositeOutput compositeOutput) { if (map.isEmpty()) { return ""; } @@ -445,7 +404,7 @@ private static String getMapPropertyValue(String varName, Map ma } } for (Iterator> mapIterator = map.entrySet().iterator(); - mapIterator.hasNext();) { + mapIterator.hasNext(); ) { Map.Entry entry = mapIterator.next(); String encodedKey = compositeOutput.getEncodedValue(entry.getKey()); String encodedValue = compositeOutput.getEncodedValue(entry.getValue().toString()); diff --git a/google-http-client/src/main/java/com/google/api/client/http/UrlEncodedContent.java b/google-http-client/src/main/java/com/google/api/client/http/UrlEncodedContent.java index 79741670a..ea08b1e18 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/UrlEncodedContent.java +++ b/google-http-client/src/main/java/com/google/api/client/http/UrlEncodedContent.java @@ -19,7 +19,6 @@ import com.google.api.client.util.Preconditions; import com.google.api.client.util.Types; import com.google.api.client.util.escape.CharEscapers; - import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStream; @@ -29,40 +28,57 @@ import java.util.Map; /** - * Implements support for HTTP form content encoding serialization of type - * {@code application/x-www-form-urlencoded} as specified in the HTML 4.0 Specification. * - *

    - * Sample usage: - *

    + *

    Sample usage: * *

    -  static void setContent(HttpRequest request, Object item) {
    -    request.setContent(new UrlEncodedContent(item));
    -  }
    + * static void setContent(HttpRequest request, Object item) {
    + * request.setContent(new UrlEncodedContent(item));
    + * }
      * 
    * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * - * @since 1.0 * @author Yaniv Inbar + * @since 1.0 */ public class UrlEncodedContent extends AbstractHttpContent { /** Key name/value data. */ private Object data; + /** Use URI Path encoder flag. False by default (use legacy and deprecated escapeUri) */ + private boolean uriPathEncodingFlag; + /** + * Initialize the UrlEncodedContent with the legacy and deprecated escapeUri encoder + * * @param data key name/value data */ public UrlEncodedContent(Object data) { super(UrlEncodedParser.MEDIA_TYPE); setData(data); + this.uriPathEncodingFlag = false; + } + + /** + * Initialize the UrlEncodedContent with or without the legacy and deprecated escapeUri encoder + * + * @param data key name/value data + * @param useUriPathEncoding escapes the string value so it can be safely included in URI path + * segments. For details on escaping URIs, see RFC 3986 - section 2.4 + */ + public UrlEncodedContent(Object data, boolean useUriPathEncoding) { + super(UrlEncodedParser.MEDIA_TYPE); + setData(data); + this.uriPathEncodingFlag = useUriPathEncoding; } + @Override public void writeTo(OutputStream out) throws IOException { Writer writer = new BufferedWriter(new OutputStreamWriter(out, getCharset())); boolean first = true; @@ -73,10 +89,10 @@ public void writeTo(OutputStream out) throws IOException { Class valueClass = value.getClass(); if (value instanceof Iterable || valueClass.isArray()) { for (Object repeatedValue : Types.iterableOf(value)) { - first = appendParam(first, writer, name, repeatedValue); + first = appendParam(first, writer, name, repeatedValue, this.uriPathEncodingFlag); } } else { - first = appendParam(first, writer, name, value); + first = appendParam(first, writer, name, value, this.uriPathEncodingFlag); } } } @@ -101,10 +117,8 @@ public final Object getData() { /** * Sets the key name/value data. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.5 */ @@ -120,8 +134,8 @@ public UrlEncodedContent setData(Object data) { * * @param request HTTP request * @return URL-encoded content - * @throws ClassCastException if the HTTP request has a content defined that is not - * {@link UrlEncodedContent} + * @throws ClassCastException if the HTTP request has a content defined that is not {@link + * UrlEncodedContent} * @since 1.7 */ public static UrlEncodedContent getContent(HttpRequest request) { @@ -134,7 +148,8 @@ public static UrlEncodedContent getContent(HttpRequest request) { return result; } - private static boolean appendParam(boolean first, Writer writer, String name, Object value) + private static boolean appendParam( + boolean first, Writer writer, String name, Object value, boolean uriPathEncodingFlag) throws IOException { // ignore nulls if (value == null || Data.isNull(value)) { @@ -147,8 +162,14 @@ private static boolean appendParam(boolean first, Writer writer, String name, Ob writer.write("&"); } writer.write(name); - String stringValue = CharEscapers.escapeUri( - value instanceof Enum ? FieldInfo.of((Enum) value).getName() : value.toString()); + String stringValue = + value instanceof Enum ? FieldInfo.of((Enum) value).getName() : value.toString(); + + if (uriPathEncodingFlag) { + stringValue = CharEscapers.escapeUriPath(stringValue); + } else { + stringValue = CharEscapers.escapeUri(stringValue); + } if (stringValue.length() != 0) { writer.write("="); writer.write(stringValue); diff --git a/google-http-client/src/main/java/com/google/api/client/http/UrlEncodedParser.java b/google-http-client/src/main/java/com/google/api/client/http/UrlEncodedParser.java index 52209740b..2be6d9b24 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/UrlEncodedParser.java +++ b/google-http-client/src/main/java/com/google/api/client/http/UrlEncodedParser.java @@ -15,7 +15,6 @@ package com.google.api.client.http; import com.google.api.client.util.ArrayValueMap; -import com.google.api.client.util.Charsets; import com.google.api.client.util.ClassInfo; import com.google.api.client.util.Data; import com.google.api.client.util.FieldInfo; @@ -25,7 +24,6 @@ import com.google.api.client.util.Throwables; import com.google.api.client.util.Types; import com.google.api.client.util.escape.CharEscapers; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -35,6 +33,7 @@ import java.io.StringWriter; import java.lang.reflect.Type; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -42,27 +41,21 @@ import java.util.Map; /** - * Implements support for HTTP form content encoding parsing of type - * {@code application/x-www-form-urlencoded} as specified in the HTML 4.0 * Specification. * - *

    - * Implementation is thread-safe. - *

    + *

    Implementation is thread-safe. * - *

    - * The data is parsed using {@link #parse(String, Object)}. - *

    + *

    The data is parsed using {@link #parse(String, Object)}. * - *

    - * Sample usage: - *

    + *

    Sample usage: * *

    -   static void setParser(HttpTransport transport) {
    -     transport.addParser(new UrlEncodedParser());
    -   }
    + * static void setParser(HttpTransport transport) {
    + * transport.addParser(new UrlEncodedParser());
    + * }
      * 
    * * @since 1.0 @@ -79,7 +72,9 @@ public class UrlEncodedParser implements ObjectParser { * @since 1.13 */ public static final String MEDIA_TYPE = - new HttpMediaType(UrlEncodedParser.CONTENT_TYPE).setCharsetParameter(Charsets.UTF_8).build(); + new HttpMediaType(UrlEncodedParser.CONTENT_TYPE) + .setCharsetParameter(StandardCharsets.UTF_8) + .build(); /** * Parses the given URL-encoded content into the given data object of data key name/value pairs @@ -89,11 +84,23 @@ public class UrlEncodedParser implements ObjectParser { * @param data data key name/value pairs */ public static void parse(String content, Object data) { + parse(content, data, true); + } + + /** + * Parses the given URL-encoded content into the given data object of data key name/value pairs + * using {@link #parse(Reader, Object)}. + * + * @param content URL-encoded content or {@code null} to ignore content + * @param data data key name/value pairs + * @param decodeEnabled flag that specifies whether decoding should be enabled. + */ + public static void parse(String content, Object data, boolean decodeEnabled) { if (content == null) { return; } try { - parse(new StringReader(content), data); + parse(new StringReader(content), data, decodeEnabled); } catch (IOException exception) { // I/O exception not expected on a string throw Throwables.propagate(exception); @@ -104,27 +111,47 @@ public static void parse(String content, Object data) { * Parses the given URL-encoded content into the given data object of data key name/value pairs, * including support for repeating data key names. * - *

    - * Declared fields of a "primitive" type (as defined by {@link Data#isPrimitive(Type)} are parsed - * using {@link Data#parsePrimitiveValue(Type, String)} where the {@link Class} parameter is the - * declared field class. Declared fields of type {@link Collection} are used to support repeating - * data key names, so each member of the collection is an additional data key value. They are - * parsed the same as "primitive" fields, except that the generic type parameter of the collection - * is used as the {@link Class} parameter. - *

    + *

    Declared fields of a "primitive" type (as defined by {@link Data#isPrimitive(Type)} are + * parsed using {@link Data#parsePrimitiveValue(Type, String)} where the {@link Class} parameter + * is the declared field class. Declared fields of type {@link Collection} are used to support + * repeating data key names, so each member of the collection is an additional data key value. + * They are parsed the same as "primitive" fields, except that the generic type parameter of the + * collection is used as the {@link Class} parameter. * - *

    - * If there is no declared field for an input parameter name, it will be ignored unless the input - * {@code data} parameter is a {@link Map}. If it is a map, the parameter value will be stored - * either as a string, or as a {@link ArrayList}<String> in the case of repeated parameters. - *

    + *

    If there is no declared field for an input parameter name, it will be ignored unless the + * input {@code data} parameter is a {@link Map}. If it is a map, the parameter value will be + * stored either as a string, or as a {@link ArrayList}<String> in the case of repeated + * parameters. * * @param reader URL-encoded reader * @param data data key name/value pairs - * * @since 1.14 */ public static void parse(Reader reader, Object data) throws IOException { + parse(reader, data, true); + } + + /** + * Parses the given URL-encoded content into the given data object of data key name/value pairs, + * including support for repeating data key names. + * + *

    Declared fields of a "primitive" type (as defined by {@link Data#isPrimitive(Type)} are + * parsed using {@link Data#parsePrimitiveValue(Type, String)} where the {@link Class} parameter + * is the declared field class. Declared fields of type {@link Collection} are used to support + * repeating data key names, so each member of the collection is an additional data key value. + * They are parsed the same as "primitive" fields, except that the generic type parameter of the + * collection is used as the {@link Class} parameter. + * + *

    If there is no declared field for an input parameter name, it is ignored unless the input + * {@code data} parameter is a {@link Map}. If it is a map, the parameter value is stored either + * as a string, or as a {@link ArrayList}<String> in the case of repeated parameters. + * + * @param reader URL-encoded reader + * @param data data key name/value pairs + * @param decodeEnabled flag that specifies whether data should be decoded. + * @since 1.14 + */ + public static void parse(Reader reader, Object data, boolean decodeEnabled) throws IOException { Class clazz = data.getClass(); ClassInfo classInfo = ClassInfo.of(clazz); List context = Arrays.asList(clazz); @@ -135,16 +162,21 @@ public static void parse(Reader reader, Object data) throws IOException { StringWriter nameWriter = new StringWriter(); StringWriter valueWriter = new StringWriter(); boolean readingName = true; - mainLoop: while (true) { + mainLoop: + while (true) { int read = reader.read(); switch (read) { case -1: - // falls through + // falls through case '&': // parse name/value pair - String name = CharEscapers.decodeUri(nameWriter.toString()); + String name = + decodeEnabled ? CharEscapers.decodeUri(nameWriter.toString()) : nameWriter.toString(); if (name.length() != 0) { - String stringValue = CharEscapers.decodeUri(valueWriter.toString()); + String stringValue = + decodeEnabled + ? CharEscapers.decodeUri(valueWriter.toString()) + : valueWriter.toString(); // get the field from the type information FieldInfo fieldInfo = classInfo.getFieldInfo(name); if (fieldInfo != null) { @@ -155,7 +187,9 @@ public static void parse(Reader reader, Object data) throws IOException { // array that can handle repeating values Class rawArrayComponentType = Types.getRawArrayComponentType(context, Types.getArrayComponentType(type)); - arrayValueMap.put(fieldInfo.getField(), rawArrayComponentType, + arrayValueMap.put( + fieldInfo.getField(), + rawArrayComponentType, parseValue(rawArrayComponentType, context, stringValue)); } else if (Types.isAssignableToOrFrom( Types.getRawArrayComponentType(context, type), Iterable.class)) { diff --git a/google-http-client/src/main/java/com/google/api/client/http/apache/ApacheHttpRequest.java b/google-http-client/src/main/java/com/google/api/client/http/apache/ApacheHttpRequest.java index b31b20594..355acfcd0 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/apache/ApacheHttpRequest.java +++ b/google-http-client/src/main/java/com/google/api/client/http/apache/ApacheHttpRequest.java @@ -25,9 +25,7 @@ import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; -/** - * @author Yaniv Inbar - */ +/** @author Yaniv Inbar */ final class ApacheHttpRequest extends LowLevelHttpRequest { private final HttpClient httpClient; @@ -54,12 +52,16 @@ public void setTimeout(int connectTimeout, int readTimeout) throws IOException { @Override public LowLevelHttpResponse execute() throws IOException { if (getStreamingContent() != null) { - Preconditions.checkArgument(request instanceof HttpEntityEnclosingRequest, + Preconditions.checkState( + request instanceof HttpEntityEnclosingRequest, "Apache HTTP client does not support %s requests with content.", request.getRequestLine().getMethod()); ContentEntity entity = new ContentEntity(getContentLength(), getStreamingContent()); entity.setContentEncoding(getContentEncoding()); entity.setContentType(getContentType()); + if (getContentLength() == -1) { + entity.setChunked(true); + } ((HttpEntityEnclosingRequest) request).setEntity(entity); } return new ApacheHttpResponse(request, httpClient.execute(request)); diff --git a/google-http-client/src/main/java/com/google/api/client/http/apache/ApacheHttpTransport.java b/google-http-client/src/main/java/com/google/api/client/http/apache/ApacheHttpTransport.java index 50e74dc87..890bdf2f2 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/apache/ApacheHttpTransport.java +++ b/google-http-client/src/main/java/com/google/api/client/http/apache/ApacheHttpTransport.java @@ -61,24 +61,23 @@ /** * Thread-safe HTTP transport based on the Apache HTTP Client library. * - *

    - * Implementation is thread-safe, as long as any parameter modification to the - * {@link #getHttpClient() Apache HTTP Client} is only done at initialization time. For maximum - * efficiency, applications should use a single globally-shared instance of the HTTP transport. - *

    + *

    Implementation is thread-safe, as long as any parameter modification to the {@link + * #getHttpClient() Apache HTTP Client} is only done at initialization time. For maximum efficiency, + * applications should use a single globally-shared instance of the HTTP transport. * - *

    - * Default settings are specified in {@link #newDefaultHttpClient()}. Use the - * {@link #ApacheHttpTransport(HttpClient)} constructor to override the Apache HTTP Client used. + *

    Default settings are specified in {@link #newDefaultHttpClient()}. Use the {@link + * #ApacheHttpTransport(HttpClient)} constructor to override the Apache HTTP Client used. * Alternatively, use {@link #ApacheHttpTransport()} and change the {@link #getHttpClient()}. Please * read the Apache HTTP * Client connection management tutorial for more complex configuration options. - *

    * * @since 1.0 * @author Yaniv Inbar + * @deprecated Please use com.google.api.client.http.apache.v2.ApacheHttpTransport provided by the + * com.google.http-client:google-http-client-apache-v2 artifact. */ +@Deprecated public final class ApacheHttpTransport extends HttpTransport { /** Apache HTTP client. */ @@ -87,9 +86,7 @@ public final class ApacheHttpTransport extends HttpTransport { /** * Constructor that uses {@link #newDefaultHttpClient()} for the Apache HTTP client. * - *

    - * Use {@link Builder} to modify HTTP client options. - *

    + *

    Use {@link Builder} to modify HTTP client options. * * @since 1.3 */ @@ -100,25 +97,22 @@ public ApacheHttpTransport() { /** * Constructor that allows an alternative Apache HTTP client to be used. * - *

    - * Note that a few settings are overridden: - *

    + *

    Note that a few settings are overridden: + * *

      - *
    • HTTP version is set to 1.1 using {@link HttpProtocolParams#setVersion} with - * {@link HttpVersion#HTTP_1_1}.
    • - *
    • Redirects are disabled using {@link ClientPNames#HANDLE_REDIRECTS}.
    • - *
    • {@link ConnManagerParams#setTimeout} and {@link HttpConnectionParams#setConnectionTimeout} - * are set on each request based on {@link HttpRequest#getConnectTimeout()}.
    • - *
    • {@link HttpConnectionParams#setSoTimeout} is set on each request based on - * {@link HttpRequest#getReadTimeout()}.
    • + *
    • HTTP version is set to 1.1 using {@link HttpProtocolParams#setVersion} with {@link + * HttpVersion#HTTP_1_1}. + *
    • Redirects are disabled using {@link ClientPNames#HANDLE_REDIRECTS}. + *
    • {@link ConnManagerParams#setTimeout} and {@link + * HttpConnectionParams#setConnectionTimeout} are set on each request based on {@link + * HttpRequest#getConnectTimeout()}. + *
    • {@link HttpConnectionParams#setSoTimeout} is set on each request based on {@link + * HttpRequest#getReadTimeout()}. *
    * - *

    - * Use {@link Builder} for a more user-friendly way to modify the HTTP client options. - *

    + *

    Use {@link Builder} for a more user-friendly way to modify the HTTP client options. * * @param httpClient Apache HTTP client to use - * * @since 1.6 */ public ApacheHttpTransport(HttpClient httpClient) { @@ -132,22 +126,21 @@ public ApacheHttpTransport(HttpClient httpClient) { } /** - * Creates a new instance of the Apache HTTP client that is used by the - * {@link #ApacheHttpTransport()} constructor. + * Creates a new instance of the Apache HTTP client that is used by the {@link + * #ApacheHttpTransport()} constructor. + * + *

    Use this constructor if you want to customize the default Apache HTTP client. Settings: * - *

    - * Use this constructor if you want to customize the default Apache HTTP client. Settings: - *

    *
      - *
    • The client connection manager is set to {@link ThreadSafeClientConnManager}.
    • - *
    • The socket buffer size is set to 8192 using - * {@link HttpConnectionParams#setSocketBufferSize}.
    • - *
    • - *
    • The route planner uses {@link ProxySelectorRoutePlanner} with - * {@link ProxySelector#getDefault()}, which uses the proxy settings from system - * properties.
    • + *
    • The client connection manager is set to {@link ThreadSafeClientConnManager}. + *
    • The socket buffer size is set to 8192 using {@link + * HttpConnectionParams#setSocketBufferSize}. + *
    • The route planner uses {@link ProxySelectorRoutePlanner} with {@link + * ProxySelector#getDefault()}, which uses the proxy settings from system + * properties. *
    * * @return new instance of the Apache HTTP client @@ -171,13 +164,13 @@ static HttpParams newDefaultHttpParams() { } /** - * Creates a new instance of the Apache HTTP client that is used by the - * {@link #ApacheHttpTransport()} constructor. + * Creates a new instance of the Apache HTTP client that is used by the {@link + * #ApacheHttpTransport()} constructor. * * @param socketFactory SSL socket factory * @param params HTTP parameters - * @param proxySelector HTTP proxy selector to use {@link ProxySelectorRoutePlanner} or - * {@code null} for {@link DefaultHttpRoutePlanner} + * @param proxySelector HTTP proxy selector to use {@link ProxySelectorRoutePlanner} or {@code + * null} for {@link DefaultHttpRoutePlanner} * @return new instance of the Apache HTTP client */ static DefaultHttpClient newDefaultHttpClient( @@ -246,9 +239,7 @@ public HttpClient getHttpClient() { /** * Builder for {@link ApacheHttpTransport}. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @since 1.13 */ @@ -261,27 +252,23 @@ public static final class Builder { private HttpParams params = newDefaultHttpParams(); /** - * HTTP proxy selector to use {@link ProxySelectorRoutePlanner} or {@code null} for - * {@link DefaultHttpRoutePlanner}. + * HTTP proxy selector to use {@link ProxySelectorRoutePlanner} or {@code null} for {@link + * DefaultHttpRoutePlanner}. */ private ProxySelector proxySelector = ProxySelector.getDefault(); /** - * Sets the HTTP proxy to use {@link DefaultHttpRoutePlanner} or {@code null} to use - * {@link #setProxySelector(ProxySelector)} with {@link ProxySelector#getDefault()}. + * Sets the HTTP proxy to use {@link DefaultHttpRoutePlanner} or {@code null} to use {@link + * #setProxySelector(ProxySelector)} with {@link ProxySelector#getDefault()}. * - *

    - * By default it is {@code null}, which uses the proxy settings from By default it is {@code null}, which uses the proxy settings from system * properties. - *

    * - *

    - * For example: - *

    + *

    For example: * *

    -       setProxy(new HttpHost("127.0.0.1", 8080))
    +     * setProxy(new HttpHost("127.0.0.1", 8080))
          * 
    */ public Builder setProxy(HttpHost proxy) { @@ -296,11 +283,9 @@ public Builder setProxy(HttpHost proxy) { * Sets the HTTP proxy selector to use {@link ProxySelectorRoutePlanner} or {@code null} for * {@link DefaultHttpRoutePlanner}. * - *

    - * By default it is {@link ProxySelector#getDefault()} which uses the proxy settings from By default it is {@link ProxySelector#getDefault()} which uses the proxy settings from system * properties. - *

    */ public Builder setProxySelector(ProxySelector proxySelector) { this.proxySelector = proxySelector; @@ -312,16 +297,14 @@ public Builder setProxySelector(ProxySelector proxySelector) { /** * Sets the SSL socket factory based on root certificates in a Java KeyStore. * - *

    - * Example usage: - *

    + *

    Example usage: * *

    -    trustCertificatesFromJavaKeyStore(new FileInputStream("certs.jks"), "password");
    +     * trustCertificatesFromJavaKeyStore(new FileInputStream("certs.jks"), "password");
          * 
    * * @param keyStoreStream input stream to the key store (closed at the end of this method in a - * finally block) + * finally block) * @param storePass password protecting the key store file * @since 1.14 */ @@ -336,12 +319,10 @@ public Builder trustCertificatesFromJavaKeyStore(InputStream keyStoreStream, Str * Sets the SSL socket factory based root certificates generated from the specified stream using * {@link CertificateFactory#generateCertificates(InputStream)}. * - *

    - * Example usage: - *

    + *

    Example usage: * *

    -    trustCertificatesFromStream(new FileInputStream("certs.pem"));
    +     * trustCertificatesFromStream(new FileInputStream("certs.pem"));
          * 
    * * @param certificateStream certificate stream @@ -360,8 +341,7 @@ public Builder trustCertificatesFromStream(InputStream certificateStream) * Sets the SSL socket factory based on a root certificate trust store. * * @param trustStore certificate trust store (use for example {@link SecurityUtils#loadKeyStore} - * or {@link SecurityUtils#loadKeyStoreFromCertificates}) - * + * or {@link SecurityUtils#loadKeyStoreFromCertificates}) * @since 1.14 */ public Builder trustCertificates(KeyStore trustStore) throws GeneralSecurityException { @@ -371,15 +351,13 @@ public Builder trustCertificates(KeyStore trustStore) throws GeneralSecurityExce } /** - * {@link Beta}
    - * Disables validating server SSL certificates by setting the SSL socket factory using - * {@link SslUtils#trustAllSSLContext()} for the SSL context and - * {@link SSLSocketFactory#ALLOW_ALL_HOSTNAME_VERIFIER} for the host name verifier. + * {@link Beta}
    + * Disables validating server SSL certificates by setting the SSL socket factory using {@link + * SslUtils#trustAllSSLContext()} for the SSL context and {@link + * SSLSocketFactory#ALLOW_ALL_HOSTNAME_VERIFIER} for the host name verifier. * - *

    - * Be careful! Disabling certificate validation is dangerous and should only be done in testing - * environments. - *

    + *

    Be careful! Disabling certificate validation is dangerous and should only be done in + * testing environments. */ @Beta public Builder doNotValidateCertificate() throws GeneralSecurityException { diff --git a/google-http-client/src/main/java/com/google/api/client/http/apache/ContentEntity.java b/google-http-client/src/main/java/com/google/api/client/http/apache/ContentEntity.java index 6ae45b742..343b35c50 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/apache/ContentEntity.java +++ b/google-http-client/src/main/java/com/google/api/client/http/apache/ContentEntity.java @@ -21,9 +21,7 @@ import java.io.OutputStream; import org.apache.http.entity.AbstractHttpEntity; -/** - * @author Yaniv Inbar - */ +/** @author Yaniv Inbar */ final class ContentEntity extends AbstractHttpEntity { /** Content length or less than zero if not known. */ @@ -41,22 +39,27 @@ final class ContentEntity extends AbstractHttpEntity { this.streamingContent = Preconditions.checkNotNull(streamingContent); } + @Override public InputStream getContent() { throw new UnsupportedOperationException(); } + @Override public long getContentLength() { return contentLength; } + @Override public boolean isRepeatable() { return false; } + @Override public boolean isStreaming() { return true; } + @Override public void writeTo(OutputStream out) throws IOException { if (contentLength != 0) { streamingContent.writeTo(out); diff --git a/google-http-client/src/main/java/com/google/api/client/http/apache/SSLSocketFactoryExtension.java b/google-http-client/src/main/java/com/google/api/client/http/apache/SSLSocketFactoryExtension.java index 3d507371b..18d9ebc58 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/apache/SSLSocketFactoryExtension.java +++ b/google-http-client/src/main/java/com/google/api/client/http/apache/SSLSocketFactoryExtension.java @@ -37,11 +37,10 @@ final class SSLSocketFactoryExtension extends SSLSocketFactory { /** Wrapped Java SSL socket factory. */ private final javax.net.ssl.SSLSocketFactory socketFactory; - /** - * @param sslContext SSL context - */ - SSLSocketFactoryExtension(SSLContext sslContext) throws KeyManagementException, - UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException { + /** @param sslContext SSL context */ + SSLSocketFactoryExtension(SSLContext sslContext) + throws KeyManagementException, UnrecoverableKeyException, NoSuchAlgorithmException, + KeyStoreException { super((KeyStore) null); socketFactory = sslContext.getSocketFactory(); } diff --git a/google-http-client/src/main/java/com/google/api/client/http/apache/package-info.java b/google-http-client/src/main/java/com/google/api/client/http/apache/package-info.java index e0f5be089..93577e38b 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/apache/package-info.java +++ b/google-http-client/src/main/java/com/google/api/client/http/apache/package-info.java @@ -17,7 +17,7 @@ * * @since 1.0 * @author Yaniv Inbar + * @deprecated Please use com.google.api.client.http.apache.v2 provided by the + * com.google.http-client:google-http-client-apache-v2 artifact. */ - package com.google.api.client.http.apache; - diff --git a/google-http-client/src/main/java/com/google/api/client/http/javanet/ConnectionFactory.java b/google-http-client/src/main/java/com/google/api/client/http/javanet/ConnectionFactory.java index 49479303b..2ee5a718b 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/javanet/ConnectionFactory.java +++ b/google-http-client/src/main/java/com/google/api/client/http/javanet/ConnectionFactory.java @@ -4,9 +4,7 @@ import java.net.HttpURLConnection; import java.net.URL; -/** - * Given a {@link URL} instance, produces an {@link HttpURLConnection}. - */ +/** Given a {@link URL} instance, produces an {@link HttpURLConnection}. */ public interface ConnectionFactory { /** diff --git a/google-http-client/src/main/java/com/google/api/client/http/javanet/DefaultConnectionFactory.java b/google-http-client/src/main/java/com/google/api/client/http/javanet/DefaultConnectionFactory.java index 9bca36848..d4151261d 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/javanet/DefaultConnectionFactory.java +++ b/google-http-client/src/main/java/com/google/api/client/http/javanet/DefaultConnectionFactory.java @@ -19,8 +19,8 @@ public DefaultConnectionFactory() { /** * @param proxy HTTP proxy or {@code null} to use the proxy settings from - * system properties + * href="http://docs.oracle.com/javase/7/docs/api/java/net/doc-files/net-properties.html"> + * system properties */ public DefaultConnectionFactory(Proxy proxy) { this.proxy = proxy; diff --git a/google-http-client/src/main/java/com/google/api/client/http/javanet/NetHttpRequest.java b/google-http-client/src/main/java/com/google/api/client/http/javanet/NetHttpRequest.java index d0d8d0cb1..fa201b06f 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/javanet/NetHttpRequest.java +++ b/google-http-client/src/main/java/com/google/api/client/http/javanet/NetHttpRequest.java @@ -17,23 +17,30 @@ import com.google.api.client.http.LowLevelHttpRequest; import com.google.api.client.http.LowLevelHttpResponse; import com.google.api.client.util.Preconditions; - +import com.google.api.client.util.StreamingContent; +import com.google.common.annotations.VisibleForTesting; import java.io.IOException; import java.io.OutputStream; import java.net.HttpURLConnection; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; -/** - * @author Yaniv Inbar - */ +/** @author Yaniv Inbar */ final class NetHttpRequest extends LowLevelHttpRequest { private final HttpURLConnection connection; + private int writeTimeout; - /** - * @param connection HTTP URL connection - */ + /** @param connection HTTP URL connection */ NetHttpRequest(HttpURLConnection connection) { this.connection = connection; + this.writeTimeout = 0; connection.setInstanceFollowRedirects(false); } @@ -42,14 +49,43 @@ public void addHeader(String name, String value) { connection.addRequestProperty(name, value); } + @VisibleForTesting + String getRequestProperty(String name) { + return connection.getRequestProperty(name); + } + @Override public void setTimeout(int connectTimeout, int readTimeout) { connection.setReadTimeout(readTimeout); connection.setConnectTimeout(connectTimeout); } + @Override + public void setWriteTimeout(int writeTimeout) throws IOException { + this.writeTimeout = writeTimeout; + } + + interface OutputWriter { + void write(OutputStream outputStream, StreamingContent content) throws IOException; + } + + static class DefaultOutputWriter implements OutputWriter { + @Override + public void write(OutputStream outputStream, final StreamingContent content) + throws IOException { + content.writeTo(outputStream); + } + } + + private static final OutputWriter DEFAULT_CONNECTION_WRITER = new DefaultOutputWriter(); + @Override public LowLevelHttpResponse execute() throws IOException { + return execute(DEFAULT_CONNECTION_WRITER); + } + + @VisibleForTesting + LowLevelHttpResponse execute(final OutputWriter outputWriter) throws IOException { HttpURLConnection connection = this.connection; // write content if (getStreamingContent() != null) { @@ -74,11 +110,19 @@ public LowLevelHttpResponse execute() throws IOException { } else { connection.setChunkedStreamingMode(0); } - OutputStream out = connection.getOutputStream(); + final OutputStream out = connection.getOutputStream(); + boolean threw = true; try { - getStreamingContent().writeTo(out); + writeContentToOutputStream(outputWriter, out); + threw = false; + } catch (IOException e) { + // If we've gotten a response back, continue on and try to parse the response. Otherwise, + // re-throw the IOException + if (!hasResponse(connection)) { + throw e; + } } finally { try { out.close(); @@ -97,6 +141,9 @@ public LowLevelHttpResponse execute() throws IOException { Preconditions.checkArgument( contentLength == 0, "%s with non-zero content length is not supported", requestMethod); } + } else if ("DELETE".equals(connection.getRequestMethod())) { + connection.setDoOutput(true); + connection.setFixedLengthStreamingMode(0L); } // connect boolean successfulConnection = false; @@ -111,4 +158,48 @@ public LowLevelHttpResponse execute() throws IOException { } } } + + private boolean hasResponse(HttpURLConnection connection) { + try { + return connection.getResponseCode() > 0; + } catch (IOException e) { + // There's some exception trying to parse the response + return false; + } + } + + private void writeContentToOutputStream(final OutputWriter outputWriter, final OutputStream out) + throws IOException { + if (writeTimeout == 0) { + outputWriter.write(out, getStreamingContent()); + } else { + // do it with timeout + final StreamingContent content = getStreamingContent(); + final Callable writeContent = + new Callable() { + @Override + public Boolean call() throws IOException { + outputWriter.write(out, content); + return Boolean.TRUE; + } + }; + + final ExecutorService executor = Executors.newSingleThreadExecutor(); + final Future future = executor.submit(new FutureTask(writeContent), null); + executor.shutdown(); + + try { + future.get(writeTimeout, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + throw new IOException("Socket write interrupted", e); + } catch (ExecutionException e) { + throw new IOException("Exception in socket write", e); + } catch (TimeoutException e) { + throw new IOException("Socket write timed out", e); + } + if (!executor.isTerminated()) { + executor.shutdown(); + } + } + } } diff --git a/google-http-client/src/main/java/com/google/api/client/http/javanet/NetHttpResponse.java b/google-http-client/src/main/java/com/google/api/client/http/javanet/NetHttpResponse.java index 3580957c1..7114f6391 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/javanet/NetHttpResponse.java +++ b/google-http-client/src/main/java/com/google/api/client/http/javanet/NetHttpResponse.java @@ -15,7 +15,6 @@ package com.google.api.client.http.javanet; import com.google.api.client.http.LowLevelHttpResponse; - import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; @@ -60,28 +59,22 @@ public int getStatusCode() { /** * {@inheritDoc} * - *

    - * Returns {@link HttpURLConnection#getInputStream} when it doesn't throw {@link IOException}, + *

    Returns {@link HttpURLConnection#getInputStream} when it doesn't throw {@link IOException}, * otherwise it returns {@link HttpURLConnection#getErrorStream}. - *

    * - *

    - * Upgrade warning: in prior version 1.16 {@link #getContent()} returned - * {@link HttpURLConnection#getInputStream} only when the status code was successful. Starting - * with version 1.17 it returns {@link HttpURLConnection#getInputStream} when it doesn't throw - * {@link IOException}, otherwise it returns {@link HttpURLConnection#getErrorStream} - *

    + *

    Upgrade warning: in prior version 1.16 {@link #getContent()} returned {@link + * HttpURLConnection#getInputStream} only when the status code was successful. Starting with + * version 1.17 it returns {@link HttpURLConnection#getInputStream} when it doesn't throw {@link + * IOException}, otherwise it returns {@link HttpURLConnection#getErrorStream} * - *

    - * Upgrade warning: in versions prior to 1.20 {@link #getContent()} returned - * {@link HttpURLConnection#getInputStream()} or {@link HttpURLConnection#getErrorStream()}, both - * of which silently returned -1 for read() calls when the connection got closed in the middle - * of receiving a response. This is highly likely a bug from JDK's {@link HttpURLConnection}. - * Since version 1.20, the bytes read off the wire will be checked and an {@link IOException} will - * be thrown if the response is not fully delivered when the connection is closed by server for + *

    Upgrade warning: in versions prior to 1.20 {@link #getContent()} returned {@link + * HttpURLConnection#getInputStream()} or {@link HttpURLConnection#getErrorStream()}, both of + * which silently returned -1 for read() calls when the connection got closed in the middle of + * receiving a response. This is highly likely a bug from JDK's {@link HttpURLConnection}. Since + * version 1.20, the bytes read off the wire will be checked and an {@link IOException} will be + * thrown if the response is not fully delivered when the connection is closed by server for * whatever reason, e.g., server restarts. Note though that this is a best-effort check: when the * response is chunk encoded, we have to rely on the underlying HTTP library to behave correctly. - *

    */ @Override public InputStream getContent() throws IOException { @@ -162,7 +155,9 @@ public SizeValidatingInputStream(InputStream in) { /** * java.io.InputStream#read(byte[], int, int) swallows IOException thrown from read() so we have * to override it. - * @see "http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8-b132/java/io/InputStream.java#185" + * + * @see + * "http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8-b132/java/io/InputStream.java#185" */ @Override public int read(byte[] b, int off, int len) throws IOException { @@ -206,8 +201,11 @@ private void throwIfFalseEOF() throws IOException { // // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4 for details. if (bytesRead != 0 && bytesRead < contentLength) { - throw new IOException("Connection closed prematurely: bytesRead = " + bytesRead - + ", Content-Length = " + contentLength); + throw new IOException( + "Connection closed prematurely: bytesRead = " + + bytesRead + + ", Content-Length = " + + contentLength); } } } diff --git a/google-http-client/src/main/java/com/google/api/client/http/javanet/NetHttpTransport.java b/google-http-client/src/main/java/com/google/api/client/http/javanet/NetHttpTransport.java index b0f751c07..2a0ae6c1f 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/javanet/NetHttpTransport.java +++ b/google-http-client/src/main/java/com/google/api/client/http/javanet/NetHttpTransport.java @@ -38,22 +38,16 @@ /** * Thread-safe HTTP low-level transport based on the {@code java.net} package. * - *

    - * Users should consider modifying the keep alive property on {@link NetHttpTransport} to control + *

    Users should consider modifying the keep alive property on {@link NetHttpTransport} to control * whether the socket should be returned to a pool of connected sockets. More information is * available here. - *

    * - *

    - * We honor the default global caching behavior. To change the default behavior use - * {@link HttpURLConnection#setDefaultUseCaches(boolean)}. - *

    + *

    We honor the default global caching behavior. To change the default behavior use {@link + * HttpURLConnection#setDefaultUseCaches(boolean)}. * - *

    - * Implementation is thread-safe. For maximum efficiency, applications should use a single + *

    Implementation is thread-safe. For maximum efficiency, applications should use a single * globally-shared instance of the HTTP transport. - *

    * * @since 1.0 * @author Yaniv Inbar @@ -61,29 +55,32 @@ public final class NetHttpTransport extends HttpTransport { private static Proxy defaultProxy() { return new Proxy( - Proxy.Type.HTTP, new InetSocketAddress( - System.getProperty("https.proxyHost"), - Integer.parseInt(System.getProperty("https.proxyPort")))); + Proxy.Type.HTTP, + new InetSocketAddress( + System.getProperty("https.proxyHost"), + Integer.parseInt(System.getProperty("https.proxyPort")))); } /** * All valid request methods as specified in {@link HttpURLConnection#setRequestMethod}, sorted in * ascending alphabetical order. */ - private static final String[] SUPPORTED_METHODS = {HttpMethods.DELETE, - HttpMethods.GET, - HttpMethods.HEAD, - HttpMethods.OPTIONS, - HttpMethods.POST, - HttpMethods.PUT, - HttpMethods.TRACE}; + private static final String[] SUPPORTED_METHODS = { + HttpMethods.DELETE, + HttpMethods.GET, + HttpMethods.HEAD, + HttpMethods.OPTIONS, + HttpMethods.POST, + HttpMethods.PUT, + HttpMethods.TRACE + }; + static { Arrays.sort(SUPPORTED_METHODS); } private static final String SHOULD_USE_PROXY_FLAG = "com.google.api.client.should_use_proxy"; - /** Factory to produce connections from {@link URL}s */ private final ConnectionFactory connectionFactory; /** SSL socket factory or {@code null} for the default. */ @@ -92,42 +89,52 @@ Proxy.Type.HTTP, new InetSocketAddress( /** Host name verifier or {@code null} for the default. */ private final HostnameVerifier hostnameVerifier; + /** Whether the transport is mTLS. Default value is {@code false}. */ + private final boolean isMtls; + /** * Constructor with the default behavior. * - *

    - * Instead use {@link Builder} to modify behavior. - *

    + *

    Instead use {@link Builder} to modify behavior. */ public NetHttpTransport() { - this((ConnectionFactory) null, null, null); + this((ConnectionFactory) null, null, null, false); } /** * @param proxy HTTP proxy or {@code null} to use the proxy settings from - * system properties + * href="http://docs.oracle.com/javase/7/docs/api/java/net/doc-files/net-properties.html"> + * system properties * @param sslSocketFactory SSL socket factory or {@code null} for the default * @param hostnameVerifier host name verifier or {@code null} for the default + * @param isMtls Whether the transport is mTLS. Default value is {@code false} + * @since 1.38 */ NetHttpTransport( - Proxy proxy, SSLSocketFactory sslSocketFactory, HostnameVerifier hostnameVerifier) { - this(new DefaultConnectionFactory(proxy), sslSocketFactory, hostnameVerifier); + Proxy proxy, + SSLSocketFactory sslSocketFactory, + HostnameVerifier hostnameVerifier, + boolean isMtls) { + this(new DefaultConnectionFactory(proxy), sslSocketFactory, hostnameVerifier, isMtls); } /** * @param connectionFactory factory to produce connections from {@link URL}s; if {@code null} then - * {@link DefaultConnectionFactory} is used + * {@link DefaultConnectionFactory} is used * @param sslSocketFactory SSL socket factory or {@code null} for the default * @param hostnameVerifier host name verifier or {@code null} for the default - * @since 1.20 + * @param isMtls Whether the transport is mTLS. Default value is {@code false} + * @since 1.38 */ - NetHttpTransport(ConnectionFactory connectionFactory, - SSLSocketFactory sslSocketFactory, HostnameVerifier hostnameVerifier) { - this.connectionFactory = - getConnectionFactory(connectionFactory); + NetHttpTransport( + ConnectionFactory connectionFactory, + SSLSocketFactory sslSocketFactory, + HostnameVerifier hostnameVerifier, + boolean isMtls) { + this.connectionFactory = getConnectionFactory(connectionFactory); this.sslSocketFactory = sslSocketFactory; this.hostnameVerifier = hostnameVerifier; + this.isMtls = isMtls; } private ConnectionFactory getConnectionFactory(ConnectionFactory connectionFactory) { @@ -145,6 +152,11 @@ public boolean supportsMethod(String method) { return Arrays.binarySearch(SUPPORTED_METHODS, method) >= 0; } + @Override + public boolean isMtls() { + return this.isMtls; + } + @Override protected NetHttpRequest buildRequest(String method, String url) throws IOException { Preconditions.checkArgument(supportsMethod(method), "HTTP method %s not supported", method); @@ -168,9 +180,7 @@ protected NetHttpRequest buildRequest(String method, String url) throws IOExcept /** * Builder for {@link NetHttpTransport}. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @since 1.13 */ @@ -195,17 +205,18 @@ public static final class Builder { */ private ConnectionFactory connectionFactory; + /** Whether the transport is mTLS. Default value is {@code false}. */ + private boolean isMtls; + /** * Sets the HTTP proxy or {@code null} to use the proxy settings from system * properties. * - *

    - * For example: - *

    + *

    For example: * *

    -       setProxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 8080)))
    +     * setProxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 8080)))
          * 
    */ public Builder setProxy(Proxy proxy) { @@ -217,10 +228,8 @@ public Builder setProxy(Proxy proxy) { * Sets the {@link ConnectionFactory} or {@code null} to use a {@link DefaultConnectionFactory}. * This value is ignored if the {@link #setProxy} has been called with a non-null value. * - *

    - * If you wish to use a {@link Proxy}, it should be included in your {@link ConnectionFactory} - * implementation. - *

    + *

    If you wish to use a {@link Proxy}, it should be included in your {@link + * ConnectionFactory} implementation. * * @since 1.20 */ @@ -232,16 +241,14 @@ public Builder setConnectionFactory(ConnectionFactory connectionFactory) { /** * Sets the SSL socket factory based on root certificates in a Java KeyStore. * - *

    - * Example usage: - *

    + *

    Example usage: * *

    -    trustCertificatesFromJavaKeyStore(new FileInputStream("certs.jks"), "password");
    +     * trustCertificatesFromJavaKeyStore(new FileInputStream("certs.jks"), "password");
          * 
    * * @param keyStoreStream input stream to the key store (closed at the end of this method in a - * finally block) + * finally block) * @param storePass password protecting the key store file * @since 1.14 */ @@ -256,12 +263,10 @@ public Builder trustCertificatesFromJavaKeyStore(InputStream keyStoreStream, Str * Sets the SSL socket factory based root certificates generated from the specified stream using * {@link CertificateFactory#generateCertificates(InputStream)}. * - *

    - * Example usage: - *

    + *

    Example usage: * *

    -    trustCertificatesFromStream(new FileInputStream("certs.pem"));
    +     * trustCertificatesFromStream(new FileInputStream("certs.pem"));
          * 
    * * @param certificateStream certificate stream @@ -280,7 +285,7 @@ public Builder trustCertificatesFromStream(InputStream certificateStream) * Sets the SSL socket factory based on a root certificate trust store. * * @param trustStore certificate trust store (use for example {@link SecurityUtils#loadKeyStore} - * or {@link SecurityUtils#loadKeyStoreFromCertificates}) + * or {@link SecurityUtils#loadKeyStoreFromCertificates}) * @since 1.14 */ public Builder trustCertificates(KeyStore trustStore) throws GeneralSecurityException { @@ -290,15 +295,43 @@ public Builder trustCertificates(KeyStore trustStore) throws GeneralSecurityExce } /** - * {@link Beta}
    - * Disables validating server SSL certificates by setting the SSL socket factory using - * {@link SslUtils#trustAllSSLContext()} for the SSL context and - * {@link SslUtils#trustAllHostnameVerifier()} for the host name verifier. + * {@link Beta}
    + * Sets the SSL socket factory based on a root certificate trust store and a client certificate + * key store. The client certificate key store will be used to establish mutual TLS. + * + * @param trustStore certificate trust store (use for example {@link SecurityUtils#loadKeyStore} + * or {@link SecurityUtils#loadKeyStoreFromCertificates}) + * @param mtlsKeyStore key store for client certificate and key to establish mutual TLS. (use + * for example {@link SecurityUtils#createMtlsKeyStore(InputStream)}) + * @param mtlsKeyStorePassword password for mtlsKeyStore parameter + * @since 1.38 + */ + @Beta + public Builder trustCertificates( + KeyStore trustStore, KeyStore mtlsKeyStore, String mtlsKeyStorePassword) + throws GeneralSecurityException { + if (mtlsKeyStore != null && mtlsKeyStore.size() > 0) { + this.isMtls = true; + } + SSLContext sslContext = SslUtils.getTlsSslContext(); + SslUtils.initSslContext( + sslContext, + trustStore, + SslUtils.getPkixTrustManagerFactory(), + mtlsKeyStore, + mtlsKeyStorePassword, + SslUtils.getDefaultKeyManagerFactory()); + return setSslSocketFactory(sslContext.getSocketFactory()); + } + + /** + * {@link Beta}
    + * Disables validating server SSL certificates by setting the SSL socket factory using {@link + * SslUtils#trustAllSSLContext()} for the SSL context and {@link + * SslUtils#trustAllHostnameVerifier()} for the host name verifier. * - *

    - * Be careful! Disabling certificate validation is dangerous and should only be done in testing - * environments. - *

    + *

    Be careful! Disabling certificate validation is dangerous and should only be done in + * testing environments. */ @Beta public Builder doNotValidateCertificate() throws GeneralSecurityException { @@ -335,8 +368,8 @@ public NetHttpTransport build() { setProxy(defaultProxy()); } return this.proxy == null - ? new NetHttpTransport(connectionFactory, sslSocketFactory, hostnameVerifier) - : new NetHttpTransport(this.proxy, sslSocketFactory, hostnameVerifier); + ? new NetHttpTransport(connectionFactory, sslSocketFactory, hostnameVerifier, isMtls) + : new NetHttpTransport(this.proxy, sslSocketFactory, hostnameVerifier, isMtls); } } } diff --git a/google-http-client/src/main/java/com/google/api/client/http/javanet/package-info.java b/google-http-client/src/main/java/com/google/api/client/http/javanet/package-info.java index a40555024..48ae2ba7f 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/javanet/package-info.java +++ b/google-http-client/src/main/java/com/google/api/client/http/javanet/package-info.java @@ -18,6 +18,4 @@ * @since 1.0 * @author Yaniv Inbar */ - package com.google.api.client.http.javanet; - diff --git a/google-http-client/src/main/java/com/google/api/client/http/json/JsonHttpContent.java b/google-http-client/src/main/java/com/google/api/client/http/json/JsonHttpContent.java index 29e743f59..04c2cf5c1 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/json/JsonHttpContent.java +++ b/google-http-client/src/main/java/com/google/api/client/http/json/JsonHttpContent.java @@ -20,28 +20,23 @@ import com.google.api.client.json.JsonFactory; import com.google.api.client.json.JsonGenerator; import com.google.api.client.util.Preconditions; - import java.io.IOException; import java.io.OutputStream; /** * Serializes JSON HTTP content based on the data key/value mapping object for an item. * - *

    - * Sample usage: - *

    + *

    Sample usage: * *

    - *
    -  static void setContent(HttpRequest request, Object data) {
    -    request.setContent(new JsonHttpContent(new JacksonFactory(), data));
    -  }
    + * 
    + * static void setContent(HttpRequest request, Object data) {
    + * request.setContent(new JsonHttpContent(new JacksonFactory(), data));
    + * }
      * 
      * 
    * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @since 1.0 * @author Yaniv Inbar @@ -118,10 +113,8 @@ public final String getWrapperKey() { /** * Sets the wrapper key for the JSON content or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.14 */ diff --git a/google-http-client/src/main/java/com/google/api/client/http/json/package-info.java b/google-http-client/src/main/java/com/google/api/client/http/json/package-info.java index b89d35e3f..e46e9e069 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/json/package-info.java +++ b/google-http-client/src/main/java/com/google/api/client/http/json/package-info.java @@ -18,6 +18,4 @@ * @since 1.0 * @author Yaniv Inbar */ - package com.google.api.client.http.json; - diff --git a/google-http-client/src/main/java/com/google/api/client/http/package-info.java b/google-http-client/src/main/java/com/google/api/client/http/package-info.java index 93b6fcb08..a689ea9b7 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/package-info.java +++ b/google-http-client/src/main/java/com/google/api/client/http/package-info.java @@ -19,6 +19,4 @@ * @since 1.0 * @author Yaniv Inbar */ - package com.google.api.client.http; - diff --git a/google-http-client/src/main/java/com/google/api/client/json/CustomizeJsonParser.java b/google-http-client/src/main/java/com/google/api/client/json/CustomizeJsonParser.java index 581c0ed2f..83a30293b 100644 --- a/google-http-client/src/main/java/com/google/api/client/json/CustomizeJsonParser.java +++ b/google-http-client/src/main/java/com/google/api/client/json/CustomizeJsonParser.java @@ -15,23 +15,18 @@ package com.google.api.client.json; import com.google.api.client.util.Beta; - import java.lang.reflect.Field; import java.util.Collection; /** - * {@link Beta}
    + * {@link Beta}
    * Customizes the behavior of a JSON parser. * - *

    - * All methods have a default trivial implementation, so subclasses need only implement the methods - * whose behavior needs customization. - *

    + *

    All methods have a default trivial implementation, so subclasses need only implement the + * methods whose behavior needs customization. * - *

    - * Implementation has no fields and therefore thread-safe, but sub-classes are not necessarily + *

    Implementation has no fields and therefore thread-safe, but sub-classes are not necessarily * thread-safe. - *

    * * @since 1.0 * @author Yaniv Inbar @@ -39,18 +34,13 @@ @Beta public class CustomizeJsonParser { - /** - * Returns whether to stop parsing at the given key of the given context object. - */ + /** Returns whether to stop parsing at the given key of the given context object. */ public boolean stopAt(Object context, String key) { return false; } - /** - * Called when the given unrecognized key is encountered in the given context object. - */ - public void handleUnrecognizedKey(Object context, String key) { - } + /** Called when the given unrecognized key is encountered in the given context object. */ + public void handleUnrecognizedKey(Object context, String key) {} /** * Returns a new instance value for the given field in the given context object for a JSON array diff --git a/google-http-client/src/main/java/com/google/api/client/json/GenericJson.java b/google-http-client/src/main/java/com/google/api/client/json/GenericJson.java index 40452e1f7..88faab2d3 100644 --- a/google-http-client/src/main/java/com/google/api/client/json/GenericJson.java +++ b/google-http-client/src/main/java/com/google/api/client/json/GenericJson.java @@ -17,22 +17,18 @@ import com.google.api.client.util.GenericData; import com.google.api.client.util.Key; import com.google.api.client.util.Throwables; - import java.io.IOException; import java.util.concurrent.ConcurrentMap; /** * Generic JSON data that stores all unknown key name/value pairs. * - *

    - * Subclasses can declare fields for known data keys using the {@link Key} annotation. Each field + *

    Subclasses can declare fields for known data keys using the {@link Key} annotation. Each field * can be of any visibility (private, package private, protected, or public) and must not be static. * {@code null} unknown data key names are not allowed, but {@code null} data values are allowed. * - *

    - * Implementation is not thread-safe. For a thread-safe choice instead use an implementation of + *

    Implementation is not thread-safe. For a thread-safe choice instead use an implementation of * {@link ConcurrentMap}. - *

    * * @since 1.0 * @author Yaniv Inbar @@ -73,8 +69,8 @@ public String toString() { } /** - * Returns a pretty-printed serialized JSON string representation or {@link #toString()} if - * {@link #getFactory()} is {@code null}. + * Returns a pretty-printed serialized JSON string representation or {@link #toString()} if {@link + * #getFactory()} is {@code null}. * * @since 1.6 */ diff --git a/google-http-client/src/main/java/com/google/api/client/json/Json.java b/google-http-client/src/main/java/com/google/api/client/json/Json.java index fcc722513..b0e4581f5 100644 --- a/google-http-client/src/main/java/com/google/api/client/json/Json.java +++ b/google-http-client/src/main/java/com/google/api/client/json/Json.java @@ -25,10 +25,8 @@ public class Json { /** * {@code "application/json; charset=utf-8"} media type used as a default for JSON parsing. * - *

    - * Use {@link com.google.api.client.http.HttpMediaType#equalsIgnoreParameters} for comparing + *

    Use {@link com.google.api.client.http.HttpMediaType#equalsIgnoreParameters} for comparing * media types. - *

    * * @since 1.10 */ diff --git a/google-http-client/src/main/java/com/google/api/client/json/JsonFactory.java b/google-http-client/src/main/java/com/google/api/client/json/JsonFactory.java index 849808414..22825f5f0 100644 --- a/google-http-client/src/main/java/com/google/api/client/json/JsonFactory.java +++ b/google-http-client/src/main/java/com/google/api/client/json/JsonFactory.java @@ -14,8 +14,6 @@ package com.google.api.client.json; -import com.google.api.client.util.Charsets; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -23,14 +21,13 @@ import java.io.Reader; import java.io.Writer; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; /** * Abstract low-level JSON factory. * - *

    - * Implementation is thread-safe, and sub-classes must be thread-safe. For maximum efficiency, + *

    Implementation is thread-safe, and sub-classes must be thread-safe. For maximum efficiency, * applications should use a single globally-shared instance of the JSON factory. - *

    * * @since 1.3 * @author Yaniv Inbar @@ -51,7 +48,7 @@ public abstract class JsonFactory { * * @param in input stream * @param charset charset in which the input stream is encoded or {@code null} to let the parser - * detect the charset + * detect the charset * @return new instance of a low-level JSON parser * @since 1.10 */ @@ -102,8 +99,8 @@ public final JsonObjectParser createJsonObjectParser() { } /** - * Returns a serialized JSON string representation for the given item using - * {@link JsonGenerator#serialize(Object)}. + * Returns a serialized JSON string representation for the given item using {@link + * JsonGenerator#serialize(Object)}. * * @param item data key/value pairs * @return serialized JSON string representation @@ -113,14 +110,12 @@ public final String toString(Object item) throws IOException { } /** - * Returns a pretty-printed serialized JSON string representation for the given item using - * {@link JsonGenerator#serialize(Object)} with {@link JsonGenerator#enablePrettyPrint()}. + * Returns a pretty-printed serialized JSON string representation for the given item using {@link + * JsonGenerator#serialize(Object)} with {@link JsonGenerator#enablePrettyPrint()}. * - *

    - * The specifics of how the JSON representation is made pretty is implementation dependent, and - * should not be relied on. However, it is assumed to be legal, and in fact differs from - * {@link #toString(Object)} only by adding whitespace that does not change its meaning. - *

    + *

    The specifics of how the JSON representation is made pretty is implementation dependent, and + * should not be relied on. However, it is assumed to be legal, and in fact differs from {@link + * #toString(Object)} only by adding whitespace that does not change its meaning. * * @param item data key/value pairs * @return serialized JSON string representation @@ -143,8 +138,8 @@ public final byte[] toByteArray(Object item) throws IOException { } /** - * Returns a serialized JSON string representation for the given item using - * {@link JsonGenerator#serialize(Object)}. + * Returns a serialized JSON string representation for the given item using {@link + * JsonGenerator#serialize(Object)}. * * @param item data key/value pairs * @param pretty whether to return a pretty representation @@ -164,7 +159,7 @@ private String toString(Object item, boolean pretty) throws IOException { */ private ByteArrayOutputStream toByteStream(Object item, boolean pretty) throws IOException { ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); - JsonGenerator generator = createJsonGenerator(byteStream, Charsets.UTF_8); + JsonGenerator generator = createJsonGenerator(byteStream, StandardCharsets.UTF_8); if (pretty) { generator.enablePrettyPrint(); } @@ -179,7 +174,7 @@ private ByteArrayOutputStream toByteStream(Object item, boolean pretty) throws I * * @param value JSON string value * @param destinationClass destination class that has an accessible default constructor to use to - * create a new instance + * create a new instance * @return new instance of the parsed destination class * @since 1.4 */ @@ -191,13 +186,11 @@ public final T fromString(String value, Class destinationClass) throws IO * Parse and close an input stream as a JSON object, array, or value into a new instance of the * given destination class using {@link JsonParser#parseAndClose(Class)}. * - *

    - * Tries to detect the charset of the input stream automatically. - *

    + *

    Tries to detect the charset of the input stream automatically. * * @param inputStream JSON value in an input stream * @param destinationClass destination class that has an accessible default constructor to use to - * create a new instance + * create a new instance * @return new instance of the parsed destination class * @since 1.7 */ @@ -213,7 +206,7 @@ public final T fromInputStream(InputStream inputStream, Class destination * @param inputStream JSON value in an input stream * @param charset Charset in which the stream is encoded * @param destinationClass destination class that has an accessible default constructor to use to - * create a new instance + * create a new instance * @return new instance of the parsed destination class * @since 1.10 */ @@ -228,7 +221,7 @@ public final T fromInputStream( * * @param reader JSON value in a reader * @param destinationClass destination class that has an accessible default constructor to use to - * create a new instance + * create a new instance * @return new instance of the parsed destination class * @since 1.7 */ diff --git a/google-http-client/src/main/java/com/google/api/client/json/JsonGenerator.java b/google-http-client/src/main/java/com/google/api/client/json/JsonGenerator.java index a58760281..2d1003390 100644 --- a/google-http-client/src/main/java/com/google/api/client/json/JsonGenerator.java +++ b/google-http-client/src/main/java/com/google/api/client/json/JsonGenerator.java @@ -21,7 +21,8 @@ import com.google.api.client.util.GenericData; import com.google.api.client.util.Preconditions; import com.google.api.client.util.Types; - +import java.io.Closeable; +import java.io.Flushable; import java.io.IOException; import java.lang.reflect.Field; import java.math.BigDecimal; @@ -31,15 +32,13 @@ /** * Abstract low-level JSON serializer. * - *

    - * Implementation has no fields and therefore thread-safe, but sub-classes are not necessarily + *

    Implementation has no fields and therefore thread-safe, but sub-classes are not necessarily * thread-safe. - *

    * * @since 1.3 * @author Yaniv Inbar */ -public abstract class JsonGenerator { +public abstract class JsonGenerator implements Closeable, Flushable { /** Returns the JSON factory from which this generator was created. */ public abstract JsonFactory getFactory(); @@ -139,7 +138,9 @@ private void serialize(boolean isJsonString, Object value) throws IOException { writeBoolean((Boolean) value); } else if (value instanceof DateTime) { writeString(((DateTime) value).toStringRfc3339()); - } else if (value instanceof Iterable || valueClass.isArray()) { + } else if ((value instanceof Iterable || valueClass.isArray()) + && !(value instanceof Map) + && !(value instanceof GenericData)) { writeStartArray(); for (Object o : Types.iterableOf(value)) { serialize(isJsonString, o); @@ -179,15 +180,11 @@ private void serialize(boolean isJsonString, Object value) throws IOException { /** * Requests that the output be pretty printed (by default it is not). * - *

    - * Default implementation does nothing, but implementations may override to provide actual pretty - * printing. - *

    + *

    Default implementation does nothing, but implementations may override to provide actual + * pretty printing. * * @throws IOException possible I/O exception (unused in default implementation) - * * @since 1.6 */ - public void enablePrettyPrint() throws IOException { - } + public void enablePrettyPrint() throws IOException {} } diff --git a/google-http-client/src/main/java/com/google/api/client/json/JsonObjectParser.java b/google-http-client/src/main/java/com/google/api/client/json/JsonObjectParser.java index 2d6951e14..dc3d43611 100644 --- a/google-http-client/src/main/java/com/google/api/client/json/JsonObjectParser.java +++ b/google-http-client/src/main/java/com/google/api/client/json/JsonObjectParser.java @@ -17,7 +17,6 @@ import com.google.api.client.util.ObjectParser; import com.google.api.client.util.Preconditions; import com.google.api.client.util.Sets; - import java.io.IOException; import java.io.InputStream; import java.io.Reader; @@ -31,19 +30,15 @@ /** * Parses JSON data into an data class of key/value pairs. * - *

    - * Implementation is thread-safe. - *

    + *

    Implementation is thread-safe. * - *

    - * Sample usage: - *

    + *

    Sample usage: * *

    - *
    -  static void setParser(HttpRequest request) {
    -    request.setParser(new JsonObjectParser(new JacksonFactory()));
    -  }
    + * 
    + * static void setParser(HttpRequest request) {
    + * request.setParser(new JsonObjectParser(new JacksonFactory()));
    + * }
      * 
      * 
    * @@ -58,16 +53,13 @@ public class JsonObjectParser implements ObjectParser { /** Wrapper keys for the JSON content or empty for none. */ private final Set wrapperKeys; - /** - * @param jsonFactory JSON factory - */ + /** @param jsonFactory JSON factory */ public JsonObjectParser(JsonFactory jsonFactory) { this(new Builder(jsonFactory)); } /** * @param builder builder - * * @since 1.14 */ protected JsonObjectParser(Builder builder) { @@ -124,8 +116,10 @@ private void initializeParser(JsonParser parser) throws IOException { boolean failed = true; try { String match = parser.skipToKey(wrapperKeys); - Preconditions.checkArgument(match != null && parser.getCurrentToken() != JsonToken.END_OBJECT, - "wrapper key(s) not found: %s", wrapperKeys); + Preconditions.checkArgument( + match != null && parser.getCurrentToken() != JsonToken.END_OBJECT, + "wrapper key(s) not found: %s", + wrapperKeys); failed = false; } finally { if (failed) { @@ -137,9 +131,7 @@ private void initializeParser(JsonParser parser) throws IOException { /** * Builder. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @since 1.14 */ @@ -151,9 +143,7 @@ public static class Builder { /** Wrapper keys for the JSON content or empty for none. */ Collection wrapperKeys = Sets.newHashSet(); - /** - * @param jsonFactory JSON factory - */ + /** @param jsonFactory JSON factory */ public Builder(JsonFactory jsonFactory) { this.jsonFactory = Preconditions.checkNotNull(jsonFactory); } @@ -176,10 +166,8 @@ public final Collection getWrapperKeys() { /** * Sets the wrapper keys for the JSON content. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Builder setWrapperKeys(Collection wrapperKeys) { this.wrapperKeys = wrapperKeys; diff --git a/google-http-client/src/main/java/com/google/api/client/json/JsonParser.java b/google-http-client/src/main/java/com/google/api/client/json/JsonParser.java index 0c3a5b249..477640650 100644 --- a/google-http-client/src/main/java/com/google/api/client/json/JsonParser.java +++ b/google-http-client/src/main/java/com/google/api/client/json/JsonParser.java @@ -23,7 +23,7 @@ import com.google.api.client.util.Preconditions; import com.google.api.client.util.Sets; import com.google.api.client.util.Types; - +import java.io.Closeable; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -43,24 +43,20 @@ import java.util.concurrent.locks.ReentrantLock; /** - * Abstract low-level JSON parser. See - * + * Abstract low-level JSON parser. See * https://developers.google.com/api-client-library/java/google-http-java-client/json * - *

    - * Implementation has no fields and therefore thread-safe, but sub-classes are not necessarily + *

    Implementation has no fields and therefore thread-safe, but sub-classes are not necessarily * thread-safe. - *

    - *

    - *

    - * If a JSON map is encountered while using a destination class of type Map, then an - * {@link ArrayMap} is used by default for the parsed values. - *

    + * + *

    If a JSON map is encountered while using a destination class of type Map, then an {@link + * java.util.ArrayMap} is used by default for the parsed values. * * @since 1.3 * @author Yaniv Inbar */ -public abstract class JsonParser { +public abstract class JsonParser implements Closeable { /** * Maps a polymorphic {@link Class} to its {@link Field} with the {@link JsonPolymorphicTypeMap} @@ -96,15 +92,15 @@ public abstract class JsonParser { public abstract String getCurrentName() throws IOException; /** - * Skips to the matching {@link JsonToken#END_ARRAY} if current token is - * {@link JsonToken#START_ARRAY}, the matching {@link JsonToken#END_OBJECT} if the current token - * is {@link JsonToken#START_OBJECT}, else does nothing. + * Skips to the matching {@link JsonToken#END_ARRAY} if current token is {@link + * JsonToken#START_ARRAY}, the matching {@link JsonToken#END_OBJECT} if the current token is + * {@link JsonToken#START_OBJECT}, else does nothing. */ public abstract JsonParser skipChildren() throws IOException; /** - * Returns a textual representation of the current token or {@code null} if - * {@link #getCurrentToken()} is {@code null}. + * Returns a textual representation of the current token or {@code null} if {@link + * #getCurrentToken()} is {@code null}. */ public abstract String getText() throws IOException; @@ -140,7 +136,7 @@ public abstract class JsonParser { * * @param destination class * @param destinationClass destination class that has a public default constructor to use to - * create a new instance + * create a new instance * @return new instance of the parsed destination class * @since 1.15 */ @@ -149,13 +145,13 @@ public final T parseAndClose(Class destinationClass) throws IOException { } /** - * {@link Beta}
    + * {@link Beta}
    * Parse a JSON object, array, or value into a new instance of the given destination class using * {@link JsonParser#parse(Class, CustomizeJsonParser)}, and then closes the parser. * * @param destination class * @param destinationClass destination class that has a public default constructor to use to - * create a new instance + * create a new instance * @param customizeParser optional parser customizer or {@code null} for none * @return new instance of the parsed destination class */ @@ -172,12 +168,10 @@ public final T parseAndClose(Class destinationClass, CustomizeJsonParser /** * Skips the values of all keys in the current object until it finds the given key. * - *

    - * Before this method is called, the parser must either point to the start or end of a JSON object - * or to a field name. After this method ends, the current token will either be the - * {@link JsonToken#END_OBJECT} of the current object if the key is not found, or the value of the - * key that was found. - *

    + *

    Before this method is called, the parser must either point to the start or end of a JSON + * object or to a field name. After this method ends, the current token will either be the {@link + * JsonToken#END_OBJECT} of the current object if the key is not found, or the value of the key + * that was found. * * @param keyToFind key to find */ @@ -188,12 +182,10 @@ public final void skipToKey(String keyToFind) throws IOException { /** * Skips the values of all keys in the current object until it finds one of the given keys. * - *

    - * Before this method is called, the parser must either point to the start or end of a JSON object - * or to a field name. After this method ends, the current token will either be the - * {@link JsonToken#END_OBJECT} of the current object if no matching key is found, or the value of - * the key that was found. - *

    + *

    Before this method is called, the parser must either point to the start or end of a JSON + * object or to a field name. After this method ends, the current token will either be the {@link + * JsonToken#END_OBJECT} of the current object if no matching key is found, or the value of the + * key that was found. * * @param keysToFind set of keys to look for * @return name of the first matching key found or {@code null} if no match was found @@ -228,13 +220,11 @@ private JsonToken startParsing() throws IOException { * Starts parsing an object or array by making sure the parser points to an object field name, * first array value or end of object or array. * - *

    - * If the parser is at the start of input, {@link #nextToken()} is called. The current token must - * then be {@link JsonToken#START_OBJECT}, {@link JsonToken#END_OBJECT}, - * {@link JsonToken#START_ARRAY}, {@link JsonToken#END_ARRAY}, or {@link JsonToken#FIELD_NAME}. - * For an object only, after the method is called, the current token must be either - * {@link JsonToken#FIELD_NAME} or {@link JsonToken#END_OBJECT}. - *

    + *

    If the parser is at the start of input, {@link #nextToken()} is called. The current token + * must then be {@link JsonToken#START_OBJECT}, {@link JsonToken#END_OBJECT}, {@link + * JsonToken#START_ARRAY}, {@link JsonToken#END_ARRAY}, or {@link JsonToken#FIELD_NAME}. For an + * object only, after the method is called, the current token must be either {@link + * JsonToken#FIELD_NAME} or {@link JsonToken#END_OBJECT}. */ private JsonToken startParsingObjectOrArray() throws IOException { JsonToken currentToken = startParsing(); @@ -258,10 +248,8 @@ private JsonToken startParsingObjectOrArray() throws IOException { * Parse a JSON Object from the given JSON parser -- which is closed after parsing completes -- * into the given destination object. * - *

    - * Before this method is called, the parser must either point to the start or end of a JSON object - * or to a field name. - *

    + *

    Before this method is called, the parser must either point to the start or end of a JSON + * object or to a field name. * * @param destination destination object * @since 1.15 @@ -271,14 +259,12 @@ public final void parseAndClose(Object destination) throws IOException { } /** - * {@link Beta}
    + * {@link Beta}
    * Parse a JSON Object from the given JSON parser -- which is closed after parsing completes -- * into the given destination object, optionally using the given parser customizer. * - *

    - * Before this method is called, the parser must either point to the start or end of a JSON object - * or to a field name. - *

    + *

    Before this method is called, the parser must either point to the start or end of a JSON + * object or to a field name. * * @param destination destination object * @param customizeParser optional parser customizer or {@code null} for none @@ -296,15 +282,13 @@ public final void parseAndClose(Object destination, CustomizeJsonParser customiz /** * Parse a JSON object, array, or value into a new instance of the given destination class. * - *

    - * If it parses an object, after this method ends, the current token will be the object's ending - * {@link JsonToken#END_OBJECT}. If it parses an array, after this method ends, the current token - * will be the array's ending {@link JsonToken#END_ARRAY}. - *

    + *

    If it parses an object, after this method ends, the current token will be the object's + * ending {@link JsonToken#END_OBJECT}. If it parses an array, after this method ends, the current + * token will be the array's ending {@link JsonToken#END_ARRAY}. * * @param destination class * @param destinationClass destination class that has a public default constructor to use to - * create a new instance + * create a new instance * @return new instance of the parsed destination class * @since 1.15 */ @@ -313,19 +297,17 @@ public final T parse(Class destinationClass) throws IOException { } /** - * {@link Beta}
    + * {@link Beta}
    * Parse a JSON object, array, or value into a new instance of the given destination class, * optionally using the given parser customizer. * - *

    - * If it parses an object, after this method ends, the current token will be the object's ending - * {@link JsonToken#END_OBJECT}. If it parses an array, after this method ends, the current token - * will be the array's ending {@link JsonToken#END_ARRAY}. - *

    + *

    If it parses an object, after this method ends, the current token will be the object's + * ending {@link JsonToken#END_OBJECT}. If it parses an array, after this method ends, the current + * token will be the array's ending {@link JsonToken#END_ARRAY}. * * @param destination class * @param destinationClass destination class that has a public default constructor to use to - * create a new instance + * create a new instance * @param customizeParser optional parser customizer or {@code null} for none * @return new instance of the parsed destination class */ @@ -340,11 +322,9 @@ public final T parse(Class destinationClass, CustomizeJsonParser customiz /** * Parse a JSON object, array, or value into a new instance of the given destination class. * - *

    - * If it parses an object, after this method ends, the current token will be the object's ending - * {@link JsonToken#END_OBJECT}. If it parses an array, after this method ends, the current token - * will be the array's ending {@link JsonToken#END_ARRAY}. - *

    + *

    If it parses an object, after this method ends, the current token will be the object's + * ending {@link JsonToken#END_OBJECT}. If it parses an array, after this method ends, the current + * token will be the array's ending {@link JsonToken#END_ARRAY}. * * @param dataType Type into which the JSON should be parsed * @param close {@code true} if {@link #close()} should be called after parsing @@ -356,15 +336,13 @@ public Object parse(Type dataType, boolean close) throws IOException { } /** - * {@link Beta}
    + * {@link Beta}
    * Parse a JSON object, array, or value into a new instance of the given destination class, * optionally using the given parser customizer. * - *

    - * If it parses an object, after this method ends, the current token will be the object's ending - * {@link JsonToken#END_OBJECT}. If it parses an array, after this method ends, the current token - * will be the array's ending {@link JsonToken#END_ARRAY}. - *

    + *

    If it parses an object, after this method ends, the current token will be the object's + * ending {@link JsonToken#END_OBJECT}. If it parses an array, after this method ends, the current + * token will be the array's ending {@link JsonToken#END_ARRAY}. * * @param dataType Type into which the JSON should be parsed * @param close {@code true} if {@link #close()} should be called after parsing @@ -390,11 +368,9 @@ public Object parse(Type dataType, boolean close, CustomizeJsonParser customizeP /** * Parse a JSON object from the given JSON parser into the given destination object. * - *

    - * Before this method is called, the parser must either point to the start or end of a JSON object - * or to a field name. After this method ends, the current token will be the - * {@link JsonToken#END_OBJECT} of the current object. - *

    + *

    Before this method is called, the parser must either point to the start or end of a JSON + * object or to a field name. After this method ends, the current token will be the {@link + * JsonToken#END_OBJECT} of the current object. * * @param destination destination object * @since 1.15 @@ -404,15 +380,13 @@ public final void parse(Object destination) throws IOException { } /** - * {@link Beta}
    + * {@link Beta}
    * Parse a JSON object from the given JSON parser into the given destination object, optionally * using the given parser customizer. * - *

    - * Before this method is called, the parser must either point to the start or end of a JSON object - * or to a field name. After this method ends, the current token will be the - * {@link JsonToken#END_OBJECT} of the current object. - *

    + *

    Before this method is called, the parser must either point to the start or end of a JSON + * object or to a field name. After this method ends, the current token will be the {@link + * JsonToken#END_OBJECT} of the current object. * * @param destination destination object * @param customizeParser optional parser customizer or {@code null} for none @@ -430,7 +404,7 @@ public final void parse(Object destination, CustomizeJsonParser customizeParser) * * @param context destination context stack (possibly empty) * @param destination destination object instance or {@code null} for none (for example empty - * context stack) + * context stack) * @param customizeParser optional parser customizer or {@code null} for none */ private void parse( @@ -448,7 +422,11 @@ private void parse( // using parseMap. @SuppressWarnings("unchecked") Map destinationMap = (Map) destination; - parseMap(null, destinationMap, Types.getMapValueParameter(destinationClass), context, + parseMap( + null, + destinationMap, + Types.getMapValueParameter(destinationClass), + context, customizeParser); return; } @@ -469,12 +447,9 @@ private void parse( Field field = fieldInfo.getField(); int contextSize = context.size(); context.add(field.getGenericType()); - Object fieldValue = parseValue(field, - fieldInfo.getGenericType(), - context, - destination, - customizeParser, - true); + Object fieldValue = + parseValue( + field, fieldInfo.getGenericType(), context, destination, customizeParser, true); context.remove(contextSize); fieldInfo.setValue(destination, fieldValue); } else if (isGenericData) { @@ -497,9 +472,9 @@ private void parse( * the given destination collection. * * @param destinationCollectionClass class of destination collection (must have a public default - * constructor) + * constructor) * @param destinationItemClass class of destination collection item (must have a public default - * constructor) + * constructor) * @since 1.15 */ public final Collection parseArrayAndClose( @@ -508,19 +483,22 @@ public final Collection parseArrayAndClose( } /** - * {@link Beta}
    + * {@link Beta}
    * Parse a JSON Array from the given JSON parser (which is closed after parsing completes) into * the given destination collection, optionally using the given parser customizer. * * @param destinationCollectionClass class of destination collection (must have a public default - * constructor) + * constructor) * @param destinationItemClass class of destination collection item (must have a public default - * constructor) + * constructor) * @param customizeParser optional parser customizer or {@code null} for none */ @Beta - public final Collection parseArrayAndClose(Class destinationCollectionClass, - Class destinationItemClass, CustomizeJsonParser customizeParser) throws IOException { + public final Collection parseArrayAndClose( + Class destinationCollectionClass, + Class destinationItemClass, + CustomizeJsonParser customizeParser) + throws IOException { try { return parseArray(destinationCollectionClass, destinationItemClass, customizeParser); } finally { @@ -534,7 +512,7 @@ public final Collection parseArrayAndClose(Class destinationCollection * * @param destinationCollection destination collection * @param destinationItemClass class of destination collection item (must have a public default - * constructor) + * constructor) * @since 1.15 */ public final void parseArrayAndClose( @@ -544,18 +522,21 @@ public final void parseArrayAndClose( } /** - * {@link Beta}
    + * {@link Beta}
    * Parse a JSON Array from the given JSON parser (which is closed after parsing completes) into * the given destination collection, optionally using the given parser customizer. * * @param destinationCollection destination collection * @param destinationItemClass class of destination collection item (must have a public default - * constructor) + * constructor) * @param customizeParser optional parser customizer or {@code null} for none */ @Beta - public final void parseArrayAndClose(Collection destinationCollection, - Class destinationItemClass, CustomizeJsonParser customizeParser) throws IOException { + public final void parseArrayAndClose( + Collection destinationCollection, + Class destinationItemClass, + CustomizeJsonParser customizeParser) + throws IOException { try { parseArray(destinationCollection, destinationItemClass, customizeParser); } finally { @@ -567,9 +548,9 @@ public final void parseArrayAndClose(Collection destinationCollec * Parse a JSON Array from the given JSON parser into the given destination collection. * * @param destinationCollectionClass class of destination collection (must have a public default - * constructor) + * constructor) * @param destinationItemClass class of destination collection item (must have a public default - * constructor) + * constructor) * @since 1.15 */ public final Collection parseArray( @@ -578,19 +559,22 @@ public final Collection parseArray( } /** - * {@link Beta}
    + * {@link Beta}
    * Parse a JSON Array from the given JSON parser into the given destination collection, optionally * using the given parser customizer. * * @param destinationCollectionClass class of destination collection (must have a public default - * constructor) + * constructor) * @param destinationItemClass class of destination collection item (must have a public default - * constructor) + * constructor) * @param customizeParser optional parser customizer or {@code null} for none */ @Beta - public final Collection parseArray(Class destinationCollectionClass, - Class destinationItemClass, CustomizeJsonParser customizeParser) throws IOException { + public final Collection parseArray( + Class destinationCollectionClass, + Class destinationItemClass, + CustomizeJsonParser customizeParser) + throws IOException { @SuppressWarnings("unchecked") Collection destinationCollection = (Collection) Data.newCollectionInstance(destinationCollectionClass); @@ -603,7 +587,7 @@ public final Collection parseArray(Class destinationCollectionClass, * * @param destinationCollection destination collection * @param destinationItemClass class of destination collection item (must have a public default - * constructor) + * constructor) * @since 1.15 */ public final void parseArray( @@ -613,18 +597,21 @@ public final void parseArray( } /** - * {@link Beta}
    + * {@link Beta}
    * Parse a JSON Array from the given JSON parser into the given destination collection, optionally * using the given parser customizer. * * @param destinationCollection destination collection * @param destinationItemClass class of destination collection item (must have a public default - * constructor) + * constructor) * @param customizeParser optional parser customizer or {@code null} for none */ @Beta - public final void parseArray(Collection destinationCollection, - Class destinationItemClass, CustomizeJsonParser customizeParser) throws IOException { + public final void parseArray( + Collection destinationCollection, + Class destinationItemClass, + CustomizeJsonParser customizeParser) + throws IOException { parseArray( null, destinationCollection, destinationItemClass, new ArrayList(), customizeParser); } @@ -639,18 +626,25 @@ public final void parseArray(Collection destinationCollection, * @param context destination context stack (possibly empty) * @param customizeParser optional parser customizer or {@code null} for none */ - private void parseArray(Field fieldContext, Collection destinationCollection, - Type destinationItemType, ArrayList context, CustomizeJsonParser customizeParser) + private void parseArray( + Field fieldContext, + Collection destinationCollection, + Type destinationItemType, + ArrayList context, + CustomizeJsonParser customizeParser) throws IOException { JsonToken curToken = startParsingObjectOrArray(); while (curToken != JsonToken.END_ARRAY) { @SuppressWarnings("unchecked") - T parsedValue = (T) parseValue(fieldContext, - destinationItemType, - context, - destinationCollection, - customizeParser, - true); + T parsedValue = + (T) + parseValue( + fieldContext, + destinationItemType, + context, + destinationCollection, + customizeParser, + true); destinationCollection.add(parsedValue); curToken = nextToken(); } @@ -666,8 +660,13 @@ private void parseArray(Field fieldContext, Collection destinationCollect * @param context destination context stack (possibly empty) * @param customizeParser optional parser customizer or {@code null} for none */ - private void parseMap(Field fieldContext, Map destinationMap, Type valueType, - ArrayList context, CustomizeJsonParser customizeParser) throws IOException { + private void parseMap( + Field fieldContext, + Map destinationMap, + Type valueType, + ArrayList context, + CustomizeJsonParser customizeParser) + throws IOException { JsonToken curToken = startParsingObjectOrArray(); while (curToken == JsonToken.FIELD_NAME) { String key = getText(); @@ -690,17 +689,19 @@ private void parseMap(Field fieldContext, Map destinationMap, Ty * @param valueType value type or {@code null} if not known (for example into a map) * @param context destination context stack (possibly empty) * @param destination destination object instance or {@code null} for none (for example empty - * context stack) + * context stack) * @param customizeParser customize parser or {@code null} for none * @param handlePolymorphic whether or not to check for polymorphic schema * @return parsed value */ - private final Object parseValue(Field fieldContext, + private final Object parseValue( + Field fieldContext, Type valueType, ArrayList context, Object destination, CustomizeJsonParser customizeParser, - boolean handlePolymorphic) throws IOException { + boolean handlePolymorphic) + throws IOException { valueType = Data.resolveWildcardTypeOrTypeVariable(context, valueType); // resolve a parameterized type to a class @@ -716,13 +717,16 @@ private final Object parseValue(Field fieldContext, // value type is now null, class, parameterized type, or generic array type JsonToken token = getCurrentToken(); try { - switch (getCurrentToken()) { + switch (token) { case START_ARRAY: case END_ARRAY: boolean isArray = Types.isArray(valueType); - Preconditions.checkArgument(valueType == null || isArray || valueClass != null - && Types.isAssignableToOrFrom(valueClass, Collection.class), - "expected collection or array type but got %s", valueType); + Preconditions.checkArgument( + valueType == null + || isArray + || valueClass != null && Types.isAssignableToOrFrom(valueClass, Collection.class), + "expected collection or array type but got %s", + valueType); Collection collectionValue = null; if (customizeParser != null && fieldContext != null) { collectionValue = customizeParser.newInstanceForArray(destination, fieldContext); @@ -769,8 +773,10 @@ private final Object parseValue(Field fieldContext, context.add(valueType); } if (isMap && !GenericData.class.isAssignableFrom(valueClass)) { - Type subValueType = Map.class.isAssignableFrom(valueClass) - ? Types.getMapValueParameter(valueType) : null; + Type subValueType = + Map.class.isAssignableFrom(valueClass) + ? Types.getMapValueParameter(valueType) + : null; if (subValueType != null) { @SuppressWarnings("unchecked") Map destinationMap = (Map) newInstance; @@ -808,9 +814,12 @@ private final Object parseValue(Field fieldContext, return parser.parseValue(fieldContext, typeClass, context, null, null, false); case VALUE_TRUE: case VALUE_FALSE: - Preconditions.checkArgument(valueType == null || valueClass == boolean.class - || valueClass != null && valueClass.isAssignableFrom(Boolean.class), - "expected type Boolean or boolean but got %s", valueType); + Preconditions.checkArgument( + valueType == null + || valueClass == boolean.class + || valueClass != null && valueClass.isAssignableFrom(Boolean.class), + "expected type Boolean or boolean but got %s", + valueType); return token == JsonToken.VALUE_TRUE ? Boolean.TRUE : Boolean.FALSE; case VALUE_NUMBER_FLOAT: case VALUE_NUMBER_INT: @@ -843,22 +852,24 @@ private final Object parseValue(Field fieldContext, } throw new IllegalArgumentException("expected numeric type but got " + valueType); case VALUE_STRING: - //TODO(user): Maybe refactor this method in multiple mini-methods for readability? + // TODO(user): Maybe refactor this method in multiple mini-methods for readability? String text = getText().trim().toLowerCase(Locale.US); // If we are expecting a Float / Double and the Text is NaN (case insensitive) // Then: Accept, even if the Annotation is JsonString. // Otherwise: Check that the Annotation is not JsonString. if (!(((valueClass == float.class || valueClass == Float.class) - || (valueClass == double.class || valueClass == Double.class)) - && (text.equals("nan") || text.equals("infinity") || text.equals("-infinity")))) { - Preconditions.checkArgument(valueClass == null - || !Number.class.isAssignableFrom(valueClass) || fieldContext != null - && fieldContext.getAnnotation(JsonString.class) != null, - "number field formatted as a JSON string must use the @JsonString annotation"); + || (valueClass == double.class || valueClass == Double.class)) + && (text.equals("nan") || text.equals("infinity") || text.equals("-infinity")))) { + Preconditions.checkArgument( + valueClass == null + || !Number.class.isAssignableFrom(valueClass) + || fieldContext != null && fieldContext.getAnnotation(JsonString.class) != null, + "number field formatted as a JSON string must use the @JsonString annotation"); } return Data.parsePrimitiveValue(valueType, getText()); case VALUE_NULL: - Preconditions.checkArgument(valueClass == null || !valueClass.isPrimitive(), + Preconditions.checkArgument( + valueClass == null || !valueClass.isPrimitive(), "primitive number field but found a JSON null"); if (valueClass != null && 0 != (valueClass.getModifiers() & (Modifier.ABSTRACT | Modifier.INTERFACE))) { @@ -894,13 +905,11 @@ private final Object parseValue(Field fieldContext, * Finds the {@link Field} on the given {@link Class} that has the {@link JsonPolymorphicTypeMap} * annotation, or {@code null} if there is none. * - *

    - * The class must contain exactly zero or one {@link JsonPolymorphicTypeMap} annotation. - *

    + *

    The class must contain exactly zero or one {@link JsonPolymorphicTypeMap} annotation. * * @param key The {@link Class} to search in, or {@code null} * @return The {@link Field} with the {@link JsonPolymorphicTypeMap} annotation, or {@code null} - * either if there is none or if the key is {@code null} + * either if there is none or if the key is {@code null} */ private static Field getCachedTypemapFieldFor(Class key) { if (key == null) { @@ -921,11 +930,14 @@ private static Field getCachedTypemapFieldFor(Class key) { JsonPolymorphicTypeMap typemapAnnotation = field.getAnnotation(JsonPolymorphicTypeMap.class); if (typemapAnnotation != null) { - Preconditions.checkArgument(value == null, + Preconditions.checkArgument( + value == null, "Class contains more than one field with @JsonPolymorphicTypeMap annotation: %s", key); - Preconditions.checkArgument(Data.isPrimitive(field.getType()), - "Field which has the @JsonPolymorphicTypeMap, %s, is not a supported type: %s", key, + Preconditions.checkArgument( + Data.isPrimitive(field.getType()), + "Field which has the @JsonPolymorphicTypeMap, %s, is not a supported type: %s", + key, field.getType()); value = field; // Check for duplicate typeDef keys @@ -934,8 +946,10 @@ private static Field getCachedTypemapFieldFor(Class key) { Preconditions.checkArgument( typeDefs.length > 0, "@JsonPolymorphicTypeMap must have at least one @TypeDef"); for (TypeDef typeDef : typeDefs) { - Preconditions.checkArgument(typeDefKeys.add(typeDef.key()), - "Class contains two @TypeDef annotations with identical key: %s", typeDef.key()); + Preconditions.checkArgument( + typeDefKeys.add(typeDef.key()), + "Class contains two @TypeDef annotations with identical key: %s", + typeDef.key()); } } } diff --git a/google-http-client/src/main/java/com/google/api/client/json/JsonPolymorphicTypeMap.java b/google-http-client/src/main/java/com/google/api/client/json/JsonPolymorphicTypeMap.java index 40132f332..69738d323 100644 --- a/google-http-client/src/main/java/com/google/api/client/json/JsonPolymorphicTypeMap.java +++ b/google-http-client/src/main/java/com/google/api/client/json/JsonPolymorphicTypeMap.java @@ -14,10 +14,8 @@ package com.google.api.client.json; - import com.google.api.client.util.Beta; import com.google.api.client.util.Data; - import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -25,15 +23,13 @@ import java.lang.reflect.Type; /** - * {@link Beta}
    + * {@link Beta}
    * Declares that the data type enclosing this field is polymorphic, and that the value of this field * in a heterogeneous JSON schema will determine what type the data should be parsed into. * - *

    - * A data structure must have no more than one field with this annotation present. The annotated + *

    A data structure must have no more than one field with this annotation present. The annotated * field's type must be considered "primitive" by {@link Data#isPrimitive(Type)}. The field's value * will be compared against the {@link TypeDef#key()} using {@link Object#toString()}. - *

    * * @author Nick Miceli * @since 1.16 @@ -46,9 +42,7 @@ /** The list of mappings from key value to a referenced {@link Class}. */ TypeDef[] typeDefinitions(); - /** - * Declares a mapping between a key value and a referenced class. - */ + /** Declares a mapping between a key value and a referenced class. */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface TypeDef { @@ -59,5 +53,4 @@ /** The {@link Class} that is referenced by this key value. */ Class ref(); } - } diff --git a/google-http-client/src/main/java/com/google/api/client/json/JsonString.java b/google-http-client/src/main/java/com/google/api/client/json/JsonString.java index 36a2e8a44..2d0a2ba70 100644 --- a/google-http-client/src/main/java/com/google/api/client/json/JsonString.java +++ b/google-http-client/src/main/java/com/google/api/client/json/JsonString.java @@ -22,44 +22,42 @@ /** * Use this annotation to specify that a declared numeric Java field should map to a JSON string. * - *

    - * By default declared Java numeric fields are stored as JSON numbers. For example: + *

    By default declared Java numeric fields are stored as JSON numbers. For example: * *

      * 
    -class A {
    -  @Key BigInteger value;
    -}
    + * class A {
    + * @Key BigInteger value;
    + * }
      * 
      * 
    * - * can be used for this JSON content: + *

    can be used for this JSON content: * *

      * 
    -{"value" : 12345768901234576890123457689012345768901234576890}
    + * {"value" : 12345768901234576890123457689012345768901234576890}
      * 
      * 
    * - * However, if instead the JSON content uses a JSON String to store the value, one needs to use the - * {@link JsonString} annotation. For example: + *

    However, if instead the JSON content uses a JSON String to store the value, one needs to use + * the {@link JsonString} annotation. For example: * *

      * 
    -class B {
    -  @Key @JsonString BigInteger value;
    -}
    + * class B {
    + * @Key @JsonString BigInteger value;
    + * }
      * 
      * 
    * - * can be used for this JSON content: + *

    can be used for this JSON content: * *

      * 
    -{"value" : "12345768901234576890123457689012345768901234576890"}
    + * {"value" : "12345768901234576890123457689012345768901234576890"}
      * 
      * 
    - *

    * * @since 1.3 * @author Yaniv Inbar @@ -68,5 +66,4 @@ class B { // BigDecimalString? @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) -public @interface JsonString { -} +public @interface JsonString {} diff --git a/google-http-client/src/main/java/com/google/api/client/json/package-info.java b/google-http-client/src/main/java/com/google/api/client/json/package-info.java index 47aaa0aef..4a177f57c 100644 --- a/google-http-client/src/main/java/com/google/api/client/json/package-info.java +++ b/google-http-client/src/main/java/com/google/api/client/json/package-info.java @@ -20,9 +20,7 @@ * @since 1.0 * @author Yaniv Inbar */ - @ReflectionSupport(value = ReflectionSupport.Level.FULL) package com.google.api.client.json; import com.google.j2objc.annotations.ReflectionSupport; - diff --git a/google-http-client/src/main/java/com/google/api/client/json/rpc2/JsonRpcRequest.java b/google-http-client/src/main/java/com/google/api/client/json/rpc2/JsonRpcRequest.java index 8ca02dcec..c928a9738 100644 --- a/google-http-client/src/main/java/com/google/api/client/json/rpc2/JsonRpcRequest.java +++ b/google-http-client/src/main/java/com/google/api/client/json/rpc2/JsonRpcRequest.java @@ -19,12 +19,10 @@ import com.google.api.client.util.Key; /** - * {@link Beta}
    + * {@link Beta}
    * JSON-RPC 2.0 request object. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @since 1.0 * @author Yaniv Inbar @@ -33,26 +31,22 @@ public class JsonRpcRequest extends GenericData { /** Version of the JSON-RPC protocol which is {@code "2.0"}. */ - @Key - private final String jsonrpc = "2.0"; + @Key private final String jsonrpc = "2.0"; /** * Identifier established by the client that must be a string or a number or {@code null} for a * notification and therefore not expect to receive a response. */ - @Key - private Object id; + @Key private Object id; /** Name of the method to be invoked. */ - @Key - private String method; + @Key private String method; /** * Structured value that holds the parameter values to be used during the invocation of the method * or {@code null} for none. */ - @Key - private Object params; + @Key private Object params; /** * Returns the version of the JSON-RPC protocol which is {@code "2.0"}. diff --git a/google-http-client/src/main/java/com/google/api/client/json/rpc2/package-info.java b/google-http-client/src/main/java/com/google/api/client/json/rpc2/package-info.java index 66cdb447b..46ec5ba19 100644 --- a/google-http-client/src/main/java/com/google/api/client/json/rpc2/package-info.java +++ b/google-http-client/src/main/java/com/google/api/client/json/rpc2/package-info.java @@ -13,7 +13,7 @@ */ /** - * {@link com.google.api.client.util.Beta}
    + * {@link com.google.api.client.util.Beta}
    * JSON-RPC 2.0 as specified in JSON-RPC 2.0 Specification * and JSON-RPC over @@ -24,4 +24,3 @@ */ @com.google.api.client.util.Beta package com.google.api.client.json.rpc2; - diff --git a/google-http-client/src/main/java/com/google/api/client/json/webtoken/DerEncoder.java b/google-http-client/src/main/java/com/google/api/client/json/webtoken/DerEncoder.java new file mode 100644 index 000000000..7d3b465b4 --- /dev/null +++ b/google-http-client/src/main/java/com/google/api/client/json/webtoken/DerEncoder.java @@ -0,0 +1,58 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.api.client.json.webtoken; + +import com.google.api.client.util.Preconditions; +import java.math.BigInteger; +import java.util.Arrays; + +/** + * Utilities for re-encoding a signature byte array with DER encoding. + * + *

    Note: that this is not a general purpose encoder and currently only handles 512 bit + * signatures. ES256 verification algorithms expect the signature bytes in DER encoding. + */ +public class DerEncoder { + private static byte DER_TAG_SIGNATURE_OBJECT = 0x30; + private static byte DER_TAG_ASN1_INTEGER = 0x02; + + static byte[] encode(byte[] signature) { + // expect the signature to be 64 bytes long + Preconditions.checkState(signature.length == 64); + + byte[] int1 = new BigInteger(1, Arrays.copyOfRange(signature, 0, 32)).toByteArray(); + byte[] int2 = new BigInteger(1, Arrays.copyOfRange(signature, 32, 64)).toByteArray(); + byte[] der = new byte[6 + int1.length + int2.length]; + + // Mark that this is a signature object + der[0] = DER_TAG_SIGNATURE_OBJECT; + der[1] = (byte) (der.length - 2); + + // Start ASN1 integer and write the first 32 bits + der[2] = DER_TAG_ASN1_INTEGER; + der[3] = (byte) int1.length; + System.arraycopy(int1, 0, der, 4, int1.length); + + // Start ASN1 integer and write the second 32 bits + int offset = int1.length + 4; + der[offset] = DER_TAG_ASN1_INTEGER; + der[offset + 1] = (byte) int2.length; + System.arraycopy(int2, 0, der, offset + 2, int2.length); + + return der; + } +} diff --git a/google-http-client/src/main/java/com/google/api/client/json/webtoken/JsonWebSignature.java b/google-http-client/src/main/java/com/google/api/client/json/webtoken/JsonWebSignature.java index 4a15796be..4886b7927 100644 --- a/google-http-client/src/main/java/com/google/api/client/json/webtoken/JsonWebSignature.java +++ b/google-http-client/src/main/java/com/google/api/client/json/webtoken/JsonWebSignature.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Google Inc. + * Copyright 2012 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 @@ -21,7 +21,6 @@ import com.google.api.client.util.Preconditions; import com.google.api.client.util.SecurityUtils; import com.google.api.client.util.StringUtils; - import java.io.ByteArrayInputStream; import java.io.IOException; import java.security.GeneralSecurityException; @@ -30,33 +29,27 @@ import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; -import java.security.Signature; import java.security.cert.X509Certificate; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; - import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; /** - * JSON Web Signature - * (JWS). + * JSON Web Signature(JWS). * - *

    - * Sample usage: - *

    + *

    Sample usage: * *

    -  public static void printPayload(JsonFactory jsonFactory, String tokenString) throws IOException {
    -    JsonWebSignature jws = JsonWebSignature.parse(jsonFactory, tokenString);
    -    System.out.println(jws.getPayload());
    -  }
    + * public static void printPayload(JsonFactory jsonFactory, String tokenString) throws IOException {
    + *   JsonWebSignature jws = JsonWebSignature.parse(jsonFactory, tokenString);
    + *   System.out.println(jws.getPayload());
    + * }
      * 
    * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @since 1.14 (since 1.7 as com.google.api.client.auth.jsontoken.JsonWebSignature) * @author Yaniv Inbar @@ -73,7 +66,7 @@ public class JsonWebSignature extends JsonWebToken { * @param header header * @param payload payload * @param signatureBytes bytes of the signature - * @param signedContentBytes bytes of the signature content + * @param signedContentBytes bytes of the signed content */ public JsonWebSignature( Header header, Payload payload, byte[] signatureBytes, byte[] signedContentBytes) { @@ -83,8 +76,7 @@ public JsonWebSignature( } /** - * Header as specified in Reserved + * Header as specified in Reserved * Header Parameter Names. */ public static class Header extends JsonWebToken.Header { @@ -142,7 +134,7 @@ public static class Header extends JsonWebToken.Header { * @since 1.19.1. */ @Key("x5c") - private List x509Certificates; + private ArrayList x509Certificates; /** * Array listing the header parameter names that define extensions that are used in the JWS @@ -169,10 +161,8 @@ public final String getAlgorithm() { * Sets the algorithm header parameter that identifies the cryptographic algorithm used to * secure the JWS or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Header setAlgorithm(String algorithm) { this.algorithm = algorithm; @@ -193,10 +183,8 @@ public final String getJwkUrl() { * for a set of JSON-encoded public keys, one of which corresponds to the key that was used to * digitally sign the JWS or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Header setJwkUrl(String jwkUrl) { this.jwkUrl = jwkUrl; @@ -215,10 +203,8 @@ public final String getJwk() { * Sets the JSON Web Key header parameter that is a public key that corresponds to the key used * to digitally sign the JWS or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Header setJwk(String jwk) { this.jwk = jwk; @@ -237,10 +223,8 @@ public final String getKeyId() { * Sets the key ID header parameter that is a hint indicating which specific key owned by the * signer should be used to validate the digital signature or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Header setKeyId(String keyId) { this.keyId = keyId; @@ -261,10 +245,8 @@ public final String getX509Url() { * X.509 public key certificate or certificate chain corresponding to the key used to digitally * sign the JWS or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Header setX509Url(String x509Url) { this.x509Url = x509Url; @@ -285,31 +267,14 @@ public final String getX509Thumbprint() { * SHA-1 thumbprint (a.k.a. digest) of the DER encoding of an X.509 certificate that can be used * to match the certificate or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Header setX509Thumbprint(String x509Thumbprint) { this.x509Thumbprint = x509Thumbprint; return this; } - /** - * Returns the X.509 certificate chain header parameter contains the X.509 public key - * certificate or corresponding to the key used to digitally sign the JWS or {@code null} for - * none. - * - *

    @deprecated Since release 1.19.1, replaced by {@link #getX509Certificates()}. - */ - @Deprecated - public final String getX509Certificate() { - if (x509Certificates == null || x509Certificates.isEmpty()) { - return null; - } - return x509Certificates.get(0); - } - /** * Returns the X.509 certificate chain header parameter contains the X.509 public key * certificate or certificate chain corresponding to the key used to digitally sign the JWS or @@ -318,27 +283,7 @@ public final String getX509Certificate() { * @since 1.19.1. */ public final List getX509Certificates() { - return x509Certificates; - } - - /** - * Sets the X.509 certificate chain header parameter contains the X.509 public key certificate - * corresponding to the key used to digitally sign the JWS or {@code null} for none. - * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    - * - *

    @deprecated Since release 1.19.1, replaced by - * {@link #setX509Certificates(List x509Certificates)}. - */ - @Deprecated - public Header setX509Certificate(String x509Certificate) { - ArrayList x509Certificates = new ArrayList(); - x509Certificates.add(x509Certificate); - this.x509Certificates = x509Certificates; - return this; + return new ArrayList<>(x509Certificates); } /** @@ -346,41 +291,40 @@ public Header setX509Certificate(String x509Certificate) { * or certificate chain corresponding to the key used to digitally sign the JWS or {@code null} * for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.19.1. */ public Header setX509Certificates(List x509Certificates) { - this.x509Certificates = x509Certificates; + this.x509Certificates = new ArrayList<>(x509Certificates); return this; } /** - * Returns the array listing the header parameter names that define extensions that are used in - * the JWS header that MUST be understood and processed or {@code null} for none. + * Returns an array listing the header parameter names that define extensions used in the JWS + * header that MUST be understood and processed or {@code null} for none. * * @since 1.16 */ public final List getCritical() { - return critical; + if (critical == null || critical.isEmpty()) { + return null; + } + return new ArrayList<>(critical); } /** - * Sets the array listing the header parameter names that define extensions that are used in the - * JWS header that MUST be understood and processed or {@code null} for none. + * Sets the header parameter names that define extensions used in the JWS header that MUST be + * understood and processed or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. * * @since 1.16 */ public Header setCritical(List critical) { - this.critical = critical; + this.critical = new ArrayList<>(critical); return this; } @@ -403,44 +347,46 @@ public Header getHeader() { /** * Verifies the signature of the content. * - *

    - * Currently only {@code "RS256"} algorithm is verified, but others may be added in the future. - * For any other algorithm it returns {@code false}. - *

    + *

    Currently only {@code "RS256"} and {@code "ES256"} algorithms are verified, but others may + * be added in the future. For any other algorithm it returns {@code false}. * * @param publicKey public key * @return whether the algorithm is recognized and it is verified * @throws GeneralSecurityException */ public final boolean verifySignature(PublicKey publicKey) throws GeneralSecurityException { - Signature signatureAlg = null; String algorithm = getHeader().getAlgorithm(); if ("RS256".equals(algorithm)) { - signatureAlg = SecurityUtils.getSha256WithRsaSignatureAlgorithm(); + return SecurityUtils.verify( + SecurityUtils.getSha256WithRsaSignatureAlgorithm(), + publicKey, + signatureBytes, + signedContentBytes); + } else if ("ES256".equals(algorithm)) { + return SecurityUtils.verify( + SecurityUtils.getEs256SignatureAlgorithm(), + publicKey, + DerEncoder.encode(signatureBytes), + signedContentBytes); } else { return false; } - return SecurityUtils.verify(signatureAlg, publicKey, signatureBytes, signedContentBytes); } /** - * {@link Beta}
    + * {@link Beta}
    * Verifies the signature of the content using the certificate chain embedded in the signature. * - *

    - * Currently only {@code "RS256"} algorithm is verified, but others may be added in the future. - * For any other algorithm it returns {@code null}. - *

    + *

    Currently only {@code "RS256"} and {@code "ES256"} algorithms are verified, but others may + * be added in the future. For any other algorithm it returns {@code null}. * - *

    - * The leaf certificate of the certificate chain must be an SSL server certificate. - *

    + *

    The leaf certificate of the certificate chain must be an SSL server certificate. * - * @param trustManager Trust manager used to verify the X509 certificate chain embedded in this - * message. - * @return The signature certificate if the signature could be verified, null otherwise. + * @param trustManager trust manager used to verify the X509 certificate chain embedded in this + * message + * @return the signature certificate if the signature could be verified, null otherwise * @throws GeneralSecurityException - * @since 1.19.1. + * @since 1.19.1 */ @Beta public final X509Certificate verifySignature(X509TrustManager trustManager) @@ -450,34 +396,37 @@ public final X509Certificate verifySignature(X509TrustManager trustManager) return null; } String algorithm = getHeader().getAlgorithm(); - Signature signatureAlg = null; if ("RS256".equals(algorithm)) { - signatureAlg = SecurityUtils.getSha256WithRsaSignatureAlgorithm(); + return SecurityUtils.verify( + SecurityUtils.getSha256WithRsaSignatureAlgorithm(), + trustManager, + x509Certificates, + signatureBytes, + signedContentBytes); + } else if ("ES256".equals(algorithm)) { + return SecurityUtils.verify( + SecurityUtils.getEs256SignatureAlgorithm(), + trustManager, + x509Certificates, + DerEncoder.encode(signatureBytes), + signedContentBytes); } else { return null; } - return SecurityUtils.verify(signatureAlg, trustManager, x509Certificates, signatureBytes, - signedContentBytes); } /** - * {@link Beta}
    + * {@link Beta}
    * Verifies the signature of the content using the certificate chain embedded in the signature. * - *

    - * Currently only {@code "RS256"} algorithm is verified, but others may be added in the future. + *

    Currently only {@code "RS256"} algorithm is verified, but others may be added in the future. * For any other algorithm it returns {@code null}. - *

    * - *

    - * The certificate chain is verified using the system default trust manager. - *

    + *

    The certificate chain is verified using the system default trust manager. * - *

    - * The leaf certificate of the certificate chain must be an SSL server certificate. - *

    + *

    The leaf certificate of the certificate chain must be an SSL server certificate. * - * @return The signature certificate if the signature could be verified, null otherwise. + * @return the signature certificate if the signature could be verified, null otherwise * @throws GeneralSecurityException * @since 1.19.1. */ @@ -508,14 +457,14 @@ private static X509TrustManager getDefaultX509TrustManager() { } } - /** Returns the modifiable array of bytes of the signature. */ + /** Returns the bytes of the signature. */ public final byte[] getSignatureBytes() { - return signatureBytes; + return Arrays.copyOf(signatureBytes, signatureBytes.length); } - /** Returns the modifiable array of bytes of the signature content. */ + /** Returns the bytes of the signature content. */ public final byte[] getSignedContentBytes() { - return signedContentBytes; + return Arrays.copyOf(signedContentBytes, signedContentBytes.length); } /** @@ -538,9 +487,7 @@ public static Parser parser(JsonFactory jsonFactory) { /** * JWS parser. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. */ public static final class Parser { @@ -553,9 +500,7 @@ public static final class Parser { /** Payload class to use for parsing. */ private Class payloadClass = Payload.class; - /** - * @param jsonFactory JSON factory - */ + /** @param jsonFactory JSON factory */ public Parser(JsonFactory jsonFactory) { this.jsonFactory = Preconditions.checkNotNull(jsonFactory); } @@ -628,14 +573,20 @@ public JsonWebSignature parse(String tokenString) throws IOException { * @return signed JWS string * @since 1.14 (since 1.7 as com.google.api.client.auth.jsontoken.RsaSHA256Signer) */ - public static String signUsingRsaSha256(PrivateKey privateKey, JsonFactory jsonFactory, - JsonWebSignature.Header header, JsonWebToken.Payload payload) + public static String signUsingRsaSha256( + PrivateKey privateKey, + JsonFactory jsonFactory, + JsonWebSignature.Header header, + JsonWebToken.Payload payload) throws GeneralSecurityException, IOException { - String content = Base64.encodeBase64URLSafeString(jsonFactory.toByteArray(header)) + "." - + Base64.encodeBase64URLSafeString(jsonFactory.toByteArray(payload)); + String content = + Base64.encodeBase64URLSafeString(jsonFactory.toByteArray(header)) + + "." + + Base64.encodeBase64URLSafeString(jsonFactory.toByteArray(payload)); byte[] contentBytes = StringUtils.getBytesUtf8(content); - byte[] signature = SecurityUtils.sign( - SecurityUtils.getSha256WithRsaSignatureAlgorithm(), privateKey, contentBytes); + byte[] signature = + SecurityUtils.sign( + SecurityUtils.getSha256WithRsaSignatureAlgorithm(), privateKey, contentBytes); return content + "." + Base64.encodeBase64URLSafeString(signature); } } diff --git a/google-http-client/src/main/java/com/google/api/client/json/webtoken/JsonWebToken.java b/google-http-client/src/main/java/com/google/api/client/json/webtoken/JsonWebToken.java index a42aff95f..bfcf63fe9 100644 --- a/google-http-client/src/main/java/com/google/api/client/json/webtoken/JsonWebToken.java +++ b/google-http-client/src/main/java/com/google/api/client/json/webtoken/JsonWebToken.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Google Inc. + * Copyright 2012 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 @@ -18,16 +18,13 @@ import com.google.api.client.util.Key; import com.google.api.client.util.Objects; import com.google.api.client.util.Preconditions; - import java.util.Collections; import java.util.List; /** - * JSON Web Token (JWT). + * JSON Web Token (JWT). * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @since 1.14 (since 1.7 as com.google.api.client.auth.jsontoken.JsonWebToken) * @author Yaniv Inbar @@ -50,8 +47,7 @@ public JsonWebToken(Header header, Payload payload) { } /** - * Header as specified in JWT Header. + * Header as specified in JWT Header. */ public static class Header extends GenericJson { @@ -60,8 +56,8 @@ public static class Header extends GenericJson { private String type; /** - * Content type header parameter used to declare structural information about the JWT or - * {@code null} for none. + * Content type header parameter used to declare structural information about the JWT or {@code + * null} for none. */ @Key("cty") private String contentType; @@ -78,10 +74,8 @@ public final String getType() { * Sets the type header parameter used to declare the type of this object or {@code null} for * none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Header setType(String type) { this.type = type; @@ -100,10 +94,8 @@ public final String getContentType() { * Sets the content type header parameter used to declare structural information about the JWT * or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Header setContentType(String contentType) { this.contentType = contentType; @@ -122,9 +114,8 @@ public Header clone() { } /** - * Payload as specified in Reserved Claim - * Names. + * Payload as specified in Reserved + * Claim Names. */ public static class Payload extends GenericJson { @@ -143,8 +134,8 @@ public static class Payload extends GenericJson { private Long notBeforeTimeSeconds; /** - * Issued at claim that identifies the time (in seconds) at which the JWT was issued or - * {@code null} for none. + * Issued at claim that identifies the time (in seconds) at which the JWT was issued or {@code + * null} for none. */ @Key("iat") private Long issuedAtTimeSeconds; @@ -160,15 +151,13 @@ public static class Payload extends GenericJson { @Key("aud") private Object audience; - /** - * JWT ID claim that provides a unique identifier for the JWT or {@code null} for none. - */ + /** JWT ID claim that provides a unique identifier for the JWT or {@code null} for none. */ @Key("jti") private String jwtId; /** - * Type claim that is used to declare a type for the contents of this JWT Claims Set or - * {@code null} for none. + * Type claim that is used to declare a type for the contents of this JWT Claims Set or {@code + * null} for none. */ @Key("typ") private String type; @@ -192,10 +181,8 @@ public final Long getExpirationTimeSeconds() { * Sets the expiration time claim that identifies the expiration time (in seconds) on or after * which the token MUST NOT be accepted for processing or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Payload setExpirationTimeSeconds(Long expirationTimeSeconds) { this.expirationTimeSeconds = expirationTimeSeconds; @@ -214,10 +201,8 @@ public final Long getNotBeforeTimeSeconds() { * Sets the not before claim that identifies the time (in seconds) before which the token MUST * NOT be accepted for processing or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Payload setNotBeforeTimeSeconds(Long notBeforeTimeSeconds) { this.notBeforeTimeSeconds = notBeforeTimeSeconds; @@ -236,10 +221,8 @@ public final Long getIssuedAtTimeSeconds() { * Sets the issued at claim that identifies the time (in seconds) at which the JWT was issued or * {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Payload setIssuedAtTimeSeconds(Long issuedAtTimeSeconds) { this.issuedAtTimeSeconds = issuedAtTimeSeconds; @@ -258,10 +241,8 @@ public final String getIssuer() { * Sets the issuer claim that identifies the principal that issued the JWT or {@code null} for * none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Payload setIssuer(String issuer) { this.issuer = issuer; @@ -295,10 +276,8 @@ public final List getAudienceAsList() { * Sets the audience claim that identifies the audience that the JWT is intended for (should * either be a {@code String} or a {@code List}) or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Payload setAudience(Object audience) { this.audience = audience; @@ -316,10 +295,8 @@ public final String getJwtId() { /** * Sets the JWT ID claim that provides a unique identifier for the JWT or {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Payload setJwtId(String jwtId) { this.jwtId = jwtId; @@ -338,10 +315,8 @@ public final String getType() { * Sets the type claim that is used to declare a type for the contents of this JWT Claims Set or * {@code null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Payload setType(String type) { this.type = type; @@ -349,21 +324,19 @@ public Payload setType(String type) { } /** - * Returns the subject claim identifying the principal that is the subject of the JWT or - * {@code null} for none. + * Returns the subject claim identifying the principal that is the subject of the JWT or {@code + * null} for none. */ public final String getSubject() { return subject; } /** - * Sets the subject claim identifying the principal that is the subject of the JWT or - * {@code null} for none. + * Sets the subject claim identifying the principal that is the subject of the JWT or {@code + * null} for none. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Payload setSubject(String subject) { this.subject = subject; @@ -389,10 +362,8 @@ public String toString() { /** * Returns the header. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Header getHeader() { return header; @@ -401,10 +372,8 @@ public Header getHeader() { /** * Returns the payload. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Payload getPayload() { return payload; diff --git a/google-http-client/src/main/java/com/google/api/client/json/webtoken/package-info.java b/google-http-client/src/main/java/com/google/api/client/json/webtoken/package-info.java index 4b134cea0..209b5388d 100644 --- a/google-http-client/src/main/java/com/google/api/client/json/webtoken/package-info.java +++ b/google-http-client/src/main/java/com/google/api/client/json/webtoken/package-info.java @@ -13,7 +13,7 @@ */ /** - * {@link com.google.api.client.util.Beta}
    + * {@link com.google.api.client.util.Beta}
    * JSON Web Token (JWT) * and JSON Web Signature * (JWS). @@ -22,4 +22,3 @@ * @author Yaniv Inbar */ package com.google.api.client.json.webtoken; - diff --git a/google-http-client/src/main/java/com/google/api/client/testing/http/FixedClock.java b/google-http-client/src/main/java/com/google/api/client/testing/http/FixedClock.java index a185fd95f..210a2fcfc 100644 --- a/google-http-client/src/main/java/com/google/api/client/testing/http/FixedClock.java +++ b/google-http-client/src/main/java/com/google/api/client/testing/http/FixedClock.java @@ -16,16 +16,13 @@ import com.google.api.client.util.Beta; import com.google.api.client.util.Clock; - import java.util.concurrent.atomic.AtomicLong; /** - * {@link Beta}
    + * {@link Beta}
    * A thread-safe fixed time implementation of the Clock to be used for unit testing. * - *

    - * Explicitly allows you to set the time to any arbitrary value. - *

    + *

    Explicitly allows you to set the time to any arbitrary value. * * @since 1.9 * @author mlinder@google.com (Matthias Linder) @@ -34,15 +31,14 @@ public class FixedClock implements Clock { private AtomicLong currentTime; - /** - * Initializes the FixedClock with 0 millis as start time. - */ + /** Initializes the FixedClock with 0 millis as start time. */ public FixedClock() { this(0L); } /** * Initializes the FixedClock with the specified time. + * * @param startTime time in milliseconds used for initialization. */ public FixedClock(long startTime) { @@ -51,6 +47,7 @@ public FixedClock(long startTime) { /** * Changes the time value this time provider is returning. + * * @param newTime New time in milliseconds. */ public FixedClock setTime(long newTime) { diff --git a/google-http-client/src/main/java/com/google/api/client/testing/http/HttpTesting.java b/google-http-client/src/main/java/com/google/api/client/testing/http/HttpTesting.java index 3c2defb40..174e2ba4b 100644 --- a/google-http-client/src/main/java/com/google/api/client/testing/http/HttpTesting.java +++ b/google-http-client/src/main/java/com/google/api/client/testing/http/HttpTesting.java @@ -18,7 +18,7 @@ import com.google.api.client.util.Beta; /** - * {@link Beta}
    + * {@link Beta}
    * Utilities and constants related to testing the HTTP library. * * @author Yaniv Inbar @@ -33,6 +33,5 @@ public final class HttpTesting { /** A simple generic URL for testing of value {@link #SIMPLE_URL}. */ public static final GenericUrl SIMPLE_GENERIC_URL = new GenericUrl(SIMPLE_URL); - private HttpTesting() { - } + private HttpTesting() {} } diff --git a/google-http-client/src/main/java/com/google/api/client/testing/http/MockHttpContent.java b/google-http-client/src/main/java/com/google/api/client/testing/http/MockHttpContent.java index 24c3fe81a..94cfae90c 100644 --- a/google-http-client/src/main/java/com/google/api/client/testing/http/MockHttpContent.java +++ b/google-http-client/src/main/java/com/google/api/client/testing/http/MockHttpContent.java @@ -17,17 +17,14 @@ import com.google.api.client.http.HttpContent; import com.google.api.client.util.Beta; import com.google.api.client.util.Preconditions; - import java.io.IOException; import java.io.OutputStream; /** - * {@link Beta}
    + * {@link Beta}
    * Mock for {@link HttpContent}. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @author Yaniv Inbar * @since 1.3 @@ -73,9 +70,7 @@ public final byte[] getContent() { /** * Sets the HTTP content. * - *

    - * Default value is an empty byte array. - *

    + *

    Default value is an empty byte array. * * @since 1.5 */ @@ -87,9 +82,7 @@ public MockHttpContent setContent(byte[] content) { /** * Returns the HTTP content length or {@code -1} for unknown. * - *

    - * Default value is {@code -1}. - *

    + *

    Default value is {@code -1}. * * @since 1.5 */ diff --git a/google-http-client/src/main/java/com/google/api/client/testing/http/MockHttpTransport.java b/google-http-client/src/main/java/com/google/api/client/testing/http/MockHttpTransport.java index 2ff06731e..b16ac31eb 100644 --- a/google-http-client/src/main/java/com/google/api/client/testing/http/MockHttpTransport.java +++ b/google-http-client/src/main/java/com/google/api/client/testing/http/MockHttpTransport.java @@ -18,19 +18,16 @@ import com.google.api.client.http.LowLevelHttpRequest; import com.google.api.client.util.Beta; import com.google.api.client.util.Preconditions; - import java.io.IOException; import java.util.Collections; import java.util.Set; /** - * {@link Beta}
    + * {@link Beta}
    * Mock for {@link HttpTransport}. * - *

    - * Implementation is thread-safe. For maximum efficiency, applications should use a single + *

    Implementation is thread-safe. For maximum efficiency, applications should use a single * globally-shared instance of the HTTP transport. - *

    * * @author Yaniv Inbar * @since 1.3 @@ -42,26 +39,22 @@ public class MockHttpTransport extends HttpTransport { private Set supportedMethods; /** - * The {@link MockLowLevelHttpRequest} to be returned by {@link #buildRequest}. If - * this field is {@code null}, {@link #buildRequest} will create a new instance - * from its arguments. - * */ + * The {@link MockLowLevelHttpRequest} to be returned by {@link #buildRequest}. If this field is + * {@code null}, {@link #buildRequest} will create a new instance from its arguments. + */ private MockLowLevelHttpRequest lowLevelHttpRequest; /** * The {@link MockLowLevelHttpResponse} to be returned when this {@link MockHttpTransport} - * executes the associated request. Note that this field is ignored if the caller provided - * a non-{@code null} {@link MockLowLevelHttpRequest} with this {@link MockHttpTransport} - * was built. + * executes the associated request. Note that this field is ignored if the caller provided a + * non-{@code null} {@link MockLowLevelHttpRequest} with this {@link MockHttpTransport} was built. */ private MockLowLevelHttpResponse lowLevelHttpResponse; - public MockHttpTransport() { - } + public MockHttpTransport() {} /** * @param builder builder - * * @since 1.14 */ protected MockHttpTransport(Builder builder) { @@ -97,8 +90,8 @@ public final Set getSupportedMethods() { } /** - * Returns the {@link MockLowLevelHttpRequest} that is associated with this {@link Builder}, - * or {@code null} if no such instance exists. + * Returns the {@link MockLowLevelHttpRequest} that is associated with this {@link Builder}, or + * {@code null} if no such instance exists. * * @since 1.18 */ @@ -107,26 +100,10 @@ public final MockLowLevelHttpRequest getLowLevelHttpRequest() { } /** - * Returns an instance of a new builder. - * - *

    - * @deprecated (to be removed in the future) Use {@link Builder#Builder()} instead. - *

    - * - * @since 1.5 - */ - @Deprecated - public static Builder builder() { - return new Builder(); - } - - /** - * {@link Beta}
    + * {@link Beta}
    * Builder for {@link MockHttpTransport}. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @since 1.5 */ @@ -137,27 +114,24 @@ public static class Builder { Set supportedMethods; /** - * The {@link MockLowLevelHttpRequest} to be returned by {@link #buildRequest}. If - * this field is {@code null}, {@link #buildRequest} will create a new instance - * from its arguments. - * */ + * The {@link MockLowLevelHttpRequest} to be returned by {@link #buildRequest}. If this field is + * {@code null}, {@link #buildRequest} will create a new instance from its arguments. + */ MockLowLevelHttpRequest lowLevelHttpRequest; /** - * The {@link MockLowLevelHttpResponse} that should be the result of the - * {@link MockLowLevelHttpRequest} to be returned by {@link #buildRequest}. Note - * that this field is ignored if the caller provides a {@link MockLowLevelHttpRequest} - * via {@link #setLowLevelHttpRequest}. + * The {@link MockLowLevelHttpResponse} that should be the result of the {@link + * MockLowLevelHttpRequest} to be returned by {@link #buildRequest}. Note that this field is + * ignored if the caller provides a {@link MockLowLevelHttpRequest} via {@link + * #setLowLevelHttpRequest}. */ MockLowLevelHttpResponse lowLevelHttpResponse; /** - * Constructs a new {@link Builder}. Note that this constructor was {@code protected} - * in version 1.17 and its predecessors, and was made {@code public} in version - * 1.18. + * Constructs a new {@link Builder}. Note that this constructor was {@code protected} in version + * 1.17 and its predecessors, and was made {@code public} in version 1.18. */ - public Builder() { - } + public Builder() {} /** Builds a new instance of {@link MockHttpTransport}. */ public MockHttpTransport build() { @@ -181,24 +155,25 @@ public final Builder setSupportedMethods(Set supportedMethods) { /** * Sets the {@link MockLowLevelHttpRequest} that will be returned by {@link #buildRequest}, if - * non-{@code null}. If {@code null}, {@link #buildRequest} will create a new - * {@link MockLowLevelHttpRequest} arguments. + * non-{@code null}. If {@code null}, {@link #buildRequest} will create a new {@link + * MockLowLevelHttpRequest} arguments. * - *

    Note that the user can set a low level HTTP Request only if a low level HTTP response - * has not been set on this instance. + *

    Note that the user can set a low level HTTP Request only if a low level HTTP response has + * not been set on this instance. * * @since 1.18 */ public final Builder setLowLevelHttpRequest(MockLowLevelHttpRequest lowLevelHttpRequest) { - Preconditions.checkState(lowLevelHttpResponse == null, + Preconditions.checkState( + lowLevelHttpResponse == null, "Cannnot set a low level HTTP request when a low level HTTP response has been set."); this.lowLevelHttpRequest = lowLevelHttpRequest; return this; } /** - * Returns the {@link MockLowLevelHttpRequest} that is associated with this {@link Builder}, - * or {@code null} if no such instance exists. + * Returns the {@link MockLowLevelHttpRequest} that is associated with this {@link Builder}, or + * {@code null} if no such instance exists. * * @since 1.18 */ @@ -206,29 +181,27 @@ public final MockLowLevelHttpRequest getLowLevelHttpRequest() { return lowLevelHttpRequest; } - /** - * Sets the {@link MockLowLevelHttpResponse} that will be the result when the - * {@link MockLowLevelHttpRequest} returned by {@link #buildRequest} is executed. - * Note that the response can be set only the caller has not provided a - * {@link MockLowLevelHttpRequest} via {@link #setLowLevelHttpRequest}. - * - * @throws IllegalStateException if the caller has already set a {@link LowLevelHttpRequest} - * in this instance + * Sets the {@link MockLowLevelHttpResponse} that will be the result when the {@link + * MockLowLevelHttpRequest} returned by {@link #buildRequest} is executed. Note that the + * response can be set only the caller has not provided a {@link MockLowLevelHttpRequest} via + * {@link #setLowLevelHttpRequest}. * + * @throws IllegalStateException if the caller has already set a {@link LowLevelHttpRequest} in + * this instance * @since 1.18 */ public final Builder setLowLevelHttpResponse(MockLowLevelHttpResponse lowLevelHttpResponse) { - Preconditions.checkState(lowLevelHttpRequest == null, + Preconditions.checkState( + lowLevelHttpRequest == null, "Cannot set a low level HTTP response when a low level HTTP request has been set."); this.lowLevelHttpResponse = lowLevelHttpResponse; return this; } - /** - * Returns the {@link MockLowLevelHttpResponse} that is associated with this {@link Builder}, - * or {@code null} if no such instance exists. + * Returns the {@link MockLowLevelHttpResponse} that is associated with this {@link Builder}, or + * {@code null} if no such instance exists. * * @since 1.18 */ diff --git a/google-http-client/src/main/java/com/google/api/client/testing/http/MockHttpUnsuccessfulResponseHandler.java b/google-http-client/src/main/java/com/google/api/client/testing/http/MockHttpUnsuccessfulResponseHandler.java index efc68f821..b069cfb02 100644 --- a/google-http-client/src/main/java/com/google/api/client/testing/http/MockHttpUnsuccessfulResponseHandler.java +++ b/google-http-client/src/main/java/com/google/api/client/testing/http/MockHttpUnsuccessfulResponseHandler.java @@ -18,16 +18,13 @@ import com.google.api.client.http.HttpResponse; import com.google.api.client.http.HttpUnsuccessfulResponseHandler; import com.google.api.client.util.Beta; - import java.io.IOException; /** - * {@link Beta}
    + * {@link Beta}
    * Mock for {@link HttpUnsuccessfulResponseHandler}. * - *

    - * Contains an {@link #isCalled} method that returns true if {@link #handleResponse} is called. - *

    + *

    Contains an {@link #isCalled} method that returns true if {@link #handleResponse} is called. * * @author Ravi Mistry * @since 1.6 @@ -47,9 +44,7 @@ public MockHttpUnsuccessfulResponseHandler(boolean successfullyHandleResponse) { this.successfullyHandleResponse = successfullyHandleResponse; } - /** - * Returns whether the {@link #handleResponse} method was called or not. - */ + /** Returns whether the {@link #handleResponse} method was called or not. */ public boolean isCalled() { return isCalled; } diff --git a/google-http-client/src/main/java/com/google/api/client/testing/http/MockLowLevelHttpRequest.java b/google-http-client/src/main/java/com/google/api/client/testing/http/MockLowLevelHttpRequest.java index 6bdac5857..115cdd7d1 100644 --- a/google-http-client/src/main/java/com/google/api/client/testing/http/MockLowLevelHttpRequest.java +++ b/google-http-client/src/main/java/com/google/api/client/testing/http/MockLowLevelHttpRequest.java @@ -18,14 +18,13 @@ import com.google.api.client.http.LowLevelHttpRequest; import com.google.api.client.http.LowLevelHttpResponse; import com.google.api.client.util.Beta; -import com.google.api.client.util.Charsets; import com.google.api.client.util.IOUtils; - import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -35,12 +34,10 @@ import java.util.zip.GZIPInputStream; /** - * {@link Beta}
    + * {@link Beta}
    * Mock for {@link LowLevelHttpRequest}. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @author Yaniv Inbar * @since 1.3 @@ -57,14 +54,11 @@ public class MockLowLevelHttpRequest extends LowLevelHttpRequest { /** * HTTP response to return from {@link #execute()}. * - *

    - * By default this is a new instance of {@link MockLowLevelHttpResponse}. - *

    + *

    By default this is a new instance of {@link MockLowLevelHttpResponse}. */ private MockLowLevelHttpResponse response = new MockLowLevelHttpResponse(); - public MockLowLevelHttpRequest() { - } + public MockLowLevelHttpRequest() {} /** * @param url Request URL or {@code null} for none @@ -102,10 +96,8 @@ public String getUrl() { /** * Returns an unmodifiable view of the map of lowercase header name to values. * - *

    - * Note that unlike this method, {@link #getFirstHeaderValue(String)} and - * {@link #getHeaderValues(String)} are not case sensitive with respect to the input header name. - *

    + *

    Note that unlike this method, {@link #getFirstHeaderValue(String)} and {@link + * #getHeaderValues(String)} are not case sensitive with respect to the input header name. * * @since 1.5 */ @@ -148,9 +140,7 @@ public MockLowLevelHttpRequest setUrl(String url) { /** * Returns HTTP content as a string, taking care of any encodings of the content if necessary. * - *

    - * Returns an empty string if there is no HTTP content. - *

    + *

    Returns an empty string if there is no HTTP content. * * @since 1.12 */ @@ -172,8 +162,10 @@ public String getContentAsString() throws IOException { // determine charset parameter from content type String contentType = getContentType(); HttpMediaType mediaType = contentType != null ? new HttpMediaType(contentType) : null; - Charset charset = mediaType == null || mediaType.getCharsetParameter() == null - ? Charsets.ISO_8859_1 : mediaType.getCharsetParameter(); + Charset charset = + mediaType == null || mediaType.getCharsetParameter() == null + ? StandardCharsets.ISO_8859_1 + : mediaType.getCharsetParameter(); return out.toString(charset.name()); } @@ -189,9 +181,7 @@ public MockLowLevelHttpResponse getResponse() { /** * Sets the HTTP response to return from {@link #execute()}. * - *

    - * By default this is a new instance of {@link MockLowLevelHttpResponse}. - *

    + *

    By default this is a new instance of {@link MockLowLevelHttpResponse}. */ public MockLowLevelHttpRequest setResponse(MockLowLevelHttpResponse response) { this.response = response; diff --git a/google-http-client/src/main/java/com/google/api/client/testing/http/MockLowLevelHttpResponse.java b/google-http-client/src/main/java/com/google/api/client/testing/http/MockLowLevelHttpResponse.java index 1a7f26920..f713206e3 100644 --- a/google-http-client/src/main/java/com/google/api/client/testing/http/MockLowLevelHttpResponse.java +++ b/google-http-client/src/main/java/com/google/api/client/testing/http/MockLowLevelHttpResponse.java @@ -19,19 +19,16 @@ import com.google.api.client.util.Beta; import com.google.api.client.util.Preconditions; import com.google.api.client.util.StringUtils; - import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; /** - * {@link Beta}
    + * {@link Beta}
    * Mock for {@link LowLevelHttpResponse}. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @author Yaniv Inbar * @since 1.3 @@ -81,10 +78,8 @@ public MockLowLevelHttpResponse addHeader(String name, String value) { /** * Sets the response content to the given content string. * - *

    - * If the input string is {@code null}, it will set the content to {@code null}. Else, it will use - * {@link TestableByteArrayInputStream} with the UTF-8 encoded string content. - *

    + *

    If the input string is {@code null}, it will set the content to {@code null}. Else, it will + * use {@link TestableByteArrayInputStream} with the UTF-8 encoded string content. * * @param stringContent content string or {@code null} for none */ @@ -98,13 +93,9 @@ public MockLowLevelHttpResponse setContent(String stringContent) { * Sets the response content to the given byte array. * * @param byteContent content byte array, or {@code null} for none. - * - *

    - * If the byte array is {@code null}, the method invokes {@link #setZeroContent}. - * Otherwise, {@code byteContent} is wrapped in a {@link TestableByteArrayInputStream} - * and becomes this {@link MockLowLevelHttpResponse}'s contents. - *

    - * + *

    If the byte array is {@code null}, the method invokes {@link #setZeroContent}. + * Otherwise, {@code byteContent} is wrapped in a {@link TestableByteArrayInputStream} and + * becomes this {@link MockLowLevelHttpResponse}'s contents. * @since 1.18 */ public MockLowLevelHttpResponse setContent(byte[] byteContent) { @@ -117,8 +108,8 @@ public MockLowLevelHttpResponse setContent(byte[] byteContent) { } /** - * Sets the content to {@code null} and the content length to 0. Note that - * the result will have a content length header whose value is 0. + * Sets the content to {@code null} and the content length to 0. Note that the result will have a + * content length header whose value is 0. * * @since 1.18 */ @@ -195,9 +186,7 @@ public final List getHeaderNames() { /** * Sets the list of header names of HTTP response. * - *

    - * Default value is an empty list. - *

    + *

    Default value is an empty list. * * @since 1.5 */ @@ -209,9 +198,7 @@ public MockLowLevelHttpResponse setHeaderNames(List headerNames) { /** * Returns the list of header values of HTTP response. * - *

    - * Default value is an empty list. - *

    + *

    Default value is an empty list. * * @since 1.5 */ @@ -262,9 +249,7 @@ public MockLowLevelHttpResponse setContentEncoding(String contentEncoding) { /** * Sets the content length or {@code -1} for unknown. * - *

    - * By default it is {@code -1}. - *

    + *

    By default it is {@code -1}. * * @since 1.5 */ @@ -277,9 +262,7 @@ public MockLowLevelHttpResponse setContentLength(long contentLength) { /** * Sets the status code of HTTP response. * - *

    - * Default value is {@code 200}. - *

    + *

    Default value is {@code 200}. * * @since 1.5 */ diff --git a/google-http-client/src/main/java/com/google/api/client/testing/http/apache/MockHttpClient.java b/google-http-client/src/main/java/com/google/api/client/testing/http/apache/MockHttpClient.java index 32e8a6617..32571a14a 100644 --- a/google-http-client/src/main/java/com/google/api/client/testing/http/apache/MockHttpClient.java +++ b/google-http-client/src/main/java/com/google/api/client/testing/http/apache/MockHttpClient.java @@ -40,12 +40,10 @@ import org.apache.http.protocol.HttpRequestExecutor; /** - * {@link Beta}
    + * {@link Beta}
    * Mock for {@link HttpClient} that does not actually make any network calls. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @since 1.14 * @author Yaniv Inbar @@ -57,12 +55,19 @@ public class MockHttpClient extends DefaultHttpClient { int responseCode; @Override - protected RequestDirector createClientRequestDirector(HttpRequestExecutor requestExec, - ClientConnectionManager conman, ConnectionReuseStrategy reustrat, - ConnectionKeepAliveStrategy kastrat, HttpRoutePlanner rouplan, HttpProcessor httpProcessor, - HttpRequestRetryHandler retryHandler, RedirectHandler redirectHandler, - AuthenticationHandler targetAuthHandler, AuthenticationHandler proxyAuthHandler, - UserTokenHandler stateHandler, HttpParams params) { + protected RequestDirector createClientRequestDirector( + HttpRequestExecutor requestExec, + ClientConnectionManager conman, + ConnectionReuseStrategy reustrat, + ConnectionKeepAliveStrategy kastrat, + HttpRoutePlanner rouplan, + HttpProcessor httpProcessor, + HttpRequestRetryHandler retryHandler, + RedirectHandler redirectHandler, + AuthenticationHandler targetAuthHandler, + AuthenticationHandler proxyAuthHandler, + UserTokenHandler stateHandler, + HttpParams params) { return new RequestDirector() { @Beta public HttpResponse execute(HttpHost target, HttpRequest request, HttpContext context) diff --git a/google-http-client/src/main/java/com/google/api/client/testing/http/apache/package-info.java b/google-http-client/src/main/java/com/google/api/client/testing/http/apache/package-info.java index d05325514..1ca14ca3d 100644 --- a/google-http-client/src/main/java/com/google/api/client/testing/http/apache/package-info.java +++ b/google-http-client/src/main/java/com/google/api/client/testing/http/apache/package-info.java @@ -13,7 +13,7 @@ */ /** - * {@link com.google.api.client.util.Beta}
    + * {@link com.google.api.client.util.Beta}
    * Testing utilities used for writing tests based on the Apache HTTP Client. * * @since 1.14 @@ -21,4 +21,3 @@ */ @com.google.api.client.util.Beta package com.google.api.client.testing.http.apache; - diff --git a/google-http-client/src/main/java/com/google/api/client/testing/http/javanet/MockHttpURLConnection.java b/google-http-client/src/main/java/com/google/api/client/testing/http/javanet/MockHttpURLConnection.java index b59648e51..ba6f2f539 100644 --- a/google-http-client/src/main/java/com/google/api/client/testing/http/javanet/MockHttpURLConnection.java +++ b/google-http-client/src/main/java/com/google/api/client/testing/http/javanet/MockHttpURLConnection.java @@ -16,7 +16,6 @@ import com.google.api.client.util.Beta; import com.google.api.client.util.Preconditions; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -30,12 +29,10 @@ import java.util.Map; /** - * {@link Beta}
    + * {@link Beta}
    * Mock for {@link HttpURLConnection}. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @since 1.11 * @author Yaniv Inbar @@ -45,21 +42,24 @@ public class MockHttpURLConnection extends HttpURLConnection { /** Whether {@link #doOutput} was called. */ private boolean doOutputCalled; + /** Whether {@link #setFixedLengthStreamingMode(int)} was called. */ + private boolean setFixedLengthStreamingModeIntCalled = false; + /** Whether {@link #setFixedLengthStreamingMode(long)} was called. */ + private boolean setFixedLengthStreamingModeLongCalled = false; /** - * Output stream or {@code null} to throw an {@link UnknownServiceException} when - * {@link #getOutputStream()} is called. + * Output stream or {@code null} to throw an {@link UnknownServiceException} when {@link + * #getOutputStream()} is called. */ private OutputStream outputStream = new ByteArrayOutputStream(0); /** - * The input byte array which represents the content when the status code is less then - * {@code 400}. + * The input byte array which represents the content when the status code is less then {@code + * 400}. * * @deprecated As of 1.20. Use {@link #setInputStream(InputStream)} instead. */ - @Deprecated - public static final byte[] INPUT_BUF = new byte[1]; + @Deprecated public static final byte[] INPUT_BUF = new byte[1]; /** * The error byte array which represents the content when the status code is greater or equal to @@ -67,8 +67,7 @@ public class MockHttpURLConnection extends HttpURLConnection { * * @deprecated As of 1.20. Use {@link #setErrorStream(InputStream)} instead. */ - @Deprecated - public static final byte[] ERROR_BUF = new byte[5]; + @Deprecated public static final byte[] ERROR_BUF = new byte[5]; /** The input stream. */ private InputStream inputStream = null; @@ -78,16 +77,13 @@ public class MockHttpURLConnection extends HttpURLConnection { private Map> headers = new LinkedHashMap>(); - /** - * @param u the URL or {@code null} for none - */ + /** @param u the URL or {@code null} for none */ public MockHttpURLConnection(URL u) { super(u); } @Override - public void disconnect() { - } + public void disconnect() {} @Override public boolean usingProxy() { @@ -95,8 +91,7 @@ public boolean usingProxy() { } @Override - public void connect() throws IOException { - } + public void connect() throws IOException {} @Override public int getResponseCode() throws IOException { @@ -122,12 +117,10 @@ public final boolean doOutputCalled() { } /** - * Sets the output stream or {@code null} to throw an {@link UnknownServiceException} when - * {@link #getOutputStream()} is called. + * Sets the output stream or {@code null} to throw an {@link UnknownServiceException} when {@link + * #getOutputStream()} is called. * - *

    - * By default it is {@code null}. - *

    + *

    By default it is {@code null}. */ public MockHttpURLConnection setOutputStream(OutputStream outputStream) { this.outputStream = outputStream; @@ -212,4 +205,28 @@ public String getHeaderField(String name) { List values = headers.get(name); return values == null ? null : values.get(0); } + + public int getChunkLength() { + return chunkLength; + } + + @Override + public void setFixedLengthStreamingMode(int contentLength) { + this.setFixedLengthStreamingModeIntCalled = true; + super.setFixedLengthStreamingMode(contentLength); + } + + @Override + public void setFixedLengthStreamingMode(long contentLength) { + this.setFixedLengthStreamingModeLongCalled = true; + super.setFixedLengthStreamingMode(contentLength); + } + + public boolean isSetFixedLengthStreamingModeIntCalled() { + return setFixedLengthStreamingModeIntCalled; + } + + public boolean isSetFixedLengthStreamingModeLongCalled() { + return setFixedLengthStreamingModeLongCalled; + } } diff --git a/google-http-client/src/main/java/com/google/api/client/testing/http/javanet/package-info.java b/google-http-client/src/main/java/com/google/api/client/testing/http/javanet/package-info.java index 501067c76..4f0934336 100644 --- a/google-http-client/src/main/java/com/google/api/client/testing/http/javanet/package-info.java +++ b/google-http-client/src/main/java/com/google/api/client/testing/http/javanet/package-info.java @@ -13,7 +13,7 @@ */ /** - * {@link com.google.api.client.util.Beta}
    + * {@link com.google.api.client.util.Beta}
    * Testing utilities used for writing tests based on the {@code java.net} package. * * @since 1.11 @@ -21,4 +21,3 @@ */ @com.google.api.client.util.Beta package com.google.api.client.testing.http.javanet; - diff --git a/google-http-client/src/main/java/com/google/api/client/testing/http/package-info.java b/google-http-client/src/main/java/com/google/api/client/testing/http/package-info.java index 3fd0332d9..615dd5626 100644 --- a/google-http-client/src/main/java/com/google/api/client/testing/http/package-info.java +++ b/google-http-client/src/main/java/com/google/api/client/testing/http/package-info.java @@ -13,7 +13,7 @@ */ /** - * {@link com.google.api.client.util.Beta}
    + * {@link com.google.api.client.util.Beta}
    * Testing utilities used for writing tests based on this library. * * @since 1.3 @@ -21,4 +21,3 @@ */ @com.google.api.client.util.Beta package com.google.api.client.testing.http; - diff --git a/google-http-client/src/main/java/com/google/api/client/testing/json/MockJsonFactory.java b/google-http-client/src/main/java/com/google/api/client/testing/json/MockJsonFactory.java index 3bdb4a9e1..eede3c00e 100644 --- a/google-http-client/src/main/java/com/google/api/client/testing/json/MockJsonFactory.java +++ b/google-http-client/src/main/java/com/google/api/client/testing/json/MockJsonFactory.java @@ -18,7 +18,6 @@ import com.google.api.client.json.JsonGenerator; import com.google.api.client.json.JsonParser; import com.google.api.client.util.Beta; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -27,12 +26,10 @@ import java.nio.charset.Charset; /** - * {@link Beta}
    + * {@link Beta}
    * Mock for {@link JsonFactory}. * - *

    - * Implementation is thread-safe. - *

    + *

    Implementation is thread-safe. * * @author rmistry@google.com (Ravi Mistry) * @since 1.15 (since 1.11 as com.google.api.client.testing.http.json.MockJsonFactory) diff --git a/google-http-client/src/main/java/com/google/api/client/testing/json/MockJsonGenerator.java b/google-http-client/src/main/java/com/google/api/client/testing/json/MockJsonGenerator.java index 506bf1e7c..7bdd13bd4 100644 --- a/google-http-client/src/main/java/com/google/api/client/testing/json/MockJsonGenerator.java +++ b/google-http-client/src/main/java/com/google/api/client/testing/json/MockJsonGenerator.java @@ -17,18 +17,15 @@ import com.google.api.client.json.JsonFactory; import com.google.api.client.json.JsonGenerator; import com.google.api.client.util.Beta; - import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; /** - * {@link Beta}
    + * {@link Beta}
    * Mock for {@link JsonGenerator}. * - *

    - * Implementation is thread-safe. - *

    + *

    Implementation is thread-safe. * * @author rmistry@google.com (Ravi Mistry) * @since 1.15 (since 1.11 as com.google.api.client.testing.http.json.MockJsonGenerator) @@ -48,70 +45,53 @@ public JsonFactory getFactory() { } @Override - public void flush() throws IOException { - } + public void flush() throws IOException {} @Override - public void close() throws IOException { - } + public void close() throws IOException {} @Override - public void writeStartArray() throws IOException { - } + public void writeStartArray() throws IOException {} @Override - public void writeEndArray() throws IOException { - } + public void writeEndArray() throws IOException {} @Override - public void writeStartObject() throws IOException { - } + public void writeStartObject() throws IOException {} @Override - public void writeEndObject() throws IOException { - } + public void writeEndObject() throws IOException {} @Override - public void writeFieldName(String name) throws IOException { - } + public void writeFieldName(String name) throws IOException {} @Override - public void writeNull() throws IOException { - } + public void writeNull() throws IOException {} @Override - public void writeString(String value) throws IOException { - } + public void writeString(String value) throws IOException {} @Override - public void writeBoolean(boolean state) throws IOException { - } + public void writeBoolean(boolean state) throws IOException {} @Override - public void writeNumber(int v) throws IOException { - } + public void writeNumber(int v) throws IOException {} @Override - public void writeNumber(long v) throws IOException { - } + public void writeNumber(long v) throws IOException {} @Override - public void writeNumber(BigInteger v) throws IOException { - } + public void writeNumber(BigInteger v) throws IOException {} @Override - public void writeNumber(float v) throws IOException { - } + public void writeNumber(float v) throws IOException {} @Override - public void writeNumber(double v) throws IOException { - } + public void writeNumber(double v) throws IOException {} @Override - public void writeNumber(BigDecimal v) throws IOException { - } + public void writeNumber(BigDecimal v) throws IOException {} @Override - public void writeNumber(String encodedValue) throws IOException { - } + public void writeNumber(String encodedValue) throws IOException {} } diff --git a/google-http-client/src/main/java/com/google/api/client/testing/json/MockJsonParser.java b/google-http-client/src/main/java/com/google/api/client/testing/json/MockJsonParser.java index 096e66476..64b48bcee 100644 --- a/google-http-client/src/main/java/com/google/api/client/testing/json/MockJsonParser.java +++ b/google-http-client/src/main/java/com/google/api/client/testing/json/MockJsonParser.java @@ -18,18 +18,15 @@ import com.google.api.client.json.JsonParser; import com.google.api.client.json.JsonToken; import com.google.api.client.util.Beta; - import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; /** - * {@link Beta}
    + * {@link Beta}
    * Mock for {@link JsonParser}. * - *

    - * Implementation is thread-safe. - *

    + *

    Implementation is thread-safe. * * @author rmistry@google.com (Ravi Mistry) * @since 1.15 (since 1.11 as com.google.api.client.testing.http.json.MockJsonParser) @@ -41,7 +38,7 @@ public class MockJsonParser extends JsonParser { private final JsonFactory factory; - MockJsonParser(JsonFactory factory) { + public MockJsonParser(JsonFactory factory) { this.factory = factory; } diff --git a/google-http-client/src/main/java/com/google/api/client/testing/json/package-info.java b/google-http-client/src/main/java/com/google/api/client/testing/json/package-info.java index 55dc1d881..b49fbbfa4 100644 --- a/google-http-client/src/main/java/com/google/api/client/testing/json/package-info.java +++ b/google-http-client/src/main/java/com/google/api/client/testing/json/package-info.java @@ -13,7 +13,7 @@ */ /** - * {@link com.google.api.client.util.Beta}
    + * {@link com.google.api.client.util.Beta}
    * Testing utilities used for writing tests based on this library. * * @since 1.15 (since 1.11 as com.google.api.client.testing.http.json) @@ -21,4 +21,3 @@ */ @com.google.api.client.util.Beta package com.google.api.client.testing.json; - diff --git a/google-http-client/src/main/java/com/google/api/client/testing/json/webtoken/TestCertificates.java b/google-http-client/src/main/java/com/google/api/client/testing/json/webtoken/TestCertificates.java index b6aeb4526..2ae6b08cc 100644 --- a/google-http-client/src/main/java/com/google/api/client/testing/json/webtoken/TestCertificates.java +++ b/google-http-client/src/main/java/com/google/api/client/testing/json/webtoken/TestCertificates.java @@ -22,7 +22,6 @@ import com.google.api.client.util.PemReader; import com.google.api.client.util.SecurityUtils; import com.google.api.client.util.StringUtils; - import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.StringReader; @@ -31,15 +30,14 @@ import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.util.List; - import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; /** - * {@link Beta}
    + * {@link Beta}
    * Test certificates. - *

    - * Contains a test certificate chain, the respective private keys and signed data. + * + *

    Contains a test certificate chain, the respective private keys and signed data. * * @since 1.19.1. */ @@ -47,7 +45,7 @@ public class TestCertificates { /** - * {@link Beta}
    + * {@link Beta}
    * Wrapper for a PEM encoded certificate providing utility routines. */ @Beta @@ -86,210 +84,214 @@ public X509TrustManager getTrustManager() throws IOException, GeneralSecurityExc /** * Test leaf certificate. + * *

        * Issuer: CN=Root
        * Subject: C=US, ST=California, L=Mountain View, O=Google Inc., CN=foo.bar.com
        * 
    */ - public static final CertData FOO_BAR_COM_CERT = new CertData( - "-----BEGIN CERTIFICATE-----\n" - + "MIIC6TCCAdECASowDQYJKoZIhvcNAQELBQAwDzENMAsGA1UEAwwEUm9vdDAeFw0x\n" - + "NDExMTgxNjU0MDNaFw0zNDExMTMxNjU0MDNaMGYxCzAJBgNVBAYTAlVTMRMwEQYD\n" - + "VQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRQwEgYDVQQK\n" - + "DAtHb29nbGUgSW5jLjEUMBIGA1UEAwwLZm9vLmJhci5jb20wggEiMA0GCSqGSIb3\n" - + "DQEBAQUAA4IBDwAwggEKAoIBAQCzFVKJOkqTmyyjMHWBOrLdpYmc0EcvG3MohaV+\n" - + "UJrVrI2SDykY8YWSkTKz9BKmF8HP/GjPPDs3184Cej9b1WeyvVB8Rj3guH3oL+sJ\n" - + "T3u9V2y4zyo5xO6FWMBYEQ6X8DkGlYtTp5theYbRrXNELul4lF+LtHTCaAANRMkO\n" - + "l0NEoLa6BRhOG68gFfIAxx5lT8REE9utvPuy+rCaBHnfHOPf8pn0LSvceBijSIFo\n" - + "S3Y5crjPVjyiPAZUHWnHTFAilfHnpLBlGxpCylePQhMKrPcgvDoD9nd0LA6xYLF7\n" - + "DPXXSa8FLO+fPV8CNJCAsFuq9Rlf2Tt3SjLtWRYuh5LuctP7AgMBAAEwDQYJKoZI\n" - + "hvcNAQELBQADggEBAEsMABZl+8Rlk0hqBktsDurri4nF/07CnSBe/zUbTiYhMpr7\n" - + "VRIDlHLoe5lslLilfXzvaymcMFeH1uBxNwhf7IO7WvIwQeUHSV+rHyNygTTieO0J\n" - + "n8Hw+4SCohHAdMvD5uWEwn3Lv+W4y7OhaSbzlhVCVCnFLVKicBayUXHtdJXJICok\n" - + "R4+h/WNM7g0iKThakZOyfb8h1phy7TMTVlPFKrcVDo5m9+GhtPC4PNjGLok6r/jx\n" - + "9CIOCapIqi8fXJEOxKvilYeAYqfjWvhx00juEUBHrpCQ8wT4TA+LlI02cRz5rxW4\n" - + "FQAz1NdoG9HZDZWa+NNFTZdAmtWPJMLd+8L8sl4=\n" - + "-----END CERTIFICATE-----"); + public static final CertData FOO_BAR_COM_CERT = + new CertData( + "-----BEGIN CERTIFICATE-----\n" + + "MIIC6TCCAdECASowDQYJKoZIhvcNAQELBQAwDzENMAsGA1UEAwwEUm9vdDAeFw0x\n" + + "NDExMTgxNjU0MDNaFw0zNDExMTMxNjU0MDNaMGYxCzAJBgNVBAYTAlVTMRMwEQYD\n" + + "VQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRQwEgYDVQQK\n" + + "DAtHb29nbGUgSW5jLjEUMBIGA1UEAwwLZm9vLmJhci5jb20wggEiMA0GCSqGSIb3\n" + + "DQEBAQUAA4IBDwAwggEKAoIBAQCzFVKJOkqTmyyjMHWBOrLdpYmc0EcvG3MohaV+\n" + + "UJrVrI2SDykY8YWSkTKz9BKmF8HP/GjPPDs3184Cej9b1WeyvVB8Rj3guH3oL+sJ\n" + + "T3u9V2y4zyo5xO6FWMBYEQ6X8DkGlYtTp5theYbRrXNELul4lF+LtHTCaAANRMkO\n" + + "l0NEoLa6BRhOG68gFfIAxx5lT8REE9utvPuy+rCaBHnfHOPf8pn0LSvceBijSIFo\n" + + "S3Y5crjPVjyiPAZUHWnHTFAilfHnpLBlGxpCylePQhMKrPcgvDoD9nd0LA6xYLF7\n" + + "DPXXSa8FLO+fPV8CNJCAsFuq9Rlf2Tt3SjLtWRYuh5LuctP7AgMBAAEwDQYJKoZI\n" + + "hvcNAQELBQADggEBAEsMABZl+8Rlk0hqBktsDurri4nF/07CnSBe/zUbTiYhMpr7\n" + + "VRIDlHLoe5lslLilfXzvaymcMFeH1uBxNwhf7IO7WvIwQeUHSV+rHyNygTTieO0J\n" + + "n8Hw+4SCohHAdMvD5uWEwn3Lv+W4y7OhaSbzlhVCVCnFLVKicBayUXHtdJXJICok\n" + + "R4+h/WNM7g0iKThakZOyfb8h1phy7TMTVlPFKrcVDo5m9+GhtPC4PNjGLok6r/jx\n" + + "9CIOCapIqi8fXJEOxKvilYeAYqfjWvhx00juEUBHrpCQ8wT4TA+LlI02cRz5rxW4\n" + + "FQAz1NdoG9HZDZWa+NNFTZdAmtWPJMLd+8L8sl4=\n" + + "-----END CERTIFICATE-----"); - /** - * Private key for {@code FOO_BAR_COM_CERT}. - */ + /** Private key for {@code FOO_BAR_COM_CERT}. */ public static final String FOO_BAR_COM_KEY = "-----BEGIN PRIVATE KEY-----\n" - + "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCzFVKJOkqTmyyj\n" - + "MHWBOrLdpYmc0EcvG3MohaV+UJrVrI2SDykY8YWSkTKz9BKmF8HP/GjPPDs3184C\n" - + "ej9b1WeyvVB8Rj3guH3oL+sJT3u9V2y4zyo5xO6FWMBYEQ6X8DkGlYtTp5theYbR\n" - + "rXNELul4lF+LtHTCaAANRMkOl0NEoLa6BRhOG68gFfIAxx5lT8REE9utvPuy+rCa\n" - + "BHnfHOPf8pn0LSvceBijSIFoS3Y5crjPVjyiPAZUHWnHTFAilfHnpLBlGxpCyleP\n" - + "QhMKrPcgvDoD9nd0LA6xYLF7DPXXSa8FLO+fPV8CNJCAsFuq9Rlf2Tt3SjLtWRYu\n" - + "h5LuctP7AgMBAAECggEBAJZQomue6vQEfq4nQaoL/BCBHwXp6KYIs1ti+msQ+zW4\n" - + "1Ueww/001LoWd+mGR5T0QfDy24J++vG/iSKZO884TAdCUmlNiCi0krIubmjtN17R\n" - + "H+frs3Sz8MUqnqANCSPNNgBpy32XJJvnppserK6hdcSJPb2E5bA8HTcF8oD1xDe4\n" - + "CgPK9PKL2PxrR0ofs09RLGTSdh2+rPWvvefk1x1uBfg+wHRlfvqMSKpZ3SDabjhy\n" - + "PgB21D86SlF5L1AfeqSTfQvmwMLtOpJCVjLK2WZmvdoY7kbwE416AMLxX4tw2a/l\n" - + "vzVyo2T/B0Wc3be+5m2o96TctHRH1yEK4huEOJojvBECgYEA5RloFMOnYNMZdoEf\n" - + "yl6TAPEmFD7vCYHXpSlQdFFKu988CNF5+grn9kjC7rF+JxPEYUsnNA11TFzFEfki\n" - + "Lu0uXirJH+0gQzEp/qGd2SjDANCk+kORjeOOmefbxziG/Y74rnJ1A7gZjL8Abrie\n" - + "K0mTfOk9DcgqX96PP4HXgX3+XYkCgYEAyBx28UNZoL3Dy8iquTV0VmXCOq2c5+aW\n" - + "3YS2BKP9rAPy5mWWy6PR28yduomuUu04GxHYf2yw0+0UxpPyWu8TdQHJKjLX4On7\n" - + "L+ZholvXpyqs51btsbBiRK022akh/MPnqdD9zt/RS2b1QM4yfEWN8kVE3zsMWxMP\n" - + "gBf9EH4taGMCgYBfsD3ttk65vVI8UfBiSSAjW5WpDSQwF2BnpprpCm8pizL7B+tn\n" - + "iZibIIbyxYXIcpQqgwZL0nc0vua8/A7QBNbCFCLPR+6awfUlWoGgi0rvkzXlJcWs\n" - + "uuf71oDQdAbF7yplSn8fX4ykYb6fgFLoB6InoQ+UKw+v3Th9sRC/EE3m6QKBgDBN\n" - + "RpyHwDufcoJe5m6cK3+rQk29mFEVhLblkLXgC5wYu+nG/bYbzcz7P9tF3nEf11oZ\n" - + "XaOsTaZp5IjmLyqp6I1mp/LqoNcmQz5Vop15A73S/Dc+8VLhm2auVL4HKDAF7YY8\n" - + "7vafabqEmJBS9Tav50piU/R6IUpeeHBX2frAKh+3AoGAPTLxTMMEbhZGJFs8GRP9\n" - + "fFyWZeEkf3tgUK19tAAOk3TX+O0TNvD8UouXq7Z/EUaE1mYhKPf5LbI6nbYEVll4\n" - + "mWLGd+o8FNFp6E5083O3Tgf0BI4l+sKnwpP/Sqg9BDGARTPS5taeX0SWtQ+HPYGC\n" - + "4e5m59uhN7t8tHtDVcK0/Pk=\n" - + "-----END PRIVATE KEY-----"; + + "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCzFVKJOkqTmyyj\n" + + "MHWBOrLdpYmc0EcvG3MohaV+UJrVrI2SDykY8YWSkTKz9BKmF8HP/GjPPDs3184C\n" + + "ej9b1WeyvVB8Rj3guH3oL+sJT3u9V2y4zyo5xO6FWMBYEQ6X8DkGlYtTp5theYbR\n" + + "rXNELul4lF+LtHTCaAANRMkOl0NEoLa6BRhOG68gFfIAxx5lT8REE9utvPuy+rCa\n" + + "BHnfHOPf8pn0LSvceBijSIFoS3Y5crjPVjyiPAZUHWnHTFAilfHnpLBlGxpCyleP\n" + + "QhMKrPcgvDoD9nd0LA6xYLF7DPXXSa8FLO+fPV8CNJCAsFuq9Rlf2Tt3SjLtWRYu\n" + + "h5LuctP7AgMBAAECggEBAJZQomue6vQEfq4nQaoL/BCBHwXp6KYIs1ti+msQ+zW4\n" + + "1Ueww/001LoWd+mGR5T0QfDy24J++vG/iSKZO884TAdCUmlNiCi0krIubmjtN17R\n" + + "H+frs3Sz8MUqnqANCSPNNgBpy32XJJvnppserK6hdcSJPb2E5bA8HTcF8oD1xDe4\n" + + "CgPK9PKL2PxrR0ofs09RLGTSdh2+rPWvvefk1x1uBfg+wHRlfvqMSKpZ3SDabjhy\n" + + "PgB21D86SlF5L1AfeqSTfQvmwMLtOpJCVjLK2WZmvdoY7kbwE416AMLxX4tw2a/l\n" + + "vzVyo2T/B0Wc3be+5m2o96TctHRH1yEK4huEOJojvBECgYEA5RloFMOnYNMZdoEf\n" + + "yl6TAPEmFD7vCYHXpSlQdFFKu988CNF5+grn9kjC7rF+JxPEYUsnNA11TFzFEfki\n" + + "Lu0uXirJH+0gQzEp/qGd2SjDANCk+kORjeOOmefbxziG/Y74rnJ1A7gZjL8Abrie\n" + + "K0mTfOk9DcgqX96PP4HXgX3+XYkCgYEAyBx28UNZoL3Dy8iquTV0VmXCOq2c5+aW\n" + + "3YS2BKP9rAPy5mWWy6PR28yduomuUu04GxHYf2yw0+0UxpPyWu8TdQHJKjLX4On7\n" + + "L+ZholvXpyqs51btsbBiRK022akh/MPnqdD9zt/RS2b1QM4yfEWN8kVE3zsMWxMP\n" + + "gBf9EH4taGMCgYBfsD3ttk65vVI8UfBiSSAjW5WpDSQwF2BnpprpCm8pizL7B+tn\n" + + "iZibIIbyxYXIcpQqgwZL0nc0vua8/A7QBNbCFCLPR+6awfUlWoGgi0rvkzXlJcWs\n" + + "uuf71oDQdAbF7yplSn8fX4ykYb6fgFLoB6InoQ+UKw+v3Th9sRC/EE3m6QKBgDBN\n" + + "RpyHwDufcoJe5m6cK3+rQk29mFEVhLblkLXgC5wYu+nG/bYbzcz7P9tF3nEf11oZ\n" + + "XaOsTaZp5IjmLyqp6I1mp/LqoNcmQz5Vop15A73S/Dc+8VLhm2auVL4HKDAF7YY8\n" + + "7vafabqEmJBS9Tav50piU/R6IUpeeHBX2frAKh+3AoGAPTLxTMMEbhZGJFs8GRP9\n" + + "fFyWZeEkf3tgUK19tAAOk3TX+O0TNvD8UouXq7Z/EUaE1mYhKPf5LbI6nbYEVll4\n" + + "mWLGd+o8FNFp6E5083O3Tgf0BI4l+sKnwpP/Sqg9BDGARTPS5taeX0SWtQ+HPYGC\n" + + "4e5m59uhN7t8tHtDVcK0/Pk=\n" + + "-----END PRIVATE KEY-----"; /** * Test CA Certificate. + * *
        * Issuer: CN=Root
        * Subject: CN=Root
        * 
    */ - public static final CertData CA_CERT = new CertData( - "-----BEGIN CERTIFICATE-----\n" - + "MIIC8TCCAdmgAwIBAgIJAMNI15HrGylkMA0GCSqGSIb3DQEBCwUAMA8xDTALBgNV\n" - + "BAMMBFJvb3QwHhcNMTQxMTE4MTY1NDAzWhcNMzQxMTEzMTY1NDAzWjAPMQ0wCwYD\n" - + "VQQDDARSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzeUNc4bS\n" - + "WHhOTU+5MQ/lOmmjQWpfBi+FJuxvoeOmQwi6frPKKsaKKYGfCTPlKE0dmrEP95bn\n" - + "i/qL5xApP17orjUe6KRtJAwFNI5EZadIfjbh/q+85C1Cp2BS2YmuZQzXZHP63yyB\n" - + "p05YcbMKwCBHXaAgYbmTTk+4+1pjNpHP6YiF2gCPvSfzokGyhbvBqnPbnTdI9w6f\n" - + "jNBYAbr/uBOTU0vK4ktzlWk5lvsm51e8vsLSqWhoHADq0AriAelU4SHsSACkRUQS\n" - + "xWV0K5hzTv4ecvCbG9dskiDCwWg+uTRSoAFeZOhONL000q7Vey3DZTcLl8/O4NQV\n" - + "aZR5iAgVWlWcswIDAQABo1AwTjAdBgNVHQ4EFgQUsimlIRDcJR0ofR7oM8KwHFOH\n" - + "+sIwHwYDVR0jBBgwFoAUsimlIRDcJR0ofR7oM8KwHFOH+sIwDAYDVR0TBAUwAwEB\n" - + "/zANBgkqhkiG9w0BAQsFAAOCAQEAWQl8SmbQoBV3tjOJ8zMlcN0xOPpSSNbx0g7E\n" - + "L/dQgJpet0McW62RHlgQAOKbS3PReo2nsRB/ZRyYDu4i13ZHZ8bMsGOES4BQpz13\n" - + "mtmXg9RhsXqL0eDYfBcjjtlruUbxhnALp4VN1zVdyWAPCj0eu3MxpgMWcyn50Qmi\n" - + "JSj/Equ/lLhve/wKvjG5WhnV8uRKRuFbFct0DHAHMnZqFHcGS5So0cYnSfK5fbBR\n" - + "NelGflhpbbPp0V0aXiqinqD0Ye3OaZdFq+2rP1oC/a5/Ou4LspY3b5oD9rENdy7b\n" - + "q0KewPFtgPvUkJrJ3TzbiwvpghZ7zG26bnJ5I7uc4y1VujqaOA==\n" - + "-----END CERTIFICATE-----"); + public static final CertData CA_CERT = + new CertData( + "-----BEGIN CERTIFICATE-----\n" + + "MIIC8TCCAdmgAwIBAgIJAMNI15HrGylkMA0GCSqGSIb3DQEBCwUAMA8xDTALBgNV\n" + + "BAMMBFJvb3QwHhcNMTQxMTE4MTY1NDAzWhcNMzQxMTEzMTY1NDAzWjAPMQ0wCwYD\n" + + "VQQDDARSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzeUNc4bS\n" + + "WHhOTU+5MQ/lOmmjQWpfBi+FJuxvoeOmQwi6frPKKsaKKYGfCTPlKE0dmrEP95bn\n" + + "i/qL5xApP17orjUe6KRtJAwFNI5EZadIfjbh/q+85C1Cp2BS2YmuZQzXZHP63yyB\n" + + "p05YcbMKwCBHXaAgYbmTTk+4+1pjNpHP6YiF2gCPvSfzokGyhbvBqnPbnTdI9w6f\n" + + "jNBYAbr/uBOTU0vK4ktzlWk5lvsm51e8vsLSqWhoHADq0AriAelU4SHsSACkRUQS\n" + + "xWV0K5hzTv4ecvCbG9dskiDCwWg+uTRSoAFeZOhONL000q7Vey3DZTcLl8/O4NQV\n" + + "aZR5iAgVWlWcswIDAQABo1AwTjAdBgNVHQ4EFgQUsimlIRDcJR0ofR7oM8KwHFOH\n" + + "+sIwHwYDVR0jBBgwFoAUsimlIRDcJR0ofR7oM8KwHFOH+sIwDAYDVR0TBAUwAwEB\n" + + "/zANBgkqhkiG9w0BAQsFAAOCAQEAWQl8SmbQoBV3tjOJ8zMlcN0xOPpSSNbx0g7E\n" + + "L/dQgJpet0McW62RHlgQAOKbS3PReo2nsRB/ZRyYDu4i13ZHZ8bMsGOES4BQpz13\n" + + "mtmXg9RhsXqL0eDYfBcjjtlruUbxhnALp4VN1zVdyWAPCj0eu3MxpgMWcyn50Qmi\n" + + "JSj/Equ/lLhve/wKvjG5WhnV8uRKRuFbFct0DHAHMnZqFHcGS5So0cYnSfK5fbBR\n" + + "NelGflhpbbPp0V0aXiqinqD0Ye3OaZdFq+2rP1oC/a5/Ou4LspY3b5oD9rENdy7b\n" + + "q0KewPFtgPvUkJrJ3TzbiwvpghZ7zG26bnJ5I7uc4y1VujqaOA==\n" + + "-----END CERTIFICATE-----"); - /** - * Private key for {@code CA_CERT}. - */ + /** Private key for {@code CA_CERT}. */ public static final String CA_KEY = "-----BEGIN PRIVATE KEY-----\n" - + "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDN5Q1zhtJYeE5N\n" - + "T7kxD+U6aaNBal8GL4Um7G+h46ZDCLp+s8oqxoopgZ8JM+UoTR2asQ/3lueL+ovn\n" - + "ECk/XuiuNR7opG0kDAU0jkRlp0h+NuH+r7zkLUKnYFLZia5lDNdkc/rfLIGnTlhx\n" - + "swrAIEddoCBhuZNOT7j7WmM2kc/piIXaAI+9J/OiQbKFu8Gqc9udN0j3Dp+M0FgB\n" - + "uv+4E5NTS8riS3OVaTmW+ybnV7y+wtKpaGgcAOrQCuIB6VThIexIAKRFRBLFZXQr\n" - + "mHNO/h5y8Jsb12ySIMLBaD65NFKgAV5k6E40vTTSrtV7LcNlNwuXz87g1BVplHmI\n" - + "CBVaVZyzAgMBAAECggEANfRuP/X2rURpkIzxxM+bjGEebQgI+r/9LqQK5OuZKDvj\n" - + "U0yeD/OTRSk4mdrFlHgQ5/a6bnFXIDF59AUiKf8fDnfRL7nW9/lGa+1UMydRMfID\n" - + "6w/2efz6WI4/Z85SqxxgXWyfM1igaU14k+MNUCelS/2oPrO4zG7L1OJs2WIAj/vE\n" - + "HnndSBa3rvTXmY37JclkChFokG0svuZMmaXWG1JI6JziSsvO4YZAYvZ10yCvbFzZ\n" - + "iczMCyyGhRcUeG3wbVDK0lPp5f1jKtyfuQtR2uFhdRHUk2+cMY6s/o3hgdW5b/z9\n" - + "Yddyw28tC6/uECHJs8dsmNM4hPc+n2+wCVwB9HbSMQKBgQD3V640Tv5UWiHM4lGq\n" - + "pSdUViNsLgDLmNplWLB0aRbBgTsJLGlzI1sGqSEydlZORYZT4GBdLmTJdumBGBAn\n" - + "4FxfyyAVjjn8WjYo9ocyMrIGLFKF3EvSyx4opsOX6QOyuyzdDhzt+BkY66Zb0Bgl\n" - + "lzUQ4S6hhvvEQc5COiNmTuDT/QKBgQDVGfpp8yBamTyRgGQWTwRqIQuJC2QHOrhV\n" - + "OKQ7NwMyMObyML0ZQm2SCu+Oo0qsMxz8Ix6sNtnJfxZxpUYCLG3HWc6EfaGT1hDR\n" - + "EgWsdl9J/xP/KwgSzHuSqZTCuNQRTg/XbNfjXnMHy8UaTBL+0jHLAnmvczBrSnEM\n" - + "r8RgkjoabwKBgQCkuklz3vQ1O33tVQEs1Cc4XNHkl1LCRb+V5ZZHQUH9h9LIjkKA\n" - + "gxh5fCR21icuo9ENhY7IIEDRiBeFeYAw/pSm28I3eOyXa4FMkLuDrA2yXMxtCEWb\n" - + "Utl4G3CCeJaU72G2q1KLDkOwvCikVxft2SFnZ4FF5H9CuszigJPY7EmCBQKBgD+/\n" - + "fra1IWeY0ZKhOs+loadx7TZ47tpuyXfM8uw337/i+yNWSytEQOzgUptz48GxpKkU\n" - + "hHd2DR6G4xrqGxBJZCmvhuUBhBVqgytX3dSisIy9PqkloUumWg0cp8C8c8wdcwW5\n" - + "rLd6qKSbY4IjYcdS78xQGEDRD5n48eqepftRowoHAoGBAMdJ5/QwIymaTBhblYiL\n" - + "nvzZZ6kvxqId+JF93skZJ4NdQ346CVcWWbjTwO/oaJ9ri3MsWY18t4uSIYeYyaCa\n" - + "5dqQo3nObq2jqxFby92GWSNrwape2FvRGzJ7hnr44EkxUlQPeICod84RI/1mdOM8\n" - + "E+VTo/KjRA8P2ogks9bltd6f\n" - + "-----END PRIVATE KEY-----"; + + "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDN5Q1zhtJYeE5N\n" + + "T7kxD+U6aaNBal8GL4Um7G+h46ZDCLp+s8oqxoopgZ8JM+UoTR2asQ/3lueL+ovn\n" + + "ECk/XuiuNR7opG0kDAU0jkRlp0h+NuH+r7zkLUKnYFLZia5lDNdkc/rfLIGnTlhx\n" + + "swrAIEddoCBhuZNOT7j7WmM2kc/piIXaAI+9J/OiQbKFu8Gqc9udN0j3Dp+M0FgB\n" + + "uv+4E5NTS8riS3OVaTmW+ybnV7y+wtKpaGgcAOrQCuIB6VThIexIAKRFRBLFZXQr\n" + + "mHNO/h5y8Jsb12ySIMLBaD65NFKgAV5k6E40vTTSrtV7LcNlNwuXz87g1BVplHmI\n" + + "CBVaVZyzAgMBAAECggEANfRuP/X2rURpkIzxxM+bjGEebQgI+r/9LqQK5OuZKDvj\n" + + "U0yeD/OTRSk4mdrFlHgQ5/a6bnFXIDF59AUiKf8fDnfRL7nW9/lGa+1UMydRMfID\n" + + "6w/2efz6WI4/Z85SqxxgXWyfM1igaU14k+MNUCelS/2oPrO4zG7L1OJs2WIAj/vE\n" + + "HnndSBa3rvTXmY37JclkChFokG0svuZMmaXWG1JI6JziSsvO4YZAYvZ10yCvbFzZ\n" + + "iczMCyyGhRcUeG3wbVDK0lPp5f1jKtyfuQtR2uFhdRHUk2+cMY6s/o3hgdW5b/z9\n" + + "Yddyw28tC6/uECHJs8dsmNM4hPc+n2+wCVwB9HbSMQKBgQD3V640Tv5UWiHM4lGq\n" + + "pSdUViNsLgDLmNplWLB0aRbBgTsJLGlzI1sGqSEydlZORYZT4GBdLmTJdumBGBAn\n" + + "4FxfyyAVjjn8WjYo9ocyMrIGLFKF3EvSyx4opsOX6QOyuyzdDhzt+BkY66Zb0Bgl\n" + + "lzUQ4S6hhvvEQc5COiNmTuDT/QKBgQDVGfpp8yBamTyRgGQWTwRqIQuJC2QHOrhV\n" + + "OKQ7NwMyMObyML0ZQm2SCu+Oo0qsMxz8Ix6sNtnJfxZxpUYCLG3HWc6EfaGT1hDR\n" + + "EgWsdl9J/xP/KwgSzHuSqZTCuNQRTg/XbNfjXnMHy8UaTBL+0jHLAnmvczBrSnEM\n" + + "r8RgkjoabwKBgQCkuklz3vQ1O33tVQEs1Cc4XNHkl1LCRb+V5ZZHQUH9h9LIjkKA\n" + + "gxh5fCR21icuo9ENhY7IIEDRiBeFeYAw/pSm28I3eOyXa4FMkLuDrA2yXMxtCEWb\n" + + "Utl4G3CCeJaU72G2q1KLDkOwvCikVxft2SFnZ4FF5H9CuszigJPY7EmCBQKBgD+/\n" + + "fra1IWeY0ZKhOs+loadx7TZ47tpuyXfM8uw337/i+yNWSytEQOzgUptz48GxpKkU\n" + + "hHd2DR6G4xrqGxBJZCmvhuUBhBVqgytX3dSisIy9PqkloUumWg0cp8C8c8wdcwW5\n" + + "rLd6qKSbY4IjYcdS78xQGEDRD5n48eqepftRowoHAoGBAMdJ5/QwIymaTBhblYiL\n" + + "nvzZZ6kvxqId+JF93skZJ4NdQ346CVcWWbjTwO/oaJ9ri3MsWY18t4uSIYeYyaCa\n" + + "5dqQo3nObq2jqxFby92GWSNrwape2FvRGzJ7hnr44EkxUlQPeICod84RI/1mdOM8\n" + + "E+VTo/KjRA8P2ogks9bltd6f\n" + + "-----END PRIVATE KEY-----"; /** * CA certificate signed with a bogus key. + * *
        * Issuer: CN=Root
        * Subject: CN=Root
        * 
    */ - public static final CertData BOGUS_CA_CERT = new CertData( - "-----BEGIN CERTIFICATE-----\n" - + "MIIC8TCCAdmgAwIBAgIJAP2af/EIgk6oMA0GCSqGSIb3DQEBCwUAMA8xDTALBgNV\n" - + "BAMMBFJvb3QwHhcNMTQxMTE5MTEwNDMyWhcNMzQxMTE0MTEwNDMyWjAPMQ0wCwYD\n" - + "VQQDDARSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtPB0EtUf\n" - + "aVS9LRljaL4NTYp0tJooMrRTI4ht4ixIv7m6XTSbxVjOtY0228ZPWeUE/3wduezW\n" - + "1rWNU4Uh4ezW0rw9CmW6m2zsMjjGwjY4A5ctMRDlgQtxzfHSPWPtTixtBr3YpdcP\n" - + "mg9xVIYvSHZ+fA3x5dRFRxdNidVrndVINzUoaoD9hZ/sgCKg9c2hdDSO9prrTpXD\n" - + "yatgLZ8LsFJO94HrkfFsQqquwxxvpixyWtjWUpnO28jnbDRC0ADOp/WZQ8exOP+a\n" - + "XUcrHdIsC0RcB6csnM6EarfwEm1jnBwDi37Rxk2BFiBYyzEbCrn7M6QY/DQrZJbw\n" - + "9gzSIvT2+5OvawIDAQABo1AwTjAdBgNVHQ4EFgQUYo97/In/SDI+pKRTSrSVhPyq\n" - + "5UQwHwYDVR0jBBgwFoAUYo97/In/SDI+pKRTSrSVhPyq5UQwDAYDVR0TBAUwAwEB\n" - + "/zANBgkqhkiG9w0BAQsFAAOCAQEABuUZ+sF4QD8H+PHvJLz+3+puXYvvE2IpcC65\n" - + "RQznp5iq5Rs4oGJvYwyD1bVUbCNz1IoyB9Lfo5QmSuyV1JybalBZ9FCDzZunBT3O\n" - + "4Tr6KfziVPHat3vYMNzzJY/IU3u6uLDmqm1J6qoSBkq4yL1AaHFon2j9gT3FXvVk\n" - + "7f1DjztAplWQBC4ScepJbiIRJkLxThDmM2g1xKUtZ6LlPL5J5CmXutzWbV5YS1eo\n" - + "uVrDRTmXr4wLzpcURWWB2gbPc0l7+1TfvTydVEp7YqN1EhvNmvsejiQCy+4Cq/D1\n" - + "m4rBV4SLLaHstTQNqcK1djxy2FbpYD7j5Himdc0oUeYif9gZ9g==\n" - + "-----END CERTIFICATE-----\n"); + public static final CertData BOGUS_CA_CERT = + new CertData( + "-----BEGIN CERTIFICATE-----\n" + + "MIIC8TCCAdmgAwIBAgIJAP2af/EIgk6oMA0GCSqGSIb3DQEBCwUAMA8xDTALBgNV\n" + + "BAMMBFJvb3QwHhcNMTQxMTE5MTEwNDMyWhcNMzQxMTE0MTEwNDMyWjAPMQ0wCwYD\n" + + "VQQDDARSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtPB0EtUf\n" + + "aVS9LRljaL4NTYp0tJooMrRTI4ht4ixIv7m6XTSbxVjOtY0228ZPWeUE/3wduezW\n" + + "1rWNU4Uh4ezW0rw9CmW6m2zsMjjGwjY4A5ctMRDlgQtxzfHSPWPtTixtBr3YpdcP\n" + + "mg9xVIYvSHZ+fA3x5dRFRxdNidVrndVINzUoaoD9hZ/sgCKg9c2hdDSO9prrTpXD\n" + + "yatgLZ8LsFJO94HrkfFsQqquwxxvpixyWtjWUpnO28jnbDRC0ADOp/WZQ8exOP+a\n" + + "XUcrHdIsC0RcB6csnM6EarfwEm1jnBwDi37Rxk2BFiBYyzEbCrn7M6QY/DQrZJbw\n" + + "9gzSIvT2+5OvawIDAQABo1AwTjAdBgNVHQ4EFgQUYo97/In/SDI+pKRTSrSVhPyq\n" + + "5UQwHwYDVR0jBBgwFoAUYo97/In/SDI+pKRTSrSVhPyq5UQwDAYDVR0TBAUwAwEB\n" + + "/zANBgkqhkiG9w0BAQsFAAOCAQEABuUZ+sF4QD8H+PHvJLz+3+puXYvvE2IpcC65\n" + + "RQznp5iq5Rs4oGJvYwyD1bVUbCNz1IoyB9Lfo5QmSuyV1JybalBZ9FCDzZunBT3O\n" + + "4Tr6KfziVPHat3vYMNzzJY/IU3u6uLDmqm1J6qoSBkq4yL1AaHFon2j9gT3FXvVk\n" + + "7f1DjztAplWQBC4ScepJbiIRJkLxThDmM2g1xKUtZ6LlPL5J5CmXutzWbV5YS1eo\n" + + "uVrDRTmXr4wLzpcURWWB2gbPc0l7+1TfvTydVEp7YqN1EhvNmvsejiQCy+4Cq/D1\n" + + "m4rBV4SLLaHstTQNqcK1djxy2FbpYD7j5Himdc0oUeYif9gZ9g==\n" + + "-----END CERTIFICATE-----\n"); /** - * A test JWS signature. - *

    - * The signed JSON is the following message: + * A test JWS signature. + * + *

    The signed JSON is the following message: + * *

        * {"foo":"bar"}
        * 
    - * The message is signed using {@code FOO_BAR_COM_KEY}. + * + *

    The message is signed using {@code FOO_BAR_COM_KEY}. */ public static final String JWS_SIGNATURE = "eyJhbGciOiJSUzI1NiIsIng1YyI6WyJNSUlDNlRDQ0FkRUNBU293RFFZSktvWklo" - + "dmNOQVFFTEJRQXdEekVOTUFzR0ExVUVBd3dFVW05dmREQWVGdzB4TkRFeE1UZ3hO" - + "alUwTUROYUZ3MHpOREV4TVRNeE5qVTBNRE5hTUdZeEN6QUpCZ05WQkFZVEFsVlRN" - + "Uk13RVFZRFZRUUlEQXBEWVd4cFptOXlibWxoTVJZd0ZBWURWUVFIREExTmIzVnVk" - + "R0ZwYmlCV2FXVjNNUlF3RWdZRFZRUUtEQXRIYjI5bmJHVWdTVzVqTGpFVU1CSUdB" - + "MVVFQXd3TFptOXZMbUpoY2k1amIyMHdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFB" - + "NElCRHdBd2dnRUtBb0lCQVFDekZWS0pPa3FUbXl5ak1IV0JPckxkcFltYzBFY3ZH" - + "M01vaGFWK1VKclZySTJTRHlrWThZV1NrVEt6OUJLbUY4SFAvR2pQUERzMzE4NENl" - + "ajliMVdleXZWQjhSajNndUgzb0wrc0pUM3U5VjJ5NHp5bzV4TzZGV01CWUVRNlg4" - + "RGtHbFl0VHA1dGhlWWJSclhORUx1bDRsRitMdEhUQ2FBQU5STWtPbDBORW9MYTZC" - + "UmhPRzY4Z0ZmSUF4eDVsVDhSRUU5dXR2UHV5K3JDYUJIbmZIT1BmOHBuMExTdmNl" - + "QmlqU0lGb1MzWTVjcmpQVmp5aVBBWlVIV25IVEZBaWxmSG5wTEJsR3hwQ3lsZVBR" - + "aE1LclBjZ3ZEb0Q5bmQwTEE2eFlMRjdEUFhYU2E4RkxPK2ZQVjhDTkpDQXNGdXE5" - + "UmxmMlR0M1NqTHRXUll1aDVMdWN0UDdBZ01CQUFFd0RRWUpLb1pJaHZjTkFRRUxC" - + "UUFEZ2dFQkFFc01BQlpsKzhSbGswaHFCa3RzRHVycmk0bkYvMDdDblNCZS96VWJU" - + "aVloTXByN1ZSSURsSExvZTVsc2xMaWxmWHp2YXltY01GZUgxdUJ4TndoZjdJTzdX" - + "dkl3UWVVSFNWK3JIeU55Z1RUaWVPMEpuOEh3KzRTQ29oSEFkTXZENXVXRXduM0x2" - + "K1c0eTdPaGFTYnpsaFZDVkNuRkxWS2ljQmF5VVhIdGRKWEpJQ29rUjQraC9XTk03" - + "ZzBpS1RoYWtaT3lmYjhoMXBoeTdUTVRWbFBGS3JjVkRvNW05K0dodFBDNFBOakdM" - + "b2s2ci9qeDlDSU9DYXBJcWk4ZlhKRU94S3ZpbFllQVlxZmpXdmh4MDBqdUVVQkhy" - + "cENROHdUNFRBK0xsSTAyY1J6NXJ4VzRGUUF6MU5kb0c5SFpEWldhK05ORlRaZEFt" - + "dFdQSk1MZCs4TDhzbDQ9IiwiTUlJQzhUQ0NBZG1nQXdJQkFnSUpBTU5JMTVIckd5" - + "bGtNQTBHQ1NxR1NJYjNEUUVCQ3dVQU1BOHhEVEFMQmdOVkJBTU1CRkp2YjNRd0ho" - + "Y05NVFF4TVRFNE1UWTFOREF6V2hjTk16UXhNVEV6TVRZMU5EQXpXakFQTVEwd0N3" - + "WURWUVFEREFSU2IyOTBNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1J" - + "SUJDZ0tDQVFFQXplVU5jNGJTV0hoT1RVKzVNUS9sT21talFXcGZCaStGSnV4dm9l" - + "T21Rd2k2ZnJQS0tzYUtLWUdmQ1RQbEtFMGRtckVQOTVibmkvcUw1eEFwUDE3b3Jq" - + "VWU2S1J0SkF3Rk5JNUVaYWRJZmpiaC9xKzg1QzFDcDJCUzJZbXVaUXpYWkhQNjN5" - + "eUJwMDVZY2JNS3dDQkhYYUFnWWJtVFRrKzQrMXBqTnBIUDZZaUYyZ0NQdlNmem9r" - + "R3loYnZCcW5QYm5UZEk5dzZmak5CWUFici91Qk9UVTB2SzRrdHpsV2s1bHZzbTUx" - + "ZTh2c0xTcVdob0hBRHEwQXJpQWVsVTRTSHNTQUNrUlVRU3hXVjBLNWh6VHY0ZWN2" - + "Q2JHOWRza2lEQ3dXZyt1VFJTb0FGZVpPaE9OTDAwMHE3VmV5M0RaVGNMbDgvTzRO" - + "UVZhWlI1aUFnVldsV2Nzd0lEQVFBQm8xQXdUakFkQmdOVkhRNEVGZ1FVc2ltbElS" - + "RGNKUjBvZlI3b004S3dIRk9IK3NJd0h3WURWUjBqQkJnd0ZvQVVzaW1sSVJEY0pS" - + "MG9mUjdvTThLd0hGT0grc0l3REFZRFZSMFRCQVV3QXdFQi96QU5CZ2txaGtpRzl3" - + "MEJBUXNGQUFPQ0FRRUFXUWw4U21iUW9CVjN0ak9KOHpNbGNOMHhPUHBTU05ieDBn" - + "N0VML2RRZ0pwZXQwTWNXNjJSSGxnUUFPS2JTM1BSZW8ybnNSQi9aUnlZRHU0aTEz" - + "WkhaOGJNc0dPRVM0QlFwejEzbXRtWGc5UmhzWHFMMGVEWWZCY2pqdGxydVVieGhu" - + "QUxwNFZOMXpWZHlXQVBDajBldTNNeHBnTVdjeW41MFFtaUpTai9FcXUvbExodmUv" - + "d0t2akc1V2huVjh1UktSdUZiRmN0MERIQUhNblpxRkhjR1M1U28wY1luU2ZLNWZi" - + "QlJOZWxHZmxocGJiUHAwVjBhWGlxaW5xRDBZZTNPYVpkRnErMnJQMW9DL2E1L091" - + "NExzcFkzYjVvRDlyRU5keTdicTBLZXdQRnRnUHZVa0pySjNUemJpd3ZwZ2haN3pH" - + "MjZibko1STd1YzR5MVZ1anFhT0E9PSJdfQ.eyJmb28iOiJiYXIifQ.eWzIsJF4PE" - + "xQap9HK6Vlz8DGlgGwoiLCtyOEK0Bfu_yHTAZeApn5rh6Uzfx06Gv6eHdM34YL_t" - + "gLRb4bjuZVA8xvQ9uHNs8UtpBIOiUcagzvtKyyfCofk5U5sNb54GgVVYxa6p4A1O" - + "bdJv1jjlUOnzR8keX5LsAM4Ia7xeqiFh0GER4l0ulVChy_bSn0IeNiKFW7HKcxtc" - + "GO_zZTtlv4HiifuyPSk_ar2IDX1w599KXniVcWkQ_W1zcp5YuPDw8mIQDVCH2uQY" - + "7qs2ejdZj5LIgIz4CbQ0wg53rlwE7DDQM6MNUgZLnzNmMSMfFrpE7_PQyxe2qJCs" - + "ucHODzEHX4Tg"; + + "dmNOQVFFTEJRQXdEekVOTUFzR0ExVUVBd3dFVW05dmREQWVGdzB4TkRFeE1UZ3hO" + + "alUwTUROYUZ3MHpOREV4TVRNeE5qVTBNRE5hTUdZeEN6QUpCZ05WQkFZVEFsVlRN" + + "Uk13RVFZRFZRUUlEQXBEWVd4cFptOXlibWxoTVJZd0ZBWURWUVFIREExTmIzVnVk" + + "R0ZwYmlCV2FXVjNNUlF3RWdZRFZRUUtEQXRIYjI5bmJHVWdTVzVqTGpFVU1CSUdB" + + "MVVFQXd3TFptOXZMbUpoY2k1amIyMHdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFB" + + "NElCRHdBd2dnRUtBb0lCQVFDekZWS0pPa3FUbXl5ak1IV0JPckxkcFltYzBFY3ZH" + + "M01vaGFWK1VKclZySTJTRHlrWThZV1NrVEt6OUJLbUY4SFAvR2pQUERzMzE4NENl" + + "ajliMVdleXZWQjhSajNndUgzb0wrc0pUM3U5VjJ5NHp5bzV4TzZGV01CWUVRNlg4" + + "RGtHbFl0VHA1dGhlWWJSclhORUx1bDRsRitMdEhUQ2FBQU5STWtPbDBORW9MYTZC" + + "UmhPRzY4Z0ZmSUF4eDVsVDhSRUU5dXR2UHV5K3JDYUJIbmZIT1BmOHBuMExTdmNl" + + "QmlqU0lGb1MzWTVjcmpQVmp5aVBBWlVIV25IVEZBaWxmSG5wTEJsR3hwQ3lsZVBR" + + "aE1LclBjZ3ZEb0Q5bmQwTEE2eFlMRjdEUFhYU2E4RkxPK2ZQVjhDTkpDQXNGdXE5" + + "UmxmMlR0M1NqTHRXUll1aDVMdWN0UDdBZ01CQUFFd0RRWUpLb1pJaHZjTkFRRUxC" + + "UUFEZ2dFQkFFc01BQlpsKzhSbGswaHFCa3RzRHVycmk0bkYvMDdDblNCZS96VWJU" + + "aVloTXByN1ZSSURsSExvZTVsc2xMaWxmWHp2YXltY01GZUgxdUJ4TndoZjdJTzdX" + + "dkl3UWVVSFNWK3JIeU55Z1RUaWVPMEpuOEh3KzRTQ29oSEFkTXZENXVXRXduM0x2" + + "K1c0eTdPaGFTYnpsaFZDVkNuRkxWS2ljQmF5VVhIdGRKWEpJQ29rUjQraC9XTk03" + + "ZzBpS1RoYWtaT3lmYjhoMXBoeTdUTVRWbFBGS3JjVkRvNW05K0dodFBDNFBOakdM" + + "b2s2ci9qeDlDSU9DYXBJcWk4ZlhKRU94S3ZpbFllQVlxZmpXdmh4MDBqdUVVQkhy" + + "cENROHdUNFRBK0xsSTAyY1J6NXJ4VzRGUUF6MU5kb0c5SFpEWldhK05ORlRaZEFt" + + "dFdQSk1MZCs4TDhzbDQ9IiwiTUlJQzhUQ0NBZG1nQXdJQkFnSUpBTU5JMTVIckd5" + + "bGtNQTBHQ1NxR1NJYjNEUUVCQ3dVQU1BOHhEVEFMQmdOVkJBTU1CRkp2YjNRd0ho" + + "Y05NVFF4TVRFNE1UWTFOREF6V2hjTk16UXhNVEV6TVRZMU5EQXpXakFQTVEwd0N3" + + "WURWUVFEREFSU2IyOTBNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1J" + + "SUJDZ0tDQVFFQXplVU5jNGJTV0hoT1RVKzVNUS9sT21talFXcGZCaStGSnV4dm9l" + + "T21Rd2k2ZnJQS0tzYUtLWUdmQ1RQbEtFMGRtckVQOTVibmkvcUw1eEFwUDE3b3Jq" + + "VWU2S1J0SkF3Rk5JNUVaYWRJZmpiaC9xKzg1QzFDcDJCUzJZbXVaUXpYWkhQNjN5" + + "eUJwMDVZY2JNS3dDQkhYYUFnWWJtVFRrKzQrMXBqTnBIUDZZaUYyZ0NQdlNmem9r" + + "R3loYnZCcW5QYm5UZEk5dzZmak5CWUFici91Qk9UVTB2SzRrdHpsV2s1bHZzbTUx" + + "ZTh2c0xTcVdob0hBRHEwQXJpQWVsVTRTSHNTQUNrUlVRU3hXVjBLNWh6VHY0ZWN2" + + "Q2JHOWRza2lEQ3dXZyt1VFJTb0FGZVpPaE9OTDAwMHE3VmV5M0RaVGNMbDgvTzRO" + + "UVZhWlI1aUFnVldsV2Nzd0lEQVFBQm8xQXdUakFkQmdOVkhRNEVGZ1FVc2ltbElS" + + "RGNKUjBvZlI3b004S3dIRk9IK3NJd0h3WURWUjBqQkJnd0ZvQVVzaW1sSVJEY0pS" + + "MG9mUjdvTThLd0hGT0grc0l3REFZRFZSMFRCQVV3QXdFQi96QU5CZ2txaGtpRzl3" + + "MEJBUXNGQUFPQ0FRRUFXUWw4U21iUW9CVjN0ak9KOHpNbGNOMHhPUHBTU05ieDBn" + + "N0VML2RRZ0pwZXQwTWNXNjJSSGxnUUFPS2JTM1BSZW8ybnNSQi9aUnlZRHU0aTEz" + + "WkhaOGJNc0dPRVM0QlFwejEzbXRtWGc5UmhzWHFMMGVEWWZCY2pqdGxydVVieGhu" + + "QUxwNFZOMXpWZHlXQVBDajBldTNNeHBnTVdjeW41MFFtaUpTai9FcXUvbExodmUv" + + "d0t2akc1V2huVjh1UktSdUZiRmN0MERIQUhNblpxRkhjR1M1U28wY1luU2ZLNWZi" + + "QlJOZWxHZmxocGJiUHAwVjBhWGlxaW5xRDBZZTNPYVpkRnErMnJQMW9DL2E1L091" + + "NExzcFkzYjVvRDlyRU5keTdicTBLZXdQRnRnUHZVa0pySjNUemJpd3ZwZ2haN3pH" + + "MjZibko1STd1YzR5MVZ1anFhT0E9PSJdfQ.eyJmb28iOiJiYXIifQ.eWzIsJF4PE" + + "xQap9HK6Vlz8DGlgGwoiLCtyOEK0Bfu_yHTAZeApn5rh6Uzfx06Gv6eHdM34YL_t" + + "gLRb4bjuZVA8xvQ9uHNs8UtpBIOiUcagzvtKyyfCofk5U5sNb54GgVVYxa6p4A1O" + + "bdJv1jjlUOnzR8keX5LsAM4Ia7xeqiFh0GER4l0ulVChy_bSn0IeNiKFW7HKcxtc" + + "GO_zZTtlv4HiifuyPSk_ar2IDX1w599KXniVcWkQ_W1zcp5YuPDw8mIQDVCH2uQY" + + "7qs2ejdZj5LIgIz4CbQ0wg53rlwE7DDQM6MNUgZLnzNmMSMfFrpE7_PQyxe2qJCs" + + "ucHODzEHX4Tg"; private static JsonWebSignature jsonWebSignature = null; @@ -307,8 +309,8 @@ public static JsonWebSignature getJsonWebSignature() throws IOException { int secondDot = JWS_SIGNATURE.indexOf('.', firstDot + 1); byte[] signatureBytes = Base64.decodeBase64(JWS_SIGNATURE.substring(secondDot + 1)); byte[] signedContentBytes = StringUtils.getBytesUtf8(JWS_SIGNATURE.substring(0, secondDot)); - JsonWebSignature signature = new JsonWebSignature( - header, payload, signatureBytes, signedContentBytes); + JsonWebSignature signature = + new JsonWebSignature(header, payload, signatureBytes, signedContentBytes); jsonWebSignature = signature; } return jsonWebSignature; diff --git a/google-http-client/src/main/java/com/google/api/client/testing/json/webtoken/package-info.java b/google-http-client/src/main/java/com/google/api/client/testing/json/webtoken/package-info.java index d5fd7da84..893f89883 100644 --- a/google-http-client/src/main/java/com/google/api/client/testing/json/webtoken/package-info.java +++ b/google-http-client/src/main/java/com/google/api/client/testing/json/webtoken/package-info.java @@ -13,7 +13,7 @@ */ /** - * {@link com.google.api.client.util.Beta}
    + * {@link com.google.api.client.util.Beta}
    * Testing utilities used for writing tests for JSON WebToken. * * @since 1.19.1 diff --git a/google-http-client/src/main/java/com/google/api/client/testing/util/LogRecordingHandler.java b/google-http-client/src/main/java/com/google/api/client/testing/util/LogRecordingHandler.java index 5d223f866..0229cd26b 100644 --- a/google-http-client/src/main/java/com/google/api/client/testing/util/LogRecordingHandler.java +++ b/google-http-client/src/main/java/com/google/api/client/testing/util/LogRecordingHandler.java @@ -16,13 +16,12 @@ import com.google.api.client.util.Beta; import com.google.api.client.util.Lists; - import java.util.List; import java.util.logging.Handler; import java.util.logging.LogRecord; /** - * {@link Beta}
    + * {@link Beta}
    * Logging handler that stores log records. * * @author Yaniv Inbar @@ -40,12 +39,10 @@ public void publish(LogRecord record) { } @Override - public void flush() { - } + public void flush() {} @Override - public void close() throws SecurityException { - } + public void close() throws SecurityException {} /** Returns a new instance of a list of published record messages. */ public List messages() { diff --git a/google-http-client/src/main/java/com/google/api/client/testing/util/MockBackOff.java b/google-http-client/src/main/java/com/google/api/client/testing/util/MockBackOff.java index 6e5a85415..cf036a83f 100644 --- a/google-http-client/src/main/java/com/google/api/client/testing/util/MockBackOff.java +++ b/google-http-client/src/main/java/com/google/api/client/testing/util/MockBackOff.java @@ -17,16 +17,13 @@ import com.google.api.client.util.BackOff; import com.google.api.client.util.Beta; import com.google.api.client.util.Preconditions; - import java.io.IOException; /** - * {@link Beta}
    + * {@link Beta}
    * Mock for {@link BackOff} that always returns a fixed number. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @author Yaniv Inbar * @since 1.15 @@ -58,10 +55,8 @@ public long nextBackOffMillis() throws IOException { /** * Sets the fixed back-off milliseconds (defaults to {@code 0}). * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public MockBackOff setBackOffMillis(long backOffMillis) { Preconditions.checkArgument(backOffMillis == STOP || backOffMillis >= 0); @@ -72,10 +67,8 @@ public MockBackOff setBackOffMillis(long backOffMillis) { /** * Sets the maximum number of tries before returning {@link #STOP} (defaults to {@code 10}). * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public MockBackOff setMaxTries(int maxTries) { Preconditions.checkArgument(maxTries >= 0); @@ -85,7 +78,7 @@ public MockBackOff setMaxTries(int maxTries) { /** Returns the maximum number of tries before returning {@link #STOP}. */ public final int getMaxTries() { - return numTries; + return maxTries; } /** Returns the number of tries so far. */ diff --git a/google-http-client/src/main/java/com/google/api/client/testing/util/MockSleeper.java b/google-http-client/src/main/java/com/google/api/client/testing/util/MockSleeper.java index 0ac4afb11..dc8fff418 100644 --- a/google-http-client/src/main/java/com/google/api/client/testing/util/MockSleeper.java +++ b/google-http-client/src/main/java/com/google/api/client/testing/util/MockSleeper.java @@ -18,12 +18,10 @@ import com.google.api.client.util.Sleeper; /** - * {@link Beta}
    + * {@link Beta}
    * Mock for {@link Sleeper}. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @author Yaniv Inbar * @since 1.15 diff --git a/google-http-client/src/main/java/com/google/api/client/testing/util/SecurityTestUtils.java b/google-http-client/src/main/java/com/google/api/client/testing/util/SecurityTestUtils.java index 29a39f31e..ca58c38b4 100644 --- a/google-http-client/src/main/java/com/google/api/client/testing/util/SecurityTestUtils.java +++ b/google-http-client/src/main/java/com/google/api/client/testing/util/SecurityTestUtils.java @@ -16,7 +16,6 @@ import com.google.api.client.util.Beta; import com.google.api.client.util.SecurityUtils; - import java.security.GeneralSecurityException; import java.security.KeyFactory; import java.security.interfaces.RSAPrivateKey; @@ -26,7 +25,7 @@ import java.security.spec.X509EncodedKeySpec; /** - * {@link Beta}
    + * {@link Beta}
    * Utilities and constants related to testing the library {@code util} package. * * @since 1.14 @@ -35,60 +34,62 @@ @Beta public final class SecurityTestUtils { - private static final byte[] ENCODED_PRIVATE_KEY = {48, -126, 2, 118, 2, 1, 0, 48, 13, 6, 9, 42, - -122, 72, -122, -9, 13, 1, 1, 1, 5, 0, 4, -126, 2, 96, 48, -126, 2, 92, 2, 1, 0, 2, -127, -127, - 0, -89, 33, 8, -124, 110, -60, 89, 8, -62, 69, 120, 95, -59, -43, 13, -18, 123, 29, -31, 13, - -80, -76, 109, -62, -79, 2, 104, -94, 76, 59, -73, -26, 99, 123, -57, -92, -100, 116, 50, -25, - 96, 53, 124, 95, 76, -59, -84, 70, 27, 0, 72, -63, 84, -77, -2, -107, -66, -32, -119, 27, -95, - 54, -44, -89, 1, 71, 44, 7, -55, 126, 5, -78, 87, -105, -114, 65, -19, 58, -78, -95, 0, 118, 83, - 76, -88, 2, -21, 127, 64, 74, -103, -114, -127, -70, -81, -127, 125, -37, 21, 113, 20, -102, 46, - -37, -111, -97, 97, -127, 32, 87, -80, 105, 18, -19, 107, -73, -50, -97, 11, -23, -59, -107, - -107, 83, -25, 15, -93, -21, 2, 3, 1, 0, 1, 2, -127, -128, 45, -34, -104, 26, -40, -41, -44, - -29, -35, -123, -7, -110, -73, -106, 80, -5, -118, 24, -38, 66, -54, -93, -54, -104, 43, -62, - -48, 122, -14, -41, 85, 18, -53, 109, 22, -113, 44, 77, -116, 7, 10, -43, -61, 43, -40, -61, 76, - 19, -11, -89, 47, 80, -72, 113, -86, 70, -23, 27, 113, 37, -1, 42, 48, 84, -80, 30, 86, 36, - -124, -22, 79, -44, 87, -40, 31, -41, -44, -16, -74, 85, 61, -122, -22, 10, -31, 78, 92, -123, - -77, 12, -80, 62, -52, 68, -46, -17, 67, 124, -78, -23, -105, -77, -2, 89, -16, -12, -56, -51, - 26, 102, 46, 39, -61, -13, -79, -65, -5, 126, 70, 29, 31, 104, -109, 65, -23, -69, 23, -7, 2, - 65, 0, -42, 18, 101, 10, -21, 37, 107, -3, -114, -29, -40, 76, 107, -122, 40, 8, -58, -32, -12, - 55, -4, -61, -66, 91, -56, -50, 78, -124, 11, -49, -62, -121, -56, 70, -92, 90, 32, -112, 49, - 26, -99, 113, 44, 26, 42, -99, -40, -123, 17, 93, 114, 125, 35, -118, -32, 125, -64, 61, 58, - -58, -105, -105, -39, 93, 2, 65, 0, -57, -36, -22, -107, -42, -79, 0, -118, 121, -76, 120, 52, - 110, 127, 115, 68, -86, -4, 96, -50, 72, -60, -57, 125, 57, 21, -81, -44, 25, 112, -75, 83, 57, - -55, 61, 24, 28, -112, -103, -8, 120, 110, -52, -108, -41, -76, -96, 87, -117, 69, 0, 64, 26, 4, - 122, 13, 6, -106, 112, -51, -1, 79, 117, -25, 2, 64, 127, 68, 60, 81, -5, 110, 41, -1, 122, 93, - -74, -113, -24, 52, -65, -60, 72, 8, 32, -24, -48, 26, -57, 38, -26, 0, -48, -24, -21, -28, -66, - 47, -33, 63, 48, 34, 108, -51, -116, -125, -40, 42, 26, 32, 12, 73, -1, 25, 77, 51, -109, 7, 22, - -124, 79, -26, 50, -51, -76, 13, -80, -66, 19, -7, 2, 65, 0, -90, 99, -20, 68, -4, -84, -11, - -105, 83, -123, -124, -63, -103, -16, -81, 101, 78, -72, -72, 91, 100, -57, -74, -111, 49, 18, - 54, 4, -19, 125, 32, -24, 125, -26, 100, -33, -117, 0, 115, -65, 33, 124, -107, 3, -95, -91, - 118, 12, 12, 29, 80, -3, 12, -20, 7, 52, -118, -12, 122, 75, 117, -81, -112, -89, 2, 64, 93, - -21, -52, -110, -54, -9, 79, -123, 105, 125, -56, 75, -77, -26, 125, -123, -69, 62, -2, 79, 8, - 72, -76, -67, 5, 33, -121, 1, -42, -17, 29, 69, -20, -68, -26, -23, 95, -7, -70, -50, -10, 58, - 16, -15, -89, -24, -121, -14, -72, -127, -89, -63, 66, 7, 77, -89, -54, -95, -90, 45, -44, -118, - 69, -1}; + private static final byte[] ENCODED_PRIVATE_KEY = { + 48, -126, 2, 118, 2, 1, 0, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 1, 5, 0, 4, -126, 2, + 96, 48, -126, 2, 92, 2, 1, 0, 2, -127, -127, 0, -89, 33, 8, -124, 110, -60, 89, 8, -62, 69, 120, + 95, -59, -43, 13, -18, 123, 29, -31, 13, -80, -76, 109, -62, -79, 2, 104, -94, 76, 59, -73, -26, + 99, 123, -57, -92, -100, 116, 50, -25, 96, 53, 124, 95, 76, -59, -84, 70, 27, 0, 72, -63, 84, + -77, -2, -107, -66, -32, -119, 27, -95, 54, -44, -89, 1, 71, 44, 7, -55, 126, 5, -78, 87, -105, + -114, 65, -19, 58, -78, -95, 0, 118, 83, 76, -88, 2, -21, 127, 64, 74, -103, -114, -127, -70, + -81, -127, 125, -37, 21, 113, 20, -102, 46, -37, -111, -97, 97, -127, 32, 87, -80, 105, 18, -19, + 107, -73, -50, -97, 11, -23, -59, -107, -107, 83, -25, 15, -93, -21, 2, 3, 1, 0, 1, 2, -127, + -128, 45, -34, -104, 26, -40, -41, -44, -29, -35, -123, -7, -110, -73, -106, 80, -5, -118, 24, + -38, 66, -54, -93, -54, -104, 43, -62, -48, 122, -14, -41, 85, 18, -53, 109, 22, -113, 44, 77, + -116, 7, 10, -43, -61, 43, -40, -61, 76, 19, -11, -89, 47, 80, -72, 113, -86, 70, -23, 27, 113, + 37, -1, 42, 48, 84, -80, 30, 86, 36, -124, -22, 79, -44, 87, -40, 31, -41, -44, -16, -74, 85, + 61, -122, -22, 10, -31, 78, 92, -123, -77, 12, -80, 62, -52, 68, -46, -17, 67, 124, -78, -23, + -105, -77, -2, 89, -16, -12, -56, -51, 26, 102, 46, 39, -61, -13, -79, -65, -5, 126, 70, 29, 31, + 104, -109, 65, -23, -69, 23, -7, 2, 65, 0, -42, 18, 101, 10, -21, 37, 107, -3, -114, -29, -40, + 76, 107, -122, 40, 8, -58, -32, -12, 55, -4, -61, -66, 91, -56, -50, 78, -124, 11, -49, -62, + -121, -56, 70, -92, 90, 32, -112, 49, 26, -99, 113, 44, 26, 42, -99, -40, -123, 17, 93, 114, + 125, 35, -118, -32, 125, -64, 61, 58, -58, -105, -105, -39, 93, 2, 65, 0, -57, -36, -22, -107, + -42, -79, 0, -118, 121, -76, 120, 52, 110, 127, 115, 68, -86, -4, 96, -50, 72, -60, -57, 125, + 57, 21, -81, -44, 25, 112, -75, 83, 57, -55, 61, 24, 28, -112, -103, -8, 120, 110, -52, -108, + -41, -76, -96, 87, -117, 69, 0, 64, 26, 4, 122, 13, 6, -106, 112, -51, -1, 79, 117, -25, 2, 64, + 127, 68, 60, 81, -5, 110, 41, -1, 122, 93, -74, -113, -24, 52, -65, -60, 72, 8, 32, -24, -48, + 26, -57, 38, -26, 0, -48, -24, -21, -28, -66, 47, -33, 63, 48, 34, 108, -51, -116, -125, -40, + 42, 26, 32, 12, 73, -1, 25, 77, 51, -109, 7, 22, -124, 79, -26, 50, -51, -76, 13, -80, -66, 19, + -7, 2, 65, 0, -90, 99, -20, 68, -4, -84, -11, -105, 83, -123, -124, -63, -103, -16, -81, 101, + 78, -72, -72, 91, 100, -57, -74, -111, 49, 18, 54, 4, -19, 125, 32, -24, 125, -26, 100, -33, + -117, 0, 115, -65, 33, 124, -107, 3, -95, -91, 118, 12, 12, 29, 80, -3, 12, -20, 7, 52, -118, + -12, 122, 75, 117, -81, -112, -89, 2, 64, 93, -21, -52, -110, -54, -9, 79, -123, 105, 125, -56, + 75, -77, -26, 125, -123, -69, 62, -2, 79, 8, 72, -76, -67, 5, 33, -121, 1, -42, -17, 29, 69, + -20, -68, -26, -23, 95, -7, -70, -50, -10, 58, 16, -15, -89, -24, -121, -14, -72, -127, -89, + -63, 66, 7, 77, -89, -54, -95, -90, 45, -44, -118, 69, -1 + }; - private static final byte[] ENCODED_PUBLIC_KEY = {48, -127, -97, 48, 13, 6, 9, 42, -122, 72, -122, - -9, 13, 1, 1, 1, 5, 0, 3, -127, -115, 0, 48, -127, -119, 2, -127, -127, 0, -89, 33, 8, -124, - 110, -60, 89, 8, -62, 69, 120, 95, -59, -43, 13, -18, 123, 29, -31, 13, -80, -76, 109, -62, -79, - 2, 104, -94, 76, 59, -73, -26, 99, 123, -57, -92, -100, 116, 50, -25, 96, 53, 124, 95, 76, -59, - -84, 70, 27, 0, 72, -63, 84, -77, -2, -107, -66, -32, -119, 27, -95, 54, -44, -89, 1, 71, 44, 7, - -55, 126, 5, -78, 87, -105, -114, 65, -19, 58, -78, -95, 0, 118, 83, 76, -88, 2, -21, 127, 64, - 74, -103, -114, -127, -70, -81, -127, 125, -37, 21, 113, 20, -102, 46, -37, -111, -97, 97, -127, - 32, 87, -80, 105, 18, -19, 107, -73, -50, -97, 11, -23, -59, -107, -107, 83, -25, 15, -93, -21, - 2, 3, 1, 0, 1}; + private static final byte[] ENCODED_PUBLIC_KEY = { + 48, -127, -97, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 1, 5, 0, 3, -127, -115, 0, 48, + -127, -119, 2, -127, -127, 0, -89, 33, 8, -124, 110, -60, 89, 8, -62, 69, 120, 95, -59, -43, 13, + -18, 123, 29, -31, 13, -80, -76, 109, -62, -79, 2, 104, -94, 76, 59, -73, -26, 99, 123, -57, + -92, -100, 116, 50, -25, 96, 53, 124, 95, 76, -59, -84, 70, 27, 0, 72, -63, 84, -77, -2, -107, + -66, -32, -119, 27, -95, 54, -44, -89, 1, 71, 44, 7, -55, 126, 5, -78, 87, -105, -114, 65, -19, + 58, -78, -95, 0, 118, 83, 76, -88, 2, -21, 127, 64, 74, -103, -114, -127, -70, -81, -127, 125, + -37, 21, 113, 20, -102, 46, -37, -111, -97, 97, -127, 32, 87, -80, 105, 18, -19, 107, -73, -50, + -97, 11, -23, -59, -107, -107, 83, -25, 15, -93, -21, 2, 3, 1, 0, 1 + }; /** - * Returns a new copy of a sample encoded RSA private key that matches - * {@link #newEncodedRsaPublicKeyBytes()}. + * Returns a new copy of a sample encoded RSA private key that matches {@link + * #newEncodedRsaPublicKeyBytes()}. */ public static byte[] newEncodedRsaPrivateKeyBytes() { return ENCODED_PRIVATE_KEY.clone(); } /** - * Returns a new copy of a sample encoded public key that matches - * {@link #newEncodedRsaPrivateKeyBytes()}. + * Returns a new copy of a sample encoded public key that matches {@link + * #newEncodedRsaPrivateKeyBytes()}. */ public static byte[] newEncodedRsaPublicKeyBytes() { return ENCODED_PUBLIC_KEY.clone(); @@ -108,6 +109,5 @@ public static RSAPublicKey newRsaPublicKey() throws GeneralSecurityException { return (RSAPublicKey) keyFactory.generatePublic(keySpec); } - private SecurityTestUtils() { - } + private SecurityTestUtils() {} } diff --git a/google-http-client/src/main/java/com/google/api/client/testing/util/TestableByteArrayInputStream.java b/google-http-client/src/main/java/com/google/api/client/testing/util/TestableByteArrayInputStream.java index 4ee176576..3513579b5 100644 --- a/google-http-client/src/main/java/com/google/api/client/testing/util/TestableByteArrayInputStream.java +++ b/google-http-client/src/main/java/com/google/api/client/testing/util/TestableByteArrayInputStream.java @@ -15,12 +15,11 @@ package com.google.api.client.testing.util; import com.google.api.client.util.Beta; - import java.io.ByteArrayInputStream; import java.io.IOException; /** - * {@link Beta}
    + * {@link Beta}
    * Testable extension for a byte array input stream. * * @author Yaniv Inbar @@ -42,16 +41,14 @@ public TestableByteArrayInputStream(byte[] buf) { * @param offset offset in the buffer of the first byte to read * @param length maximum number of bytes to read from the buffer */ - public TestableByteArrayInputStream(byte buf[], int offset, int length) { + public TestableByteArrayInputStream(byte[] buf, int offset, int length) { super(buf); } /** * {@inheritDoc} * - *

    - * Overriding is supported, but overriding method must call the super implementation. - *

    + *

    Overriding is supported, but overriding method must call the super implementation. */ @Override public void close() throws IOException { diff --git a/google-http-client/src/main/java/com/google/api/client/testing/util/TestableByteArrayOutputStream.java b/google-http-client/src/main/java/com/google/api/client/testing/util/TestableByteArrayOutputStream.java index d1cbe25be..d8daee198 100644 --- a/google-http-client/src/main/java/com/google/api/client/testing/util/TestableByteArrayOutputStream.java +++ b/google-http-client/src/main/java/com/google/api/client/testing/util/TestableByteArrayOutputStream.java @@ -15,12 +15,11 @@ package com.google.api.client.testing.util; import com.google.api.client.util.Beta; - import java.io.ByteArrayOutputStream; import java.io.IOException; /** - * {@link Beta}
    + * {@link Beta}
    * Testable extension for a byte array output stream. * * @author Yaniv Inbar @@ -35,9 +34,7 @@ public class TestableByteArrayOutputStream extends ByteArrayOutputStream { /** * {@inheritDoc} * - *

    - * Overriding is supported, but overriding method must call the super implementation. - *

    + *

    Overriding is supported, but overriding method must call the super implementation. */ @Override public void close() throws IOException { diff --git a/google-http-client/src/main/java/com/google/api/client/testing/util/package-info.java b/google-http-client/src/main/java/com/google/api/client/testing/util/package-info.java index 55fe90904..de6f5502a 100644 --- a/google-http-client/src/main/java/com/google/api/client/testing/util/package-info.java +++ b/google-http-client/src/main/java/com/google/api/client/testing/util/package-info.java @@ -13,7 +13,7 @@ */ /** - * {@link com.google.api.client.util.Beta}
    + * {@link com.google.api.client.util.Beta}
    * Testing utilities used for writing tests based on this library. * * @since 1.14 @@ -21,4 +21,3 @@ */ @com.google.api.client.util.Beta package com.google.api.client.testing.util; - diff --git a/google-http-client/src/main/java/com/google/api/client/util/ArrayMap.java b/google-http-client/src/main/java/com/google/api/client/util/ArrayMap.java index 079c8ff22..f5a0aa7b0 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/ArrayMap.java +++ b/google-http-client/src/main/java/com/google/api/client/util/ArrayMap.java @@ -25,22 +25,17 @@ /** * Memory-efficient map of keys to values with list-style random-access semantics. * - *

    - * Supports null keys and values. Conceptually, the keys and values are stored in a simpler array in - * order to minimize memory use and provide for fast access to a key/value at a certain index (for - * example {@link #getKey(int)}). However, traditional mapping operations like {@link #get(Object)} - * and {@link #put(Object, Object)} are slower because they need to look up all key/value pairs in - * the worst case. - *

    + *

    Supports null keys and values. Conceptually, the keys and values are stored in a simpler array + * in order to minimize memory use and provide for fast access to a key/value at a certain index + * (for example {@link #getKey(int)}). However, traditional mapping operations like {@link + * #get(Object)} and {@link #put(Object, Object)} are slower because they need to look up all + * key/value pairs in the worst case. * - *

    - * Implementation is not thread-safe. For a thread-safe choice instead use an implementation of + *

    Implementation is not thread-safe. For a thread-safe choice instead use an implementation of * {@link ConcurrentMap}. - *

    * * @param the type of keys maintained by this map * @param the type of mapped values - * * @since 1.0 * @author Yaniv Inbar */ @@ -71,8 +66,8 @@ public static ArrayMap create(int initialCapacity) { * Returns a new instance of an array map of the given key value pairs in alternating order. For * example: {@code ArrayMap map = ArrayMap.of("key1", "value1", "key2", "value2", * ...);}. - *

    - * WARNING: there is no compile-time checking of the {@code keyValuePairs} parameter to ensure + * + *

    WARNING: there is no compile-time checking of the {@code keyValuePairs} parameter to ensure * that the keys or values have the correct type, so if the wrong type is passed in, any problems * will occur at runtime. Also, there is no checking that the keys are unique, which the caller * must ensure is true. @@ -116,10 +111,10 @@ public final V getValue(int index) { /** * Sets the key/value mapping at the given index, overriding any existing key/value mapping. - *

    - * There is no checking done to ensure that the key does not already exist. Therefore, this method - * is dangerous to call unless the caller can be certain the key does not already exist in the - * map. + * + *

    There is no checking done to ensure that the key does not already exist. Therefore, this + * method is dangerous to call unless the caller can be certain the key does not already exist in + * the map. * * @return previous value or {@code null} for none * @throws IndexOutOfBoundsException if index is negative @@ -224,9 +219,7 @@ public final void trim() { setDataCapacity(this.size << 1); } - /** - * Ensures that the capacity of the internal arrays is at least a given capacity. - */ + /** Ensures that the capacity of the internal arrays is at least a given capacity. */ public final void ensureCapacity(int minCapacity) { if (minCapacity < 0) { throw new IndexOutOfBoundsException(); @@ -276,9 +269,7 @@ private V valueAtDataIndex(int dataIndex) { return result; } - /** - * Returns the data index of the given key or {@code -2} if there is no such key. - */ + /** Returns the data index of the given key or {@code -2} if there is no such key. */ private int getDataIndexOfKey(Object key) { int dataSize = this.size << 1; Object[] data = this.data; diff --git a/google-http-client/src/main/java/com/google/api/client/util/ArrayValueMap.java b/google-http-client/src/main/java/com/google/api/client/util/ArrayValueMap.java index 99d3c71aa..41d5ca717 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/ArrayValueMap.java +++ b/google-http-client/src/main/java/com/google/api/client/util/ArrayValueMap.java @@ -11,6 +11,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ + package com.google.api.client.util; import java.lang.reflect.Field; @@ -22,23 +23,17 @@ * Collects the array values of a key/value data object, writing the fields or map values only after * all values have been collected. * - *

    - * The typical application for this is when parsing JSON or XML when the value type is known to be - * an array. It stores the values in a collection during the parsing, and only when the parsing of - * an object is finished does it convert the collection into an array and stores it. - *

    + *

    The typical application for this is when parsing JSON or XML when the value type is known to + * be an array. It stores the values in a collection during the parsing, and only when the parsing + * of an object is finished does it convert the collection into an array and stores it. * - *

    - * Use {@link #put(String, Class, Object)} when the destination object is a map with string keys and - * whose values accept an array of objects. Use {@link #put(Field, Class, Object)} when setting the - * value of a field using reflection, assuming its type accepts an array of objects. One can + *

    Use {@link #put(String, Class, Object)} when the destination object is a map with string keys + * and whose values accept an array of objects. Use {@link #put(Field, Class, Object)} when setting + * the value of a field using reflection, assuming its type accepts an array of objects. One can * potentially use both {@code put} methods for example on an instance of {@link GenericData}. - *

    * - *

    - * Implementation is not thread-safe. For a thread-safe choice instead use an implementation of + *

    Implementation is not thread-safe. For a thread-safe choice instead use an implementation of * {@link ConcurrentMap}. - *

    * * @since 1.4 * @author Yaniv Inbar @@ -54,9 +49,7 @@ static class ArrayValue { /** Values to be stored in the array. */ final ArrayList values = new ArrayList(); - /** - * @param componentType array component type - */ + /** @param componentType array component type */ ArrayValue(Class componentType) { this.componentType = componentType; } @@ -87,7 +80,7 @@ void addValue(Class componentType, Object value) { /** * @param destination destination object whose fields must be set, or destination map whose values - * must be set + * must be set */ public ArrayValueMap(Object destination) { this.destination = destination; diff --git a/google-http-client/src/main/java/com/google/api/client/util/BackOff.java b/google-http-client/src/main/java/com/google/api/client/util/BackOff.java index 8f02134e7..a48c2a570 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/BackOff.java +++ b/google-http-client/src/main/java/com/google/api/client/util/BackOff.java @@ -34,17 +34,15 @@ public interface BackOff { * Gets the number of milliseconds to wait before retrying the operation or {@link #STOP} to * indicate that no retries should be made. * - *

    - * Example usage: - *

    + *

    Example usage: * *

    -   long backOffMillis = backoff.nextBackOffMillis();
    -   if (backOffMillis == Backoff.STOP) {
    -     // do not retry operation
    -   } else {
    -     // sleep for backOffMillis milliseconds and retry operation
    -   }
    +   * long backOffMillis = backoff.nextBackOffMillis();
    +   * if (backOffMillis == Backoff.STOP) {
    +   * // do not retry operation
    +   * } else {
    +   * // sleep for backOffMillis milliseconds and retry operation
    +   * }
        * 
    */ long nextBackOffMillis() throws IOException; @@ -53,27 +51,27 @@ public interface BackOff { * Fixed back-off policy whose back-off time is always zero, meaning that the operation is retried * immediately without waiting. */ - BackOff ZERO_BACKOFF = new BackOff() { + BackOff ZERO_BACKOFF = + new BackOff() { - public void reset() throws IOException { - } + public void reset() throws IOException {} - public long nextBackOffMillis() throws IOException { - return 0; - } - }; + public long nextBackOffMillis() throws IOException { + return 0; + } + }; /** * Fixed back-off policy that always returns {@code #STOP} for {@link #nextBackOffMillis()}, * meaning that the operation should not be retried. */ - BackOff STOP_BACKOFF = new BackOff() { + BackOff STOP_BACKOFF = + new BackOff() { - public void reset() throws IOException { - } + public void reset() throws IOException {} - public long nextBackOffMillis() throws IOException { - return STOP; - } - }; + public long nextBackOffMillis() throws IOException { + return STOP; + } + }; } diff --git a/google-http-client/src/main/java/com/google/api/client/util/BackOffUtils.java b/google-http-client/src/main/java/com/google/api/client/util/BackOffUtils.java index 543060820..8ad9d0ccf 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/BackOffUtils.java +++ b/google-http-client/src/main/java/com/google/api/client/util/BackOffUtils.java @@ -17,7 +17,7 @@ import java.io.IOException; /** - * {@link Beta}
    + * {@link Beta}
    * Utilities for {@link BackOff}. * * @since 1.15 @@ -30,15 +30,13 @@ public final class BackOffUtils { * Runs the next iteration of the back-off policy, and returns whether to continue to retry the * operation. * - *

    - * If {@code true}, it will call {@link Sleeper#sleep(long)} with the specified number of + *

    If {@code true}, it will call {@link Sleeper#sleep(long)} with the specified number of * milliseconds from {@link BackOff#nextBackOffMillis()}. - *

    * * @param sleeper sleeper * @param backOff back-off policy - * @return whether to continue to back off; in other words, whether - * {@link BackOff#nextBackOffMillis()} did not return {@link BackOff#STOP} + * @return whether to continue to back off; in other words, whether {@link + * BackOff#nextBackOffMillis()} did not return {@link BackOff#STOP} * @throws InterruptedException if any thread has interrupted the current thread */ public static boolean next(Sleeper sleeper, BackOff backOff) @@ -51,6 +49,5 @@ public static boolean next(Sleeper sleeper, BackOff backOff) return true; } - private BackOffUtils() { - } + private BackOffUtils() {} } diff --git a/google-http-client/src/main/java/com/google/api/client/util/Base64.java b/google-http-client/src/main/java/com/google/api/client/util/Base64.java index f93b0c095..178bc4829 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/Base64.java +++ b/google-http-client/src/main/java/com/google/api/client/util/Base64.java @@ -14,32 +14,35 @@ package com.google.api.client.util; +import com.google.common.io.BaseEncoding; +import com.google.common.io.BaseEncoding.DecodingException; + /** - * Proxy for version 1.6 (or newer) of the Apache Commons Codec - * {@link org.apache.commons.codec.binary.Base64} implementation. - * - *

    - * This is needed in order to support platforms like Android which already include an older version - * of the Apache Commons Codec (Android includes version 1.3). To avoid a dependency library - * conflict, this library includes a reduced private copy of version 1.6 (or newer) of the Apache - * Commons Codec (using a tool like jarjar). - *

    + * Proxy for handling Base64 encoding/decoding. * * @since 1.8 * @author Yaniv Inbar + * @deprecated use com.google.common.io.BaseEncoding#base64 */ +@Deprecated public class Base64 { + // Guava's Base64 (https://datatracker.ietf.org/doc/html/rfc4648#section-4) decoders. When + // decoding, they discard the new line character so that the behavior matches what we had with + // Apache Commons Codec's decodeBase64. + // The 2nd argument of the withSeparator method, "64", does not have any effect in decoding. + private static final BaseEncoding BASE64_DECODER = BaseEncoding.base64().withSeparator("\n", 64); + private static final BaseEncoding BASE64URL_DECODER = + BaseEncoding.base64Url().withSeparator("\n", 64); /** * Encodes binary data using the base64 algorithm but does not chunk the output. * * @param binaryData binary data to encode or {@code null} for {@code null} result * @return byte[] containing Base64 characters in their UTF-8 representation or {@code null} for - * {@code null} input - * @see org.apache.commons.codec.binary.Base64#encodeBase64(byte[]) + * {@code null} input */ public static byte[] encodeBase64(byte[] binaryData) { - return org.apache.commons.codec.binary.Base64.encodeBase64(binaryData); + return StringUtils.getBytesUtf8(encodeBase64String(binaryData)); } /** @@ -47,24 +50,24 @@ public static byte[] encodeBase64(byte[] binaryData) { * * @param binaryData binary data to encode or {@code null} for {@code null} result * @return String containing Base64 characters or {@code null} for {@code null} input - * @see org.apache.commons.codec.binary.Base64#encodeBase64String(byte[]) */ public static String encodeBase64String(byte[] binaryData) { - return org.apache.commons.codec.binary.Base64.encodeBase64String(binaryData); + if (binaryData == null) { + return null; + } + return BaseEncoding.base64().encode(binaryData); } - /** * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the * output. The url-safe variation emits - and _ instead of + and / characters. * * @param binaryData binary data to encode or {@code null} for {@code null} result * @return byte[] containing Base64 characters in their UTF-8 representation or {@code null} for - * {@code null} input - * @see org.apache.commons.codec.binary.Base64#encodeBase64URLSafe(byte[]) + * {@code null} input */ public static byte[] encodeBase64URLSafe(byte[] binaryData) { - return org.apache.commons.codec.binary.Base64.encodeBase64URLSafe(binaryData); + return StringUtils.getBytesUtf8(encodeBase64URLSafeString(binaryData)); } /** @@ -73,34 +76,48 @@ public static byte[] encodeBase64URLSafe(byte[] binaryData) { * * @param binaryData binary data to encode or {@code null} for {@code null} result * @return String containing Base64 characters or {@code null} for {@code null} input - * @see org.apache.commons.codec.binary.Base64#encodeBase64URLSafeString(byte[]) */ public static String encodeBase64URLSafeString(byte[] binaryData) { - return org.apache.commons.codec.binary.Base64.encodeBase64URLSafeString(binaryData); + if (binaryData == null) { + return null; + } + return BaseEncoding.base64Url().omitPadding().encode(binaryData); } /** - * Decodes Base64 data into octets. + * Decodes Base64 data into octets. Note that this method handles both URL-safe and non-URL-safe + * base 64 encoded inputs. * * @param base64Data Byte array containing Base64 data or {@code null} for {@code null} result * @return Array containing decoded data or {@code null} for {@code null} input - * @see org.apache.commons.codec.binary.Base64#decodeBase64(byte[]) */ public static byte[] decodeBase64(byte[] base64Data) { - return org.apache.commons.codec.binary.Base64.decodeBase64(base64Data); + return decodeBase64(StringUtils.newStringUtf8(base64Data)); } /** - * Decodes a Base64 String into octets. + * Decodes a Base64 String into octets. Note that this method handles both URL-safe and + * non-URL-safe base 64 encoded strings. + * + *

    For the compatibility with the old version that used Apache Commons Coded's decodeBase64, + * this method discards new line characters and trailing whitespaces. * * @param base64String String containing Base64 data or {@code null} for {@code null} result * @return Array containing decoded data or {@code null} for {@code null} input - * @see org.apache.commons.codec.binary.Base64#decodeBase64(String) */ public static byte[] decodeBase64(String base64String) { - return org.apache.commons.codec.binary.Base64.decodeBase64(base64String); + if (base64String == null) { + return null; + } + try { + return BASE64_DECODER.decode(base64String); + } catch (IllegalArgumentException e) { + if (e.getCause() instanceof DecodingException) { + return BASE64URL_DECODER.decode(base64String.trim()); + } + throw e; + } } - private Base64() { - } + private Base64() {} } diff --git a/google-http-client/src/main/java/com/google/api/client/util/Beta.java b/google-http-client/src/main/java/com/google/api/client/util/Beta.java index 2ac456c75..87e4f710f 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/Beta.java +++ b/google-http-client/src/main/java/com/google/api/client/util/Beta.java @@ -21,40 +21,37 @@ /** * Use this annotation to indicate that a public API (class, method or field) is beta. * - *

    - * Beta API is subject to incompatible changes or removal in the future. It may also mean - * that the server features it depends on are potentially subject to breakage at any time. - *

    + *

    Beta API is subject to incompatible changes or removal in the future. It may also mean that + * the server features it depends on are potentially subject to breakage at any time. * - *

    - * That API is exempt from any compatibility guarantees made by its containing library. Read + *

    That API is exempt from any compatibility guarantees made by its containing library. Read * carefully the JavaDoc of the API bearing this annotation for better understanding of the risk. - *

    * - *

    - * To provide a smoother upgrade path when we make incompatible changes to beta API, - * whenever possible we try to deprecate the old beta API in the first minor release, and - * then remove it in the second minor release. - *

    + *

    To provide a smoother upgrade path when we make incompatible changes to beta API, whenever + * possible we try to deprecate the old beta API in the first minor release, and then remove it in + * the second minor release. * - *

    - * It is generally inadvisable for other non-beta libraries to use beta API from - * this library. The problem is that other libraries don't have control over the version of this - * library being used in client applications, and if the wrong version of this library is used, it - * has the potential to break client applications. - *

    + *

    It is generally inadvisable for other non-beta libraries to use beta API from this library. + * The problem is that other libraries don't have control over the version of this library being + * used in client applications, and if the wrong version of this library is used, it has the + * potential to break client applications. * - *

    - * You may use the google-http-client-findbugs plugin to find usages of API bearing this annotation. - *

    + *

    You may use the google-http-client-findbugs plugin to find usages of API bearing this + * annotation. * * @since 1.15 * @author Eyal Peled + * @deprecated use com.google.common.annotations.Beta */ -@Target(value = { - ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, - ElementType.TYPE, ElementType.PACKAGE}) +@Target( + value = { + ElementType.ANNOTATION_TYPE, + ElementType.CONSTRUCTOR, + ElementType.FIELD, + ElementType.METHOD, + ElementType.TYPE, + ElementType.PACKAGE + }) @Documented -public @interface Beta { - -} +@Deprecated +public @interface Beta {} diff --git a/google-http-client/src/main/java/com/google/api/client/util/ByteArrayStreamingContent.java b/google-http-client/src/main/java/com/google/api/client/util/ByteArrayStreamingContent.java index 6ec69a488..bb4820c4d 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/ByteArrayStreamingContent.java +++ b/google-http-client/src/main/java/com/google/api/client/util/ByteArrayStreamingContent.java @@ -20,13 +20,13 @@ /** * Streaming content whose source is a byte array. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @since 1.14 * @author Yaniv Inbar + * @deprecated use com.google.common.io.ByteSource */ +@Deprecated public class ByteArrayStreamingContent implements StreamingContent { /** Byte array content. */ @@ -38,9 +38,7 @@ public class ByteArrayStreamingContent implements StreamingContent { /** Length of bytes to read from byte array. */ private final int length; - /** - * @param byteArray byte array content - */ + /** @param byteArray byte array content */ public ByteArrayStreamingContent(byte[] byteArray) { this(byteArray, 0, byteArray.length); } diff --git a/google-http-client/src/main/java/com/google/api/client/util/ByteStreams.java b/google-http-client/src/main/java/com/google/api/client/util/ByteStreams.java index 7c3a4c3e7..6443214cf 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/ByteStreams.java +++ b/google-http-client/src/main/java/com/google/api/client/util/ByteStreams.java @@ -22,14 +22,11 @@ /** * Provides utility methods for working with byte arrays and I/O streams. * - *

    - * NOTE: this is a copy of a subset of Guava's {@link com.google.common.io.ByteStreams}. The - * implementation must match as closely as possible to Guava's implementation. - *

    - * * @since 1.14 * @author Yaniv Inbar + * @deprecated use Guava's com.google.common.io.ByteStreams */ +@Deprecated public final class ByteStreams { private static final int BUF_SIZE = 0x1000; // 4K @@ -145,24 +142,18 @@ public long skip(long n) throws IOException { /** * Reads some bytes from an input stream and stores them into the buffer array {@code b}. * - *

    - * This method blocks until {@code len} bytes of input data have been read into the array, or end - * of file is detected. The number of bytes read is returned, possibly zero. Does not close the - * stream. - *

    + *

    This method blocks until {@code len} bytes of input data have been read into the array, or + * end of file is detected. The number of bytes read is returned, possibly zero. Does not close + * the stream. * - *

    - * A caller can detect EOF if the number of bytes read is less than {@code len}. All subsequent + *

    A caller can detect EOF if the number of bytes read is less than {@code len}. All subsequent * calls on the same stream will return zero. - *

    * - *

    - * If {@code b} is null, a {@code NullPointerException} is thrown. If {@code off} is negative, or - * {@code len} is negative, or {@code off+len} is greater than the length of the array {@code b}, - * then an {@code IndexOutOfBoundsException} is thrown. If {@code len} is zero, then no bytes are - * read. Otherwise, the first byte read is stored into element {@code b[off]}, the next one into - * {@code b[off+1]}, and so on. The number of bytes read is, at most, equal to {@code len}. - *

    + *

    If {@code b} is null, a {@code NullPointerException} is thrown. If {@code off} is negative, + * or {@code len} is negative, or {@code off+len} is greater than the length of the array {@code + * b}, then an {@code IndexOutOfBoundsException} is thrown. If {@code len} is zero, then no bytes + * are read. Otherwise, the first byte read is stored into element {@code b[off]}, the next one + * into {@code b[off+1]}, and so on. The number of bytes read is, at most, equal to {@code len}. * * @param in the input stream to read from * @param b the buffer into which the data is read @@ -187,6 +178,5 @@ public static int read(InputStream in, byte[] b, int off, int len) throws IOExce return total; } - private ByteStreams() { - } + private ByteStreams() {} } diff --git a/google-http-client/src/main/java/com/google/api/client/util/Charsets.java b/google-http-client/src/main/java/com/google/api/client/util/Charsets.java index 322fb9fc1..7546cceb8 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/Charsets.java +++ b/google-http-client/src/main/java/com/google/api/client/util/Charsets.java @@ -15,27 +15,27 @@ package com.google.api.client.util; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; /** * Contains constant definitions for some standard {@link Charset} instances that are guaranteed to * be supported by all Java platform implementations. * - *

    - * NOTE: this is a copy of a subset of Guava's {@link com.google.common.base.Charsets}. The + *

    NOTE: this is a copy of a subset of Guava's {@link com.google.common.base.Charsets}. The * implementation must match as closely as possible to Guava's implementation. - *

    * * @since 1.14 * @author Yaniv Inbar + * @deprecated use java.nio.charset.StandardCharsets */ +@Deprecated public final class Charsets { /** UTF-8 charset. */ - public static final Charset UTF_8 = Charset.forName("UTF-8"); + public static final Charset UTF_8 = StandardCharsets.UTF_8; /** ISO-8859-1 charset. */ - public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1"); + public static final Charset ISO_8859_1 = StandardCharsets.ISO_8859_1; - private Charsets() { - } + private Charsets() {} } diff --git a/google-http-client/src/main/java/com/google/api/client/util/ClassInfo.java b/google-http-client/src/main/java/com/google/api/client/util/ClassInfo.java index 16c71dda7..f11f6ffb0 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/ClassInfo.java +++ b/google-http-client/src/main/java/com/google/api/client/util/ClassInfo.java @@ -24,14 +24,13 @@ import java.util.Locale; import java.util.Map; import java.util.TreeSet; -import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; /** * Computes class information to determine data key name/value pairs associated with the class. * - *

    - * Implementation is thread-safe. - *

    + *

    Implementation is thread-safe. * * @since 1.0 * @author Yaniv Inbar @@ -39,11 +38,12 @@ public final class ClassInfo { /** Class information cache, with case-sensitive field names. */ - private static final Map, ClassInfo> CACHE = new WeakHashMap, ClassInfo>(); + private static final ConcurrentMap, ClassInfo> CACHE = + new ConcurrentHashMap, ClassInfo>(); /** Class information cache, with case-insensitive fields names. */ - private static final Map, ClassInfo> CACHE_IGNORE_CASE = - new WeakHashMap, ClassInfo>(); + private static final ConcurrentMap, ClassInfo> CACHE_IGNORE_CASE = + new ConcurrentHashMap, ClassInfo>(); /** Class. */ private final Class clazz; @@ -83,16 +83,15 @@ public static ClassInfo of(Class underlyingClass, boolean ignoreCase) { if (underlyingClass == null) { return null; } - final Map, ClassInfo> cache = ignoreCase ? CACHE_IGNORE_CASE : CACHE; - ClassInfo classInfo; - synchronized (cache) { - classInfo = cache.get(underlyingClass); - if (classInfo == null) { - classInfo = new ClassInfo(underlyingClass, ignoreCase); - cache.put(underlyingClass, classInfo); - } - } - return classInfo; + final ConcurrentMap, ClassInfo> cache = ignoreCase ? CACHE_IGNORE_CASE : CACHE; + + // Logic copied from ConcurrentMap.computeIfAbsent + ClassInfo v, newValue; + return ((v = cache.get(underlyingClass)) == null + && (newValue = new ClassInfo(underlyingClass, ignoreCase)) != null + && (v = cache.putIfAbsent(underlyingClass, newValue)) == null) + ? newValue + : v; } /** @@ -150,8 +149,8 @@ public boolean isEnum() { } /** - * Returns an unmodifiable sorted set (with any possible {@code null} member first) of - * {@link FieldInfo#getName() names}. + * Returns an unmodifiable sorted set (with any possible {@code null} member first) of {@link + * FieldInfo#getName() names}. */ public Collection getNames() { return names; @@ -163,11 +162,15 @@ private ClassInfo(Class srcClass, boolean ignoreCase) { Preconditions.checkArgument( !ignoreCase || !srcClass.isEnum(), "cannot ignore case on an enum: " + srcClass); // name set has a special comparator to keep null first - TreeSet nameSet = new TreeSet(new Comparator() { - public int compare(String s0, String s1) { - return Objects.equal(s0, s1) ? 0 : s0 == null ? -1 : s1 == null ? 1 : s0.compareTo(s1); - } - }); + TreeSet nameSet = + new TreeSet( + new Comparator() { + public int compare(String s0, String s1) { + return Objects.equal(s0, s1) + ? 0 + : s0 == null ? -1 : s1 == null ? 1 : s0.compareTo(s1); + } + }); // iterate over declared fields for (Field field : srcClass.getDeclaredFields()) { FieldInfo fieldInfo = FieldInfo.of(field); @@ -179,7 +182,8 @@ public int compare(String s0, String s1) { fieldName = fieldName.toLowerCase(Locale.US).intern(); } FieldInfo conflictingFieldInfo = nameToFieldInfoMap.get(fieldName); - Preconditions.checkArgument(conflictingFieldInfo == null, + Preconditions.checkArgument( + conflictingFieldInfo == null, "two fields have the same %sname <%s>: %s and %s", ignoreCase ? "case-insensitive " : "", fieldName, @@ -200,17 +204,18 @@ public int compare(String s0, String s1) { } } } - names = nameSet.isEmpty() ? Collections.emptyList() : Collections.unmodifiableList( - new ArrayList(nameSet)); + names = + nameSet.isEmpty() + ? Collections.emptyList() + : Collections.unmodifiableList(new ArrayList(nameSet)); } /** * Returns an unmodifiable collection of the {@code FieldInfo}s for this class, without any * guarantee of order. * - *

    - * If you need sorted order, instead use {@link #getNames()} with {@link #getFieldInfo(String)}. - *

    + *

    If you need sorted order, instead use {@link #getNames()} with {@link + * #getFieldInfo(String)}. * * @since 1.16 */ diff --git a/google-http-client/src/main/java/com/google/api/client/util/Clock.java b/google-http-client/src/main/java/com/google/api/client/util/Clock.java index d1d18b88f..61242dc76 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/Clock.java +++ b/google-http-client/src/main/java/com/google/api/client/util/Clock.java @@ -17,10 +17,8 @@ /** * Clock which can be used to get the amount of elapsed milliseconds in system time. * - *

    - * The default system implementation can be accessed at {@link #SYSTEM}. Alternative implementations - * may be used for testing. - *

    + *

    The default system implementation can be accessed at {@link #SYSTEM}. Alternative + * implementations may be used for testing. * * @since 1.9 * @author mlinder@google.com (Matthias Linder) @@ -33,12 +31,13 @@ public interface Clock { long currentTimeMillis(); /** - * Provides the default System implementation of a Clock by using - * {@link System#currentTimeMillis()}. + * Provides the default System implementation of a Clock by using {@link + * System#currentTimeMillis()}. */ - Clock SYSTEM = new Clock() { - public long currentTimeMillis() { - return System.currentTimeMillis(); - } - }; + Clock SYSTEM = + new Clock() { + public long currentTimeMillis() { + return System.currentTimeMillis(); + } + }; } diff --git a/google-http-client/src/main/java/com/google/api/client/util/Collections2.java b/google-http-client/src/main/java/com/google/api/client/util/Collections2.java index ee565c62b..7c609496b 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/Collections2.java +++ b/google-http-client/src/main/java/com/google/api/client/util/Collections2.java @@ -19,14 +19,11 @@ /** * Static utility methods pertaining to {@link Collection} instances. * - *

    - * NOTE: this is a copy of a subset of Guava's {@link com.google.common.collect.Collections2}. The - * implementation must match as closely as possible to Guava's implementation. - *

    - * * @since 1.14 * @author Yaniv Inbar + * @deprecated use Guava's {@link com.google.common.collect.Collections2} */ +@Deprecated public final class Collections2 { /** Used to avoid http://bugs.sun.com/view_bug.do?bug_id=6558557. */ @@ -34,6 +31,5 @@ static Collection cast(Iterable iterable) { return (Collection) iterable; } - private Collections2() { - } + private Collections2() {} } diff --git a/google-http-client/src/main/java/com/google/api/client/util/Data.java b/google-http-client/src/main/java/com/google/api/client/util/Data.java index 2a8c9ac69..f50b45206 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/Data.java +++ b/google-http-client/src/main/java/com/google/api/client/util/Data.java @@ -45,36 +45,47 @@ public class Data { // NOTE: create new instances to avoid cache, e.g. new String() /** The single instance of the magic null object for a {@link Boolean}. */ + @SuppressWarnings("deprecation") public static final Boolean NULL_BOOLEAN = new Boolean(true); /** The single instance of the magic null object for a {@link String}. */ + @SuppressWarnings("deprecation") public static final String NULL_STRING = new String(); /** The single instance of the magic null object for a {@link Character}. */ + @SuppressWarnings("deprecation") public static final Character NULL_CHARACTER = new Character((char) 0); /** The single instance of the magic null object for a {@link Byte}. */ + @SuppressWarnings("deprecation") public static final Byte NULL_BYTE = new Byte((byte) 0); /** The single instance of the magic null object for a {@link Short}. */ + @SuppressWarnings("deprecation") public static final Short NULL_SHORT = new Short((short) 0); /** The single instance of the magic null object for a {@link Integer}. */ + @SuppressWarnings("deprecation") public static final Integer NULL_INTEGER = new Integer(0); /** The single instance of the magic null object for a {@link Float}. */ + @SuppressWarnings("deprecation") public static final Float NULL_FLOAT = new Float(0); /** The single instance of the magic null object for a {@link Long}. */ + @SuppressWarnings("deprecation") public static final Long NULL_LONG = new Long(0); /** The single instance of the magic null object for a {@link Double}. */ + @SuppressWarnings("deprecation") public static final Double NULL_DOUBLE = new Double(0); /** The single instance of the magic null object for a {@link BigInteger}. */ + @SuppressWarnings("deprecation") public static final BigInteger NULL_BIG_INTEGER = new BigInteger("0"); /** The single instance of the magic null object for a {@link BigDecimal}. */ + @SuppressWarnings("deprecation") public static final BigDecimal NULL_BIG_DECIMAL = new BigDecimal("0"); /** The single instance of the magic null object for a {@link DateTime}. */ @@ -83,6 +94,7 @@ public class Data { /** Cache of the magic null object for the given Java class. */ private static final ConcurrentHashMap, Object> NULL_CACHE = new ConcurrentHashMap, Object>(); + static { // special cases for some primitives NULL_CACHE.put(Boolean.class, NULL_BOOLEAN); @@ -107,35 +119,22 @@ public class Data { * @return magic object instance that represents the "null" value (not Java {@code null}) * @throws IllegalArgumentException if unable to create a new instance */ - public static T nullOf(Class objClass) { + public static T nullOf(Class objClass) { + // ConcurrentMap.computeIfAbsent is explicitly NOT used in the following logic. The + // ConcurrentHashMap implementation of that method BLOCKS if the mappingFunction triggers + // modification of the map which createNullInstance can do depending on the state of class + // loading. Object result = NULL_CACHE.get(objClass); if (result == null) { - synchronized (NULL_CACHE) { - result = NULL_CACHE.get(objClass); - if (result == null) { - if (objClass.isArray()) { - // arrays are special because we need to compute both the dimension and component type - int dims = 0; - Class componentType = objClass; - do { - componentType = componentType.getComponentType(); - dims++; - } while (componentType.isArray()); - result = Array.newInstance(componentType, new int[dims]); - } else if (objClass.isEnum()) { - // enum requires look for constant with @NullValue - FieldInfo fieldInfo = ClassInfo.of(objClass).getFieldInfo(null); - Preconditions.checkNotNull( - fieldInfo, "enum missing constant with @NullValue annotation: %s", objClass); - @SuppressWarnings({"unchecked", "rawtypes"}) - Enum e = fieldInfo.enumValue(); - result = e; - } else { - // other classes are simpler - result = Types.newInstance(objClass); - } - NULL_CACHE.put(objClass, result); - } + // If nullOf is called concurrently for the same class createNullInstance may be executed + // multiple times. However putIfAbsent ensures that no matter what the concurrent access + // pattern looks like callers always get a singleton instance returned. Since + // createNullInstance has no side-effects beyond triggering class loading this multiple-call + // pattern is safe. + Object newValue = createNullInstance(objClass); + result = NULL_CACHE.putIfAbsent(objClass, newValue); + if (result == null) { + result = newValue; } } @SuppressWarnings("unchecked") @@ -143,6 +142,30 @@ public static T nullOf(Class objClass) { return tResult; } + private static Object createNullInstance(Class objClass) { + if (objClass.isArray()) { + // arrays are special because we need to compute both the dimension and component type + int dims = 0; + Class componentType = objClass; + do { + componentType = componentType.getComponentType(); + dims++; + } while (componentType.isArray()); + return Array.newInstance(componentType, new int[dims]); + } + if (objClass.isEnum()) { + // enum requires look for constant with @NullValue + FieldInfo fieldInfo = ClassInfo.of(objClass).getFieldInfo(null); + Preconditions.checkNotNull( + fieldInfo, "enum missing constant with @NullValue annotation: %s", objClass); + @SuppressWarnings({"unchecked", "rawtypes"}) + Enum e = fieldInfo.enumValue(); + return e; + } + // other classes are simpler + return Types.newInstance(objClass); + } + /** * Returns whether the given object is the magic object that represents the null value of its * class. @@ -159,17 +182,15 @@ public static boolean isNull(Object object) { * Returns the map to use for the given data that is treated as a map from string key to some * value. * - *

    - * If the input is {@code null}, it returns an empty map. If the input is a map, it simply returns - * the input. Otherwise, it will create a map view using reflection that is backed by the object, - * so that any changes to the map will be reflected on the object. The map keys of that map view - * are based on the {@link Key} annotation, and null is not a possible map value, although the - * magic null instance is possible (see {@link #nullOf(Class)} and {@link #isNull(Object)}). - * Iteration order of the data keys is based on the sorted (ascending) key names of the declared - * fields. Note that since the map view is backed by the object, and that the object may change, - * many methods in the map view must recompute the field values using reflection, for example - * {@link Map#size()} must check the number of non-null fields. - *

    + *

    If the input is {@code null}, it returns an empty map. If the input is a map, it simply + * returns the input. Otherwise, it will create a map view using reflection that is backed by the + * object, so that any changes to the map will be reflected on the object. The map keys of that + * map view are based on the {@link Key} annotation, and null is not a possible map value, + * although the magic null instance is possible (see {@link #nullOf(Class)} and {@link + * #isNull(Object)}). Iteration order of the data keys is based on the sorted (ascending) key + * names of the declared fields. Note that since the map view is backed by the object, and that + * the object may change, many methods in the map view must recompute the field values using + * reflection, for example {@link Map#size()} must check the number of non-null fields. * * @param data any key value data, represented by an object or a map, or {@code null} * @return key/value map to use @@ -190,16 +211,14 @@ public static Map mapOf(Object data) { /** * Returns a deep clone of the given key/value data, such that the result is a completely * independent copy. - *

    - * This should not be used directly in the implementation of {@code Object.clone()}. Instead use - * {@link #deepCopy(Object, Object)} for that purpose. - *

    - *

    - * Final fields cannot be changed and therefore their value won't be copied. - *

    + * + *

    This should not be used directly in the implementation of {@code Object.clone()}. Instead + * use {@link #deepCopy(Object, Object)} for that purpose. + * + *

    Final fields cannot be changed and therefore their value won't be copied. * * @param data key/value data object or map to clone or {@code null} for a {@code null} return - * value + * value * @return deep clone or {@code null} for {@code null} input */ @SuppressWarnings("unchecked") @@ -236,30 +255,27 @@ public static T clone(T data) { * Makes a deep copy of the given source object into the destination object that is assumed to be * constructed using {@code Object.clone()}. * - *

    - * Example usage of this method in {@code Object.clone()}: - *

    + *

    Example usage of this method in {@code Object.clone()}: * *

    -  @Override
    -  public MyObject clone() {
    -    try {
    -      @SuppressWarnings("unchecked")
    -      MyObject result = (MyObject) super.clone();
    -      Data.deepCopy(this, result);
    -      return result;
    -    } catch (CloneNotSupportedException e) {
    -      throw new IllegalStateException(e);
    -    }
    -  }
    +   * @Override
    +   * public MyObject clone() {
    +   * try {
    +   * @SuppressWarnings("unchecked")
    +   * MyObject result = (MyObject) super.clone();
    +   * Data.deepCopy(this, result);
    +   * return result;
    +   * } catch (CloneNotSupportedException e) {
    +   * throw new IllegalStateException(e);
    +   * }
    +   * }
        * 
    - *

    - * Final fields cannot be changed and therefore their value won't be copied. - *

    + * + *

    Final fields cannot be changed and therefore their value won't be copied. * * @param src source object * @param dest destination object of identical type as source object, and any contained arrays - * must be the same length + * must be the same length */ public static void deepCopy(Object src, Object dest) { Class srcClass = src.getClass(); @@ -332,12 +348,10 @@ public static void deepCopy(Object src, Object dest) { * Returns whether the given type is one of the supported primitive classes like number and * date/time, or is a wildcard of one. * - *

    - * A primitive class is any class for whom {@link Class#isPrimitive()} is true, as well as any - * classes of type: {@link Character}, {@link String}, {@link Integer}, {@link Long}, - * {@link Short}, {@link Byte}, {@link Float}, {@link Double}, {@link BigInteger}, - * {@link BigDecimal}, {@link Boolean}, and {@link DateTime}. - *

    + *

    A primitive class is any class for whom {@link Class#isPrimitive()} is true, as well as any + * classes of type: {@link Character}, {@link String}, {@link Integer}, {@link Long}, {@link + * Short}, {@link Byte}, {@link Float}, {@link Double}, {@link BigInteger}, {@link BigDecimal}, + * {@link Boolean}, and {@link DateTime}. * * @param type type or {@code null} for {@code false} result * @return whether it is a primitive @@ -351,16 +365,24 @@ public static boolean isPrimitive(Type type) { return false; } Class typeClass = (Class) type; - return typeClass.isPrimitive() || typeClass == Character.class || typeClass == String.class - || typeClass == Integer.class || typeClass == Long.class || typeClass == Short.class - || typeClass == Byte.class || typeClass == Float.class || typeClass == Double.class - || typeClass == BigInteger.class || typeClass == BigDecimal.class - || typeClass == DateTime.class || typeClass == Boolean.class; + return typeClass.isPrimitive() + || typeClass == Character.class + || typeClass == String.class + || typeClass == Integer.class + || typeClass == Long.class + || typeClass == Short.class + || typeClass == Byte.class + || typeClass == Float.class + || typeClass == Double.class + || typeClass == BigInteger.class + || typeClass == BigDecimal.class + || typeClass == DateTime.class + || typeClass == Boolean.class; } /** - * Returns whether to given value is {@code null} or its class is primitive as defined by - * {@link Data#isPrimitive(Type)}. + * Returns whether to given value is {@code null} or its class is primitive as defined by {@link + * Data#isPrimitive(Type)}. */ public static boolean isValueOfPrimitiveType(Object fieldValue) { return fieldValue == null || Data.isPrimitive(fieldValue.getClass()); @@ -368,29 +390,27 @@ public static boolean isValueOfPrimitiveType(Object fieldValue) { /** * Parses the given string value based on the given primitive type. - *

    - * Types are parsed as follows: - *

    + * + *

    Types are parsed as follows: + * *

      - *
    • {@link Void}: null
    • - *
    • {@code null} or is assignable from {@link String} (like {@link Object}): no parsing
    • - *
    • {@code char} or {@link Character}: {@link String#charAt(int) String.charAt}(0) (requires - * length to be exactly 1)
    • - *
    • {@code boolean} or {@link Boolean}: {@link Boolean#valueOf(String)}
    • - *
    • {@code byte} or {@link Byte}: {@link Byte#valueOf(String)}
    • - *
    • {@code short} or {@link Short}: {@link Short#valueOf(String)}
    • - *
    • {@code int} or {@link Integer}: {@link Integer#valueOf(String)}
    • - *
    • {@code long} or {@link Long}: {@link Long#valueOf(String)}
    • - *
    • {@code float} or {@link Float}: {@link Float#valueOf(String)}
    • - *
    • {@code double} or {@link Double}: {@link Double#valueOf(String)}
    • - *
    • {@link BigInteger}: {@link BigInteger#BigInteger(String) BigInteger(String)}
    • - *
    • {@link BigDecimal}: {@link BigDecimal#BigDecimal(String) BigDecimal(String)}
    • - *
    • {@link DateTime}: {@link DateTime#parseRfc3339(String)}
    • + *
    • {@link Void}: null + *
    • {@code null} or is assignable from {@link String} (like {@link Object}): no parsing + *
    • {@code char} or {@link Character}: {@link String#charAt(int) String.charAt}(0) (requires + * length to be exactly 1) + *
    • {@code boolean} or {@link Boolean}: {@link Boolean#valueOf(String)} + *
    • {@code byte} or {@link Byte}: {@link Byte#valueOf(String)} + *
    • {@code short} or {@link Short}: {@link Short#valueOf(String)} + *
    • {@code int} or {@link Integer}: {@link Integer#valueOf(String)} + *
    • {@code long} or {@link Long}: {@link Long#valueOf(String)} + *
    • {@code float} or {@link Float}: {@link Float#valueOf(String)} + *
    • {@code double} or {@link Double}: {@link Double#valueOf(String)} + *
    • {@link BigInteger}: {@link BigInteger#BigInteger(String) BigInteger(String)} + *
    • {@link BigDecimal}: {@link BigDecimal#BigDecimal(String) BigDecimal(String)} + *
    • {@link DateTime}: {@link DateTime#parseRfc3339(String)} *
    * - *

    - * Note that this may not be the right behavior for some use cases. - *

    + *

    Note that this may not be the right behavior for some use cases. * * @param type primitive type or {@code null} to parse as a string * @param stringValue string value to parse or {@code null} for {@code null} result @@ -403,7 +423,8 @@ public static Object parsePrimitiveValue(Type type, String stringValue) { if (primitiveClass == Void.class) { return null; } - if (stringValue == null || primitiveClass == null + if (stringValue == null + || primitiveClass == null || primitiveClass.isAssignableFrom(String.class)) { return stringValue; } @@ -446,8 +467,8 @@ public static Object parsePrimitiveValue(Type type, String stringValue) { } if (primitiveClass.isEnum()) { if (!ClassInfo.of(primitiveClass).names.contains(stringValue)) { - throw new IllegalArgumentException(String.format("given enum name %s not part of " + - "enumeration", stringValue)); + throw new IllegalArgumentException( + String.format("given enum name %s not part of " + "enumeration", stringValue)); } @SuppressWarnings({"unchecked", "rawtypes"}) Enum result = ClassInfo.of(primitiveClass).getFieldInfo(stringValue).enumValue(); @@ -459,15 +480,16 @@ public static Object parsePrimitiveValue(Type type, String stringValue) { /** * Returns a new collection instance for the given type. - *

    - * Creates a new collection instance specified for the first input collection class that matches - * as follows: + * + *

    Creates a new collection instance specified for the first input collection class that + * matches as follows: + * *

      - *
    • {@code null} or an array or assignable from {@link ArrayList} (like {@link List} or - * {@link Collection} or {@link Object}): returns an {@link ArrayList}
    • - *
    • assignable from {@link HashSet}: returns a {@link HashSet}
    • - *
    • assignable from {@link TreeSet}: returns a {@link TreeSet}
    • - *
    • else: calls {@link Types#newInstance(Class)}
    • + *
    • {@code null} or an array or assignable from {@link ArrayList} (like {@link List} or + * {@link Collection} or {@link Object}): returns an {@link ArrayList} + *
    • assignable from {@link HashSet}: returns a {@link HashSet} + *
    • assignable from {@link TreeSet}: returns a {@link TreeSet} + *
    • else: calls {@link Types#newInstance(Class)} *
    * * @param type type or {@code null} for {@link ArrayList}. @@ -482,8 +504,10 @@ public static Collection newCollectionInstance(Type type) { type = ((ParameterizedType) type).getRawType(); } Class collectionClass = type instanceof Class ? (Class) type : null; - if (type == null || type instanceof GenericArrayType || collectionClass != null - && (collectionClass.isArray() || collectionClass.isAssignableFrom(ArrayList.class))) { + if (type == null + || type instanceof GenericArrayType + || collectionClass != null + && (collectionClass.isArray() || collectionClass.isAssignableFrom(ArrayList.class))) { return new ArrayList(); } if (collectionClass == null) { @@ -502,14 +526,14 @@ public static Collection newCollectionInstance(Type type) { /** * Returns a new instance of a map based on the given field class. - *

    - * Creates a new map instance specified for the first input map class that matches as follows: - *

    + * + *

    Creates a new map instance specified for the first input map class that matches as follows: + * *

      - *
    • {@code null} or assignable from {@link ArrayMap} (like {@link Map} or {@link Object}): - * returns an {@link ArrayMap}
    • - *
    • assignable from {@link TreeMap} (like {@link SortedMap}): returns a {@link TreeMap}
    • - *
    • else: calls {@link Types#newInstance(Class)}
    • + *
    • {@code null} or assignable from {@link ArrayMap} (like {@link Map} or {@link Object}): + * returns an {@link ArrayMap} + *
    • assignable from {@link TreeMap} (like {@link SortedMap}): returns a {@link TreeMap} + *
    • else: calls {@link Types#newInstance(Class)} *
    * * @param mapClass field class @@ -533,10 +557,10 @@ public static Map newMapInstance(Class mapClass) { * resolved. * * @param context context list, ordering from least specific to most specific type context, for - * example container class and then its field + * example container class and then its field * @param type type or {@code null} for {@code null} result * @return resolved type (which may be class, parameterized type, or generic array type, but not - * wildcard type or type variable) or {@code null} for {@code null} input + * wildcard type or type variable) or {@code null} for {@code null} input */ public static Type resolveWildcardTypeOrTypeVariable(List context, Type type) { // first deal with a wildcard, e.g. ? extends Number diff --git a/google-http-client/src/main/java/com/google/api/client/util/DataMap.java b/google-http-client/src/main/java/com/google/api/client/util/DataMap.java index cf234d15c..a858bf1db 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/DataMap.java +++ b/google-http-client/src/main/java/com/google/api/client/util/DataMap.java @@ -22,8 +22,8 @@ import java.util.NoSuchElementException; /** - * Map that uses {@link ClassInfo} to parse the key/value pairs into a map for use in - * {@link Data#mapOf(Object)}. + * Map that uses {@link ClassInfo} to parse the key/value pairs into a map for use in {@link + * Data#mapOf(Object)}. * * @author Yaniv Inbar */ @@ -37,13 +37,10 @@ final class DataMap extends AbstractMap { /** Object's class info. */ final ClassInfo classInfo; - /** - * @param object object being reflected - */ + /** @param object object being reflected */ DataMap(Object object, boolean ignoreCase) { this.object = object; classInfo = ClassInfo.of(object.getClass(), ignoreCase); - Preconditions.checkArgument(!classInfo.isEnum()); } @Override @@ -118,8 +115,8 @@ public boolean isEmpty() { final class EntryIterator implements Iterator> { /** - * Next index into key names array computed in {@link #hasNext()} or {@code -1} before - * {@link #hasNext()} has been called. + * Next index into key names array computed in {@link #hasNext()} or {@code -1} before {@link + * #hasNext()} has been called. */ private int nextKeyIndex = -1; @@ -181,16 +178,14 @@ public void remove() { /** * Entry in the reflection map. - *

    - * Null key or value is not allowed. - *

    + * + *

    Null key or value is not allowed. */ final class Entry implements Map.Entry { /** - * Current field value, possibly modified only by {@link #setValue(Object)}. As specified - * {@link java.util.Map.Entry}, behavior is undefined if the field value is modified by other - * means. + * Current field value, possibly modified only by {@link #setValue(Object)}. As specified {@link + * java.util.Map.Entry}, behavior is undefined if the field value is modified by other means. */ private Object fieldValue; diff --git a/google-http-client/src/main/java/com/google/api/client/util/DateTime.java b/google-http-client/src/main/java/com/google/api/client/util/DateTime.java index 0417b4282..2aa914e9c 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/DateTime.java +++ b/google-http-client/src/main/java/com/google/api/client/util/DateTime.java @@ -14,12 +14,15 @@ package com.google.api.client.util; +import com.google.common.base.Strings; import java.io.Serializable; import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; +import java.util.Objects; import java.util.TimeZone; +import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -27,9 +30,7 @@ * Immutable representation of a date with an optional time and an optional time zone based on RFC 3339. * - *

    - * Implementation is immutable and therefore thread-safe. - *

    + *

    Implementation is immutable and therefore thread-safe. * * @since 1.0 * @author Yaniv Inbar @@ -41,18 +42,18 @@ public final class DateTime implements Serializable { private static final TimeZone GMT = TimeZone.getTimeZone("GMT"); /** Regular expression for parsing RFC3339 date/times. */ - private static final Pattern RFC3339_PATTERN = Pattern.compile( - "^(\\d{4})-(\\d{2})-(\\d{2})" // yyyy-MM-dd - + "([Tt](\\d{2}):(\\d{2}):(\\d{2})(\\.\\d+)?)?" // 'T'HH:mm:ss.milliseconds - + "([Zz]|([+-])(\\d{2}):(\\d{2}))?"); // 'Z' or time zone shift HH:mm following '+' or '-' + private static final String RFC3339_REGEX = + "(\\d{4})-(\\d{2})-(\\d{2})" // yyyy-MM-dd + + "([Tt](\\d{2}):(\\d{2}):(\\d{2})(\\.\\d{1,9})?)?" // 'T'HH:mm:ss.nanoseconds + + "([Zz]|([+-])(\\d{2}):(\\d{2}))?"; // 'Z' or time zone shift HH:mm following '+' or '-' + + private static final Pattern RFC3339_PATTERN = Pattern.compile(RFC3339_REGEX); /** * Date/time value expressed as the number of ms since the Unix epoch. * - *

    - * If the time zone is specified, this value is normalized to UTC, so to format this date/time + *

    If the time zone is specified, this value is normalized to UTC, so to format this date/time * value, the time zone shift has to be applied. - *

    */ private final long value; @@ -75,10 +76,8 @@ public DateTime(Date date, TimeZone zone) { /** * Instantiates {@link DateTime} from the number of milliseconds since the Unix epoch. * - *

    - * The time zone is interpreted as {@code TimeZone.getDefault()}, which may vary with + *

    The time zone is interpreted as {@code TimeZone.getDefault()}, which may vary with * implementation. - *

    * * @param value number of milliseconds since the Unix epoch (January 1, 1970, 00:00:00 GMT) */ @@ -89,10 +88,8 @@ public DateTime(long value) { /** * Instantiates {@link DateTime} from a {@link Date}. * - *

    - * The time zone is interpreted as {@code TimeZone.getDefault()}, which may vary with + *

    The time zone is interpreted as {@code TimeZone.getDefault()}, which may vary with * implementation. - *

    * * @param value date and time */ @@ -118,7 +115,7 @@ public DateTime(long value, int tzShift) { * @param dateOnly specifies if this should represent a date-only value * @param value number of milliseconds since the Unix epoch (January 1, 1970, 00:00:00 GMT) * @param tzShift time zone, represented by the number of minutes off of UTC, or {@code null} for - * {@code TimeZone.getDefault()}. + * {@code TimeZone.getDefault()}. */ public DateTime(boolean dateOnly, long value, Integer tzShift) { this.dateOnly = dateOnly; @@ -131,14 +128,12 @@ public DateTime(boolean dateOnly, long value, Integer tzShift) { * Instantiates {@link DateTime} from an RFC 3339 * date/time value. * - *

    - * Upgrade warning: in prior version 1.17, this method required milliseconds to be exactly 3 + *

    Upgrade warning: in prior version 1.17, this method required milliseconds to be exactly 3 * digits (if included), and did not throw an exception for all types of invalid input values, but * starting in version 1.18, the parsing done by this method has become more strict to enforce - * that only valid RFC3339 strings are entered, and if not, it throws a - * {@link NumberFormatException}. Also, in accordance with the RFC3339 standard, any number of + * that only valid RFC3339 strings are entered, and if not, it throws a {@link + * NumberFormatException}. Also, in accordance with the RFC3339 standard, any number of * milliseconds digits is now allowed. - *

    * * @param value an RFC 3339 date/time value. * @since 1.11 @@ -156,10 +151,8 @@ public DateTime(String value) { /** * Returns the date/time value expressed as the number of milliseconds since the Unix epoch. * - *

    - * If the time zone is specified, this value is normalized to UTC, so to format this date/time + *

    If the time zone is specified, this value is normalized to UTC, so to format this date/time * value, the time zone shift has to be applied. - *

    * * @since 1.5 */ @@ -240,10 +233,8 @@ public String toString() { /** * {@inheritDoc} * - *

    - * A check is added that the time zone is the same. If you ONLY want to check equality of time + *

    A check is added that the time zone is the same. If you ONLY want to check equality of time * value, check equality on the {@link #getValue()}. - *

    */ @Override public boolean equals(Object o) { @@ -265,26 +256,120 @@ public int hashCode() { /** * Parses an RFC3339 date/time value. * - *

    - * Upgrade warning: in prior version 1.17, this method required milliseconds to be exactly 3 + *

    Upgrade warning: in prior version 1.17, this method required milliseconds to be exactly 3 * digits (if included), and did not throw an exception for all types of invalid input values, but * starting in version 1.18, the parsing done by this method has become more strict to enforce - * that only valid RFC3339 strings are entered, and if not, it throws a - * {@link NumberFormatException}. Also, in accordance with the RFC3339 standard, any number of + * that only valid RFC3339 strings are entered, and if not, it throws a {@link + * NumberFormatException}. Also, in accordance with the RFC3339 standard, any number of * milliseconds digits is now allowed. - *

    * - *

    - * For the date-only case, the time zone is ignored and the hourOfDay, minute, second, and + *

    Any time information beyond millisecond precision is truncated. + * + *

    For the date-only case, the time zone is ignored and the hourOfDay, minute, second, and * millisecond parameters are set to zero. - *

    * * @param str Date/time string in RFC3339 format * @throws NumberFormatException if {@code str} doesn't match the RFC3339 standard format; an - * exception is thrown if {@code str} doesn't match {@code RFC3339_REGEX} or if it - * contains a time zone shift but no time. + * exception is thrown if {@code str} doesn't match {@code RFC3339_REGEX} or if it contains a + * time zone shift but no time. + */ + public static DateTime parseRfc3339(String str) { + return parseRfc3339WithNanoSeconds(str).toDateTime(); + } + + /** + * Parses an RFC3339 timestamp to a pair of seconds and nanoseconds since Unix Epoch. + * + * @param str Date/time string in RFC3339 format + * @throws IllegalArgumentException if {@code str} doesn't match the RFC3339 standard format; an + * exception is thrown if {@code str} doesn't match {@code RFC3339_REGEX} or if it contains a + * time zone shift but no time. */ - public static DateTime parseRfc3339(String str) throws NumberFormatException { + public static SecondsAndNanos parseRfc3339ToSecondsAndNanos(String str) { + Rfc3339ParseResult time = parseRfc3339WithNanoSeconds(str); + return time.toSecondsAndNanos(); + } + + /** A timestamp represented as the number of seconds and nanoseconds since Epoch. */ + public static final class SecondsAndNanos implements Serializable { + private static long serialVersionUID = 1L; + + private final long seconds; + private final int nanos; + + public static SecondsAndNanos ofSecondsAndNanos(long seconds, int nanos) { + return new SecondsAndNanos(seconds, nanos); + } + + private SecondsAndNanos(long seconds, int nanos) { + this.seconds = seconds; + this.nanos = nanos; + } + + public long getSeconds() { + return seconds; + } + + public int getNanos() { + return nanos; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SecondsAndNanos that = (SecondsAndNanos) o; + return seconds == that.seconds && nanos == that.nanos; + } + + @Override + public int hashCode() { + return Objects.hash(seconds, nanos); + } + + @Override + public String toString() { + return String.format("Seconds: %d, Nanos: %d", seconds, nanos); + } + } + + /** Result of parsing an RFC 3339 string. */ + private static class Rfc3339ParseResult implements Serializable { + private static final long serialVersionUID = 1L; + + private final long seconds; + private final int nanos; + private final boolean timeGiven; + private final Integer tzShift; + + private Rfc3339ParseResult(long seconds, int nanos, boolean timeGiven, Integer tzShift) { + this.seconds = seconds; + this.nanos = nanos; + this.timeGiven = timeGiven; + this.tzShift = tzShift; + } + + /** + * Convert this {@link Rfc3339ParseResult} to a {@link DateTime} with millisecond precision. Any + * fraction of a millisecond will be truncated. + */ + private DateTime toDateTime() { + long seconds = TimeUnit.SECONDS.toMillis(this.seconds); + long nanos = TimeUnit.NANOSECONDS.toMillis(this.nanos); + return new DateTime(!timeGiven, seconds + nanos, tzShift); + } + + private SecondsAndNanos toSecondsAndNanos() { + return new SecondsAndNanos(seconds, nanos); + } + } + + private static Rfc3339ParseResult parseRfc3339WithNanoSeconds(String str) + throws NumberFormatException { Matcher matcher = RFC3339_PATTERN.matcher(str); if (!matcher.matches()) { throw new NumberFormatException("Invalid date/time format: " + str); @@ -299,45 +384,47 @@ public static DateTime parseRfc3339(String str) throws NumberFormatException { int hourOfDay = 0; int minute = 0; int second = 0; - int milliseconds = 0; + int nanoseconds = 0; Integer tzShiftInteger = null; if (isTzShiftGiven && !isTimeGiven) { - throw new NumberFormatException("Invalid date/time format, cannot specify time zone shift" + - " without specifying time: " + str); + throw new NumberFormatException( + "Invalid date/time format, cannot specify time zone shift" + + " without specifying time: " + + str); } if (isTimeGiven) { hourOfDay = Integer.parseInt(matcher.group(5)); // HH minute = Integer.parseInt(matcher.group(6)); // mm second = Integer.parseInt(matcher.group(7)); // ss - if (matcher.group(8) != null) { // contains .milliseconds? - milliseconds = Integer.parseInt(matcher.group(8).substring(1)); // milliseconds - // The number of digits after the dot may not be 3. Need to renormalize. - int fractionDigits = matcher.group(8).substring(1).length() - 3; - milliseconds = (int) ((float) milliseconds / Math.pow(10, fractionDigits)); + if (matcher.group(8) != null) { // contains .nanoseconds? + String fraction = Strings.padEnd(matcher.group(8).substring(1), 9, '0'); + nanoseconds = Integer.parseInt(fraction); } } Calendar dateTime = new GregorianCalendar(GMT); + dateTime.clear(); dateTime.set(year, month, day, hourOfDay, minute, second); - dateTime.set(Calendar.MILLISECOND, milliseconds); long value = dateTime.getTimeInMillis(); if (isTimeGiven && isTzShiftGiven) { - int tzShift; - if (Character.toUpperCase(tzShiftRegexGroup.charAt(0)) == 'Z') { - tzShift = 0; - } else { - tzShift = Integer.parseInt(matcher.group(11)) * 60 // time zone shift HH - + Integer.parseInt(matcher.group(12)); // time zone shift mm + if (Character.toUpperCase(tzShiftRegexGroup.charAt(0)) != 'Z') { + int tzShift = + Integer.parseInt(matcher.group(11)) * 60 // time zone shift HH + + Integer.parseInt(matcher.group(12)); // time zone shift mm if (matcher.group(10).charAt(0) == '-') { // time zone shift + or - tzShift = -tzShift; } value -= tzShift * 60000L; // e.g. if 1 hour ahead of UTC, subtract an hour to get UTC time + tzShiftInteger = tzShift; + } else { + tzShiftInteger = 0; } - tzShiftInteger = tzShift; } - return new DateTime(!isTimeGiven, value, tzShiftInteger); + // convert to seconds and nanoseconds + long secondsSinceEpoch = value / 1000L; + return new Rfc3339ParseResult(secondsSinceEpoch, nanoseconds, isTimeGiven, tzShiftInteger); } /** Appends a zero-padded number to a string builder. */ diff --git a/google-http-client/src/main/java/com/google/api/client/util/ExponentialBackOff.java b/google-http-client/src/main/java/com/google/api/client/util/ExponentialBackOff.java index c949b6f23..12e744542 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/ExponentialBackOff.java +++ b/google-http-client/src/main/java/com/google/api/client/util/ExponentialBackOff.java @@ -20,56 +20,44 @@ * Implementation of {@link BackOff} that increases the back off period for each retry attempt using * a randomization function that grows exponentially. * - *

    - * {@link #nextBackOffMillis()} is calculated using the following formula: - *

    + *

    {@link #nextBackOffMillis()} is calculated using the following formula: * *

    -   randomized_interval =
    -       retry_interval * (random value in range [1 - randomization_factor, 1 + randomization_factor])
    + * randomized_interval =
    + * retry_interval * (random value in range [1 - randomization_factor, 1 + randomization_factor])
      * 
    * - *

    - * In other words {@link #nextBackOffMillis()} will range between the randomization factor + *

    In other words {@link #nextBackOffMillis()} will range between the randomization factor * percentage below and above the retry interval. For example, using 2 seconds as the base retry * interval and 0.5 as the randomization factor, the actual back off period used in the next retry * attempt will be between 1 and 3 seconds. - *

    * - *

    - * Note: max_interval caps the retry_interval and not the randomized_interval. - *

    + *

    Note: max_interval caps the retry_interval and not the randomized_interval. * - *

    - * If the time elapsed since an {@link ExponentialBackOff} instance is created goes past the - * max_elapsed_time then the method {@link #nextBackOffMillis()} starts returning - * {@link BackOff#STOP}. The elapsed time can be reset by calling {@link #reset()}. - *

    + *

    If the time elapsed since an {@link ExponentialBackOff} instance is created goes past the + * max_elapsed_time then the method {@link #nextBackOffMillis()} starts returning {@link + * BackOff#STOP}. The elapsed time can be reset by calling {@link #reset()}. * - *

    - * Example: The default retry_interval is .5 seconds, default randomization_factor is 0.5, default - * multiplier is 1.5 and the default max_interval is 1 minute. For 10 tries the sequence will be - * (values in seconds) and assuming we go over the max_elapsed_time on the 10th try: - *

    + *

    Example: The default retry_interval is .5 seconds, default randomization_factor is 0.5, + * default multiplier is 1.5 and the default max_interval is 1 minute. For 10 tries the sequence + * will be (values in seconds) and assuming we go over the max_elapsed_time on the 10th try: * *

    -   request#     retry_interval     randomized_interval
    -
    -   1             0.5                [0.25,   0.75]
    -   2             0.75               [0.375,  1.125]
    -   3             1.125              [0.562,  1.687]
    -   4             1.687              [0.8435, 2.53]
    -   5             2.53               [1.265,  3.795]
    -   6             3.795              [1.897,  5.692]
    -   7             5.692              [2.846,  8.538]
    -   8             8.538              [4.269, 12.807]
    -   9            12.807              [6.403, 19.210]
    -   10           19.210              {@link BackOff#STOP}
    + * request#     retry_interval     randomized_interval
    + *
    + * 1             0.5                [0.25,   0.75]
    + * 2             0.75               [0.375,  1.125]
    + * 3             1.125              [0.562,  1.687]
    + * 4             1.687              [0.8435, 2.53]
    + * 5             2.53               [1.265,  3.795]
    + * 6             3.795              [1.897,  5.692]
    + * 7             5.692              [2.846,  8.538]
    + * 8             8.538              [4.269, 12.807]
    + * 9            12.807              [6.403, 19.210]
    + * 10           19.210              {@link BackOff#STOP}
      * 
    * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @since 1.15 * @author Ravi Mistry @@ -103,10 +91,8 @@ public class ExponentialBackOff implements BackOff { /** * The randomization factor to use for creating a range around the retry interval. * - *

    - * A randomization factor of 0.5 results in a random period ranging between 50% below and 50% + *

    A randomization factor of 0.5 results in a random period ranging between 50% below and 50% * above the retry interval. - *

    */ private final double randomizationFactor; @@ -126,8 +112,8 @@ public class ExponentialBackOff implements BackOff { long startTimeNanos; /** - * The maximum elapsed time after instantiating {@link ExponentialBackOff} or calling - * {@link #reset()} after which {@link #nextBackOffMillis()} returns {@link BackOff#STOP}. + * The maximum elapsed time after instantiating {@link ExponentialBackOff} or calling {@link + * #reset()} after which {@link #nextBackOffMillis()} returns {@link BackOff#STOP}. */ private final int maxElapsedTimeMillis; @@ -137,25 +123,21 @@ public class ExponentialBackOff implements BackOff { /** * Creates an instance of ExponentialBackOffPolicy using default values. * - *

    - * To override the defaults use {@link Builder}. - *

    + *

    To override the defaults use {@link Builder}. * *

      - *
    • {@code initialIntervalMillis} defaults to {@link #DEFAULT_INITIAL_INTERVAL_MILLIS}
    • - *
    • {@code randomizationFactor} defaults to {@link #DEFAULT_RANDOMIZATION_FACTOR}
    • - *
    • {@code multiplier} defaults to {@link #DEFAULT_MULTIPLIER}
    • - *
    • {@code maxIntervalMillis} defaults to {@link #DEFAULT_MAX_INTERVAL_MILLIS}
    • - *
    • {@code maxElapsedTimeMillis} defaults in {@link #DEFAULT_MAX_ELAPSED_TIME_MILLIS}
    • + *
    • {@code initialIntervalMillis} defaults to {@link #DEFAULT_INITIAL_INTERVAL_MILLIS} + *
    • {@code randomizationFactor} defaults to {@link #DEFAULT_RANDOMIZATION_FACTOR} + *
    • {@code multiplier} defaults to {@link #DEFAULT_MULTIPLIER} + *
    • {@code maxIntervalMillis} defaults to {@link #DEFAULT_MAX_INTERVAL_MILLIS} + *
    • {@code maxElapsedTimeMillis} defaults in {@link #DEFAULT_MAX_ELAPSED_TIME_MILLIS} *
    */ public ExponentialBackOff() { this(new Builder()); } - /** - * @param builder builder - */ + /** @param builder builder */ protected ExponentialBackOff(Builder builder) { initialIntervalMillis = builder.initialIntervalMillis; randomizationFactor = builder.randomizationFactor; @@ -180,14 +162,10 @@ public final void reset() { /** * {@inheritDoc} * - *

    - * This method calculates the next back off interval using the formula: randomized_interval = + *

    This method calculates the next back off interval using the formula: randomized_interval = * retry_interval +/- (randomization_factor * retry_interval) - *

    * - *

    - * Subclasses may override if a different algorithm is required. - *

    + *

    Subclasses may override if a different algorithm is required. */ public long nextBackOffMillis() throws IOException { // Make sure we have not gone over the maximum elapsed time. @@ -224,25 +202,19 @@ public final int getInitialIntervalMillis() { /** * Returns the randomization factor to use for creating a range around the retry interval. * - *

    - * A randomization factor of 0.5 results in a random period ranging between 50% below and 50% + *

    A randomization factor of 0.5 results in a random period ranging between 50% below and 50% * above the retry interval. - *

    */ public final double getRandomizationFactor() { return randomizationFactor; } - /** - * Returns the current retry interval in milliseconds. - */ + /** Returns the current retry interval in milliseconds. */ public final int getCurrentIntervalMillis() { return currentIntervalMillis; } - /** - * Returns the value to multiply the current interval with for each retry attempt. - */ + /** Returns the value to multiply the current interval with for each retry attempt. */ public final double getMultiplier() { return multiplier; } @@ -258,11 +230,9 @@ public final int getMaxIntervalMillis() { /** * Returns the maximum elapsed time in milliseconds. * - *

    - * If the time elapsed since an {@link ExponentialBackOff} instance is created goes past the - * max_elapsed_time then the method {@link #nextBackOffMillis()} starts returning - * {@link BackOff#STOP}. The elapsed time can be reset by calling {@link #reset()}. - *

    + *

    If the time elapsed since an {@link ExponentialBackOff} instance is created goes past the + * max_elapsed_time then the method {@link #nextBackOffMillis()} starts returning {@link + * BackOff#STOP}. The elapsed time can be reset by calling {@link #reset()}. */ public final int getMaxElapsedTimeMillis() { return maxElapsedTimeMillis; @@ -272,17 +242,13 @@ public final int getMaxElapsedTimeMillis() { * Returns the elapsed time in milliseconds since an {@link ExponentialBackOff} instance is * created and is reset when {@link #reset()} is called. * - *

    - * The elapsed time is computed using {@link System#nanoTime()}. - *

    + *

    The elapsed time is computed using {@link System#nanoTime()}. */ public final long getElapsedTimeMillis() { return (nanoClock.nanoTime() - startTimeNanos) / 1000000; } - /** - * Increments the current interval by multiplying it with the multiplier. - */ + /** Increments the current interval by multiplying it with the multiplier. */ private void incrementCurrentInterval() { // Check for overflow, if overflow is detected set the current interval to the max interval. if (currentIntervalMillis >= maxIntervalMillis / multiplier) { @@ -295,9 +261,7 @@ private void incrementCurrentInterval() { /** * Builder for {@link ExponentialBackOff}. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. */ public static class Builder { @@ -307,10 +271,8 @@ public static class Builder { /** * The randomization factor to use for creating a range around the retry interval. * - *

    - * A randomization factor of 0.5 results in a random period ranging between 50% below and 50% + *

    A randomization factor of 0.5 results in a random period ranging between 50% below and 50% * above the retry interval. - *

    */ double randomizationFactor = DEFAULT_RANDOMIZATION_FACTOR; @@ -325,16 +287,15 @@ public static class Builder { /** * The maximum elapsed time in milliseconds after instantiating {@link ExponentialBackOff} or - * calling {@link #reset()} after which {@link #nextBackOffMillis()} returns - * {@link BackOff#STOP}. + * calling {@link #reset()} after which {@link #nextBackOffMillis()} returns {@link + * BackOff#STOP}. */ int maxElapsedTimeMillis = DEFAULT_MAX_ELAPSED_TIME_MILLIS; /** Nano clock. */ NanoClock nanoClock = NanoClock.SYSTEM; - public Builder() { - } + public Builder() {} /** Builds a new instance of {@link ExponentialBackOff}. */ public ExponentialBackOff build() { @@ -342,21 +303,19 @@ public ExponentialBackOff build() { } /** - * Returns the initial retry interval in milliseconds. The default value is - * {@link #DEFAULT_INITIAL_INTERVAL_MILLIS}. + * Returns the initial retry interval in milliseconds. The default value is {@link + * #DEFAULT_INITIAL_INTERVAL_MILLIS}. */ public final int getInitialIntervalMillis() { return initialIntervalMillis; } /** - * Sets the initial retry interval in milliseconds. The default value is - * {@link #DEFAULT_INITIAL_INTERVAL_MILLIS}. Must be {@code > 0}. + * Sets the initial retry interval in milliseconds. The default value is {@link + * #DEFAULT_INITIAL_INTERVAL_MILLIS}. Must be {@code > 0}. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Builder setInitialIntervalMillis(int initialIntervalMillis) { this.initialIntervalMillis = initialIntervalMillis; @@ -367,15 +326,11 @@ public Builder setInitialIntervalMillis(int initialIntervalMillis) { * Returns the randomization factor to use for creating a range around the retry interval. The * default value is {@link #DEFAULT_RANDOMIZATION_FACTOR}. * - *

    - * A randomization factor of 0.5 results in a random period ranging between 50% below and 50% + *

    A randomization factor of 0.5 results in a random period ranging between 50% below and 50% * above the retry interval. - *

    * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public final double getRandomizationFactor() { return randomizationFactor; @@ -383,18 +338,14 @@ public final double getRandomizationFactor() { /** * Sets the randomization factor to use for creating a range around the retry interval. The - * default value is {@link #DEFAULT_RANDOMIZATION_FACTOR}. Must fall in the range - * {@code 0 <= randomizationFactor < 1}. + * default value is {@link #DEFAULT_RANDOMIZATION_FACTOR}. Must fall in the range {@code 0 <= + * randomizationFactor < 1}. * - *

    - * A randomization factor of 0.5 results in a random period ranging between 50% below and 50% + *

    A randomization factor of 0.5 results in a random period ranging between 50% below and 50% * above the retry interval. - *

    * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Builder setRandomizationFactor(double randomizationFactor) { this.randomizationFactor = randomizationFactor; @@ -413,10 +364,8 @@ public final double getMultiplier() { * Sets the value to multiply the current interval with for each retry attempt. The default * value is {@link #DEFAULT_MULTIPLIER}. Must be {@code >= 1}. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Builder setMultiplier(double multiplier) { this.multiplier = multiplier; @@ -425,8 +374,8 @@ public Builder setMultiplier(double multiplier) { /** * Returns the maximum value of the back off period in milliseconds. Once the current interval - * reaches this value it stops increasing. The default value is - * {@link #DEFAULT_MAX_INTERVAL_MILLIS}. Must be {@code >= initialInterval}. + * reaches this value it stops increasing. The default value is {@link + * #DEFAULT_MAX_INTERVAL_MILLIS}. Must be {@code >= initialInterval}. */ public final int getMaxIntervalMillis() { return maxIntervalMillis; @@ -434,13 +383,11 @@ public final int getMaxIntervalMillis() { /** * Sets the maximum value of the back off period in milliseconds. Once the current interval - * reaches this value it stops increasing. The default value is - * {@link #DEFAULT_MAX_INTERVAL_MILLIS}. + * reaches this value it stops increasing. The default value is {@link + * #DEFAULT_MAX_INTERVAL_MILLIS}. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Builder setMaxIntervalMillis(int maxIntervalMillis) { this.maxIntervalMillis = maxIntervalMillis; @@ -448,33 +395,27 @@ public Builder setMaxIntervalMillis(int maxIntervalMillis) { } /** - * Returns the maximum elapsed time in milliseconds. The default value is - * {@link #DEFAULT_MAX_ELAPSED_TIME_MILLIS}. + * Returns the maximum elapsed time in milliseconds. The default value is {@link + * #DEFAULT_MAX_ELAPSED_TIME_MILLIS}. * - *

    - * If the time elapsed since an {@link ExponentialBackOff} instance is created goes past the - * max_elapsed_time then the method {@link #nextBackOffMillis()} starts returning - * {@link BackOff#STOP}. The elapsed time can be reset by calling {@link #reset()}. - *

    + *

    If the time elapsed since an {@link ExponentialBackOff} instance is created goes past the + * max_elapsed_time then the method {@link #nextBackOffMillis()} starts returning {@link + * BackOff#STOP}. The elapsed time can be reset by calling {@link #reset()}. */ public final int getMaxElapsedTimeMillis() { return maxElapsedTimeMillis; } /** - * Sets the maximum elapsed time in milliseconds. The default value is - * {@link #DEFAULT_MAX_ELAPSED_TIME_MILLIS}. Must be {@code > 0}. + * Sets the maximum elapsed time in milliseconds. The default value is {@link + * #DEFAULT_MAX_ELAPSED_TIME_MILLIS}. Must be {@code > 0}. * - *

    - * If the time elapsed since an {@link ExponentialBackOff} instance is created goes past the - * max_elapsed_time then the method {@link #nextBackOffMillis()} starts returning - * {@link BackOff#STOP}. The elapsed time can be reset by calling {@link #reset()}. - *

    + *

    If the time elapsed since an {@link ExponentialBackOff} instance is created goes past the + * max_elapsed_time then the method {@link #nextBackOffMillis()} starts returning {@link + * BackOff#STOP}. The elapsed time can be reset by calling {@link #reset()}. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Builder setMaxElapsedTimeMillis(int maxElapsedTimeMillis) { this.maxElapsedTimeMillis = maxElapsedTimeMillis; @@ -489,10 +430,8 @@ public final NanoClock getNanoClock() { /** * Sets the nano clock ({@link NanoClock#SYSTEM} by default). * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public Builder setNanoClock(NanoClock nanoClock) { this.nanoClock = Preconditions.checkNotNull(nanoClock); diff --git a/google-http-client/src/main/java/com/google/api/client/util/FieldInfo.java b/google-http-client/src/main/java/com/google/api/client/util/FieldInfo.java index 5b7fa7900..36d0dc9dc 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/FieldInfo.java +++ b/google-http-client/src/main/java/com/google/api/client/util/FieldInfo.java @@ -14,18 +14,21 @@ package com.google.api.client.util; +import com.google.common.base.Ascii; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.WeakHashMap; /** * Parses field information to determine data key name/value pair associated with the field. * - *

    - * Implementation is thread-safe. - *

    + *

    Implementation is thread-safe. * * @since 1.0 * @author Yaniv Inbar @@ -60,7 +63,7 @@ public static FieldInfo of(Enum enumValue) { * * @param field field or {@code null} for {@code null} result * @return field information or {@code null} if the field has no {@link #name} or for {@code null} - * input + * input */ public static FieldInfo of(Field field) { if (field == null) { @@ -112,14 +115,14 @@ public static FieldInfo of(Field field) { /** Field. */ private final Field field; + private final Method[] setters; + /** * Data key name associated with the field for a non-enum-constant with a {@link Key} annotation, * or data key value associated with the enum constant with a {@link Value} annotation or {@code * null} for an enum constant with a {@link NullValue} annotation. * - *

    - * This string is interned. - *

    + *

    This string is interned. */ private final String name; @@ -127,6 +130,27 @@ public static FieldInfo of(Field field) { this.field = field; this.name = name == null ? null : name.intern(); isPrimitive = Data.isPrimitive(getType()); + this.setters = settersMethodForField(field); + } + + /** Creates list of setter methods for a field only in declaring class. */ + private Method[] settersMethodForField(final Field field) { + List methods = new ArrayList<>(); + String fieldSetter = "set" + Ascii.toUpperCase(field.getName().substring(0, 1)); + if (field.getName().length() > 1) { + fieldSetter += field.getName().substring(1); + } + for (Method method : field.getDeclaringClass().getDeclaredMethods()) { + if (method.getParameterTypes().length == 1) { + // add case-sensitive matches first in the list + if (method.getName().equals(fieldSetter)) { + methods.add(0, method); + } else if (Ascii.toLowerCase(method.getName()).equals(Ascii.toLowerCase(fieldSetter))) { + methods.add(method); + } + } + } + return methods.toArray(new Method[0]); } /** @@ -143,9 +167,7 @@ public Field getField() { * annotation, or data key value associated with the enum constant with a {@link Value} annotation * or {@code null} for an enum constant with a {@link NullValue} annotation. * - *

    - * This string is interned. - *

    + *

    This string is interned. * * @since 1.4 */ @@ -190,19 +212,28 @@ public boolean isPrimitive() { return isPrimitive; } - /** - * Returns the value of the field in the given object instance using reflection. - */ + /** Returns the value of the field in the given object instance using reflection. */ public Object getValue(Object obj) { return getFieldValue(field, obj); } /** - * Sets to the given value of the field in the given object instance using reflection. - *

    - * If the field is final, it checks that value being set is identical to the existing value. + * Sets this field in the given object to the given value using reflection. + * + *

    If the field is final, it checks that the value being set is identical to the existing + * value. */ public void setValue(Object obj, Object value) { + for (Method method : setters) { + if (value == null || method.getParameterTypes()[0].isAssignableFrom(value.getClass())) { + try { + method.invoke(obj, value); + return; + } catch (IllegalAccessException | InvocationTargetException e) { + // try to set field directly + } + } + } setFieldValue(field, obj, value); } @@ -216,9 +247,7 @@ public > T enumValue() { return Enum.valueOf((Class) field.getDeclaringClass(), field.getName()); } - /** - * Returns the value of the given field in the given object instance using reflection. - */ + /** Returns the value of the given field in the given object using reflection. */ public static Object getFieldValue(Field field, Object obj) { try { return field.get(obj); @@ -228,17 +257,24 @@ public static Object getFieldValue(Field field, Object obj) { } /** - * Sets to the given value of the given field in the given object instance using reflection. - *

    - * If the field is final, it checks that value being set is identical to the existing value. + * Sets the given field in the given object to the given value using reflection. + * + *

    If the field is final, it checks that the value being set is identical to the existing + * value. */ public static void setFieldValue(Field field, Object obj, Object value) { if (Modifier.isFinal(field.getModifiers())) { Object finalValue = getFieldValue(field, obj); if (value == null ? finalValue != null : !value.equals(finalValue)) { throw new IllegalArgumentException( - "expected final value <" + finalValue + "> but was <" + value + "> on " - + field.getName() + " field in " + obj.getClass().getName()); + "expected final value <" + + finalValue + + "> but was <" + + value + + "> on " + + field.getName() + + " field in " + + obj.getClass().getName()); } } else { try { diff --git a/google-http-client/src/main/java/com/google/api/client/util/GenericData.java b/google-http-client/src/main/java/com/google/api/client/util/GenericData.java index 6d1b5b14a..5ba12fe47 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/GenericData.java +++ b/google-http-client/src/main/java/com/google/api/client/util/GenericData.java @@ -20,27 +20,22 @@ import java.util.Iterator; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentMap; /** * Generic data that stores all unknown data key name/value pairs. * - *

    - * Subclasses can declare fields for known data keys using the {@link Key} annotation. Each field + *

    Subclasses can declare fields for known data keys using the {@link Key} annotation. Each field * can be of any visibility (private, package private, protected, or public) and must not be static. * {@code null} unknown data key names are not allowed, but {@code null} data values are allowed. - *

    * - *

    - * Iteration order of the data keys is based on the sorted (ascending) key names of the declared + *

    Iteration order of the data keys is based on the sorted (ascending) key names of the declared * fields, followed by the iteration order of all of the unknown data key name/value pairs. - *

    * - *

    - * Implementation is not thread-safe. For a thread-safe choice instead use an implementation of + *

    Implementation is not thread-safe. For a thread-safe choice instead use an implementation of * {@link ConcurrentMap}. - *

    * * @since 1.0 * @author Yaniv Inbar @@ -55,15 +50,14 @@ public class GenericData extends AbstractMap implements Cloneabl /** Class information. */ final ClassInfo classInfo; - /** - * Constructs with case-insensitive keys. - */ + /** Constructs with case-insensitive keys. */ public GenericData() { this(EnumSet.noneOf(Flags.class)); } /** * Flags that impact behavior of generic data. + * * @since 1.10 */ public enum Flags { @@ -112,13 +106,11 @@ public final Object put(String fieldName, Object value) { /** * Sets the given field value (may be {@code null}) for the given field name. Any existing value - * for the field will be overwritten. It may be more slightly more efficient than - * {@link #put(String, Object)} because it avoids accessing the field's original value. + * for the field will be overwritten. It may be more slightly more efficient than {@link + * #put(String, Object)} because it avoids accessing the field's original value. * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public GenericData set(String fieldName, Object value) { FieldInfo fieldInfo = classInfo.getFieldInfo(fieldName); @@ -196,6 +188,28 @@ public final void setUnknownKeys(Map unknownFields) { this.unknownFields = unknownFields; } + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o == null || !(o instanceof GenericData)) { + return false; + } + GenericData that = (GenericData) o; + return super.equals(that) && Objects.equals(this.classInfo, that.classInfo); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), classInfo); + } + + @Override + public String toString() { + return "GenericData{" + "classInfo=" + classInfo.names + ", " + super.toString() + "}"; + } + /** * Returns the class information. * diff --git a/google-http-client/src/main/java/com/google/api/client/util/IOUtils.java b/google-http-client/src/main/java/com/google/api/client/util/IOUtils.java index fdf64751a..a74a59493 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/IOUtils.java +++ b/google-http-client/src/main/java/com/google/api/client/util/IOUtils.java @@ -37,56 +37,53 @@ public class IOUtils { * Writes the content provided by the given source input stream into the given destination output * stream. * - *

    - * The input stream is guaranteed to be closed at the end of this method. - *

    + *

    The input stream is guaranteed to be closed at the end of this method. * - *

    - * Sample use: - *

    + *

    Sample use: * *

    -  static void copy(InputStream inputStream, File file) throws IOException {
    -    FileOutputStream out = new FileOutputStream(file);
    -    try {
    -      IOUtils.copy(inputStream, out);
    -    } finally {
    -      out.close();
    -    }
    -  }
    +   * static void copy(InputStream inputStream, File file) throws IOException {
    +   * FileOutputStream out = new FileOutputStream(file);
    +   * try {
    +   * IOUtils.copy(inputStream, out);
    +   * } finally {
    +   * out.close();
    +   * }
    +   * }
        * 
    * * @param inputStream source input stream * @param outputStream destination output stream + * @deprecated use {@link com.google.common.io.ByteStreams#copy(InputStream, OutputStream)} */ + @Deprecated public static void copy(InputStream inputStream, OutputStream outputStream) throws IOException { copy(inputStream, outputStream, true); } - /** * Writes the content provided by the given source input stream into the given destination output * stream, optionally closing the input stream. * - *

    - * Sample use: - *

    + *

    Sample use: * *

    -  static void copy(InputStream inputStream, File file) throws IOException {
    -    FileOutputStream out = new FileOutputStream(file);
    -    try {
    -      IOUtils.copy(inputStream, out, true);
    -    } finally {
    -      out.close();
    -    }
    -  }
    +   * static void copy(InputStream inputStream, File file) throws IOException {
    +   * FileOutputStream out = new FileOutputStream(file);
    +   * try {
    +   * IOUtils.copy(inputStream, out, true);
    +   * } finally {
    +   * out.close();
    +   * }
    +   * }
        * 
    * * @param inputStream source input stream * @param outputStream destination output stream * @param closeInputStream whether the input stream should be closed at the end of this method + * @deprecated use {@link com.google.common.io.ByteStreams#copy(InputStream, OutputStream)} */ + @Deprecated public static void copy( InputStream inputStream, OutputStream outputStream, boolean closeInputStream) throws IOException { @@ -99,11 +96,9 @@ public static void copy( } } - /** - * Computes and returns the byte content length for a streaming content by calling - * {@link StreamingContent#writeTo(OutputStream)} on a fake output stream that only counts bytes - * written. + * Computes and returns the byte content length for a streaming content by calling {@link + * StreamingContent#writeTo(OutputStream)} on a fake output stream that only counts bytes written. * * @param content streaming content */ @@ -182,7 +177,9 @@ public static S deserialize(InputStream inputStream) th * Returns whether the given file is a symbolic link. * * @since 1.16 + * @deprecated use java.nio.file.Path#isSymbolicLink */ + @Deprecated public static boolean isSymbolicLink(File file) throws IOException { // first try using Java 7 try { diff --git a/google-http-client/src/main/java/com/google/api/client/util/Joiner.java b/google-http-client/src/main/java/com/google/api/client/util/Joiner.java index a1ba8790c..3fce25e12 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/Joiner.java +++ b/google-http-client/src/main/java/com/google/api/client/util/Joiner.java @@ -20,9 +20,7 @@ * An object which joins pieces of text (specified as an array, {@link Iterable}, varargs or even a * {@link Map}) with a separator. * - *

    - * NOTE: proxy for the Guava implementation of {@link com.google.common.base.Joiner}. - *

    + *

    NOTE: proxy for the Guava implementation of {@link com.google.common.base.Joiner}. * * @since 1.14 * @author Yaniv Inbar @@ -32,16 +30,12 @@ public final class Joiner { /** Wrapped joiner. */ private final com.google.common.base.Joiner wrapped; - /** - * Returns a joiner which automatically places {@code separator} between consecutive elements. - */ + /** Returns a joiner which automatically places {@code separator} between consecutive elements. */ public static Joiner on(char separator) { return new Joiner(com.google.common.base.Joiner.on(separator)); } - /** - * @param wrapped wrapped joiner - */ + /** @param wrapped wrapped joiner */ private Joiner(com.google.common.base.Joiner wrapped) { this.wrapped = wrapped; } diff --git a/google-http-client/src/main/java/com/google/api/client/util/Key.java b/google-http-client/src/main/java/com/google/api/client/util/Key.java index e8b9dc0af..40094065c 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/Key.java +++ b/google-http-client/src/main/java/com/google/api/client/util/Key.java @@ -22,24 +22,23 @@ /** * Use this annotation to specify that a field is a data key, optionally providing the data key name * to use. - *

    - * If the data key name is not specified, the default is the Java field's name. For example: - *

    + * + *

    If the data key name is not specified, the default is the Java field's name. For example: * *

    
    -  public class A {
    -
    -    // uses data key name of "dataKeyNameMatchesFieldName"
    -    @Key
    -    public String dataKeyNameMatchesFieldName;
    -
    -    // uses data key name of "some_other_name"
    -    @Key("some_other_name")
    -    private String dataKeyNameIsOverriden;
    -
    -    // not a data key
    -    private String notADataKey;
    -  }
    + * public class A {
    + *
    + * // uses data key name of "dataKeyNameMatchesFieldName"
    + * @Key
    + * public String dataKeyNameMatchesFieldName;
    + *
    + * // uses data key name of "some_other_name"
    + * @Key("some_other_name")
    + * private String dataKeyNameIsOverridden;
    + *
    + * // not a data key
    + * private String notADataKey;
    + * }
      * 
    * * @since 1.0 diff --git a/google-http-client/src/main/java/com/google/api/client/util/Lists.java b/google-http-client/src/main/java/com/google/api/client/util/Lists.java index 881bc5977..0bb52fde7 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/Lists.java +++ b/google-http-client/src/main/java/com/google/api/client/util/Lists.java @@ -22,10 +22,8 @@ /** * Static utility methods pertaining to {@link List} instances. * - *

    - * NOTE: this is a copy of a subset of Guava's {@link com.google.common.collect.Lists}. The + *

    NOTE: this is a copy of a subset of Guava's {@link com.google.common.collect.Lists}. The * implementation must match as closely as possible to Guava's implementation. - *

    * * @since 1.14 * @author Yaniv Inbar @@ -42,9 +40,9 @@ public static ArrayList newArrayList() { * equivalent to {@link ArrayList#ArrayList(int)}. * * @param initialArraySize the exact size of the initial backing array for the returned array list - * ({@code ArrayList} documentation calls this value the "capacity") + * ({@code ArrayList} documentation calls this value the "capacity") * @return a new, empty {@code ArrayList} which is guaranteed not to resize itself unless its size - * reaches {@code initialArraySize + 1} + * reaches {@code initialArraySize + 1} * @throws IllegalArgumentException if {@code initialArraySize} is negative */ public static ArrayList newArrayListWithCapacity(int initialArraySize) { @@ -59,7 +57,8 @@ public static ArrayList newArrayListWithCapacity(int initialArraySize) { */ public static ArrayList newArrayList(Iterable elements) { return (elements instanceof Collection) - ? new ArrayList(Collections2.cast(elements)) : newArrayList(elements.iterator()); + ? new ArrayList(Collections2.cast(elements)) + : newArrayList(elements.iterator()); } /** @@ -76,6 +75,5 @@ public static ArrayList newArrayList(Iterator elements) { return list; } - private Lists() { - } + private Lists() {} } diff --git a/google-http-client/src/main/java/com/google/api/client/util/LoggingByteArrayOutputStream.java b/google-http-client/src/main/java/com/google/api/client/util/LoggingByteArrayOutputStream.java index 318b98fa0..412486c5e 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/LoggingByteArrayOutputStream.java +++ b/google-http-client/src/main/java/com/google/api/client/util/LoggingByteArrayOutputStream.java @@ -23,15 +23,13 @@ /** * Thread-safe byte array output stream that logs what was written to it when the stream is closed. * - *

    - * Use this as a safe way to log a limited amount of content. As content is written to the stream, - * it is stored as a byte array, up to the maximum number of bytes limit that was set in the + *

    Use this as a safe way to log a limited amount of content. As content is written to the + * stream, it is stored as a byte array, up to the maximum number of bytes limit that was set in the * constructor. Note that if the maximum limit is set too high, it risks an {@link OutOfMemoryError} * on low-memory devices. This class also keeps track of the total number of bytes written, * regardless of whether they were logged. On {@link #close()}, it then logs two records to the * specified logger and logging level: the total number of bytes written, and the bounded content * logged (assuming charset "UTF-8"). Any control characters are stripped out of the content. - *

    * * @since 1.9 * @author Yaniv Inbar @@ -57,7 +55,7 @@ public class LoggingByteArrayOutputStream extends ByteArrayOutputStream { * @param logger logger * @param loggingLevel logging level * @param maximumBytesToLog maximum number of bytes to log (may be {@code 0} to avoid logging - * content) + * content) */ public LoggingByteArrayOutputStream(Logger logger, Level loggingLevel, int maximumBytesToLog) { this.logger = Preconditions.checkNotNull(logger); @@ -106,7 +104,8 @@ public synchronized void close() throws IOException { // log response content if (count != 0) { // strip out some unprintable control chars - logger.log(loggingLevel, + logger.log( + loggingLevel, toString("UTF-8").replaceAll("[\\x00-\\x09\\x0B\\x0C\\x0E-\\x1F\\x7F]", " ")); } } diff --git a/google-http-client/src/main/java/com/google/api/client/util/LoggingInputStream.java b/google-http-client/src/main/java/com/google/api/client/util/LoggingInputStream.java index 3076508fb..e48167a6c 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/LoggingInputStream.java +++ b/google-http-client/src/main/java/com/google/api/client/util/LoggingInputStream.java @@ -37,7 +37,7 @@ public class LoggingInputStream extends FilterInputStream { * @param logger logger * @param loggingLevel logging level * @param contentLoggingLimit maximum number of bytes to log (may be {@code 0} to avoid logging - * content) + * content) */ public LoggingInputStream( InputStream inputStream, Logger logger, Level loggingLevel, int contentLoggingLimit) { diff --git a/google-http-client/src/main/java/com/google/api/client/util/LoggingOutputStream.java b/google-http-client/src/main/java/com/google/api/client/util/LoggingOutputStream.java index bc4fd914f..731422773 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/LoggingOutputStream.java +++ b/google-http-client/src/main/java/com/google/api/client/util/LoggingOutputStream.java @@ -14,7 +14,6 @@ package com.google.api.client.util; - import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -38,7 +37,7 @@ public class LoggingOutputStream extends FilterOutputStream { * @param logger logger * @param loggingLevel logging level * @param contentLoggingLimit maximum number of bytes to log (may be {@code 0} to avoid logging - * content) + * content) */ public LoggingOutputStream( OutputStream outputStream, Logger logger, Level loggingLevel, int contentLoggingLimit) { diff --git a/google-http-client/src/main/java/com/google/api/client/util/LoggingStreamingContent.java b/google-http-client/src/main/java/com/google/api/client/util/LoggingStreamingContent.java index 53f4a4238..af4b88c36 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/LoggingStreamingContent.java +++ b/google-http-client/src/main/java/com/google/api/client/util/LoggingStreamingContent.java @@ -23,9 +23,7 @@ * Wraps another streaming content without modifying the content, but also logging content using * {@link LoggingOutputStream}. * - *

    - * Implementation is not thread-safe. - *

    + *

    Implementation is not thread-safe. * * @author Yaniv Inbar * @since 1.14 diff --git a/google-http-client/src/main/java/com/google/api/client/util/Maps.java b/google-http-client/src/main/java/com/google/api/client/util/Maps.java index a68f226fa..3fba9f812 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/Maps.java +++ b/google-http-client/src/main/java/com/google/api/client/util/Maps.java @@ -22,10 +22,8 @@ /** * Static utility methods pertaining to {@link Map} instances. * - *

    - * NOTE: this is a copy of a subset of Guava's {@link com.google.common.collect.Maps}. The + *

    NOTE: this is a copy of a subset of Guava's {@link com.google.common.collect.Maps}. The * implementation must match as closely as possible to Guava's implementation. - *

    * * @since 1.14 * @author Yaniv Inbar @@ -50,6 +48,5 @@ public static , V> TreeMap newTreeMap() { return new TreeMap(); } - private Maps() { - } + private Maps() {} } diff --git a/google-http-client/src/main/java/com/google/api/client/util/NanoClock.java b/google-http-client/src/main/java/com/google/api/client/util/NanoClock.java index 1736ac880..16cb6c8ae 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/NanoClock.java +++ b/google-http-client/src/main/java/com/google/api/client/util/NanoClock.java @@ -17,10 +17,8 @@ /** * Nano clock which can be used to measure elapsed time in nanoseconds. * - *

    - * The default system implementation can be accessed at {@link #SYSTEM}. Alternative implementations - * may be used for testing. - *

    + *

    The default system implementation can be accessed at {@link #SYSTEM}. Alternative + * implementations may be used for testing. * * @since 1.14 * @author Yaniv Inbar @@ -36,9 +34,10 @@ public interface NanoClock { /** * Provides the default System implementation of a nano clock by using {@link System#nanoTime()}. */ - NanoClock SYSTEM = new NanoClock() { - public long nanoTime() { - return System.nanoTime(); - } - }; + NanoClock SYSTEM = + new NanoClock() { + public long nanoTime() { + return System.nanoTime(); + } + }; } diff --git a/google-http-client/src/main/java/com/google/api/client/util/NullValue.java b/google-http-client/src/main/java/com/google/api/client/util/NullValue.java index 4c24f6068..e09c70d3c 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/NullValue.java +++ b/google-http-client/src/main/java/com/google/api/client/util/NullValue.java @@ -20,17 +20,14 @@ import java.lang.annotation.Target; /** - * Use this annotation to specify that an enum constant is the "null" data value to use for - * {@link Data#nullOf(Class)}. - *

    - * See {@link Value} for an example. - *

    + * Use this annotation to specify that an enum constant is the "null" data value to use for {@link + * Data#nullOf(Class)}. + * + *

    See {@link Value} for an example. * * @since 1.4 * @author Yaniv Inbar */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) -public @interface NullValue { - -} +public @interface NullValue {} diff --git a/google-http-client/src/main/java/com/google/api/client/util/ObjectParser.java b/google-http-client/src/main/java/com/google/api/client/util/ObjectParser.java index 929913770..548043189 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/ObjectParser.java +++ b/google-http-client/src/main/java/com/google/api/client/util/ObjectParser.java @@ -23,9 +23,7 @@ /** * Parses a data source into the specified data type. * - *

    - * Implementations should normally be thread-safe. - *

    + *

    Implementations should normally be thread-safe. * * @author Yaniv Inbar * @since 1.10 @@ -38,7 +36,7 @@ public interface ObjectParser { * * @param in input stream which contains the data to parse * @param charset charset which should be used to decode the input stream or {@code null} if - * unknown + * unknown * @param dataClass class into which the data is parsed */ T parseAndClose(InputStream in, Charset charset, Class dataClass) throws IOException; @@ -49,7 +47,7 @@ public interface ObjectParser { * * @param in input stream which contains the data to parse * @param charset charset which should be used to decode the input stream or {@code null} if - * unknown + * unknown * @param dataType type into which the data is parsed */ Object parseAndClose(InputStream in, Charset charset, Type dataType) throws IOException; diff --git a/google-http-client/src/main/java/com/google/api/client/util/Objects.java b/google-http-client/src/main/java/com/google/api/client/util/Objects.java index 3424a0a83..08da55d0b 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/Objects.java +++ b/google-http-client/src/main/java/com/google/api/client/util/Objects.java @@ -26,16 +26,14 @@ public final class Objects { * Determines whether two possibly-null objects are equal. Returns: * *

      - *
    • {@code true} if {@code a} and {@code b} are both null.
    • - *
    • {@code true} if {@code a} and {@code b} are both non-null and they are equal according to - * {@link Object#equals(Object)}.
    • - *
    • {@code false} in all other situations.
    • + *
    • {@code true} if {@code a} and {@code b} are both null. + *
    • {@code true} if {@code a} and {@code b} are both non-null and they are equal according to + * {@link Object#equals(Object)}. + *
    • {@code false} in all other situations. *
    * - *

    - * This assumes that any non-null objects passed to this function conform to the {@code equals()} - * contract. - *

    + *

    This assumes that any non-null objects passed to this function conform to the {@code + * equals()} contract. */ public static boolean equal(Object a, Object b) { return com.google.common.base.Objects.equal(a, b); @@ -44,41 +42,39 @@ public static boolean equal(Object a, Object b) { /** * Creates an instance of {@link ToStringHelper}. * - *

    - * This is helpful for implementing {@link Object#toString()}. Specification by example: - *

    + *

    This is helpful for implementing {@link Object#toString()}. Specification by example: * *

    -   // Returns "ClassName{}"
    -   Objects.toStringHelper(this)
    -       .toString();
    -
    -   // Returns "ClassName{x=1}"
    -   Objects.toStringHelper(this)
    -       .add("x", 1)
    -       .toString();
    -
    -   // Returns "MyObject{x=1}"
    -   Objects.toStringHelper("MyObject")
    -       .add("x", 1)
    -       .toString();
    -
    -   // Returns "ClassName{x=1, y=foo}"
    -   Objects.toStringHelper(this)
    -       .add("x", 1)
    -       .add("y", "foo")
    -       .toString();
    -
    -   // Returns "ClassName{x=1}"
    -   Objects.toStringHelper(this)
    -       .omitNullValues()
    -       .add("x", 1)
    -       .add("y", null)
    -       .toString();
    +   * // Returns "ClassName{}"
    +   * Objects.toStringHelper(this)
    +   * .toString();
    +   *
    +   * // Returns "ClassName{x=1}"
    +   * Objects.toStringHelper(this)
    +   * .add("x", 1)
    +   * .toString();
    +   *
    +   * // Returns "MyObject{x=1}"
    +   * Objects.toStringHelper("MyObject")
    +   * .add("x", 1)
    +   * .toString();
    +   *
    +   * // Returns "ClassName{x=1, y=foo}"
    +   * Objects.toStringHelper(this)
    +   * .add("x", 1)
    +   * .add("y", "foo")
    +   * .toString();
    +   *
    +   * // Returns "ClassName{x=1}"
    +   * Objects.toStringHelper(this)
    +   * .omitNullValues()
    +   * .add("x", 1)
    +   * .add("y", null)
    +   * .toString();
        * 
    * * @param self the object to generate the string for (typically {@code this}), used only for its - * class name + * class name */ public static ToStringHelper toStringHelper(Object self) { return new ToStringHelper(self.getClass().getSimpleName()); @@ -92,9 +88,7 @@ public static final class ToStringHelper { private ValueHolder holderTail = holderHead; private boolean omitNullValues; - /** - * @param wrapped wrapped object - */ + /** @param className wrapped object */ ToStringHelper(String className) { this.className = className; } @@ -160,6 +154,5 @@ private static final class ValueHolder { } } - private Objects() { - } + private Objects() {} } diff --git a/google-http-client/src/main/java/com/google/api/client/util/PemReader.java b/google-http-client/src/main/java/com/google/api/client/util/PemReader.java index b4bdbd94f..a1f06a3bc 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/PemReader.java +++ b/google-http-client/src/main/java/com/google/api/client/util/PemReader.java @@ -21,32 +21,27 @@ import java.util.regex.Pattern; /** - * {@link Beta}
    + * {@link Beta}
    * PEM file reader. * - *

    - * Supports reading any PEM stream that contains Base64 encoded content stored inside - * {@code "-----BEGIN ...-----"} and {@code "-----END ...-----"} tags. Each call to - * {@link #readNextSection()} parses the next section in the PEM file. If you need a section of a - * certain title use {@link #readNextSection(String)}, for example - * {@code readNextSection("PRIVATE KEY")}. To ensure that the stream is closed properly, call - * {@link #close()} in a finally block. - *

    + *

    Supports reading any PEM stream that contains Base64 encoded content stored inside {@code + * "-----BEGIN ...-----"} and {@code "-----END ...-----"} tags. Each call to {@link + * #readNextSection()} parses the next section in the PEM file. If you need a section of a certain + * title use {@link #readNextSection(String)}, for example {@code readNextSection("PRIVATE KEY")}. + * To ensure that the stream is closed properly, call {@link #close()} in a finally block. * - *

    - * As a convenience, use {@link #readFirstSectionAndClose(Reader)} or - * {@link #readFirstSectionAndClose(Reader, String)} for the common case of only a single section in - * a PEM file (or only a single section of a given title). - *

    + *

    As a convenience, use {@link #readFirstSectionAndClose(Reader)} or {@link + * #readFirstSectionAndClose(Reader, String)} for the common case of only a single section in a PEM + * file (or only a single section of a given title). + * + *

    Limitations: * - *

    - * Limitations: *

    * *

      - *
    • Assumes the PEM file section content is not encrypted and cannot handle the case of any - * headers inside the BEGIN and END tag.
    • - *
    • It also ignores any attributes associated with any PEM file section.
    • + *
    • Assumes the PEM file section content is not encrypted and cannot handle the case of any + * headers inside the BEGIN and END tag. + *
    • It also ignores any attributes associated with any PEM file section. *
    * * @since 1.14 @@ -61,9 +56,7 @@ public final class PemReader { /** Reader. */ private BufferedReader reader; - /** - * @param reader reader - */ + /** @param reader reader */ public PemReader(Reader reader) { this.reader = new BufferedReader(reader); } @@ -101,8 +94,8 @@ public Section readNextSection(String titleToLookFor) throws IOException { Matcher m = END_PATTERN.matcher(line); if (m.matches()) { String endTitle = m.group(1); - Preconditions.checkArgument(endTitle.equals(title), - "end tag (%s) doesn't match begin tag (%s)", endTitle, title); + Preconditions.checkArgument( + endTitle.equals(title), "end tag (%s) doesn't match begin tag (%s)", endTitle, title); return new Section(title, Base64.decodeBase64(keyBuilder.toString())); } keyBuilder.append(line); @@ -141,9 +134,7 @@ public static Section readFirstSectionAndClose(Reader reader, String titleToLook /** * Closes the reader. * - *

    - * To ensure that the stream is closed properly, call {@link #close()} in a finally block. - *

    + *

    To ensure that the stream is closed properly, call {@link #close()} in a finally block. */ public void close() throws IOException { reader.close(); diff --git a/google-http-client/src/main/java/com/google/api/client/util/Preconditions.java b/google-http-client/src/main/java/com/google/api/client/util/Preconditions.java index b2ef960cd..880848289 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/Preconditions.java +++ b/google-http-client/src/main/java/com/google/api/client/util/Preconditions.java @@ -18,9 +18,7 @@ * Simple static methods to be called at the start of your own methods to verify correct arguments * and state. * - *

    - * NOTE: proxy for the Guava implementation of {@link com.google.common.base.Preconditions}. - *

    + *

    NOTE: proxy for the Guava implementation of {@link com.google.common.base.Preconditions}. * * @since 1.14 * @author Yaniv Inbar @@ -42,7 +40,7 @@ public static void checkArgument(boolean expression) { * * @param expression a boolean expression * @param errorMessage the exception message to use if the check fails; will be converted to a - * string using {@link String#valueOf(Object)} + * string using {@link String#valueOf(Object)} * @throws IllegalArgumentException if {@code expression} is false */ public static void checkArgument(boolean expression, Object errorMessage) { @@ -54,15 +52,15 @@ public static void checkArgument(boolean expression, Object errorMessage) { * * @param expression a boolean expression * @param errorMessageTemplate a template for the exception message should the check fail. The - * message is formed by replacing each {@code %s} placeholder in the template with an - * argument. These are matched by position - the first {@code %s} gets - * {@code errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted - * message in square braces. Unmatched placeholders will be left as-is. + * message is formed by replacing each {@code %s} placeholder in the template with an + * argument. These are matched by position - the first {@code %s} gets {@code + * errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted message in + * square braces. Unmatched placeholders will be left as-is. * @param errorMessageArgs the arguments to be substituted into the message template. Arguments - * are converted to strings using {@link String#valueOf(Object)}. + * are converted to strings using {@link String#valueOf(Object)}. * @throws IllegalArgumentException if {@code expression} is false * @throws NullPointerException if the check fails and either {@code errorMessageTemplate} or - * {@code errorMessageArgs} is null (don't let this happen) + * {@code errorMessageArgs} is null (don't let this happen) */ public static void checkArgument( boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { @@ -87,7 +85,7 @@ public static void checkState(boolean expression) { * * @param expression a boolean expression * @param errorMessage the exception message to use if the check fails; will be converted to a - * string using {@link String#valueOf(Object)} + * string using {@link String#valueOf(Object)} * @throws IllegalStateException if {@code expression} is false */ public static void checkState(boolean expression, Object errorMessage) { @@ -100,15 +98,15 @@ public static void checkState(boolean expression, Object errorMessage) { * * @param expression a boolean expression * @param errorMessageTemplate a template for the exception message should the check fail. The - * message is formed by replacing each {@code %s} placeholder in the template with an - * argument. These are matched by position - the first {@code %s} gets - * {@code errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted - * message in square braces. Unmatched placeholders will be left as-is. + * message is formed by replacing each {@code %s} placeholder in the template with an + * argument. These are matched by position - the first {@code %s} gets {@code + * errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted message in + * square braces. Unmatched placeholders will be left as-is. * @param errorMessageArgs the arguments to be substituted into the message template. Arguments - * are converted to strings using {@link String#valueOf(Object)}. + * are converted to strings using {@link String#valueOf(Object)}. * @throws IllegalStateException if {@code expression} is false * @throws NullPointerException if the check fails and either {@code errorMessageTemplate} or - * {@code errorMessageArgs} is null (don't let this happen) + * {@code errorMessageArgs} is null (don't let this happen) */ public static void checkState( boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { @@ -132,7 +130,7 @@ public static T checkNotNull(T reference) { * * @param reference an object reference * @param errorMessage the exception message to use if the check fails; will be converted to a - * string using {@link String#valueOf(Object)} + * string using {@link String#valueOf(Object)} * @return the non-null reference that was validated * @throws NullPointerException if {@code reference} is null */ @@ -145,12 +143,12 @@ public static T checkNotNull(T reference, Object errorMessage) { * * @param reference an object reference * @param errorMessageTemplate a template for the exception message should the check fail. The - * message is formed by replacing each {@code %s} placeholder in the template with an - * argument. These are matched by position - the first {@code %s} gets - * {@code errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted - * message in square braces. Unmatched placeholders will be left as-is. + * message is formed by replacing each {@code %s} placeholder in the template with an + * argument. These are matched by position - the first {@code %s} gets {@code + * errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted message in + * square braces. Unmatched placeholders will be left as-is. * @param errorMessageArgs the arguments to be substituted into the message template. Arguments - * are converted to strings using {@link String#valueOf(Object)}. + * are converted to strings using {@link String#valueOf(Object)}. * @return the non-null reference that was validated * @throws NullPointerException if {@code reference} is null */ @@ -160,6 +158,5 @@ public static T checkNotNull( reference, errorMessageTemplate, errorMessageArgs); } - private Preconditions() { - } + private Preconditions() {} } diff --git a/google-http-client/src/main/java/com/google/api/client/util/SecurityUtils.java b/google-http-client/src/main/java/com/google/api/client/util/SecurityUtils.java index b28233c38..25e40dbff 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/SecurityUtils.java +++ b/google-http-client/src/main/java/com/google/api/client/util/SecurityUtils.java @@ -17,6 +17,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.security.GeneralSecurityException; import java.security.InvalidKeyException; import java.security.KeyFactory; @@ -31,8 +32,8 @@ import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.security.spec.PKCS8EncodedKeySpec; import java.util.List; - import javax.net.ssl.X509TrustManager; /** @@ -61,19 +62,16 @@ public static KeyStore getPkcs12KeyStore() throws KeyStoreException { /** * Loads a key store from a stream. * - * - *

    - * Example usage: - *

    + *

    Example usage: * *

    -    KeyStore keyStore = SecurityUtils.getJavaKeyStore();
    -    SecurityUtils.loadKeyStore(keyStore, new FileInputStream("certs.jks"), "password");
    +   * KeyStore keyStore = SecurityUtils.getJavaKeyStore();
    +   * SecurityUtils.loadKeyStore(keyStore, new FileInputStream("certs.jks"), "password");
        * 
    * * @param keyStore key store * @param keyStream input stream to the key store stream (closed at the end of this method in a - * finally block) + * finally block) * @param storePass password protecting the key store file */ public static void loadKeyStore(KeyStore keyStore, InputStream keyStream, String storePass) @@ -103,7 +101,7 @@ public static PrivateKey getPrivateKey(KeyStore keyStore, String alias, String k * * @param keyStore key store * @param keyStream input stream to the key store (closed at the end of this method in a finally - * block) + * block) * @param storePass password protecting the key store file * @param alias alias under which the key is stored * @param keyPass password protecting the key @@ -131,6 +129,11 @@ public static Signature getSha256WithRsaSignatureAlgorithm() throws NoSuchAlgori return Signature.getInstance("SHA256withRSA"); } + /** Returns the SHA-256 with ECDSA signature algorithm */ + public static Signature getEs256SignatureAlgorithm() throws NoSuchAlgorithmException { + return Signature.getInstance("SHA256withECDSA"); + } + /** * Signs content using a private key. * @@ -161,7 +164,7 @@ public static boolean verify( throws InvalidKeyException, SignatureException { signatureAlgorithm.initVerify(publicKey); signatureAlgorithm.update(contentBytes); - // SignatureException may be thrown if we are tring the wrong key. + // SignatureException may be thrown if we are trying the wrong key. try { return signatureAlgorithm.verify(signatureBytes); } catch (SignatureException e) { @@ -175,14 +178,18 @@ public static boolean verify( * @param signatureAlgorithm signature algorithm * @param trustManager trust manager used to verify the certificate chain * @param certChainBase64 Certificate chain used for verification. The certificates must be base64 - * encoded DER, the leaf certificate must be the first element. + * encoded DER, the leaf certificate must be the first element. * @param signatureBytes signature bytes * @param contentBytes content bytes * @return The signature certificate if the signature could be verified, null otherwise. * @since 1.19.1. */ - public static X509Certificate verify(Signature signatureAlgorithm, X509TrustManager trustManager, - List certChainBase64, byte[] signatureBytes, byte[] contentBytes) + public static X509Certificate verify( + Signature signatureAlgorithm, + X509TrustManager trustManager, + List certChainBase64, + byte[] signatureBytes, + byte[] contentBytes) throws InvalidKeyException, SignatureException { CertificateFactory certificateFactory; try { @@ -223,28 +230,24 @@ public static CertificateFactory getX509CertificateFactory() throws CertificateE } /** - * Loads a key store with certificates generated from the specified stream using - * {@link CertificateFactory#generateCertificates(InputStream)}. + * Loads a key store with certificates generated from the specified stream using {@link + * CertificateFactory#generateCertificates(InputStream)}. * - *

    - * For each certificate, {@link KeyStore#setCertificateEntry(String, Certificate)} is called with - * an alias that is the string form of incrementing non-negative integers starting with 0 (0, 1, - * 2, 3, ...). - *

    + *

    For each certificate, {@link KeyStore#setCertificateEntry(String, Certificate)} is called + * with an alias that is the string form of incrementing non-negative integers starting with 0 (0, + * 1, 2, 3, ...). * - *

    - * Example usage: - *

    + *

    Example usage: * *

    -    KeyStore keyStore = SecurityUtils.getJavaKeyStore();
    -    SecurityUtils.loadKeyStoreFromCertificates(keyStore, SecurityUtils.getX509CertificateFactory(),
    -        new FileInputStream(pemFile));
    +   * KeyStore keyStore = SecurityUtils.getJavaKeyStore();
    +   * SecurityUtils.loadKeyStoreFromCertificates(keyStore, SecurityUtils.getX509CertificateFactory(),
    +   * new FileInputStream(pemFile));
        * 
    * * @param keyStore key store (for example {@link #getJavaKeyStore()}) - * @param certificateFactory certificate factory (for example - * {@link #getX509CertificateFactory()}) + * @param certificateFactory certificate factory (for example {@link + * #getX509CertificateFactory()}) * @param certificateStream certificate stream */ public static void loadKeyStoreFromCertificates( @@ -257,6 +260,62 @@ public static void loadKeyStoreFromCertificates( } } - private SecurityUtils() { + /** + * {@link Beta}
    + * Create a keystore for mutual TLS with the certificate and private key provided. + * + * @param certAndKey Certificate and private key input stream. The stream should contain one + * certificate and one unencrypted private key. If there are multiple certificates, only the + * first certificate will be used. + * @return keystore for mutual TLS. + * @since 1.38 + */ + @Beta + public static KeyStore createMtlsKeyStore(InputStream certAndKey) + throws GeneralSecurityException, IOException { + KeyStore keystore = KeyStore.getInstance("JKS"); + keystore.load(null); + + PemReader.Section certSection = null; + PemReader.Section keySection = null; + PemReader reader = new PemReader(new InputStreamReader(certAndKey)); + + while (certSection == null || keySection == null) { + // Read the certificate and private key. + PemReader.Section section = reader.readNextSection(); + if (section == null) { + break; + } + + if (certSection == null && "CERTIFICATE".equals(section.getTitle())) { + certSection = section; + } else if ("PRIVATE KEY".equals(section.getTitle())) { + keySection = section; + } + } + + if (certSection == null) { + throw new IllegalArgumentException("certificate is missing from certAndKey string"); + } + if (keySection == null) { + throw new IllegalArgumentException("private key is missing from certAndKey string"); + } + + CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + X509Certificate cert = + (X509Certificate) + certFactory.generateCertificate( + new ByteArrayInputStream(certSection.getBase64DecodedBytes())); + + PKCS8EncodedKeySpec keySpecPKCS8 = new PKCS8EncodedKeySpec(keySection.getBase64DecodedBytes()); + PrivateKey key = + KeyFactory.getInstance(cert.getPublicKey().getAlgorithm()).generatePrivate(keySpecPKCS8); + + // Fit the certificate and private key into the keystore. + keystore.setKeyEntry("alias", key, new char[] {}, new X509Certificate[] {cert}); + + return keystore; } + + private SecurityUtils() {} } diff --git a/google-http-client/src/main/java/com/google/api/client/util/Sets.java b/google-http-client/src/main/java/com/google/api/client/util/Sets.java index a253874d1..f159ccb0c 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/Sets.java +++ b/google-http-client/src/main/java/com/google/api/client/util/Sets.java @@ -21,10 +21,8 @@ /** * Static utility methods pertaining to {@link Set} instances. * - *

    - * NOTE: this is a copy of a subset of Guava's {@link com.google.common.collect.Sets}. The + *

    NOTE: this is a copy of a subset of Guava's {@link com.google.common.collect.Sets}. The * implementation must match as closely as possible to Guava's implementation. - *

    * * @since 1.14 * @author Yaniv Inbar @@ -44,6 +42,5 @@ public static > TreeSet newTreeSet() { return new TreeSet(); } - private Sets() { - } + private Sets() {} } diff --git a/google-http-client/src/main/java/com/google/api/client/util/Sleeper.java b/google-http-client/src/main/java/com/google/api/client/util/Sleeper.java index 94d98e8a2..bd0c0f108 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/Sleeper.java +++ b/google-http-client/src/main/java/com/google/api/client/util/Sleeper.java @@ -15,12 +15,10 @@ package com.google.api.client.util; /** - * Sleeper interface to use for requesting the current thread to sleep as specified in - * {@link Thread#sleep(long)}. + * Sleeper interface to use for requesting the current thread to sleep as specified in {@link + * Thread#sleep(long)}. * - *

    - * The default implementation can be accessed at {@link #DEFAULT}. Primarily used for testing. - *

    + *

    The default implementation can be accessed at {@link #DEFAULT}. Primarily used for testing. * * @since 1.14 * @author Yaniv Inbar @@ -37,11 +35,11 @@ public interface Sleeper { void sleep(long millis) throws InterruptedException; /** Provides the default implementation based on {@link Thread#sleep(long)}. */ - Sleeper DEFAULT = new Sleeper() { - - public void sleep(long millis) throws InterruptedException { - Thread.sleep(millis); - } - }; + Sleeper DEFAULT = + new Sleeper() { + public void sleep(long millis) throws InterruptedException { + Thread.sleep(millis); + } + }; } diff --git a/google-http-client/src/main/java/com/google/api/client/util/SslUtils.java b/google-http-client/src/main/java/com/google/api/client/util/SslUtils.java index eb4eb225b..a578c7383 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/SslUtils.java +++ b/google-http-client/src/main/java/com/google/api/client/util/SslUtils.java @@ -14,12 +14,12 @@ package com.google.api.client.util; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; - import javax.net.ssl.HostnameVerifier; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; @@ -96,13 +96,13 @@ public static KeyManagerFactory getPkixKeyManagerFactory() throws NoSuchAlgorith * given trust store. * * @param sslContext SSL context (for example {@link SSLContext#getInstance}) - * @param trustStore key store for certificates to trust (for example - * {@link SecurityUtils#getJavaKeyStore()}) - * @param trustManagerFactory trust manager factory (for example - * {@link #getPkixTrustManagerFactory()}) - * + * @param trustStore key store for certificates to trust (for example {@link + * SecurityUtils#getJavaKeyStore()}) + * @param trustManagerFactory trust manager factory (for example {@link + * #getPkixTrustManagerFactory()}) * @since 1.14 */ + @CanIgnoreReturnValue public static SSLContext initSslContext( SSLContext sslContext, KeyStore trustStore, TrustManagerFactory trustManagerFactory) throws GeneralSecurityException { @@ -112,43 +112,73 @@ public static SSLContext initSslContext( } /** - * {@link Beta}
    + * {@link Beta}
    + * Initializes the SSL context to the trust managers supplied by the trust manager factory for the + * given trust store, and to the key managers supplied by the key manager factory for the given + * key store. + * + * @param sslContext SSL context (for example {@link SSLContext#getInstance}) + * @param trustStore key store for certificates to trust (for example {@link + * SecurityUtils#getJavaKeyStore()}) + * @param trustManagerFactory trust manager factory (for example {@link + * #getPkixTrustManagerFactory()}) + * @param mtlsKeyStore key store for client certificate and key to establish mutual TLS + * @param mtlsKeyStorePassword password for mtlsKeyStore parameter + * @param keyManagerFactory key manager factory (for example {@link + * #getDefaultKeyManagerFactory()}) + * @since 1.38 + */ + @Beta + public static SSLContext initSslContext( + SSLContext sslContext, + KeyStore trustStore, + TrustManagerFactory trustManagerFactory, + KeyStore mtlsKeyStore, + String mtlsKeyStorePassword, + KeyManagerFactory keyManagerFactory) + throws GeneralSecurityException { + trustManagerFactory.init(trustStore); + keyManagerFactory.init(mtlsKeyStore, mtlsKeyStorePassword.toCharArray()); + sslContext.init( + keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null); + return sslContext; + } + + /** + * {@link Beta}
    * Returns an SSL context in which all X.509 certificates are trusted. * - *

    - * Be careful! Disabling SSL certificate validation is dangerous and should only be done in + *

    Be careful! Disabling SSL certificate validation is dangerous and should only be done in * testing environments. - *

    */ @Beta public static SSLContext trustAllSSLContext() throws GeneralSecurityException { - TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() { + TrustManager[] trustAllCerts = + new TrustManager[] { + new X509TrustManager() { - public void checkClientTrusted(X509Certificate[] chain, String authType) - throws CertificateException { - } + public void checkClientTrusted(X509Certificate[] chain, String authType) + throws CertificateException {} - public void checkServerTrusted(X509Certificate[] chain, String authType) - throws CertificateException { - } + public void checkServerTrusted(X509Certificate[] chain, String authType) + throws CertificateException {} - public X509Certificate[] getAcceptedIssuers() { - return null; - } - }}; + public X509Certificate[] getAcceptedIssuers() { + return null; + } + } + }; SSLContext context = getTlsSslContext(); context.init(null, trustAllCerts, null); return context; } /** - * {@link Beta}
    + * {@link Beta}
    * Returns a verifier that trusts all host names. * - *

    - * Be careful! Disabling host name verification is dangerous and should only be done in testing + *

    Be careful! Disabling host name verification is dangerous and should only be done in testing * environments. - *

    */ @Beta public static HostnameVerifier trustAllHostnameVerifier() { @@ -160,6 +190,5 @@ public boolean verify(String arg0, SSLSession arg1) { }; } - private SslUtils() { - } + private SslUtils() {} } diff --git a/google-http-client/src/main/java/com/google/api/client/util/StreamingContent.java b/google-http-client/src/main/java/com/google/api/client/util/StreamingContent.java index a855d9c13..27347bafb 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/StreamingContent.java +++ b/google-http-client/src/main/java/com/google/api/client/util/StreamingContent.java @@ -20,23 +20,21 @@ /** * Streaming content interface to write bytes to an output stream. * - *

    - * Implementations don't need to be thread-safe. - *

    + *

    Implementations don't need to be thread-safe. * * @since 1.14 * @author Yaniv Inbar + * @deprecated use com.google.common.io.ByteSink */ +@Deprecated public interface StreamingContent { /** * Writes the byte content to the given output stream. * - *

    - * Implementations must not close the output stream, and instead should flush the output stream. - * Some callers may assume that the the output stream has not been closed, and will fail to work - * if it has been closed. - *

    + *

    Implementations must not close the output stream, and instead should flush the output + * stream. Some callers may assume that the output stream has not been closed, and will fail to + * work if it has been closed. * * @param out output stream */ diff --git a/google-http-client/src/main/java/com/google/api/client/util/StringUtils.java b/google-http-client/src/main/java/com/google/api/client/util/StringUtils.java index 8d0c92532..0efb24674 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/StringUtils.java +++ b/google-http-client/src/main/java/com/google/api/client/util/StringUtils.java @@ -15,19 +15,11 @@ package com.google.api.client.util; import java.io.UnsupportedEncodingException; - +import java.nio.charset.StandardCharsets; /** * Utilities for strings. * - *

    - * Some of these methods are a proxy for version 1.6 (or newer) of the Apache Commons Codec - * {@link StringUtils} implementation. This is needed in order to support platforms like Android - * which already include an older version of the Apache Commons Codec (Android includes version - * 1.3). To avoid a dependency library conflict, this library includes a reduced private copy of - * version 1.6 (or newer) of the Apache Commons Codec (using a tool like jarjar). - *

    - * * @since 1.8 * @author Yaniv Inbar */ @@ -47,14 +39,16 @@ public class StringUtils { * @param string the String to encode, may be null * @return encoded bytes, or null if the input string was null * @throws IllegalStateException Thrown when the charset is missing, which should be never - * according the the Java specification. - * @see Standard charsets - * @see org.apache.commons.codec.binary.StringUtils#getBytesUtf8(String) + * according the Java specification. + * @see Standard charsets * @since 1.8 */ public static byte[] getBytesUtf8(String string) { - return org.apache.commons.codec.binary.StringUtils.getBytesUtf8(string); + if (string == null) { + return null; + } + return string.getBytes(StandardCharsets.UTF_8); } /** @@ -63,16 +57,17 @@ public static byte[] getBytesUtf8(String string) { * * @param bytes The bytes to be decoded into characters * @return A new String decoded from the specified array of bytes using the UTF-8 - * charset, or null if the input byte array was null. + * charset, or null if the input byte array was null. * @throws IllegalStateException Thrown when a {@link UnsupportedEncodingException} is caught, - * which should never happen since the charset is required. - * @see org.apache.commons.codec.binary.StringUtils#newStringUtf8(byte[]) + * which should never happen since the charset is required. * @since 1.8 */ public static String newStringUtf8(byte[] bytes) { - return org.apache.commons.codec.binary.StringUtils.newStringUtf8(bytes); + if (bytes == null) { + return null; + } + return new String(bytes, StandardCharsets.UTF_8); } - private StringUtils() { - } + private StringUtils() {} } diff --git a/google-http-client/src/main/java/com/google/api/client/util/Strings.java b/google-http-client/src/main/java/com/google/api/client/util/Strings.java index 9c4067285..ae3625848 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/Strings.java +++ b/google-http-client/src/main/java/com/google/api/client/util/Strings.java @@ -17,9 +17,7 @@ /** * Static utility methods pertaining to {@code String} instances. * - *

    - * NOTE: proxy for the Guava implementation of {@link com.google.common.base.Strings}. - *

    + *

    NOTE: proxy for the Guava implementation of {@link com.google.common.base.Strings}. * * @since 1.14 * @author Yaniv Inbar @@ -36,6 +34,5 @@ public static boolean isNullOrEmpty(String string) { return com.google.common.base.Strings.isNullOrEmpty(string); } - private Strings() { - } + private Strings() {} } diff --git a/google-http-client/src/main/java/com/google/api/client/util/Throwables.java b/google-http-client/src/main/java/com/google/api/client/util/Throwables.java index 32b01cdc8..605355b88 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/Throwables.java +++ b/google-http-client/src/main/java/com/google/api/client/util/Throwables.java @@ -17,9 +17,7 @@ /** * Static utility methods pertaining to instances of {@link Throwable}. * - *

    - * NOTE: proxy for the Guava implementation of {@link com.google.common.base.Throwables}. - *

    + *

    NOTE: proxy for the Guava implementation of {@link com.google.common.base.Throwables}. * * @since 1.14 * @author Yaniv Inbar @@ -27,48 +25,46 @@ public final class Throwables { /** - * Propagates {@code throwable} as-is if it is an instance of {@link RuntimeException} or - * {@link Error}, or else as a last resort, wraps it in a {@code RuntimeException} then - * propagates. - *

    - * This method always throws an exception. The {@code RuntimeException} return type is only for + * Propagates {@code throwable} as-is if it is an instance of {@link RuntimeException} or {@link + * Error}, or else as a last resort, wraps it in a {@code RuntimeException} then propagates. + * + *

    This method always throws an exception. The {@code RuntimeException} return type is only for * client code to make Java type system happy in case a return value is required by the enclosing * method. Example usage: - *

    * *
    -    T doSomething() {
    -      try {
    -        return someMethodThatCouldThrowAnything();
    -      } catch (IKnowWhatToDoWithThisException e) {
    -        return handle(e);
    -      } catch (Throwable t) {
    -        throw Throwables.propagate(t);
    -      }
    -    }
    -   *
    + * T doSomething() { + * try { + * return someMethodThatCouldThrowAnything(); + * } catch (IKnowWhatToDoWithThisException e) { + * return handle(e); + * } catch (Throwable t) { + * throw Throwables.propagate(t); + * } + * } + * * * @param throwable the Throwable to propagate * @return nothing will ever be returned; this return type is only for your convenience, as - * illustrated in the example above + * illustrated in the example above */ public static RuntimeException propagate(Throwable throwable) { return com.google.common.base.Throwables.propagate(throwable); } /** - * Propagates {@code throwable} exactly as-is, if and only if it is an instance of - * {@link RuntimeException} or {@link Error}. Example usage: + * Propagates {@code throwable} exactly as-is, if and only if it is an instance of {@link + * RuntimeException} or {@link Error}. Example usage: * *
    -    try {
    -      someMethodThatCouldThrowAnything();
    -    } catch (IKnowWhatToDoWithThisException e) {
    -      handle(e);
    -    } catch (Throwable t) {
    -      Throwables.propagateIfPossible(t);
    -      throw new RuntimeException("unexpected", t);
    -    }
    +   * try {
    +   * someMethodThatCouldThrowAnything();
    +   * } catch (IKnowWhatToDoWithThisException e) {
    +   * handle(e);
    +   * } catch (Throwable t) {
    +   * Throwables.propagateIfPossible(t);
    +   * throw new RuntimeException("unexpected", t);
    +   * }
        * 
    * * @param throwable throwable (may be {@code null}) @@ -80,19 +76,19 @@ public static void propagateIfPossible(Throwable throwable) { } /** - * Propagates {@code throwable} exactly as-is, if and only if it is an instance of - * {@link RuntimeException}, {@link Error}, or {@code declaredType}. Example usage: + * Propagates {@code throwable} exactly as-is, if and only if it is an instance of {@link + * RuntimeException}, {@link Error}, or {@code declaredType}. Example usage: * *
    -    try {
    -      someMethodThatCouldThrowAnything();
    -    } catch (IKnowWhatToDoWithThisException e) {
    -      handle(e);
    -    } catch (Throwable t) {
    -      Throwables.propagateIfPossible(t, OtherException.class);
    -      throw new RuntimeException("unexpected", t);
    -    }
    -   *
    + * try { + * someMethodThatCouldThrowAnything(); + * } catch (IKnowWhatToDoWithThisException e) { + * handle(e); + * } catch (Throwable t) { + * Throwables.propagateIfPossible(t, OtherException.class); + * throw new RuntimeException("unexpected", t); + * } + * * * @param throwable throwable (may be {@code null}) * @param declaredType the single checked exception type declared by the calling method @@ -102,6 +98,5 @@ public static void propagateIfPossible( com.google.common.base.Throwables.propagateIfPossible(throwable, declaredType); } - private Throwables() { - } + private Throwables() {} } diff --git a/google-http-client/src/main/java/com/google/api/client/util/Types.java b/google-http-client/src/main/java/com/google/api/client/util/Types.java index d977b026f..8a49b438e 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/Types.java +++ b/google-http-client/src/main/java/com/google/api/client/util/Types.java @@ -14,7 +14,6 @@ package com.google.api.client.util; - import java.lang.reflect.Array; import java.lang.reflect.GenericArrayType; import java.lang.reflect.GenericDeclaration; @@ -43,11 +42,9 @@ public class Types { * Returns the parameterized type that is or extends the given type that matches the given super * class. * - *

    - * For example, if the input type is {@code HashMap} and the input super class is - * {@code Map.class}, it will return the extended parameterized type {@link Map}, but which + *

    For example, if the input type is {@code HashMap} and the input super class + * is {@code Map.class}, it will return the extended parameterized type {@link Map}, but which * retains the actual type information from the original {@code HashMap}. - *

    * * @param type class or parameterized type * @param superClass super class @@ -55,7 +52,8 @@ public class Types { */ public static ParameterizedType getSuperParameterizedType(Type type, Class superClass) { if (type instanceof Class || type instanceof ParameterizedType) { - outer: while (type != null && type != Object.class) { + outer: + while (type != null && type != Object.class) { Class rawType; if (type instanceof Class) { // type is a class @@ -73,8 +71,9 @@ public static ParameterizedType getSuperParameterizedType(Type type, Class su for (Type interfaceType : rawType.getGenericInterfaces()) { // interface type is class or parameterized type Class interfaceClass = - interfaceType instanceof Class ? (Class) interfaceType : getRawClass( - (ParameterizedType) interfaceType); + interfaceType instanceof Class + ? (Class) interfaceType + : getRawClass((ParameterizedType) interfaceType); if (superClass.isAssignableFrom(interfaceClass)) { type = interfaceType; continue outer; @@ -103,10 +102,8 @@ public static boolean isAssignableToOrFrom(Class classToCheck, Class anoth /** * Creates a new instance of the given class by invoking its default constructor. * - *

    - * The given class must be public and must have a public default constructor, and must not be an - * array or an interface or be abstract. If an enclosing class, it must be static. - *

    + *

    The given class must be public and must have a public default constructor, and must not be + * an array or an interface or be abstract. If an enclosing class, it must be static. */ public static T newInstance(Class clazz) { // TODO(yanivi): investigate "sneaky" options for allocating the class that GSON uses, like @@ -167,28 +164,27 @@ private static IllegalArgumentException handleExceptionForNewInstance( /** Returns whether the given type is an array. */ public static boolean isArray(Type type) { - return type instanceof GenericArrayType || type instanceof Class - && ((Class) type).isArray(); + return type instanceof GenericArrayType + || type instanceof Class && ((Class) type).isArray(); } /** * Returns the component type of the given array type, assuming {@link #isArray(Type)}. * - *

    - * Return type will either be class, parameterized type, generic array type, or type variable, but - * not a wildcard type. - *

    + *

    Return type will either be class, parameterized type, generic array type, or type variable, + * but not a wildcard type. * * @throws ClassCastException if {@link #isArray(Type)} is false */ public static Type getArrayComponentType(Type array) { - return array instanceof GenericArrayType ? ((GenericArrayType) array).getGenericComponentType() + return array instanceof GenericArrayType + ? ((GenericArrayType) array).getGenericComponentType() : ((Class) array).getComponentType(); } /** - * Returns the raw class for the given parameter type as defined in - * {@link ParameterizedType#getRawType()}. + * Returns the raw class for the given parameter type as defined in {@link + * ParameterizedType#getRawType()}. * * @param parameterType parameter type * @return raw class @@ -214,17 +210,15 @@ public static Type getBound(WildcardType wildcardType) { /** * Resolves the actual type of the given type variable that comes from a field type based on the * given context list. - *

    - * In case the type variable can be resolved partially, it will return the partially resolved type - * variable. - *

    + * + *

    In case the type variable can be resolved partially, it will return the partially resolved + * type variable. * * @param context context list, ordering from least specific to most specific type context, for - * example container class and then its field + * example container class and then its field * @param typeVariable type variable * @return resolved or partially resolved actual type (type variable, class, parameterized type, - * or generic array type, but not wildcard type) or {@code null} if unable to resolve at - * all + * or generic array type, but not wildcard type) or {@code null} if unable to resolve at all */ public static Type resolveTypeVariable(List context, TypeVariable typeVariable) { // determine where the type variable was declared @@ -265,11 +259,11 @@ public static Type resolveTypeVariable(List context, TypeVariable typeV } /** - * Returns the raw array component type to use -- for example for the first parameter of - * {@link Array#newInstance(Class, int)} -- for the given component type. + * Returns the raw array component type to use -- for example for the first parameter of {@link + * Array#newInstance(Class, int)} -- for the given component type. * * @param context context list, ordering from least specific to most specific type context, for - * example container class and then its field + * example container class and then its field * @param componentType array component type or {@code null} for {@code Object.class} result * @return raw array component type */ @@ -295,10 +289,8 @@ public static Class getRawArrayComponentType(List context, Type compone /** * Returns the type parameter of {@link Iterable} that is assignable from the given iterable type. * - *

    - * For example, for the type {@code ArrayList} -- or for a class that extends {@code + *

    For example, for the type {@code ArrayList} -- or for a class that extends {@code * ArrayList} -- it will return {@code Integer}. - *

    * * @param iterableType iterable type (must extend {@link Iterable}) * @return type parameter, which may be any type @@ -310,10 +302,8 @@ public static Type getIterableParameter(Type iterableType) { /** * Returns the value type parameter of {@link Map} that is assignable from the given map type. * - *

    - * For example, for the type {@code Map} -- or for a class that extends {@code + *

    For example, for the type {@code Map} -- or for a class that extends {@code * Map} -- it will return {@code Integer}. - *

    * * @param mapType map type (must extend {@link Map}) * @return type parameter, which may be any type @@ -342,10 +332,9 @@ private static Type getActualParameterAtPosition(Type type, Class superClass, /** * Returns an iterable for an input iterable or array value. * - *

    - * If the input value extends {@link Iterable}, it will just return the input value. Otherwise, it - * will return an iterable that can handle arrays of primitive and non-primitive component type. - *

    + *

    If the input value extends {@link Iterable}, it will just return the input value. Otherwise, + * it will return an iterable that can handle arrays of primitive and non-primitive component + * type. * * @param value iterable (extends {@link Iterable}) or array value * @return iterable @@ -408,6 +397,5 @@ public static Object toArray(Collection collection, Class componentType) { return collection.toArray((Object[]) Array.newInstance(componentType, collection.size())); } - private Types() { - } + private Types() {} } diff --git a/google-http-client/src/main/java/com/google/api/client/util/Value.java b/google-http-client/src/main/java/com/google/api/client/util/Value.java index 52f24bf6a..bce75bfa8 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/Value.java +++ b/google-http-client/src/main/java/com/google/api/client/util/Value.java @@ -22,28 +22,27 @@ /** * Use this annotation to specify that an enum constant is a string data value, optionally providing * the string data value to use. - *

    - * If the string data value is not specified, the default is the Java field's name. For example: - *

    + * + *

    If the string data value is not specified, the default is the Java field's name. For example: * *

    -  public enum A {
    -
    -    // value is "USE_FIELD_NAME"
    -    @Value
    -    USE_FIELD_NAME,
    -    
    -    // value is "specifiedValue"
    -    @Value("specifiedValue")
    -    USE_SPECIFIED_VALUE, 
    -    
    -    // value is null
    -    @NullValue
    -    NULL_VALUE
    -
    -    // not a value
    -    NOT_A_VALUE
    -  }
    + * public enum A {
    + *
    + * // value is "USE_FIELD_NAME"
    + * @Value
    + * USE_FIELD_NAME,
    + *
    + * // value is "specifiedValue"
    + * @Value("specifiedValue")
    + * USE_SPECIFIED_VALUE,
    + *
    + * // value is null
    + * @NullValue
    + * NULL_VALUE
    + *
    + * // not a value
    + * NOT_A_VALUE
    + * }
      * 
    * * @since 1.4 diff --git a/google-http-client/src/main/java/com/google/api/client/util/escape/CharEscapers.java b/google-http-client/src/main/java/com/google/api/client/util/escape/CharEscapers.java index 49470c81b..d434403dc 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/escape/CharEscapers.java +++ b/google-http-client/src/main/java/com/google/api/client/util/escape/CharEscapers.java @@ -1,95 +1,149 @@ /* - * Copyright (c) 2010 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ +* Copyright (c) 2010 Google Inc. + +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except +* in compliance with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software distributed under the License +* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +* or implied. See the License for the specific language governing permissions and limitations under +* the License. +*/ package com.google.api.client.util.escape; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; /** - * Utility functions for dealing with {@code CharEscaper}s, and some commonly used - * {@code CharEscaper} instances. + * Utility functions for encoding and decoding URIs. * * @since 1.0 */ public final class CharEscapers { - private static final Escaper URI_ESCAPER = + private static final Escaper APPLICATION_X_WWW_FORM_URLENCODED = new PercentEscaper(PercentEscaper.SAFECHARS_URLENCODER, true); + private static final Escaper URI_ESCAPER = + new PercentEscaper(PercentEscaper.SAFECHARS_URLENCODER, false); + private static final Escaper URI_PATH_ESCAPER = - new PercentEscaper(PercentEscaper.SAFEPATHCHARS_URLENCODER, false); + new PercentEscaper(PercentEscaper.SAFEPATHCHARS_URLENCODER); private static final Escaper URI_RESERVED_ESCAPER = - new PercentEscaper(PercentEscaper.SAFE_PLUS_RESERVED_CHARS_URLENCODER, false); + new PercentEscaper(PercentEscaper.SAFE_PLUS_RESERVED_CHARS_URLENCODER); private static final Escaper URI_USERINFO_ESCAPER = - new PercentEscaper(PercentEscaper.SAFEUSERINFOCHARS_URLENCODER, false); + new PercentEscaper(PercentEscaper.SAFEUSERINFOCHARS_URLENCODER); private static final Escaper URI_QUERY_STRING_ESCAPER = - new PercentEscaper(PercentEscaper.SAFEQUERYSTRINGCHARS_URLENCODER, false); + new PercentEscaper(PercentEscaper.SAFEQUERYSTRINGCHARS_URLENCODER); + + private static final Escaper URI_RESERVED_AND_PERCENT_ENCODED_ESCAPER = + new PercentEncodedEscaper(URI_RESERVED_ESCAPER); /** - * Escapes the string value so it can be safely included in URIs. For details on escaping URIs, - * see RFC 3986 - section 2.4. + * Escapes the string value so it can be safely included in application/x-www-form-urlencoded + * data. This is not appropriate for generic URI escaping. In particular it encodes the space + * character as a plus sign instead of percent escaping it, in contravention of the URI + * specification. For details on application/x-www-form-urlencoded encoding see the see HTML 4 specification, + * section 17.13.4.1. + * + *

    When encoding a String, the following rules apply: * - *

    - * When encoding a String, the following rules apply: *

      - *
    • The alphanumeric characters "a" through "z", "A" through "Z" and "0" through "9" remain the - * same. - *
    • The special characters ".", "-", "*", and "_" remain the same. - *
    • The space character " " is converted into a plus sign "+". - *
    • All other characters are converted into one or more bytes using UTF-8 encoding and each - * byte is then represented by the 3-character string "%XY", where "XY" is the two-digit, - * uppercase, hexadecimal representation of the byte value. + *
    • The alphanumeric characters "a" through "z", "A" through "Z" and "0" through "9" remain + * the same. + *
    • The special characters ".", "-", "*", and "_" remain the same. + *
    • The space character " " is converted into a plus sign "+". + *
    • All other characters are converted into one or more bytes using UTF-8 encoding and each + * byte is then represented by the 3-character string "%XY", where "XY" is the two-digit, + * uppercase, hexadecimal representation of the byte value. *
    - *

    * - *

    - * Note: Unlike other escapers, URI escapers produce uppercase hexadecimal sequences. From - * RFC 3986:
    + *

    Note: Unlike other escapers, URI escapers produce uppercase hexadecimal sequences. + * From RFC 3986:
    * "URI producers and normalizers should use uppercase hexadecimal digits for all * percent-encodings." - *

    * - *

    - * This escaper has identical behavior to (but is potentially much faster than): + *

    This escaper has identical behavior to (but is potentially much faster than): + * *

      - *
    • {@link java.net.URLEncoder#encode(String, String)} with the encoding name "UTF-8" + *
    • {@link java.net.URLEncoder#encode(String, String)} with the encoding name "UTF-8" *
    - *

    */ + @Deprecated public static String escapeUri(String value) { + return APPLICATION_X_WWW_FORM_URLENCODED.escape(value); + } + + /** + * Escapes the string value so it can be safely included in any part of a URI. For details on + * escaping URIs, see RFC 3986 - section + * 2.4. + * + *

    When encoding a String, the following rules apply: + * + *

      + *
    • The alphanumeric characters "a" through "z", "A" through "Z" and "0" through "9" remain + * the same. + *
    • The special characters ".", "-", "*", and "_" remain the same. + *
    • The space character " " is converted into "%20". + *
    • All other characters are converted into one or more bytes using UTF-8 encoding and each + * byte is then represented by the 3-character string "%XY", where "XY" is the two-digit, + * uppercase, hexadecimal representation of the byte value. + *
    + * + *

    Note: Unlike other escapers, URI escapers produce uppercase hexadecimal sequences. + * From RFC 3986:
    + * "URI producers and normalizers should use uppercase hexadecimal digits for all + * percent-encodings." + */ + public static String escapeUriConformant(String value) { return URI_ESCAPER.escape(value); } /** - * Percent-decodes a US-ASCII string into a Unicode string. UTF-8 encoding is used to determine - * what characters are represented by any consecutive sequences of the form "%XX". + * Decodes application/x-www-form-urlencoded strings. The UTF-8 character set determines what + * characters are represented by any consecutive sequences of the form "%XX". * - *

    - * This replaces each occurrence of '+' with a space, ' '. So this method should not be used for - * non application/x-www-form-urlencoded strings such as host and path. - *

    + *

    This replaces each occurrence of '+' with a space, ' '. This method should not be used for + * non-application/x-www-form-urlencoded strings such as host and path. * * @param uri a percent-encoded US-ASCII string - * @return a Unicode string + * @return a string without any percent escapes or plus signs */ public static String decodeUri(String uri) { try { - return URLDecoder.decode(uri, "UTF-8"); + return URLDecoder.decode(uri, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + // UTF-8 encoding guaranteed to be supported by JVM + throw new RuntimeException(e); + } + } + + /** + * Decodes the path component of a URI. This does not convert + into spaces (the behavior of + * {@link java.net.URLDecoder#decode(String, String)}). This method transforms URI encoded values + * into their decoded symbols. + * + *

    e.g. {@code decodePath("%3Co%3E")} returns {@code ""} + * + * @param path the value to be decoded + * @return decoded version of {@code path} + */ + public static String decodeUriPath(String path) { + if (path == null) { + return null; + } + try { + return URLDecoder.decode(path.replace("+", "%2B"), StandardCharsets.UTF_8.name()); } catch (UnsupportedEncodingException e) { // UTF-8 encoding guaranteed to be supported by JVM throw new RuntimeException(e); @@ -101,67 +155,71 @@ public static String decodeUri(String uri) { * escaping URIs, see RFC 3986 - section * 2.4. * - *

    - * When encoding a String, the following rules apply: + *

    When encoding a String, the following rules apply: + * *

      - *
    • The alphanumeric characters "a" through "z", "A" through "Z" and "0" through "9" remain the - * same. - *
    • The unreserved characters ".", "-", "~", and "_" remain the same. - *
    • The general delimiters "@" and ":" remain the same. - *
    • The subdelimiters "!", "$", "&", "'", "(", ")", "*", ",", ";", and "=" remain the same. - *
    • The space character " " is converted into %20. - *
    • All other characters are converted into one or more bytes using UTF-8 encoding and each - * byte is then represented by the 3-character string "%XY", where "XY" is the two-digit, - * uppercase, hexadecimal representation of the byte value. + *
    • The alphanumeric characters "a" through "z", "A" through "Z" and "0" through "9" remain + * the same. + *
    • The unreserved characters ".", "-", "~", and "_" remain the same. + *
    • The general delimiters "@" and ":" remain the same. + *
    • The subdelimiters "!", "$", "&", "'", "(", ")", "*", ",", ";", and "=" remain the + * same. + *
    • The space character " " is converted into %20. + *
    • All other characters are converted into one or more bytes using UTF-8 encoding and each + * byte is then represented by the 3-character string "%XY", where "XY" is the two-digit, + * uppercase, hexadecimal representation of the byte value. *
    - *

    * - *

    - * Note: Unlike other escapers, URI escapers produce uppercase hexadecimal sequences. From - * RFC 3986:
    + *

    Note: Unlike other escapers, URI escapers produce uppercase hexadecimal sequences. + * From RFC 3986:
    * "URI producers and normalizers should use uppercase hexadecimal digits for all * percent-encodings." - *

    */ public static String escapeUriPath(String value) { return URI_PATH_ESCAPER.escape(value); } /** - * Escapes a URI path but retains all reserved characters, including all general delimiters. - * That is the same as {@link #escapeUriPath(String)} except that it keeps '?', '+', and '/' - * unescaped. + * Escapes a URI path but retains all reserved characters, including all general delimiters. That + * is the same as {@link #escapeUriPath(String)} except that it does not escape '?', '+', and '/'. */ public static String escapeUriPathWithoutReserved(String value) { return URI_RESERVED_ESCAPER.escape(value); } + /** + * Escapes a URI path but retains all reserved and percent-encoded characters. That is the same as + * {@link #escapeUriPathWithoutReserved(String)} except that it also escapes percent encoded + * parts. + */ + public static String escapeUriPathWithoutReservedAndPercentEncoded(String value) { + return URI_RESERVED_AND_PERCENT_ENCODED_ESCAPER.escape(value); + } + /** * Escapes the string value so it can be safely included in URI user info part. For details on * escaping URIs, see RFC 3986 - section * 2.4. * - *

    - * When encoding a String, the following rules apply: + *

    When encoding a String, the following rules apply: + * *

      - *
    • The alphanumeric characters "a" through "z", "A" through "Z" and "0" through "9" remain the - * same. - *
    • The unreserved characters ".", "-", "~", and "_" remain the same. - *
    • The general delimiter ":" remains the same. - *
    • The subdelimiters "!", "$", "&", "'", "(", ")", "*", ",", ";", and "=" remain the same. - *
    • The space character " " is converted into %20. - *
    • All other characters are converted into one or more bytes using UTF-8 encoding and each - * byte is then represented by the 3-character string "%XY", where "XY" is the two-digit, - * uppercase, hexadecimal representation of the byte value. + *
    • The alphanumeric characters "a" through "z", "A" through "Z" and "0" through "9" remain + * the same. + *
    • The unreserved characters ".", "-", "~", and "_" remain the same. + *
    • The general delimiter ":" remains the same. + *
    • The subdelimiters "!", "$", "&", "'", "(", ")", "*", ",", ";", and "=" remain the + * same. + *
    • The space character " " is converted into %20. + *
    • All other characters are converted into one or more bytes using UTF-8 encoding and each + * byte is then represented by the 3-character string "%XY", where "XY" is the two-digit, + * uppercase, hexadecimal representation of the byte value. *
    - *

    * - *

    - * Note: Unlike other escapers, URI escapers produce uppercase hexadecimal sequences. From - * RFC 3986:
    + *

    Note: Unlike other escapers, URI escapers produce uppercase hexadecimal sequences. + * From RFC 3986:
    * "URI producers and normalizers should use uppercase hexadecimal digits for all * percent-encodings." - *

    * * @since 1.15 */ @@ -175,44 +233,36 @@ public static String escapeUriUserInfo(String value) { * values should be individually encoded. If you escape an entire query string in one pass with * this escaper, then the "=" and "&" characters used as separators will also be escaped. * - *

    - * This escaper is also suitable for escaping fragment identifiers. - *

    + *

    This escaper is also suitable for escaping fragment identifiers. * - *

    - * For details on escaping URIs, see RFC - * 3986 - section 2.4. - *

    + *

    For details on escaping URIs, see RFC 3986 - section 2.4. + * + *

    When encoding a String, the following rules apply: * - *

    - * When encoding a String, the following rules apply: *

      - *
    • The alphanumeric characters "a" through "z", "A" through "Z" and "0" through "9" remain the - * same. - *
    • The unreserved characters ".", "-", "~", and "_" remain the same. - *
    • The general delimiters "@" and ":" remain the same. - *
    • The path delimiters "/" and "?" remain the same. - *
    • The subdelimiters "!", "$", "'", "(", ")", "*", ",", and ";", remain the same. - *
    • The space character " " is converted into %20. - *
    • The equals sign "=" is converted into %3D. - *
    • The ampersand "&" is converted into %26. - *
    • All other characters are converted into one or more bytes using UTF-8 encoding and each - * byte is then represented by the 3-character string "%XY", where "XY" is the two-digit, - * uppercase, hexadecimal representation of the byte value. + *
    • The alphanumeric characters "a" through "z", "A" through "Z" and "0" through "9" remain + * the same. + *
    • The unreserved characters ".", "-", "~", and "_" remain the same. + *
    • The general delimiters "@" and ":" remain the same. + *
    • The path delimiters "/" and "?" remain the same. + *
    • The subdelimiters "!", "$", "'", "(", ")", "*", ",", and ";", remain the same. + *
    • The space character " " is converted into %20. + *
    • The equals sign "=" is converted into %3D. + *
    • The ampersand "&" is converted into %26. + *
    • All other characters are converted into one or more bytes using UTF-8 encoding and each + * byte is then represented by the 3-character string "%XY", where "XY" is the two-digit, + * uppercase, hexadecimal representation of the byte value. *
    - *

    * - *

    - * Note: Unlike other escapers, URI escapers produce uppercase hexadecimal sequences. From - * RFC 3986:
    + *

    Note: Unlike other escapers, URI escapers produce uppercase hexadecimal sequences. + * From RFC 3986:
    * "URI producers and normalizers should use uppercase hexadecimal digits for all * percent-encodings." - *

    */ public static String escapeUriQuery(String value) { return URI_QUERY_STRING_ESCAPER.escape(value); } - private CharEscapers() { - } + private CharEscapers() {} } diff --git a/google-http-client/src/main/java/com/google/api/client/util/escape/Escaper.java b/google-http-client/src/main/java/com/google/api/client/util/escape/Escaper.java index 7c49418fb..ddf91c69b 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/escape/Escaper.java +++ b/google-http-client/src/main/java/com/google/api/client/util/escape/Escaper.java @@ -19,18 +19,15 @@ * (such as an XML document). Typically (but not always), the inverse process of "unescaping" the * text is performed automatically by the relevant parser. * - *

    - * For example, an XML escaper would convert the literal string {@code "Foo"} into {@code + *

    For example, an XML escaper would convert the literal string {@code "Foo"} into {@code * "Foo<Bar>"} to prevent {@code ""} from being confused with an XML tag. When the * resulting XML document is parsed, the parser API will return this text as the original literal * string {@code "Foo"}. * - *

    - * An {@code Escaper} instance is required to be stateless, and safe when used concurrently by + *

    An {@code Escaper} instance is required to be stateless, and safe when used concurrently by * multiple threads. * - *

    - * Several popular escapers are defined as constants in the class {@link CharEscapers}. + *

    Several popular escapers are defined as constants in the class {@link CharEscapers}. * * @since 1.0 */ @@ -39,20 +36,20 @@ public abstract class Escaper { /** * Returns the escaped form of a given literal string. * - *

    - * Note that this method may treat input characters differently depending on the specific escaper - * implementation. + *

    Note that this method may treat input characters differently depending on the specific + * escaper implementation. + * *

      - *
    • {@link UnicodeEscaper} handles UTF-16 - * correctly, including surrogate character pairs. If the input is badly formed the escaper should - * throw {@link IllegalArgumentException}. + *
    • {@link UnicodeEscaper} handles UTF-16 + * correctly, including surrogate character pairs. If the input is badly formed the escaper + * should throw {@link IllegalArgumentException}. *
    * * @param string the literal string to be escaped * @return the escaped form of {@code string} * @throws NullPointerException if {@code string} is null * @throws IllegalArgumentException if {@code string} contains badly formed UTF-16 or cannot be - * escaped for any other reason + * escaped for any other reason */ public abstract String escape(String string); } diff --git a/google-http-client/src/main/java/com/google/api/client/util/escape/PercentEncodedEscaper.java b/google-http-client/src/main/java/com/google/api/client/util/escape/PercentEncodedEscaper.java new file mode 100644 index 000000000..6de6a0c1d --- /dev/null +++ b/google-http-client/src/main/java/com/google/api/client/util/escape/PercentEncodedEscaper.java @@ -0,0 +1,57 @@ +package com.google.api.client.util.escape; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * An {@link Escaper} implementation that preserves percent-encoded sequences in the input string. + * + *

    This escaper applies the provided {@link Escaper} to all parts of the input string except for + * valid percent-encoded sequences (e.g., %20), which are left unchanged. + */ +final class PercentEncodedEscaper extends Escaper { + + /** Pattern to match valid percent-encoded sequences (e.g., %20). */ + static final Pattern PCT_ENCODE_PATTERN = Pattern.compile("%[0-9A-Fa-f]{2}"); + + private final Escaper escaper; + + public PercentEncodedEscaper(Escaper escaper) { + if (escaper == null) { + throw new NullPointerException("Escaper cannot be null"); + } + this.escaper = escaper; + } + + /** + * Escapes the input string using the provided {@link Escaper}, preserving valid percent-encoded + * sequences. + * + * @param string the input string to escape + * @return the escaped string with percent-encoded sequences left unchanged + */ + @Override + public String escape(String string) { + if (string == null || string.isEmpty()) { + return string; + } + + Matcher matcher = PCT_ENCODE_PATTERN.matcher(string); + StringBuilder sb = new StringBuilder(); + + int lastEnd = 0; + while (matcher.find()) { + sb.append(escaper.escape(string.substring(lastEnd, matcher.start()))); + + sb.append(string.substring(matcher.start(), matcher.end())); + + lastEnd = matcher.end(); + } + + if (lastEnd < string.length()) { + sb.append(escaper.escape(string.substring(lastEnd))); + } + + return sb.toString(); + } +} diff --git a/google-http-client/src/main/java/com/google/api/client/util/escape/PercentEscaper.java b/google-http-client/src/main/java/com/google/api/client/util/escape/PercentEscaper.java index 996f681b3..014ecbc90 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/escape/PercentEscaper.java +++ b/google-http-client/src/main/java/com/google/api/client/util/escape/PercentEscaper.java @@ -16,51 +16,44 @@ /** * A {@code UnicodeEscaper} that escapes some set of Java characters using the URI percent encoding - * scheme. The set of safe characters (those which remain unescaped) can be specified on - * construction. + * scheme. The set of safe characters (those which remain unescaped) is specified on construction. * - *

    - * For details on escaping URIs for use in web pages, see For details on escaping URIs for use in web pages, see RFC 3986 - section 2.4 and RFC 3986 - appendix A * - *

    - * When encoding a String, the following rules apply: + *

    When encoding a String, the following rules apply: + * *

      - *
    • The alphanumeric characters "a" through "z", "A" through "Z" and "0" through "9" remain the - * same. - *
    • Any additionally specified safe characters remain the same. - *
    • If {@code plusForSpace} was specified, the space character " " is converted into a plus sign - * "+". - *
    • All other characters are converted into one or more bytes using UTF-8 encoding and each byte - * is then represented by the 3-character string "%XY", where "XY" is the two-digit, uppercase, - * hexadecimal representation of the byte value. + *
    • The alphanumeric characters "a" through "z", "A" through "Z" and "0" through "9" remain the + * same. + *
    • Any additionally specified safe characters remain the same. + *
    • If {@code plusForSpace} is true, the space character " " is converted into a plus sign "+". + *
    • All other characters are converted into one or more bytes using UTF-8 encoding. Each byte + * is then represented by the 3-character string "%XY", where "XY" is the two-digit, + * uppercase, hexadecimal representation of the byte value. *
    * - *

    - * RFC 2396 specifies the set of unreserved characters as "-", "_", ".", "!", "~", "*", "'", "(" and - * ")". It goes on to state: - * - *

    - * Unreserved characters can be escaped without changing the semantics of the URI, but this - * should not be done unless the URI is being used in a context that does not allow the unescaped - * character to appear. + *

    RFC 3986 defines the set of unreserved characters as "-", "_", "~", and "." It goes on to + * state: * - *

    - * For performance reasons the only currently supported character encoding of this class is UTF-8. + *

    URIs that differ in the replacement of an unreserved character with its corresponding + * percent-encoded US-ASCII octet are equivalent: they identify the same resource. However, URI + * comparison implementations do not always perform normalization prior to comparison (see Section + * 6). For consistency, percent-encoded octets in the ranges of ALPHA (%41-%5A and %61-%7A), DIGIT + * (%30-%39), hyphen (%2D), period (%2E), underscore (%5F), or tilde (%7E) should not be created by + * URI producers and, when found in a URI, should be decoded to their corresponding unreserved + * characters by URI normalizers. * - *

    - * Note: This escaper produces uppercase hexadecimal sequences. From RFC 3986:
    + *

    Note: This escaper produces uppercase hexadecimal sequences. From RFC 3986:
    * "URI producers and normalizers should use uppercase hexadecimal digits for all * percent-encodings." * * @since 1.0 */ public class PercentEscaper extends UnicodeEscaper { - /** - * A string of safe characters that mimics the behavior of {@link java.net.URLEncoder}. - */ + /** A string of safe characters that mimics the behavior of {@link java.net.URLEncoder}. */ public static final String SAFECHARS_URLENCODER = "-_.*"; /** @@ -71,11 +64,15 @@ public class PercentEscaper extends UnicodeEscaper { public static final String SAFEPATHCHARS_URLENCODER = "-_.!~*'()@:$&,;="; /** - * Contains the save characters plus all reserved characters. This happens to be the safe path - * characters plus those characters which are reserved for URI segments, namely '+', '/', and - * '?'. + * A string of characters that do not need to be encoded when used in URI Templates reserved + * expansion, as specified in RFC 6570. This includes the safe characters plus all reserved + * characters. + * + *

    For details on escaping URI Templates using the reserved expansion, see RFC 6570 - section 3.2.3. */ - public static final String SAFE_PLUS_RESERVED_CHARS_URLENCODER = SAFEPATHCHARS_URLENCODER + "+/?"; + public static final String SAFE_PLUS_RESERVED_CHARS_URLENCODER = + SAFEPATHCHARS_URLENCODER + "+/?#[]"; /** * A string of characters that do not need to be encoded when used in URI user info part, as @@ -98,9 +95,7 @@ public class PercentEscaper extends UnicodeEscaper { private static final char[] UPPER_HEX_DIGITS = "0123456789ABCDEF".toCharArray(); - /** - * If true we should convert space to the {@code +} character. - */ + /** If true we should convert space to the {@code +} character. */ private final boolean plusForSpace; /** @@ -111,19 +106,37 @@ public class PercentEscaper extends UnicodeEscaper { private final boolean[] safeOctets; /** - * Constructs a URI escaper with the specified safe characters and optional handling of the space - * character. + * Constructs a URI escaper with the specified safe characters. The space character is escaped to + * %20 in accordance with the URI specification. * * @param safeChars a non null string specifying additional safe characters for this escaper (the - * ranges 0..9, a..z and A..Z are always safe and should not be specified here) + * ranges 0..9, a..z and A..Z are always safe and should not be specified here) + * @throws IllegalArgumentException if any of the parameters are invalid + */ + public PercentEscaper(String safeChars) { + this(safeChars, false); + } + + /** + * Constructs a URI escaper that converts all but the specified safe characters into hexadecimal + * percent escapes. Optionally space characters can be converted into a plus sign {@code +} + * instead of {@code %20}. and optional handling of the space + * + * @param safeChars a non null string specifying additional safe characters for this escaper. The + * ranges 0..9, a..z and A..Z are always safe and should not be specified here. * @param plusForSpace true if ASCII space should be escaped to {@code +} rather than {@code %20} - * @throws IllegalArgumentException if any of the parameters were invalid + * @throws IllegalArgumentException if safeChars includes characters that are always safe or + * characters that must always be escaped + * @deprecated use {@code PercentEscaper(String safeChars)} instead which is the same as invoking + * this method with plusForSpace set to false. Escaping spaces as plus signs does not conform + * to the URI specification. */ + @Deprecated public PercentEscaper(String safeChars, boolean plusForSpace) { // Avoid any misunderstandings about the behavior of this escaper if (safeChars.matches(".*[0-9A-Za-z].*")) { throw new IllegalArgumentException( - "Alphanumeric characters are always 'safe' and should not be " + "explicitly specified"); + "Alphanumeric ASCII characters are always 'safe' and should not be " + "escaped."); } // Avoid ambiguous parameters. Safe characters are never modified so if // space is a safe character then setting plusForSpace is meaningless. @@ -196,9 +209,7 @@ public String escape(String s) { return s; } - /** - * Escapes the given Unicode code point in UTF-8. - */ + /** Escapes the given Unicode code point in UTF-8. */ @Override protected char[] escape(int cp) { // We should never get negative values here but if we do it will throw an diff --git a/google-http-client/src/main/java/com/google/api/client/util/escape/Platform.java b/google-http-client/src/main/java/com/google/api/client/util/escape/Platform.java index f54b0d208..db81b2836 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/escape/Platform.java +++ b/google-http-client/src/main/java/com/google/api/client/util/escape/Platform.java @@ -14,12 +14,9 @@ package com.google.api.client.util.escape; -/** - * Methods factored out so that they can be emulated differently in GWT. - */ +/** Methods factored out so that they can be emulated differently in GWT. */ final class Platform { - private Platform() { - } + private Platform() {} /** Returns a thread-local 1024-char array. */ // DEST_TL.get() is not null because initialValue() below returns a non-null. @@ -33,10 +30,11 @@ static char[] charBufferFromThreadLocal() { * 1024 characters. If we grow past this we don't put it back in the threadlocal, we just keep * going and grow as needed. */ - private static final ThreadLocal DEST_TL = new ThreadLocal() { - @Override - protected char[] initialValue() { - return new char[1024]; - } - }; + private static final ThreadLocal DEST_TL = + new ThreadLocal() { + @Override + protected char[] initialValue() { + return new char[1024]; + } + }; } diff --git a/google-http-client/src/main/java/com/google/api/client/util/escape/UnicodeEscaper.java b/google-http-client/src/main/java/com/google/api/client/util/escape/UnicodeEscaper.java index 66b47ac1c..1fdcf4079 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/escape/UnicodeEscaper.java +++ b/google-http-client/src/main/java/com/google/api/client/util/escape/UnicodeEscaper.java @@ -19,23 +19,19 @@ * context (such as an XML document). Typically (but not always), the inverse process of * "unescaping" the text is performed automatically by the relevant parser. * - *

    - * For example, an XML escaper would convert the literal string {@code "Foo"} into {@code + *

    For example, an XML escaper would convert the literal string {@code "Foo"} into {@code * "Foo<Bar>"} to prevent {@code ""} from being confused with an XML tag. When the * resulting XML document is parsed, the parser API will return this text as the original literal * string {@code "Foo"}. * - *

    - * As there are important reasons, including potential security issues, to handle Unicode correctly - * if you are considering implementing a new escaper you should favor using UnicodeEscaper wherever - * possible. + *

    As there are important reasons, including potential security issues, to handle Unicode + * correctly if you are considering implementing a new escaper you should favor using UnicodeEscaper + * wherever possible. * - *

    - * A {@code UnicodeEscaper} instance is required to be stateless, and safe when used concurrently by - * multiple threads. + *

    A {@code UnicodeEscaper} instance is required to be stateless, and safe when used concurrently + * by multiple threads. * - *

    - * Several popular escapers are defined as constants in the class {@link CharEscapers}. To create + *

    Several popular escapers are defined as constants in the class {@link CharEscapers}. To create * your own escapers extend this class and implement the {@link #escape(int)} method. * * @since 1.0 @@ -49,17 +45,14 @@ public abstract class UnicodeEscaper extends Escaper { * does not need to be escaped. When called as part of an escaping operation, the given code point * is guaranteed to be in the range {@code 0 <= cp <= Character#MAX_CODE_POINT}. * - *

    - * If an empty array is returned, this effectively strips the input character from the resulting - * text. + *

    If an empty array is returned, this effectively strips the input character from the + * resulting text. * - *

    - * If the character does not need to be escaped, this method should return {@code null}, rather + *

    If the character does not need to be escaped, this method should return {@code null}, rather * than an array containing the character representation of the code point. This enables the * escaping algorithm to perform more efficiently. * - *

    - * If the implementation of this method cannot correctly handle a particular code point then it + *

    If the implementation of this method cannot correctly handle a particular code point then it * should either throw an appropriate runtime exception or return a suitable replacement * character. It must never silently discard invalid input as this may constitute a security risk. * @@ -72,35 +65,31 @@ public abstract class UnicodeEscaper extends Escaper { * Scans a sub-sequence of characters from a given {@link CharSequence}, returning the index of * the next character that requires escaping. * - *

    - * Note: When implementing an escaper, it is a good idea to override this method for + *

    Note: When implementing an escaper, it is a good idea to override this method for * efficiency. The base class implementation determines successive Unicode code points and invokes * {@link #escape(int)} for each of them. If the semantics of your escaper are such that code * points in the supplementary range are either all escaped or all unescaped, this method can be * implemented more efficiently using {@link CharSequence#charAt(int)}. * - *

    - * Note however that if your escaper does not escape characters in the supplementary range, you + *

    Note however that if your escaper does not escape characters in the supplementary range, you * should either continue to validate the correctness of any surrogate characters encountered or * provide a clear warning to users that your escaper does not validate its input. * - *

    - * See {@link PercentEscaper} for an example. + *

    See {@link PercentEscaper} for an example. * * @param csq a sequence of characters * @param start the index of the first character to be scanned * @param end the index immediately after the last character to be scanned * @throws IllegalArgumentException if the scanned sub-sequence of {@code csq} contains invalid - * surrogate pairs + * surrogate pairs */ protected abstract int nextEscapeIndex(CharSequence csq, int start, int end); /** * Returns the escaped form of a given literal string. * - *

    - * If you are escaping input in arbitrary successive chunks, then it is not generally safe to use - * this method. If an input string ends with an unmatched high surrogate character, then this + *

    If you are escaping input in arbitrary successive chunks, then it is not generally safe to + * use this method. If an input string ends with an unmatched high surrogate character, then this * method will throw {@link IllegalArgumentException}. You should ensure your input is valid UTF-16 before calling this method. * @@ -118,9 +107,8 @@ public abstract class UnicodeEscaper extends Escaper { * protected to allow subclasses to override the fastpath escaping function to inline their * escaping test. * - *

    - * This method is not reentrant and may only be invoked by the top level {@link #escape(String)} - * method. + *

    This method is not reentrant and may only be invoked by the top level {@link + * #escape(String)} method. * * @param s the literal string to be escaped * @param index the index to start escaping from @@ -188,34 +176,33 @@ protected final String escapeSlow(String s, int index) { /** * Returns the Unicode code point of the character at the given index. * - *

    - * Unlike {@link Character#codePointAt(CharSequence, int)} or {@link String#codePointAt(int)} this - * method will never fail silently when encountering an invalid surrogate pair. + *

    Unlike {@link Character#codePointAt(CharSequence, int)} or {@link String#codePointAt(int)} + * this method will never fail silently when encountering an invalid surrogate pair. + * + *

    The behaviour of this method is as follows: * - *

    - * The behaviour of this method is as follows: - *

      - *
    1. If {@code index >= end}, {@link IndexOutOfBoundsException} is thrown. - *
    2. If the character at the specified index is not a surrogate, it is returned. - *
    3. If the first character was a high surrogate value, then an attempt is made to read the next - * character. *
        - *
      1. If the end of the sequence was reached, the negated value of the trailing high surrogate - * is returned. - *
      2. If the next character was a valid low surrogate, the code point value of the high/low - * surrogate pair is returned. - *
      3. If the next character was not a low surrogate value, then {@link IllegalArgumentException} - * is thrown. - *
      - *
    4. If the first character was a low surrogate value, {@link IllegalArgumentException} is - * thrown. + *
    5. If {@code index >= end}, {@link IndexOutOfBoundsException} is thrown. + *
    6. If the character at the specified index is not a surrogate, it is returned. + *
    7. If the first character was a high surrogate value, then an attempt is made to read the + * next character. + *
        + *
      1. If the end of the sequence was reached, the negated value of the trailing high + * surrogate is returned. + *
      2. If the next character was a valid low surrogate, the code point value of the + * high/low surrogate pair is returned. + *
      3. If the next character was not a low surrogate value, then {@link + * IllegalArgumentException} is thrown. + *
      + *
    8. If the first character was a low surrogate value, {@link IllegalArgumentException} is + * thrown. *
    * * @param seq the sequence of characters from which to decode the code point * @param index the index of the first character to decode * @param end the index beyond the last valid character to decode * @return the Unicode code point for the given index or the negated value of the trailing high - * surrogate character at the end of the sequence + * surrogate character at the end of the sequence */ protected static int codePointAt(CharSequence seq, int index, int end) { if (index < end) { @@ -234,11 +221,19 @@ protected static int codePointAt(CharSequence seq, int index, int end) { return Character.toCodePoint(c1, c2); } throw new IllegalArgumentException( - "Expected low surrogate but got char '" + c2 + "' with value " + (int) c2 + " at index " + "Expected low surrogate but got char '" + + c2 + + "' with value " + + (int) c2 + + " at index " + index); } else { throw new IllegalArgumentException( - "Unexpected low surrogate character '" + c1 + "' with value " + (int) c1 + " at index " + "Unexpected low surrogate character '" + + c1 + + "' with value " + + (int) c1 + + " at index " + (index - 1)); } } diff --git a/google-http-client/src/main/java/com/google/api/client/util/escape/package-info.java b/google-http-client/src/main/java/com/google/api/client/util/escape/package-info.java index 35367554b..09ec250f2 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/escape/package-info.java +++ b/google-http-client/src/main/java/com/google/api/client/util/escape/package-info.java @@ -18,6 +18,4 @@ * @since 1.0 * @author Yaniv Inbar */ - package com.google.api.client.util.escape; - diff --git a/google-http-client/src/main/java/com/google/api/client/util/package-info.java b/google-http-client/src/main/java/com/google/api/client/util/package-info.java index 1875e047e..b6c2ee331 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/package-info.java +++ b/google-http-client/src/main/java/com/google/api/client/util/package-info.java @@ -18,9 +18,7 @@ * @since 1.0 * @author Yaniv Inbar */ - @ReflectionSupport(value = ReflectionSupport.Level.FULL) package com.google.api.client.util; import com.google.j2objc.annotations.ReflectionSupport; - diff --git a/google-http-client/src/main/java/com/google/api/client/util/store/AbstractDataStore.java b/google-http-client/src/main/java/com/google/api/client/util/store/AbstractDataStore.java index d3688ceab..00cf7cc86 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/store/AbstractDataStore.java +++ b/google-http-client/src/main/java/com/google/api/client/util/store/AbstractDataStore.java @@ -15,7 +15,6 @@ package com.google.api.client.util.store; import com.google.api.client.util.Preconditions; - import java.io.IOException; import java.io.Serializable; import java.util.Collection; @@ -25,7 +24,6 @@ * Abstract data store implementation. * * @param serializable type of the mapped value - * * @author Yaniv Inbar * @since 1.16 */ @@ -49,10 +47,8 @@ protected AbstractDataStore(DataStoreFactory dataStoreFactory, String id) { /** * {@inheritDoc} * - *

    - * Overriding is only supported for the purpose of calling the super implementation and changing - * the return type, but nothing else. - *

    + *

    Overriding is only supported for the purpose of calling the super implementation and + * changing the return type, but nothing else. */ public DataStoreFactory getDataStoreFactory() { return dataStoreFactory; @@ -65,9 +61,7 @@ public final String getId() { /** * {@inheritDoc} * - *

    - * Default implementation is to call {@link #get(String)} and check if it is {@code null}. - *

    + *

    Default implementation is to call {@link #get(String)} and check if it is {@code null}. */ public boolean containsKey(String key) throws IOException { return get(key) != null; @@ -76,9 +70,7 @@ public boolean containsKey(String key) throws IOException { /** * {@inheritDoc} * - *

    - * Default implementation is to call {@link Collection#contains(Object)} on {@link #values()}. - *

    + *

    Default implementation is to call {@link Collection#contains(Object)} on {@link #values()}. */ public boolean containsValue(V value) throws IOException { return values().contains(value); @@ -87,9 +79,7 @@ public boolean containsValue(V value) throws IOException { /** * {@inheritDoc} * - *

    - * Default implementation is to check if {@link #size()} is {@code 0}. - *

    + *

    Default implementation is to check if {@link #size()} is {@code 0}. */ public boolean isEmpty() throws IOException { return size() == 0; @@ -98,9 +88,7 @@ public boolean isEmpty() throws IOException { /** * {@inheritDoc} * - *

    - * Default implementation is to call {@link Set#size()} on {@link #keySet()}. - *

    + *

    Default implementation is to call {@link Set#size()} on {@link #keySet()}. */ public int size() throws IOException { return keySet().size(); diff --git a/google-http-client/src/main/java/com/google/api/client/util/store/AbstractDataStoreFactory.java b/google-http-client/src/main/java/com/google/api/client/util/store/AbstractDataStoreFactory.java index 3e4c48959..d70de7d43 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/store/AbstractDataStoreFactory.java +++ b/google-http-client/src/main/java/com/google/api/client/util/store/AbstractDataStoreFactory.java @@ -14,14 +14,15 @@ package com.google.api.client.util.store; -import com.google.api.client.util.Maps; -import com.google.api.client.util.Preconditions; +import static com.google.api.client.util.Preconditions.checkArgument; +import com.google.api.client.util.Maps; import java.io.IOException; import java.io.Serializable; import java.util.Map; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; +import java.util.logging.Logger; import java.util.regex.Pattern; /** @@ -39,14 +40,22 @@ public abstract class AbstractDataStoreFactory implements DataStoreFactory { private final Map> dataStoreMap = Maps.newHashMap(); /** - * Pattern to control possible values for the {@code id} parameter of - * {@link #getDataStore(String)}. + * Pattern to control possible values for the {@code id} parameter of {@link + * #getDataStore(String)}. */ - private static final Pattern ID_PATTERN = Pattern.compile("\\w{1,30}"); + private static final Pattern ID_PATTERN; + + static { + try { + ID_PATTERN = Pattern.compile("\\w{1,30}"); + } catch (Throwable t) { + Logger.getLogger(AbstractDataStoreFactory.class.getName()).severe(t.getMessage()); + throw t; + } + } public final DataStore getDataStore(String id) throws IOException { - Preconditions.checkArgument( - ID_PATTERN.matcher(id).matches(), "%s does not match pattern %s", id, ID_PATTERN); + checkArgument(ID_PATTERN.matcher(id).matches(), "%s does not match pattern %s", id, ID_PATTERN); lock.lock(); try { @SuppressWarnings("unchecked") @@ -64,9 +73,7 @@ public final DataStore getDataStore(String id) throw /** * Returns a new instance of a type-specific data store based on the given unique ID. * - *

    - * The {@link DataStore#getId()} must match the {@code id} parameter from this method. - *

    + *

    The {@link DataStore#getId()} must match the {@code id} parameter from this method. * * @param id unique ID to refer to typed data store * @param serializable type of the mapped value diff --git a/google-http-client/src/main/java/com/google/api/client/util/store/AbstractMemoryDataStore.java b/google-http-client/src/main/java/com/google/api/client/util/store/AbstractMemoryDataStore.java index 863483a2b..daf796611 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/store/AbstractMemoryDataStore.java +++ b/google-http-client/src/main/java/com/google/api/client/util/store/AbstractMemoryDataStore.java @@ -18,7 +18,6 @@ import com.google.api.client.util.Lists; import com.google.api.client.util.Maps; import com.google.api.client.util.Preconditions; - import java.io.IOException; import java.io.Serializable; import java.util.Arrays; @@ -34,7 +33,6 @@ * Abstract, thread-safe, in-memory implementation of a data store factory. * * @param serializable type of the mapped value - * * @author Yaniv Inbar */ public class AbstractMemoryDataStore extends AbstractDataStore { @@ -182,8 +180,7 @@ public int size() throws IOException { * {@link #clear()}. */ @SuppressWarnings("unused") - public void save() throws IOException { - } + public void save() throws IOException {} @Override public String toString() { diff --git a/google-http-client/src/main/java/com/google/api/client/util/store/DataStore.java b/google-http-client/src/main/java/com/google/api/client/util/store/DataStore.java index 353bb0f43..3bd9cc64a 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/store/DataStore.java +++ b/google-http-client/src/main/java/com/google/api/client/util/store/DataStore.java @@ -14,7 +14,6 @@ package com.google.api.client.util.store; - import java.io.IOException; import java.io.Serializable; import java.util.Collection; @@ -24,12 +23,9 @@ * Stores and manages serializable data of a specific type, where the key is a string and the value * is a {@link Serializable} object. * - *

    - * {@code null} keys or values are not allowed. Implementation should be thread-safe. - *

    + *

    {@code null} keys or values are not allowed. Implementation should be thread-safe. * * @param serializable type of the mapped value - * * @author Yaniv Inbar * @since 1.16 */ @@ -56,9 +52,7 @@ public interface DataStore { /** * Returns the unmodifiable set of all stored keys. * - *

    - * Order of the keys is not specified. - *

    + *

    Order of the keys is not specified. */ Set keySet() throws IOException; diff --git a/google-http-client/src/main/java/com/google/api/client/util/store/DataStoreFactory.java b/google-http-client/src/main/java/com/google/api/client/util/store/DataStoreFactory.java index 502a4c213..5d62b258c 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/store/DataStoreFactory.java +++ b/google-http-client/src/main/java/com/google/api/client/util/store/DataStoreFactory.java @@ -14,7 +14,6 @@ package com.google.api.client.util.store; - import java.io.IOException; import java.io.Serializable; @@ -22,19 +21,15 @@ * Factory for a store that manages serializable data, where the key is a string and the value is a * {@link Serializable} object. * - *

    - * Users should keep a single globally shared instance of the data store factory. Otherwise, some + *

    Users should keep a single globally shared instance of the data store factory. Otherwise, some * implementations may not share the internal copies of the data, and you'll end up with multiple * data stores by the same IDs, each living in a separate implementation. Some implementations may * also have some overhead, or may have caching implemented, and so multiple instances may defeat * that. Finally, have multiple instances may defeat the thread-safety guarantee for some * implementations. - *

    * - *

    - * Implementation should store the data in a persistent storage such as a database. Implementation - * should be thread-safe. Read the JavaDoc of the implementation for those details. - *

    + *

    Implementation should store the data in a persistent storage such as a database. + * Implementation should be thread-safe. Read the JavaDoc of the implementation for those details. * * @see MemoryDataStoreFactory * @see FileDataStoreFactory @@ -46,16 +41,13 @@ public interface DataStoreFactory { /** * Returns a type-specific data store based on the given unique ID. * - *

    - * If a data store by that ID does not already exist, it should be created now, stored for later - * access, and returned. Otherwise, if there is already a data store by that ID, it should be - * returned. The {@link DataStore#getId()} must match the {@code id} parameter from this method. - *

    + *

    If a data store by that ID does not already exist, it should be created now, stored for + * later access, and returned. Otherwise, if there is already a data store by that ID, it should + * be returned. The {@link DataStore#getId()} must match the {@code id} parameter from this + * method. * - *

    - * The ID must be at least 1 and at most 30 characters long, and must contain only alphanumeric or - * underscore characters. - *

    + *

    The ID must be at least 1 and at most 30 characters long, and must contain only alphanumeric + * or underscore characters. * * @param id unique ID to refer to typed data store * @param serializable type of the mapped value diff --git a/google-http-client/src/main/java/com/google/api/client/util/store/DataStoreUtils.java b/google-http-client/src/main/java/com/google/api/client/util/store/DataStoreUtils.java index 9e5405c3d..23782cc96 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/store/DataStoreUtils.java +++ b/google-http-client/src/main/java/com/google/api/client/util/store/DataStoreUtils.java @@ -14,7 +14,6 @@ package com.google.api.client.util.store; - import java.io.IOException; /** @@ -26,13 +25,11 @@ public final class DataStoreUtils { /** - * Returns a debug string for the given data store to be used as an implementation of - * {@link Object#toString()}. + * Returns a debug string for the given data store to be used as an implementation of {@link + * Object#toString()}. * - *

    - * Implementation iterates over {@link DataStore#keySet()}, calling {@link DataStore#get(String)} - * on each key. - *

    + *

    Implementation iterates over {@link DataStore#keySet()}, calling {@link + * DataStore#get(String)} on each key. * * @param dataStore data store * @return debug string @@ -56,6 +53,5 @@ public static String toString(DataStore dataStore) { } } - private DataStoreUtils() { - } + private DataStoreUtils() {} } diff --git a/google-http-client/src/main/java/com/google/api/client/util/store/FileDataStoreFactory.java b/google-http-client/src/main/java/com/google/api/client/util/store/FileDataStoreFactory.java index 229ac95e3..16b33b093 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/store/FileDataStoreFactory.java +++ b/google-http-client/src/main/java/com/google/api/client/util/store/FileDataStoreFactory.java @@ -16,42 +16,63 @@ import com.google.api.client.util.IOUtils; import com.google.api.client.util.Maps; -import com.google.api.client.util.Throwables; - +import com.google.common.base.StandardSystemProperty; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.Serializable; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.AclEntry; +import java.nio.file.attribute.AclEntryPermission; +import java.nio.file.attribute.AclEntryType; +import java.nio.file.attribute.AclFileAttributeView; +import java.nio.file.attribute.FileOwnerAttributeView; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.UserPrincipal; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; import java.util.logging.Logger; /** * Thread-safe file implementation of a credential store. * - *

    - * For security purposes, the file's permissions are set to be accessible only by the file's owner. - * Note that Java 1.5 does not support manipulating file permissions, and must be done manually or - * using the JNI. - *

    + *

    For security purposes, the file's permissions are set such that the file is only accessible by + * the file's owner. + * + *

    Note: this class is not compatible with Android lower than API level 26 (Oreo). For an + * implementation compatible with Android < 26, please use + * com.google.api.client.extensions.android.util.store.FileDataStoreFactory which is provided by + * com.google.http-client:google-http-client-android. * * @since 1.16 * @author Yaniv Inbar */ public class FileDataStoreFactory extends AbstractDataStoreFactory { - private static final Logger LOGGER = Logger.getLogger(FileDataStoreFactory.class.getName()); + private static final boolean IS_WINDOWS; + + static { + try { + IS_WINDOWS = + StandardSystemProperty.OS_NAME.value().toLowerCase(Locale.ENGLISH).startsWith("windows"); + } catch (Throwable ex) { + Logger.getLogger(FileDataStoreFactory.class.getName()).severe(ex.getMessage()); + throw ex; + } + } /** Directory to store data. */ private final File dataDirectory; - /** - * @param dataDirectory data directory - */ + /** @param dataDirectory data directory */ public FileDataStoreFactory(File dataDirectory) throws IOException { dataDirectory = dataDirectory.getCanonicalFile(); - this.dataDirectory = dataDirectory; // error if it is a symbolic link if (IOUtils.isSymbolicLink(dataDirectory)) { throw new IOException("unable to use a symbolic link: " + dataDirectory); @@ -60,7 +81,13 @@ public FileDataStoreFactory(File dataDirectory) throws IOException { if (!dataDirectory.exists() && !dataDirectory.mkdirs()) { throw new IOException("unable to create directory: " + dataDirectory); } - setPermissionsToOwnerOnly(dataDirectory); + this.dataDirectory = dataDirectory; + + if (IS_WINDOWS) { + setPermissionsToOwnerOnlyWindows(dataDirectory); + } else { + setPermissionsToOwnerOnly(dataDirectory); + } } /** Returns the data directory. */ @@ -119,39 +146,59 @@ public FileDataStoreFactory getDataStoreFactory() { * executed by the file's owner. * * @param file the file's permissions to modify - * @throws IOException + * @throws IOException if the permissions can't be set */ - static void setPermissionsToOwnerOnly(File file) throws IOException { - // Disable access by other users if O/S allows it and set file permissions to readable and - // writable by user. Use reflection since JDK 1.5 will not have these methods + private static void setPermissionsToOwnerOnly(File file) throws IOException { + Set permissions = new HashSet(); + permissions.add(PosixFilePermission.OWNER_READ); + permissions.add(PosixFilePermission.OWNER_WRITE); + permissions.add(PosixFilePermission.OWNER_EXECUTE); try { - Method setReadable = File.class.getMethod("setReadable", boolean.class, boolean.class); - Method setWritable = File.class.getMethod("setWritable", boolean.class, boolean.class); - Method setExecutable = File.class.getMethod("setExecutable", boolean.class, boolean.class); - if (!(Boolean) setReadable.invoke(file, false, false) - || !(Boolean) setWritable.invoke(file, false, false) - || !(Boolean) setExecutable.invoke(file, false, false)) { - LOGGER.warning("unable to change permissions for everybody: " + file); - } - if (!(Boolean) setReadable.invoke(file, true, true) - || !(Boolean) setWritable.invoke(file, true, true) - || !(Boolean) setExecutable.invoke(file, true, true)) { - LOGGER.warning("unable to change permissions for owner: " + file); - } - } catch (InvocationTargetException exception) { - Throwable cause = exception.getCause(); - Throwables.propagateIfPossible(cause, IOException.class); - // shouldn't reach this point, but just in case... - throw new RuntimeException(cause); - } catch (NoSuchMethodException exception) { - LOGGER.warning("Unable to set permissions for " + file - + ", likely because you are running a version of Java prior to 1.6"); - } catch (SecurityException exception) { - // ignored - } catch (IllegalAccessException exception) { - // ignored - } catch (IllegalArgumentException exception) { - // ignored + Files.setPosixFilePermissions(Paths.get(file.getAbsolutePath()), permissions); + } catch (RuntimeException exception) { + throw new IOException("Unable to set permissions for " + file, exception); + } + } + + private static void setPermissionsToOwnerOnlyWindows(File file) throws IOException { + Path path = Paths.get(file.getAbsolutePath()); + FileOwnerAttributeView fileAttributeView = + Files.getFileAttributeView(path, FileOwnerAttributeView.class); + UserPrincipal owner = fileAttributeView.getOwner(); + + // get view + AclFileAttributeView view = Files.getFileAttributeView(path, AclFileAttributeView.class); + + // All available entries + Set permissions = + ImmutableSet.of( + AclEntryPermission.APPEND_DATA, + AclEntryPermission.DELETE, + AclEntryPermission.DELETE_CHILD, + AclEntryPermission.READ_ACL, + AclEntryPermission.READ_ATTRIBUTES, + AclEntryPermission.READ_DATA, + AclEntryPermission.READ_NAMED_ATTRS, + AclEntryPermission.SYNCHRONIZE, + AclEntryPermission.WRITE_ACL, + AclEntryPermission.WRITE_ATTRIBUTES, + AclEntryPermission.WRITE_DATA, + AclEntryPermission.WRITE_NAMED_ATTRS, + AclEntryPermission.WRITE_OWNER); + + // create ACL to give owner everything + AclEntry entry = + AclEntry.newBuilder() + .setType(AclEntryType.ALLOW) + .setPrincipal(owner) + .setPermissions(permissions) + .build(); + + // Overwrite the ACL with only this permission + try { + view.setAcl(ImmutableList.of(entry)); + } catch (SecurityException ex) { + throw new IOException("Unable to set permissions for " + file, ex); } } } diff --git a/google-http-client/src/main/java/com/google/api/client/util/store/MemoryDataStoreFactory.java b/google-http-client/src/main/java/com/google/api/client/util/store/MemoryDataStoreFactory.java index f51cf2469..5b2fe9cad 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/store/MemoryDataStoreFactory.java +++ b/google-http-client/src/main/java/com/google/api/client/util/store/MemoryDataStoreFactory.java @@ -14,17 +14,13 @@ package com.google.api.client.util.store; - - import java.io.IOException; import java.io.Serializable; /** * Thread-safe in-memory implementation of a data store factory. * - *

    - * For convenience, a default global instance is provided in {@link #getDefaultInstance()}. - *

    + *

    For convenience, a default global instance is provided in {@link #getDefaultInstance()}. * * @since 1.16 * @author Yaniv Inbar diff --git a/google-http-client/src/main/java/com/google/api/client/util/store/package-info.java b/google-http-client/src/main/java/com/google/api/client/util/store/package-info.java index 2e9d65182..e05e7e478 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/store/package-info.java +++ b/google-http-client/src/main/java/com/google/api/client/util/store/package-info.java @@ -19,4 +19,3 @@ * @author Yaniv Inbar */ package com.google.api.client.util.store; - diff --git a/google-http-client/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client/native-image.properties b/google-http-client/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client/native-image.properties new file mode 100644 index 000000000..a90ef2471 --- /dev/null +++ b/google-http-client/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client/native-image.properties @@ -0,0 +1 @@ +Args=--enable-url-protocols=http,https \ No newline at end of file diff --git a/google-http-client/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client/reflect-config.json b/google-http-client/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client/reflect-config.json new file mode 100644 index 000000000..69783d263 --- /dev/null +++ b/google-http-client/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client/reflect-config.json @@ -0,0 +1,114 @@ +[ + { + "name": "com.google.api.client.http.HttpHeaders", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true, + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "name": "com.google.api.client.testing.http.MockLowLevelHttpRequest", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true, + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "name": "com.google.api.client.http.HttpRequest", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true, + "allDeclaredClasses" : true, + "allPublicClasses": true + }, + { + "name": "io.opencensus.trace.Tracing", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true, + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "name": "com.google.api.client.http.OpenCensusUtils", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true, + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "name":"io.opencensus.impl.trace.TraceComponentImpl", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "name":"io.opencensus.impl.metrics.MetricsComponentImpl", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "name": "java.util.LinkedList", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "name": "java.util.ArrayList", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + {"name": "java.util.HashMap", + "methods": [ + { "name": "", "parameterTypes": [] }, + { "name": "", "parameterTypes": ["int", "float"] }, + { "name": "", "parameterTypes": ["int"] }, + { "name": "", "parameterTypes": ["java.util.Map"] }, + { "name" : "writeObject", "parameterTypes" : ["java.io.ObjectOutputStream"] } + + ] + } +] \ No newline at end of file diff --git a/google-http-client/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client/resource-config.json b/google-http-client/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client/resource-config.json new file mode 100644 index 000000000..482eb4389 --- /dev/null +++ b/google-http-client/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client/resource-config.json @@ -0,0 +1,5 @@ +{ + "resources":[ + {"pattern":"\\Qcom/google/api/client/http/google-http-client.properties\\E"}], + "bundles":[] +} \ No newline at end of file diff --git a/google-http-client/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client/serialization-config.json b/google-http-client/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client/serialization-config.json new file mode 100644 index 000000000..6b75b6c19 --- /dev/null +++ b/google-http-client/src/main/resources/META-INF/native-image/com.google.http-client/google-http-client/serialization-config.json @@ -0,0 +1,16 @@ +{ + "types":[ + {"name": "com.google.api.client.http.HttpResponseException"}, + {"name": "java.lang.StackTraceElement"}, + {"name":"java.lang.String"}, + {"name": "java.lang.Throwable"}, + {"name": "java.lang.Exception"}, + {"name": "java.io.IOException"}, + {"name": "java.lang.Object"}, + {"name": "java.util.Collections$EmptyList"} + ], + "lambdaCapturingTypes":[ + ], + "proxies":[ + ] +} \ No newline at end of file diff --git a/google-http-client/src/main/resources/com/google/api/client/http/google-http-client.properties b/google-http-client/src/main/resources/com/google/api/client/http/google-http-client.properties new file mode 100644 index 000000000..a69f45fa8 --- /dev/null +++ b/google-http-client/src/main/resources/com/google/api/client/http/google-http-client.properties @@ -0,0 +1 @@ +google-http-client.version=${project.version} \ No newline at end of file diff --git a/google-http-client/src/test/java/com/google/api/client/http/AbstractHttpContentTest.java b/google-http-client/src/test/java/com/google/api/client/http/AbstractHttpContentTest.java index 78269ae7b..0d916512e 100644 --- a/google-http-client/src/test/java/com/google/api/client/http/AbstractHttpContentTest.java +++ b/google-http-client/src/test/java/com/google/api/client/http/AbstractHttpContentTest.java @@ -14,17 +14,23 @@ package com.google.api.client.http; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import java.io.IOException; import java.io.OutputStream; import java.util.Arrays; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link AbstractHttpContent}. * * @author Yaniv Inbar */ -public class AbstractHttpContentTest extends TestCase { +@RunWith(JUnit4.class) +public class AbstractHttpContentTest { static class TestHttpContent extends AbstractHttpContent { @@ -52,15 +58,15 @@ public void writeTo(OutputStream out) throws IOException { public boolean retrySupported() { return retrySupported; } - - } + @Test public void testRetrySupported() { AbstractHttpContent content = new TestHttpContent(true, 0); assertTrue(content.retrySupported()); } + @Test public void testComputeLength() throws Exception { subtestComputeLength(true, 0, 0); subtestComputeLength(true, 1, 1); diff --git a/google-http-client/src/test/java/com/google/api/client/http/BasicAuthenticationTest.java b/google-http-client/src/test/java/com/google/api/client/http/BasicAuthenticationTest.java index 5b3d62b71..be7e3dd83 100644 --- a/google-http-client/src/test/java/com/google/api/client/http/BasicAuthenticationTest.java +++ b/google-http-client/src/test/java/com/google/api/client/http/BasicAuthenticationTest.java @@ -14,17 +14,21 @@ package com.google.api.client.http; +import static org.junit.Assert.assertEquals; + import com.google.api.client.testing.http.HttpTesting; import com.google.api.client.testing.http.MockHttpTransport; - -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link BasicAuthentication}. * * @author Yaniv Inbar */ -public class BasicAuthenticationTest extends TestCase { +@RunWith(JUnit4.class) +public class BasicAuthenticationTest { static final String USERNAME = "Aladdin"; @@ -32,17 +36,20 @@ public class BasicAuthenticationTest extends TestCase { static final String AUTH_HEADER = "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="; + @Test public void testConstructor() { BasicAuthentication auth = new BasicAuthentication(USERNAME, PASSWORD); assertEquals(USERNAME, auth.getUsername()); assertEquals(PASSWORD, auth.getPassword()); } + @Test public void testInitialize() throws Exception { BasicAuthentication auth = new BasicAuthentication(USERNAME, PASSWORD); HttpRequest request = - new MockHttpTransport().createRequestFactory().buildGetRequest( - HttpTesting.SIMPLE_GENERIC_URL); + new MockHttpTransport() + .createRequestFactory() + .buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL); auth.intercept(request); assertEquals(AUTH_HEADER, request.getHeaders().getAuthorization()); } diff --git a/google-http-client/src/test/java/com/google/api/client/http/ByteArrayContentTest.java b/google-http-client/src/test/java/com/google/api/client/http/ByteArrayContentTest.java index da73c5437..66f28c53d 100644 --- a/google-http-client/src/test/java/com/google/api/client/http/ByteArrayContentTest.java +++ b/google-http-client/src/test/java/com/google/api/client/http/ByteArrayContentTest.java @@ -14,20 +14,28 @@ package com.google.api.client.http; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import com.google.api.client.util.IOUtils; import com.google.api.client.util.StringUtils; import java.io.ByteArrayOutputStream; import java.io.IOException; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link ByteArrayContent}. * * @author Yaniv Inbar */ -public class ByteArrayContentTest extends TestCase { +@RunWith(JUnit4.class) +public class ByteArrayContentTest { private static final byte[] FOO = StringUtils.getBytesUtf8("foo"); + @Test public void testConstructor() throws IOException { subtestConstructor(new ByteArrayContent("type", FOO), "foo"); subtestConstructor(new ByteArrayContent("type", FOO, 0, 3), "foo"); diff --git a/google-http-client/src/test/java/com/google/api/client/http/ConsumingInputStreamTest.java b/google-http-client/src/test/java/com/google/api/client/http/ConsumingInputStreamTest.java new file mode 100644 index 000000000..d86a1d811 --- /dev/null +++ b/google-http-client/src/test/java/com/google/api/client/http/ConsumingInputStreamTest.java @@ -0,0 +1,68 @@ +/* + * 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 + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.api.client.http; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class ConsumingInputStreamTest { + + @Test + public void testClose_drainsBytesOnClose() throws IOException { + MockInputStream mockInputStream = + new MockInputStream("abc123".getBytes(StandardCharsets.UTF_8)); + InputStream consumingInputStream = new ConsumingInputStream(mockInputStream); + + assertEquals(6, mockInputStream.getBytesToRead()); + + // read one byte + consumingInputStream.read(); + assertEquals(5, mockInputStream.getBytesToRead()); + + // closing the stream should read the remaining bytes + consumingInputStream.close(); + assertEquals(0, mockInputStream.getBytesToRead()); + } + + private class MockInputStream extends InputStream { + private int bytesToRead; + + MockInputStream(byte[] data) { + this.bytesToRead = data.length; + } + + @Override + public int read() throws IOException { + if (bytesToRead == 0) { + return -1; + } + bytesToRead--; + return 1; + } + + int getBytesToRead() { + return bytesToRead; + } + } +} diff --git a/google-http-client/src/test/java/com/google/api/client/http/EmptyContentTest.java b/google-http-client/src/test/java/com/google/api/client/http/EmptyContentTest.java index 501e9d70e..06e942a38 100644 --- a/google-http-client/src/test/java/com/google/api/client/http/EmptyContentTest.java +++ b/google-http-client/src/test/java/com/google/api/client/http/EmptyContentTest.java @@ -14,18 +14,25 @@ package com.google.api.client.http; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import java.io.ByteArrayOutputStream; import java.io.IOException; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link EmptyContent}. * * @author Yaniv Inbar */ -public class EmptyContentTest extends TestCase { +@RunWith(JUnit4.class) +public class EmptyContentTest { + @Test public void test() throws IOException { EmptyContent content = new EmptyContent(); assertEquals(0L, content.getLength()); diff --git a/google-http-client/src/test/java/com/google/api/client/http/ExponentialBackOffPolicyTest.java b/google-http-client/src/test/java/com/google/api/client/http/ExponentialBackOffPolicyTest.java index fc9dc9bd1..159305d43 100644 --- a/google-http-client/src/test/java/com/google/api/client/http/ExponentialBackOffPolicyTest.java +++ b/google-http-client/src/test/java/com/google/api/client/http/ExponentialBackOffPolicyTest.java @@ -14,9 +14,13 @@ package com.google.api.client.http; -import com.google.api.client.util.NanoClock; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; -import junit.framework.TestCase; +import com.google.api.client.util.NanoClock; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link ExponentialBackOffPolicy}. @@ -24,39 +28,48 @@ * @author Ravi Mistry */ @Deprecated -public class ExponentialBackOffPolicyTest extends TestCase { - - public ExponentialBackOffPolicyTest(String name) { - super(name); - } +@RunWith(JUnit4.class) +public class ExponentialBackOffPolicyTest { + @Test public void testConstructor() { ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy(); - assertEquals(ExponentialBackOffPolicy.DEFAULT_INITIAL_INTERVAL_MILLIS, + assertEquals( + ExponentialBackOffPolicy.DEFAULT_INITIAL_INTERVAL_MILLIS, backOffPolicy.getInitialIntervalMillis()); - assertEquals(ExponentialBackOffPolicy.DEFAULT_INITIAL_INTERVAL_MILLIS, + assertEquals( + ExponentialBackOffPolicy.DEFAULT_INITIAL_INTERVAL_MILLIS, backOffPolicy.getCurrentIntervalMillis()); - assertEquals(ExponentialBackOffPolicy.DEFAULT_RANDOMIZATION_FACTOR, - backOffPolicy.getRandomizationFactor()); - assertEquals(ExponentialBackOffPolicy.DEFAULT_MULTIPLIER, backOffPolicy.getMultiplier()); + assertEquals( + ExponentialBackOffPolicy.DEFAULT_RANDOMIZATION_FACTOR, + backOffPolicy.getRandomizationFactor(), + 0); + assertEquals(ExponentialBackOffPolicy.DEFAULT_MULTIPLIER, backOffPolicy.getMultiplier(), 0); assertEquals( ExponentialBackOffPolicy.DEFAULT_MAX_INTERVAL_MILLIS, backOffPolicy.getMaxIntervalMillis()); - assertEquals(ExponentialBackOffPolicy.DEFAULT_MAX_ELAPSED_TIME_MILLIS, + assertEquals( + ExponentialBackOffPolicy.DEFAULT_MAX_ELAPSED_TIME_MILLIS, backOffPolicy.getMaxElapsedTimeMillis()); } + @Test public void testBuilder() { ExponentialBackOffPolicy backOffPolicy = ExponentialBackOffPolicy.builder().build(); - assertEquals(ExponentialBackOffPolicy.DEFAULT_INITIAL_INTERVAL_MILLIS, + assertEquals( + ExponentialBackOffPolicy.DEFAULT_INITIAL_INTERVAL_MILLIS, backOffPolicy.getInitialIntervalMillis()); - assertEquals(ExponentialBackOffPolicy.DEFAULT_INITIAL_INTERVAL_MILLIS, + assertEquals( + ExponentialBackOffPolicy.DEFAULT_INITIAL_INTERVAL_MILLIS, backOffPolicy.getCurrentIntervalMillis()); - assertEquals(ExponentialBackOffPolicy.DEFAULT_RANDOMIZATION_FACTOR, - backOffPolicy.getRandomizationFactor()); - assertEquals(ExponentialBackOffPolicy.DEFAULT_MULTIPLIER, backOffPolicy.getMultiplier()); + assertEquals( + ExponentialBackOffPolicy.DEFAULT_RANDOMIZATION_FACTOR, + backOffPolicy.getRandomizationFactor(), + 0); + assertEquals(ExponentialBackOffPolicy.DEFAULT_MULTIPLIER, backOffPolicy.getMultiplier(), 0); assertEquals( ExponentialBackOffPolicy.DEFAULT_MAX_INTERVAL_MILLIS, backOffPolicy.getMaxIntervalMillis()); - assertEquals(ExponentialBackOffPolicy.DEFAULT_MAX_ELAPSED_TIME_MILLIS, + assertEquals( + ExponentialBackOffPolicy.DEFAULT_MAX_ELAPSED_TIME_MILLIS, backOffPolicy.getMaxElapsedTimeMillis()); int testInitialInterval = 1; @@ -65,21 +78,23 @@ public void testBuilder() { int testMaxInterval = 10; int testMaxElapsedTime = 900000; - backOffPolicy = ExponentialBackOffPolicy.builder() - .setInitialIntervalMillis(testInitialInterval) - .setRandomizationFactor(testRandomizationFactor) - .setMultiplier(testMultiplier) - .setMaxIntervalMillis(testMaxInterval) - .setMaxElapsedTimeMillis(testMaxElapsedTime) - .build(); + backOffPolicy = + ExponentialBackOffPolicy.builder() + .setInitialIntervalMillis(testInitialInterval) + .setRandomizationFactor(testRandomizationFactor) + .setMultiplier(testMultiplier) + .setMaxIntervalMillis(testMaxInterval) + .setMaxElapsedTimeMillis(testMaxElapsedTime) + .build(); assertEquals(testInitialInterval, backOffPolicy.getInitialIntervalMillis()); assertEquals(testInitialInterval, backOffPolicy.getCurrentIntervalMillis()); - assertEquals(testRandomizationFactor, backOffPolicy.getRandomizationFactor()); - assertEquals(testMultiplier, backOffPolicy.getMultiplier()); + assertEquals(testRandomizationFactor, backOffPolicy.getRandomizationFactor(), 0); + assertEquals(testMultiplier, backOffPolicy.getMultiplier(), 0); assertEquals(testMaxInterval, backOffPolicy.getMaxIntervalMillis()); assertEquals(testMaxElapsedTime, backOffPolicy.getMaxElapsedTimeMillis()); } + @Test public void testBackOff() throws Exception { int testInitialInterval = 500; double testRandomizationFactor = 0.1; @@ -87,13 +102,14 @@ public void testBackOff() throws Exception { int testMaxInterval = 5000; int testMaxElapsedTime = 900000; - ExponentialBackOffPolicy backOffPolicy = ExponentialBackOffPolicy.builder() - .setInitialIntervalMillis(testInitialInterval) - .setRandomizationFactor(testRandomizationFactor) - .setMultiplier(testMultiplier) - .setMaxIntervalMillis(testMaxInterval) - .setMaxElapsedTimeMillis(testMaxElapsedTime) - .build(); + ExponentialBackOffPolicy backOffPolicy = + ExponentialBackOffPolicy.builder() + .setInitialIntervalMillis(testInitialInterval) + .setRandomizationFactor(testRandomizationFactor) + .setMultiplier(testMultiplier) + .setMaxIntervalMillis(testMaxInterval) + .setMaxElapsedTimeMillis(testMaxElapsedTime) + .build(); int[] expectedResults = {500, 1000, 2000, 4000, 5000, 5000, 5000, 5000, 5000, 5000}; for (int expected : expectedResults) { assertEquals(expected, backOffPolicy.getCurrentIntervalMillis()); @@ -110,8 +126,7 @@ static class MyNanoClock implements NanoClock { private int i = 0; private long startSeconds; - MyNanoClock() { - } + MyNanoClock() {} MyNanoClock(long startSeconds) { this.startSeconds = startSeconds; @@ -122,6 +137,7 @@ public long nanoTime() { } } + @Test public void testGetElapsedTimeMillis() { ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy.Builder().setNanoClock(new MyNanoClock()).build(); @@ -129,15 +145,17 @@ public void testGetElapsedTimeMillis() { assertEquals("elapsedTimeMillis=" + elapsedTimeMillis, 1000, elapsedTimeMillis); } + @Test public void testBackOffOverflow() throws Exception { int testInitialInterval = Integer.MAX_VALUE / 2; double testMultiplier = 2.1; int testMaxInterval = Integer.MAX_VALUE; - ExponentialBackOffPolicy backOffPolicy = ExponentialBackOffPolicy.builder() - .setInitialIntervalMillis(testInitialInterval) - .setMultiplier(testMultiplier) - .setMaxIntervalMillis(testMaxInterval) - .build(); + ExponentialBackOffPolicy backOffPolicy = + ExponentialBackOffPolicy.builder() + .setInitialIntervalMillis(testInitialInterval) + .setMultiplier(testMultiplier) + .setMaxIntervalMillis(testMaxInterval) + .build(); backOffPolicy.getNextBackOffMillis(); // Assert that when an overflow is possible the current interval is set to the max interval. assertEquals(testMaxInterval, backOffPolicy.getCurrentIntervalMillis()); diff --git a/google-http-client/src/test/java/com/google/api/client/http/GZipEncodingTest.java b/google-http-client/src/test/java/com/google/api/client/http/GZipEncodingTest.java index 8746a5c66..e73edc311 100644 --- a/google-http-client/src/test/java/com/google/api/client/http/GZipEncodingTest.java +++ b/google-http-client/src/test/java/com/google/api/client/http/GZipEncodingTest.java @@ -14,31 +14,53 @@ package com.google.api.client.http; +import static org.junit.Assert.assertFalse; import com.google.api.client.testing.util.TestableByteArrayOutputStream; import com.google.api.client.util.ByteArrayStreamingContent; import com.google.api.client.util.StringUtils; import java.io.IOException; -import junit.framework.TestCase; import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link GZipEncoding}. * * @author Yaniv Inbar */ -public class GZipEncodingTest extends TestCase { +@RunWith(JUnit4.class) +public class GZipEncodingTest { - byte[] EXPECED_ZIPPED = new byte[] {31, -117, 8, 0, 0, 0, 0, 0, 0, 0, -53, -49, -57, 13, 0, -30, - -66, -14, 54, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + private static final byte[] EXPECED_ZIPPED = + new byte[] { + 31, -117, 8, 0, 0, 0, 0, 0, 0, -1, -53, -49, -57, 13, 0, -30, -66, -14, 54, 28, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }; + // TODO: remove when no longer using Java < 16: https://bugs.openjdk.java.net/browse/JDK-8244706 + @Deprecated + private static final byte[] EXPECED_ZIPPED_BELOW_JAVA_16 = + new byte[] { + 31, -117, 8, 0, 0, 0, 0, 0, 0, 0, -53, -49, -57, 13, 0, -30, -66, -14, 54, 28, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }; + + @Test public void test() throws IOException { + // TODO: remove when no longer using Java < 16. + byte[] expected = + System.getProperty("java.version").compareTo("16") >= 0 + ? EXPECED_ZIPPED + : EXPECED_ZIPPED_BELOW_JAVA_16; + GZipEncoding encoding = new GZipEncoding(); ByteArrayStreamingContent content = new ByteArrayStreamingContent(StringUtils.getBytesUtf8("oooooooooooooooooooooooooooo")); TestableByteArrayOutputStream out = new TestableByteArrayOutputStream(); encoding.encode(content, out); assertFalse(out.isClosed()); - Assert.assertArrayEquals(EXPECED_ZIPPED, out.getBuffer()); + Assert.assertArrayEquals(expected, out.getBuffer()); } } diff --git a/google-http-client/src/test/java/com/google/api/client/http/GenericUrlTest.java b/google-http-client/src/test/java/com/google/api/client/http/GenericUrlTest.java index f4a8b22a2..1e2dfddf1 100644 --- a/google-http-client/src/test/java/com/google/api/client/http/GenericUrlTest.java +++ b/google-http-client/src/test/java/com/google/api/client/http/GenericUrlTest.java @@ -14,6 +14,11 @@ package com.google.api.client.http; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + import com.google.api.client.util.Key; import java.net.MalformedURLException; import java.net.URI; @@ -24,25 +29,21 @@ import java.util.Collection; import java.util.Iterator; import java.util.List; -import junit.framework.TestCase; -import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link GenericUrl}. * * @author Yaniv Inbar */ -public class GenericUrlTest extends TestCase { - - public GenericUrlTest() { - } - - public GenericUrlTest(String name) { - super(name); - } +@RunWith(JUnit4.class) +public class GenericUrlTest { private static final String MINIMAL = "http://bar"; + @Test public void testBuild_minimal() { GenericUrl url = new GenericUrl(); url.setScheme("http"); @@ -50,6 +51,7 @@ public void testBuild_minimal() { assertEquals(MINIMAL, url.build()); } + @Test public void testParse_minimal() { GenericUrl url = new GenericUrl(MINIMAL); assertEquals("http", url.getScheme()); @@ -57,6 +59,7 @@ public void testParse_minimal() { private static final String NO_PATH = "http://bar?a=b"; + @Test public void testBuild_noPath() { GenericUrl url = new GenericUrl(); url.setScheme("http"); @@ -65,11 +68,13 @@ public void testBuild_noPath() { assertEquals(NO_PATH, url.build()); } + @Test public void testBuild_noUserInfo() { GenericUrl url = new GenericUrl(NO_PATH); assertNull(url.getUserInfo()); } + @Test public void testBuild_noScheme() { GenericUrl url = new GenericUrl(); try { @@ -80,6 +85,7 @@ public void testBuild_noScheme() { } } + @Test public void testBuild_noHost() { GenericUrl url = new GenericUrl(); try { @@ -91,6 +97,7 @@ public void testBuild_noHost() { } } + @Test public void testParse_noPath() { GenericUrl url = new GenericUrl(NO_PATH); assertEquals("http", url.getScheme()); @@ -103,6 +110,7 @@ public void testParse_noPath() { private static final List SHORT_PATH_PARTS = Arrays.asList("", "path"); + @Test public void testBuild_shortPath() { GenericUrl url = new GenericUrl(); url.setScheme("http"); @@ -112,6 +120,7 @@ public void testBuild_shortPath() { assertEquals(SHORT_PATH, url.build()); } + @Test public void testParse_shortPath() { GenericUrl url = new GenericUrl(SHORT_PATH); assertEquals("http", url.getScheme()); @@ -124,6 +133,7 @@ public void testParse_shortPath() { private static final List LONG_PATH_PARTS = Arrays.asList("", "path", "to", "resource"); + @Test public void testBuild_longPath() { GenericUrl url = new GenericUrl(); url.setScheme("http"); @@ -133,6 +143,7 @@ public void testBuild_longPath() { assertEquals(LONG_PATH, url.build()); } + @Test public void testParse_longPath() { GenericUrl url = new GenericUrl(LONG_PATH); assertEquals("http", url.getScheme()); @@ -142,24 +153,30 @@ public void testParse_longPath() { } public static class TestUrl extends GenericUrl { - @Key - String foo; + @Key String foo; public String hidden; - public TestUrl() { - } + public TestUrl() {} public TestUrl(String encodedUrl) { super(encodedUrl); } + + public TestUrl(String encodedUrl, boolean verbatim) { + super(encodedUrl, verbatim); + } } private static final String FULL = "https://user:%3Cpa&$w%40rd%3E@www.google.com:223/m8/feeds/contacts/" - + "someone=%23%25&%20%3F%3Co%3E%7B%7D@gmail.com/" - + "full?" + "foo=bar&" + "alt=json&" + "max-results=3&" + "prettyprint=true&" - + "q=Go%3D%23/%25%26%20?%3Co%3Egle#%3CD@WNL:ADING%3E"; + + "someone=%23%25&%20%3F%3Co%3E%7B%7D@gmail.com/" + + "full?" + + "foo=bar&" + + "alt=json&" + + "max-results=3&" + + "prettyprint=true&" + + "q=Go%3D%23/%25%26%20?%3Co%3Egle#%3CD@WNL:ADING%3E"; private static final List FULL_PARTS = Arrays.asList("", "m8", "feeds", "contacts", "someone=#%& ?{}@gmail.com", "full"); @@ -167,6 +184,7 @@ public TestUrl(String encodedUrl) { private static final String USER_INFO = "user:"; private static final String FRAGMENT = ""; + @Test public void testBuild_full() { TestUrl url = new TestUrl(); url.setScheme("https"); @@ -174,7 +192,9 @@ public void testBuild_full() { url.setPort(223); url.setPathParts(FULL_PARTS); url.set("alt", "json") - .set("max-results", 3).set("prettyprint", true).set("q", "Go=#/%& ?gle"); + .set("max-results", 3) + .set("prettyprint", true) + .set("q", "Go=#/%& ?gle"); url.foo = "bar"; url.hidden = "invisible"; url.setFragment(FRAGMENT); @@ -182,6 +202,7 @@ public void testBuild_full() { assertEquals(FULL, url.build()); } + @Test public void testParse_full() { TestUrl url = new TestUrl(FULL); subtestFull(url); @@ -190,21 +211,32 @@ public void testParse_full() { assertEquals("bar", url.foo); } + @Test + public void testParse_full_verbatim() { + TestUrl url = new TestUrl(FULL, true); + assertNull(url.hidden); + assertEquals("Go%3D%23/%25%26%20?%3Co%3Egle", url.getFirst("q")); + } + + @Test public void testConstructor_url() throws MalformedURLException { GenericUrl url = new GenericUrl(new URL(FULL)); subtestFull(url); } + @Test public void testConstructor_uri() throws URISyntaxException { GenericUrl url = new GenericUrl(new URI(FULL)); subtestFull(url); } + @Test public void testConstructor_string() { GenericUrl url = new GenericUrl(FULL); subtestFull(url); } + @Test public void testConstructor_schemeToLowerCase() throws URISyntaxException, MalformedURLException { GenericUrl url = new GenericUrl("HTTps://www.google.com:223"); assertEquals("https", url.getScheme()); @@ -230,31 +262,23 @@ private void subtestFull(GenericUrl url) { public static class FieldTypesUrl extends GenericUrl { - @Key - Boolean B; + @Key Boolean B; - @Key - Double D; + @Key Double D; - @Key - Integer I; + @Key Integer I; - @Key - boolean b; + @Key boolean b; - @Key - double d; + @Key double d; - @Key - int i; + @Key int i; - @Key - String s; + @Key String s; String hidden; - FieldTypesUrl() { - } + FieldTypesUrl() {} FieldTypesUrl(String encodedUrl) { super(encodedUrl); @@ -269,6 +293,7 @@ public FieldTypesUrl set(String fieldName, Object value) { private static final String FIELD_TYPES = "http://bar?B=true&D=-3.14&I=-3&b=true&d=-3.14&i=-3&s=a&a=b"; + @Test public void testBuild_fieldTypes() { FieldTypesUrl url = new FieldTypesUrl(); url.setScheme("http"); @@ -285,6 +310,7 @@ public void testBuild_fieldTypes() { assertEquals(FIELD_TYPES, url.build()); } + @Test public void testParse_fieldTypes() { FieldTypesUrl url = new FieldTypesUrl(FIELD_TYPES); assertEquals("http", url.getScheme()); @@ -303,6 +329,7 @@ public void testParse_fieldTypes() { private static final String FRAGMENT1 = "http://bar/path/to/resource#fragme=%23/%25&%20?%3Co%3Ent"; + @Test public void testBuild_fragment1() { GenericUrl url = new GenericUrl(); url.setScheme("http"); @@ -312,6 +339,7 @@ public void testBuild_fragment1() { assertEquals(FRAGMENT1, url.build()); } + @Test public void testParse_fragment1() { GenericUrl url = new GenericUrl(FRAGMENT1); assertEquals("http", url.getScheme()); @@ -322,6 +350,7 @@ public void testParse_fragment1() { private static final String FRAGMENT2 = "http://bar/path/to/resource?a=b#fragment"; + @Test public void testBuild_fragment2() { GenericUrl url = new GenericUrl(); url.setScheme("http"); @@ -332,6 +361,7 @@ public void testBuild_fragment2() { assertEquals(FRAGMENT2, url.build()); } + @Test public void testParse_fragment2() { GenericUrl url = new GenericUrl(FRAGMENT2); assertEquals("http", url.getScheme()); @@ -341,6 +371,7 @@ public void testParse_fragment2() { assertEquals("fragment", url.getFragment()); } + @Test public void testBuildAuthority_exception() { // Test without a scheme. GenericUrl url = new GenericUrl(); @@ -348,7 +379,7 @@ public void testBuildAuthority_exception() { try { url.buildAuthority(); - Assert.fail("no exception was thrown"); + fail("no exception was thrown"); } catch (NullPointerException expected) { } @@ -358,11 +389,12 @@ public void testBuildAuthority_exception() { try { url.buildAuthority(); - Assert.fail("no exception was thrown"); + fail("no exception was thrown"); } catch (NullPointerException expected) { } } + @Test public void testBuildAuthority_simple() { GenericUrl url = new GenericUrl(); url.setScheme("http"); @@ -370,6 +402,7 @@ public void testBuildAuthority_simple() { assertEquals("http://example.com", url.buildAuthority()); } + @Test public void testBuildAuthority_withPort() { GenericUrl url = new GenericUrl(); url.setScheme("http"); @@ -378,6 +411,7 @@ public void testBuildAuthority_withPort() { assertEquals("http://example.com:1234", url.buildAuthority()); } + @Test public void testBuildAuthority_withUserInfo() { GenericUrl url = new GenericUrl(); url.setScheme("http"); @@ -386,6 +420,7 @@ public void testBuildAuthority_withUserInfo() { assertEquals("http://first.last:pa%40%40@www.example.com", url.buildAuthority()); } + @Test public void testBuildRelativeUrl_empty() { GenericUrl url = new GenericUrl(); url.setScheme("foo"); @@ -394,6 +429,7 @@ public void testBuildRelativeUrl_empty() { assertEquals("", url.buildRelativeUrl()); } + @Test public void testBuildRelativeUrl_simple() { GenericUrl url = new GenericUrl(); url.setScheme("foo"); @@ -402,6 +438,7 @@ public void testBuildRelativeUrl_simple() { assertEquals("/example", url.buildRelativeUrl()); } + @Test public void testBuildRelativeUrl_simpleQuery() { GenericUrl url = new GenericUrl(); url.setScheme("foo"); @@ -411,6 +448,7 @@ public void testBuildRelativeUrl_simpleQuery() { assertEquals("/example?key=value", url.buildRelativeUrl()); } + @Test public void testBuildRelativeUrl_fragment() { GenericUrl url = new GenericUrl(); url.setScheme("foo"); @@ -420,6 +458,7 @@ public void testBuildRelativeUrl_fragment() { assertEquals("/example#test", url.buildRelativeUrl()); } + @Test public void testBuildRelativeUrl_onlyQuery() { GenericUrl url = new GenericUrl(); url.setScheme("foo"); @@ -432,6 +471,7 @@ public void testBuildRelativeUrl_onlyQuery() { private static final String BASE_URL = "http://google.com"; private static final String FULL_PATH = "/some/path/someone%2Fis%2F@gmail.com/test/?one=1&two=2"; + @Test public void testBuildRelativeUrl_full() { GenericUrl url = new GenericUrl(BASE_URL + FULL_PATH); assertEquals(FULL_PATH, url.buildRelativeUrl()); @@ -443,6 +483,7 @@ public void testBuildRelativeUrl_full() { private static final List PATH_WITH_SLASH_PARTS = Arrays.asList("", "m8", "feeds", "contacts", "someone/is/@gmail.com", "full", ""); + @Test public void testBuild_pathWithSlash() { GenericUrl url = new GenericUrl(); url.setScheme("http"); @@ -451,12 +492,14 @@ public void testBuild_pathWithSlash() { assertEquals(PATH_WITH_SLASH, url.build()); } + @Test public void testConstructorUnderscore() { String url = "http://url_with_underscore.google.com"; GenericUrl parsed = new GenericUrl(url); assertEquals("url_with_underscore.google.com", parsed.getHost()); } + @Test public void testParse_pathWithSlash() { GenericUrl url = new GenericUrl(PATH_WITH_SLASH); assertEquals("http", url.getScheme()); @@ -464,6 +507,7 @@ public void testParse_pathWithSlash() { assertEquals(PATH_WITH_SLASH_PARTS, url.getPathParts()); } + @Test public void testToPathParts() { subtestToPathParts(null, (String[]) null); subtestToPathParts(null, ""); @@ -475,10 +519,12 @@ public void testToPathParts() { subtestToPathParts("/path/to/resource", "", "path", "to", "resource"); subtestToPathParts("/path/to/resource/", "", "path", "to", "resource", ""); subtestToPathParts("/Go%3D%23%2F%25%26%20?%3Co%3Egle/2nd", "", "Go=#/%& ?gle", "2nd"); + subtestToPathParts("/plus+test/resource", "", "plus+test", "resource"); + subtestToPathParts("/plus%2Btest/resource", "", "plus+test", "resource"); } private void subtestToPathParts(String encodedPath, String... expectedDecodedParts) { - List result = GenericUrl.toPathParts(encodedPath); + List result = GenericUrl.toPathParts(encodedPath, false); if (encodedPath == null) { assertNull(result); } else { @@ -486,6 +532,7 @@ private void subtestToPathParts(String encodedPath, String... expectedDecodedPar } } + @Test public void testAppendPath() { GenericUrl url = new GenericUrl("http://google.com"); assertNull(url.getPathParts()); @@ -518,6 +565,7 @@ public void testAppendPath() { private static final String REPEATED_PARAM = PREFIX + REPEATED_PARAM_PATH + "?q=c&q=a&q=b&s=e"; + @Test public void testRepeatedParam_build() { GenericUrl url = new GenericUrl(); url.setScheme("https"); @@ -528,6 +576,7 @@ public void testRepeatedParam_build() { assertEquals(REPEATED_PARAM, url.build()); } + @Test public void testRepeatedParam_parse() { GenericUrl url = new GenericUrl(REPEATED_PARAM); assertEquals("https", url.getScheme()); @@ -545,6 +594,7 @@ public void testRepeatedParam_parse() { assertEquals(Arrays.asList("c", "a", "b"), new ArrayList(url.getAll("q"))); } + @Test public void testBuild_noValue() { GenericUrl url = new GenericUrl(); url.setScheme("https"); @@ -554,12 +604,14 @@ public void testBuild_noValue() { assertEquals(PREFIX + REPEATED_PARAM_PATH + "?noval", url.build()); } + @Test public void testClone() { GenericUrl url = new GenericUrl("http://www.google.com"); GenericUrl clone = url.clone(); assertEquals("http://www.google.com", clone.build()); } + @Test public void testToUrl_relative() { // relative redirect testRedirectUtility("http://www.google.com/test", "http://www.google.com", "/test"); diff --git a/google-http-client/src/test/java/com/google/api/client/http/GzipSupportTest.java b/google-http-client/src/test/java/com/google/api/client/http/GzipSupportTest.java new file mode 100644 index 000000000..ba7d2df32 --- /dev/null +++ b/google-http-client/src/test/java/com/google/api/client/http/GzipSupportTest.java @@ -0,0 +1,104 @@ +/* + * 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. + */ + +package com.google.api.client.http; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.io.ByteStreams; +import com.google.common.io.CountingInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.SequenceInputStream; +import java.util.zip.GZIPInputStream; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public final class GzipSupportTest { + + @SuppressWarnings("UnstableApiUsage") // CountingInputStream is @Beta + @Test + public void gzipInputStreamConsumesAllBytes() throws IOException { + byte[] data = new byte[] {(byte) 'a', (byte) 'b'}; + // `echo -n a > a.txt && gzip -n9 a.txt` + byte[] member0 = + new byte[] { + 0x1f, + (byte) 0x8b, + 0x08, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x02, + 0x03, + 0x4b, + 0x04, + 0x00, + (byte) 0x43, + (byte) 0xbe, + (byte) 0xb7, + (byte) 0xe8, + 0x01, + 0x00, + 0x00, + 0x00 + }; + // `echo -n b > b.txt && gzip -n9 b.txt` + byte[] member1 = + new byte[] { + 0x1f, + (byte) 0x8b, + 0x08, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x02, + 0x03, + 0x4b, + 0x02, + 0x00, + (byte) 0xf9, + (byte) 0xef, + (byte) 0xbe, + (byte) 0x71, + 0x01, + 0x00, + 0x00, + 0x00 + }; + int totalZippedBytes = member0.length + member1.length; + try (InputStream s = + new SequenceInputStream( + new ByteArrayInputStream(member0), new ByteArrayInputStream(member1)); + CountingInputStream countS = new CountingInputStream(s); + GZIPInputStream g = GzipSupport.newGzipInputStream(countS); + CountingInputStream countG = new CountingInputStream(g)) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ByteStreams.copy(countG, baos); + assertThat(baos.toByteArray()).isEqualTo(data); + assertThat(countG.getCount()).isEqualTo(data.length); + assertThat(countS.getCount()).isEqualTo(totalZippedBytes); + } + } +} diff --git a/google-http-client/src/test/java/com/google/api/client/http/HttpBackOffIOExpcetionHandlerTest.java b/google-http-client/src/test/java/com/google/api/client/http/HttpBackOffIOExpcetionHandlerTest.java index db4fcdc54..f2760c447 100644 --- a/google-http-client/src/test/java/com/google/api/client/http/HttpBackOffIOExpcetionHandlerTest.java +++ b/google-http-client/src/test/java/com/google/api/client/http/HttpBackOffIOExpcetionHandlerTest.java @@ -14,19 +14,28 @@ package com.google.api.client.http; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import com.google.api.client.testing.util.MockBackOff; import com.google.api.client.testing.util.MockSleeper; import com.google.api.client.util.BackOff; +import com.google.api.client.util.Sleeper; import java.io.IOException; -import junit.framework.TestCase; +import java.util.concurrent.atomic.AtomicBoolean; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link HttpBackOffIOExceptionHandler}. * * @author Eyal Peled */ -public class HttpBackOffIOExpcetionHandlerTest extends TestCase { +@RunWith(JUnit4.class) +public class HttpBackOffIOExpcetionHandlerTest { + @Test public void testHandle() throws IOException { subsetHandle(0, 0, true, BackOff.STOP_BACKOFF); subsetHandle(0, 0, false, new MockBackOff().setBackOffMillis(0).setMaxTries(5)); @@ -46,4 +55,36 @@ public void subsetHandle(long count, long millis, boolean retrySupported, BackOf } assertEquals(count, sleeper.getCount()); } + + @Test + public void testHandleIOException_returnsFalseAndThreadRemainsInterrupted_whenSleepIsInterrupted() + throws Exception { + final AtomicBoolean stillInterrupted = new AtomicBoolean(false); + Thread runningThread = + new Thread() { + @Override + public void run() { + HttpBackOffIOExceptionHandler testTarget = + new HttpBackOffIOExceptionHandler( + new MockBackOff() + .setBackOffMillis(Long.MAX_VALUE) // Sleep until we interrupt it. + .setMaxTries(1)) + .setSleeper( + Sleeper.DEFAULT); // Needs to be a real sleeper so we can interrupt it. + + try { + testTarget.handleIOException(null, /* retrySupported= */ true); + } catch (Exception ignored) { + } + stillInterrupted.set(Thread.currentThread().isInterrupted()); + } + }; + runningThread.start(); + // Give runningThread some time to start. + Thread.sleep(500L); + runningThread.interrupt(); + runningThread.join(); + + assertTrue(stillInterrupted.get()); + } } diff --git a/google-http-client/src/test/java/com/google/api/client/http/HttpBackOffUnsuccessfulResponseHandlerTest.java b/google-http-client/src/test/java/com/google/api/client/http/HttpBackOffUnsuccessfulResponseHandlerTest.java index a5dd36538..daefab430 100644 --- a/google-http-client/src/test/java/com/google/api/client/http/HttpBackOffUnsuccessfulResponseHandlerTest.java +++ b/google-http-client/src/test/java/com/google/api/client/http/HttpBackOffUnsuccessfulResponseHandlerTest.java @@ -14,32 +14,48 @@ package com.google.api.client.http; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import com.google.api.client.http.HttpBackOffUnsuccessfulResponseHandler.BackOffRequired; import com.google.api.client.testing.util.MockBackOff; import com.google.api.client.testing.util.MockSleeper; import com.google.api.client.util.BackOff; +import com.google.api.client.util.Sleeper; import java.io.IOException; -import junit.framework.TestCase; +import java.util.concurrent.atomic.AtomicBoolean; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Test {@link HttpBackOffUnsuccessfulResponseHandler}. * * @author Eyal Peled */ -public class HttpBackOffUnsuccessfulResponseHandlerTest extends TestCase { +@RunWith(JUnit4.class) +public class HttpBackOffUnsuccessfulResponseHandlerTest { + @Test public void testHandleResponse_retryFalse() throws IOException { subsetHandleResponse(0, 0, false, new MockBackOff(), BackOffRequired.ALWAYS); } + @Test public void testHandleResponse_requiredFalse() throws IOException { - subsetHandleResponse(0, 0, true, new MockBackOff(), new BackOffRequired() { - public boolean isRequired(HttpResponse response) { - return false; - } - }); + subsetHandleResponse( + 0, + 0, + true, + new MockBackOff(), + new BackOffRequired() { + public boolean isRequired(HttpResponse response) { + return false; + } + }); } + @Test public void testHandleResponse_requiredTrue() throws IOException { BackOff backOff = new MockBackOff().setBackOffMillis(4).setMaxTries(7); subsetHandleResponse(7, 4, true, backOff, BackOffRequired.ALWAYS); @@ -52,12 +68,47 @@ private void subsetHandleResponse( throws IOException { // create the handler MockSleeper sleeper = new MockSleeper(); - HttpBackOffUnsuccessfulResponseHandler handler = new HttpBackOffUnsuccessfulResponseHandler( - backOff).setSleeper(sleeper).setBackOffRequired(backOffRequired); + HttpBackOffUnsuccessfulResponseHandler handler = + new HttpBackOffUnsuccessfulResponseHandler(backOff) + .setSleeper(sleeper) + .setBackOffRequired(backOffRequired); while (handler.handleResponse(null, null, retry)) { assertEquals(millis, sleeper.getLastMillis()); } assertEquals(count, sleeper.getCount()); } + + @Test + public void testHandleResponse_returnsFalseAndThreadRemainsInterrupted_whenSleepIsInterrupted() + throws Exception { + final AtomicBoolean stillInterrupted = new AtomicBoolean(false); + Thread runningThread = + new Thread() { + @Override + public void run() { + HttpBackOffUnsuccessfulResponseHandler testTarget = + new HttpBackOffUnsuccessfulResponseHandler( + new MockBackOff() + .setBackOffMillis(Long.MAX_VALUE) // Sleep until we interrupt it. + .setMaxTries(1)) + .setSleeper( + Sleeper.DEFAULT) // Needs to be a real sleeper so we can interrupt it. + .setBackOffRequired(BackOffRequired.ALWAYS); + + try { + testTarget.handleResponse(null, null, /* retrySupported= */ true); + } catch (Exception ignored) { + } + stillInterrupted.set(Thread.currentThread().isInterrupted()); + } + }; + runningThread.start(); + // Give runningThread some time to start. + Thread.sleep(500L); + runningThread.interrupt(); + runningThread.join(); + + assertTrue(stillInterrupted.get()); + } } diff --git a/google-http-client/src/test/java/com/google/api/client/http/HttpEncodingStreamingContentTest.java b/google-http-client/src/test/java/com/google/api/client/http/HttpEncodingStreamingContentTest.java index 42fec080a..96c16a244 100644 --- a/google-http-client/src/test/java/com/google/api/client/http/HttpEncodingStreamingContentTest.java +++ b/google-http-client/src/test/java/com/google/api/client/http/HttpEncodingStreamingContentTest.java @@ -14,24 +14,47 @@ package com.google.api.client.http; +import static org.junit.Assert.assertFalse; + import com.google.api.client.testing.util.TestableByteArrayOutputStream; import com.google.api.client.util.ByteArrayStreamingContent; import com.google.api.client.util.StringUtils; import java.io.IOException; -import junit.framework.TestCase; import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link HttpEncodingStreamingContent}. * * @author Yaniv Inbar */ -public class HttpEncodingStreamingContentTest extends TestCase { +@RunWith(JUnit4.class) +public class HttpEncodingStreamingContentTest { + + private static final byte[] EXPECED_ZIPPED = + new byte[] { + 31, -117, 8, 0, 0, 0, 0, 0, 0, -1, -53, -49, -57, 13, 0, -30, -66, -14, 54, 28, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }; - byte[] EXPECED_ZIPPED = new byte[] {31, -117, 8, 0, 0, 0, 0, 0, 0, 0, -53, -49, -57, 13, 0, -30, - -66, -14, 54, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + // TODO: remove when no longer using Java < 16: https://bugs.openjdk.java.net/browse/JDK-8244706 + @Deprecated + private static final byte[] EXPECED_ZIPPED_BELOW_JAVA_16 = + new byte[] { + 31, -117, 8, 0, 0, 0, 0, 0, 0, 0, -53, -49, -57, 13, 0, -30, -66, -14, 54, 28, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }; + @Test public void test() throws IOException { + // TODO: remove when no longer using Java < 16. + byte[] expected = + System.getProperty("java.version").compareTo("16") >= 0 + ? EXPECED_ZIPPED + : EXPECED_ZIPPED_BELOW_JAVA_16; + GZipEncoding encoding = new GZipEncoding(); ByteArrayStreamingContent content = new ByteArrayStreamingContent(StringUtils.getBytesUtf8("oooooooooooooooooooooooooooo")); @@ -40,6 +63,6 @@ public void test() throws IOException { new HttpEncodingStreamingContent(content, encoding); encodingContent.writeTo(out); assertFalse(out.isClosed()); - Assert.assertArrayEquals(EXPECED_ZIPPED, out.getBuffer()); + Assert.assertArrayEquals(expected, out.getBuffer()); } } diff --git a/google-http-client/src/test/java/com/google/api/client/http/HttpHeadersTest.java b/google-http-client/src/test/java/com/google/api/client/http/HttpHeadersTest.java index 46ba7de72..3b9baf113 100644 --- a/google-http-client/src/test/java/com/google/api/client/http/HttpHeadersTest.java +++ b/google-http-client/src/test/java/com/google/api/client/http/HttpHeadersTest.java @@ -14,6 +14,10 @@ package com.google.api.client.http; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + import com.google.api.client.http.HttpRequestTest.E; import com.google.api.client.testing.http.MockLowLevelHttpRequest; import com.google.api.client.testing.http.MockLowLevelHttpResponse; @@ -25,22 +29,19 @@ import java.io.Writer; import java.util.Arrays; import java.util.List; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link HttpHeaders}. * * @author Yaniv Inbar */ -public class HttpHeadersTest extends TestCase { - - public HttpHeadersTest() { - } - - public HttpHeadersTest(String name) { - super(name); - } +@RunWith(JUnit4.class) +public class HttpHeadersTest { + @Test public void testBasicAuthentication() { HttpHeaders headers = new HttpHeaders(); headers.setBasicAuthentication( @@ -50,31 +51,24 @@ public void testBasicAuthentication() { public static class MyHeaders extends HttpHeaders { - @Key - public String foo; + @Key public String foo; - @Key - Object objNum; + @Key Object objNum; - @Key - Object objList; + @Key Object objList; - @Key - long someLong; + @Key long someLong; - @Key - List list; + @Key List list; - @Key - String[] r; + @Key String[] r; - @Key - E value; + @Key E value; - @Key - E otherValue; + @Key E otherValue; } + @Test public void testSerializeHeaders() throws Exception { // custom headers MyHeaders myHeaders = new MyHeaders(); @@ -86,6 +80,8 @@ public void testSerializeHeaders() throws Exception { myHeaders.setAcceptEncoding(null); myHeaders.setContentLength(Long.MAX_VALUE); myHeaders.setUserAgent("foo"); + myHeaders.addWarning("warn0"); + myHeaders.addWarning("warn1"); myHeaders.set("a", "b"); myHeaders.value = E.VALUE; myHeaders.otherValue = E.OTHER_VALUE; @@ -103,10 +99,12 @@ public void testSerializeHeaders() throws Exception { assertEquals(ImmutableList.of("a1", "a2"), lowLevelRequest.getHeaderValues("r")); assertTrue(lowLevelRequest.getHeaderValues("accept-encoding").isEmpty()); assertEquals(ImmutableList.of("foo"), lowLevelRequest.getHeaderValues("user-agent")); + assertEquals(ImmutableList.of("warn0", "warn1"), lowLevelRequest.getHeaderValues("warning")); assertEquals(ImmutableList.of("b"), lowLevelRequest.getHeaderValues("a")); assertEquals(ImmutableList.of("VALUE"), lowLevelRequest.getHeaderValues("value")); assertEquals(ImmutableList.of("other"), lowLevelRequest.getHeaderValues("othervalue")); - assertEquals(ImmutableList.of(String.valueOf(Long.MAX_VALUE)), + assertEquals( + ImmutableList.of(String.valueOf(Long.MAX_VALUE)), lowLevelRequest.getHeaderValues("content-length")); HttpHeaders.serializeHeadersForMultipartRequests(myHeaders, null, null, writer); @@ -128,17 +126,22 @@ public void testSerializeHeaders() throws Exception { expectedOutput.append("someLong: 0\r\n"); expectedOutput.append("User-Agent: foo\r\n"); expectedOutput.append("value: VALUE\r\n"); + expectedOutput.append("Warning: warn0\r\n"); + expectedOutput.append("Warning: warn1\r\n"); expectedOutput.append("a: b\r\n"); assertEquals(expectedOutput.toString(), outputStream.toString()); } @SuppressWarnings("unchecked") + @Test public void testFromHttpHeaders() { HttpHeaders rawHeaders = new HttpHeaders(); rawHeaders.setContentLength(Long.MAX_VALUE); rawHeaders.setContentType("foo/bar"); rawHeaders.setUserAgent("FooBar"); + rawHeaders.addWarning("warn0"); + rawHeaders.addWarning("warn1"); rawHeaders.set("foo", "bar"); rawHeaders.set("someLong", "5"); rawHeaders.set("list", ImmutableList.of("a", "b", "c")); @@ -154,6 +157,8 @@ public void testFromHttpHeaders() { assertEquals(Long.MAX_VALUE, myHeaders.getContentLength().longValue()); assertEquals("foo/bar", myHeaders.getContentType()); assertEquals("FooBar", myHeaders.getUserAgent()); + assertEquals("warn0", myHeaders.getWarning().get(0)); + assertEquals("warn1", myHeaders.getWarning().get(1)); assertEquals("bar", myHeaders.foo); assertEquals(5, myHeaders.someLong); assertEquals(ImmutableList.of("5"), myHeaders.objNum); @@ -168,6 +173,7 @@ public void testFromHttpHeaders() { private static final String AUTHORIZATION_HEADERS = "Accept-Encoding: gzip\r\nAuthorization: Foo\r\nAuthorization: Bar\r\n"; + @Test public void testAuthorizationHeader() throws IOException { // serialization HttpHeaders headers = new HttpHeaders(); @@ -186,6 +192,7 @@ public void testAuthorizationHeader() throws IOException { assertTrue(authHeader.toString(), ImmutableList.of("Foo", "Bar").equals(authHeader)); } + @Test public void testHeaderStringValues() { // custom headers MyHeaders myHeaders = new MyHeaders(); @@ -197,6 +204,7 @@ public void testHeaderStringValues() { myHeaders.setAcceptEncoding(null); myHeaders.setContentLength(Long.MAX_VALUE); myHeaders.setUserAgent("foo"); + myHeaders.addWarning("warn"); myHeaders.set("a", "b"); myHeaders.value = E.VALUE; myHeaders.otherValue = E.OTHER_VALUE; @@ -207,6 +215,7 @@ public void testHeaderStringValues() { assertEquals("a1", myHeaders.getFirstHeaderStringValue("r")); assertNull(myHeaders.getFirstHeaderStringValue("accept-encoding")); assertEquals("foo", myHeaders.getFirstHeaderStringValue("user-agent")); + assertEquals("warn", myHeaders.getFirstHeaderStringValue("warning")); assertEquals("b", myHeaders.getFirstHeaderStringValue("a")); assertEquals("VALUE", myHeaders.getFirstHeaderStringValue("value")); assertEquals("other", myHeaders.getFirstHeaderStringValue("othervalue")); @@ -219,10 +228,12 @@ public void testHeaderStringValues() { assertEquals(ImmutableList.of("a1", "a2"), myHeaders.getHeaderStringValues("r")); assertTrue(myHeaders.getHeaderStringValues("accept-encoding").isEmpty()); assertEquals(ImmutableList.of("foo"), myHeaders.getHeaderStringValues("user-agent")); + assertEquals(ImmutableList.of("warn"), myHeaders.getHeaderStringValues("warning")); assertEquals(ImmutableList.of("b"), myHeaders.getHeaderStringValues("a")); assertEquals(ImmutableList.of("VALUE"), myHeaders.getHeaderStringValues("value")); assertEquals(ImmutableList.of("other"), myHeaders.getHeaderStringValues("othervalue")); - assertEquals(ImmutableList.of(String.valueOf(Long.MAX_VALUE)), + assertEquals( + ImmutableList.of(String.valueOf(Long.MAX_VALUE)), myHeaders.getHeaderStringValues("content-length")); } @@ -231,19 +242,24 @@ public static class SlugHeaders extends HttpHeaders { String slug; } + @Test public void testParseAge() throws Exception { - MockLowLevelHttpResponse httpResponse = new MockLowLevelHttpResponse().setHeaderNames( - Arrays.asList("Age")).setHeaderValues(Arrays.asList("3456")); + MockLowLevelHttpResponse httpResponse = + new MockLowLevelHttpResponse() + .setHeaderNames(Arrays.asList("Age")) + .setHeaderValues(Arrays.asList("3456")); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.fromHttpResponse(httpResponse, null); assertEquals(3456L, httpHeaders.getAge().longValue()); } + @Test public void testFromHttpResponse_normalFlow() throws Exception { - MockLowLevelHttpResponse httpResponse = new MockLowLevelHttpResponse().setHeaderNames( - Arrays.asList("Content-Type", "Slug")) - .setHeaderValues(Arrays.asList("foo/bar", "123456789")); + MockLowLevelHttpResponse httpResponse = + new MockLowLevelHttpResponse() + .setHeaderNames(Arrays.asList("Content-Type", "Slug")) + .setHeaderValues(Arrays.asList("foo/bar", "123456789")); // Test the normal HttpHeaders class HttpHeaders httpHeaders = new HttpHeaders(); @@ -258,10 +274,12 @@ public void testFromHttpResponse_normalFlow() throws Exception { assertEquals("123456789", slugHeaders.slug); } + @Test public void testFromHttpResponse_doubleConvert() throws Exception { - MockLowLevelHttpResponse httpResponse = new MockLowLevelHttpResponse().setHeaderNames( - Arrays.asList("Content-Type", "Slug")) - .setHeaderValues(Arrays.asList("foo/bar", "123456789")); + MockLowLevelHttpResponse httpResponse = + new MockLowLevelHttpResponse() + .setHeaderNames(Arrays.asList("Content-Type", "Slug")) + .setHeaderValues(Arrays.asList("foo/bar", "123456789")); // Test the normal HttpHeaders class SlugHeaders slugHeaders = new SlugHeaders(); @@ -276,24 +294,29 @@ public void testFromHttpResponse_doubleConvert() throws Exception { assertEquals("123456789", slugHeaders2.slug); } + @Test public void testFromHttpResponse_clearOldValue() throws Exception { HttpHeaders headers = new HttpHeaders(); headers.put("Foo", "oldValue"); - headers.fromHttpResponse(new MockLowLevelHttpResponse().setHeaderNames(Arrays.asList("Foo")) - .setHeaderValues(Arrays.asList("newvalue")), null); + headers.fromHttpResponse( + new MockLowLevelHttpResponse() + .setHeaderNames(Arrays.asList("Foo")) + .setHeaderValues(Arrays.asList("newvalue")), + null); assertEquals(Arrays.asList("newvalue"), headers.get("Foo")); } public static class V extends HttpHeaders { - @Key - Void v; - @Key - String s; + @Key Void v; + @Key String s; } - public void testFromHttpResponse_void(String value) throws Exception { - MockLowLevelHttpResponse httpResponse = new MockLowLevelHttpResponse().setHeaderNames( - Arrays.asList("v", "v", "s")).setHeaderValues(Arrays.asList("ignore", "ignore2", "svalue")); + @Test + public void testFromHttpResponse_void() throws Exception { + MockLowLevelHttpResponse httpResponse = + new MockLowLevelHttpResponse() + .setHeaderNames(Arrays.asList("v", "v", "s")) + .setHeaderValues(Arrays.asList("ignore", "ignore2", "svalue")); V v = new V(); v.fromHttpResponse(httpResponse, null); assertNull(v.v); diff --git a/google-http-client/src/test/java/com/google/api/client/http/HttpMediaTypeTest.java b/google-http-client/src/test/java/com/google/api/client/http/HttpMediaTypeTest.java index 80765d409..da5600a2e 100644 --- a/google-http-client/src/test/java/com/google/api/client/http/HttpMediaTypeTest.java +++ b/google-http-client/src/test/java/com/google/api/client/http/HttpMediaTypeTest.java @@ -14,9 +14,14 @@ package com.google.api.client.http; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + import com.google.common.base.Charsets; import com.google.common.testing.EqualsTester; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests for the {@link HttpMediaType} class. @@ -24,18 +29,22 @@ * @author Matthias Linder (mlinder) * @since 1.10 */ -public class HttpMediaTypeTest extends TestCase { +@RunWith(JUnit4.class) +public class HttpMediaTypeTest { + @Test public void testBuild() { HttpMediaType m = new HttpMediaType("main", "sub"); assertEquals("main/sub", m.build()); } + @Test public void testBuild_star() { HttpMediaType m = new HttpMediaType("*", "*"); assertEquals("*/*", m.build()); } + @Test public void testBuild_parameters() { HttpMediaType m = new HttpMediaType("main", "sub"); m.setParameter("bbb", ";/ "); @@ -43,42 +52,50 @@ public void testBuild_parameters() { assertEquals("main/sub; aaa=1; bbb=\";/ \"", m.build()); } + @Test public void testBuild_json() { HttpMediaType m = new HttpMediaType("application/json").setCharsetParameter(Charsets.UTF_8); assertEquals("application/json; charset=UTF-8", m.build()); } + @Test public void testBuild_multipartSpec() { HttpMediaType m = new HttpMediaType("main", "sub"); m.setParameter("bbb", "foo=/bar"); assertEquals("main/sub; bbb=\"foo=/bar\"", m.build()); } + @Test public void testBuild_parametersCasing() { HttpMediaType m = new HttpMediaType("main", "sub"); m.setParameter("foo", "FooBar"); assertEquals("main/sub; foo=FooBar", m.build()); } + @Test public void testFromString() { HttpMediaType m = new HttpMediaType("main/sub"); assertEquals("main", m.getType()); assertEquals("sub", m.getSubType()); } + @Test public void testFromString_star() { HttpMediaType m = new HttpMediaType("text/*"); assertEquals("text", m.getType()); assertEquals("*", m.getSubType()); } + @Test public void testFromString_null() { try { new HttpMediaType(null); fail("Method did not NullPointerException"); - } catch (NullPointerException expected) {} + } catch (NullPointerException expected) { + } } + @Test public void testFromString_multipartSpec() { // Values allowed by the spec: http://www.w3.org/Protocols/rfc1341/7_2_Multipart.html String value = "f00'()+_,-./:=?bar"; @@ -87,6 +104,7 @@ public void testFromString_multipartSpec() { assertEquals("bar", m.getParameter("foo")); } + @Test public void testFromString_full() { HttpMediaType m = new HttpMediaType("text/plain; charset=utf-8; foo=\"foo; =bar\""); assertEquals("text", m.getType()); @@ -96,33 +114,41 @@ public void testFromString_full() { assertEquals(2, m.getParameters().size()); } + @Test public void testFromString_case() { HttpMediaType m = new HttpMediaType("text/plain; Foo=Bar"); assertEquals("Bar", m.getParameter("fOO")); } + @Test public void testSetMainType() { assertEquals("foo", new HttpMediaType("text", "plain").setType("foo").getType()); } + @Test public void testSetMainType_invalid() { try { new HttpMediaType("text", "plain").setType("foo/bar"); fail("Method did not throw IllegalArgumentException"); - } catch (IllegalArgumentException expected) {} + } catch (IllegalArgumentException expected) { + } } + @Test public void testSetSubType() { assertEquals("foo", new HttpMediaType("text", "plain").setSubType("foo").getSubType()); } + @Test public void testSetSubType_invalid() { try { new HttpMediaType("text", "plain").setSubType("foo/bar"); fail("Method did not throw IllegalArgumentException"); - } catch (IllegalArgumentException expected) {} + } catch (IllegalArgumentException expected) { + } } + @Test public void testSetParameter_casing() { HttpMediaType mt = new HttpMediaType("text", "plain"); mt.setParameter("Foo", "Bar"); @@ -142,6 +168,7 @@ private void assertFullSerialization(String str) { assertEquals(str, new HttpMediaType(str).build()); } + @Test public void testFullSerialization() { assertFullSerialization("text/plain"); assertFullSerialization("text/plain; foo=bar"); @@ -152,6 +179,7 @@ public void testFullSerialization() { assertFullSerialization("text/*; charset=utf-8; foo=\"bar bar bar\""); } + @Test public void testInvalidCharsRegex() { assertEquals(false, containsInvalidChar("foo")); assertEquals(false, containsInvalidChar("X-Foo-Bar")); @@ -160,6 +188,7 @@ public void testInvalidCharsRegex() { assertEquals(true, containsInvalidChar("foo;bar")); } + @Test public void testCharset() { HttpMediaType hmt = new HttpMediaType("foo/bar"); assertEquals(null, hmt.getCharsetParameter()); @@ -168,14 +197,20 @@ public void testCharset() { assertEquals(Charsets.UTF_8, hmt.getCharsetParameter()); } + @Test public void testEqualsIgnoreParameters() { - assertEquals(true, new HttpMediaType("foo/bar").equalsIgnoreParameters(new HttpMediaType("Foo/bar"))); - assertEquals(true, new HttpMediaType("foo/bar").equalsIgnoreParameters( - new HttpMediaType("foo/bar; charset=utf-8"))); - assertEquals(false, new HttpMediaType("foo/bar").equalsIgnoreParameters(new HttpMediaType("bar/foo"))); + assertEquals( + true, new HttpMediaType("foo/bar").equalsIgnoreParameters(new HttpMediaType("Foo/bar"))); + assertEquals( + true, + new HttpMediaType("foo/bar") + .equalsIgnoreParameters(new HttpMediaType("foo/bar; charset=utf-8"))); + assertEquals( + false, new HttpMediaType("foo/bar").equalsIgnoreParameters(new HttpMediaType("bar/foo"))); assertEquals(false, new HttpMediaType("foo/bar").equalsIgnoreParameters(null)); } + @Test public void testEqualsIgnoreParameters_static() { assertEquals(true, HttpMediaType.equalsIgnoreParameters(null, null)); assertEquals(false, HttpMediaType.equalsIgnoreParameters(null, "foo/bar")); @@ -183,6 +218,7 @@ public void testEqualsIgnoreParameters_static() { assertEquals(true, HttpMediaType.equalsIgnoreParameters("foo/bar; a=c", "foo/bar; b=d")); } + @Test public void testEquals() { new EqualsTester() .addEqualityGroup(new HttpMediaType("foo/bar"), new HttpMediaType("foo/bar")) diff --git a/google-http-client/src/test/java/com/google/api/client/http/HttpRequestFactoryTest.java b/google-http-client/src/test/java/com/google/api/client/http/HttpRequestFactoryTest.java new file mode 100644 index 000000000..66d21826e --- /dev/null +++ b/google-http-client/src/test/java/com/google/api/client/http/HttpRequestFactoryTest.java @@ -0,0 +1,47 @@ +/* + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.http; + +import static org.junit.Assert.assertEquals; + +import com.google.api.client.http.javanet.NetHttpTransport; +import java.io.IOException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests {@link HttpRequestFactory}. */ +@RunWith(JUnit4.class) +public class HttpRequestFactoryTest { + + @Test + public void testBuildRequest_urlShouldBeSet() throws IllegalArgumentException, IOException { + HttpRequestFactory requestFactory = + new NetHttpTransport() + .createRequestFactory( + new HttpRequestInitializer() { + @Override + public void initialize(HttpRequest request) { + // Url should be set by buildRequest method before calling initialize. + if (request.getUrl() == null) { + throw new IllegalArgumentException("url is not set in request"); + } + } + }); + GenericUrl url = new GenericUrl("https://foo.googleapis.com/"); + HttpRequest request = requestFactory.buildRequest("GET", url, null); + assertEquals(url, request.getUrl()); + } +} diff --git a/google-http-client/src/test/java/com/google/api/client/http/HttpRequestTest.java b/google-http-client/src/test/java/com/google/api/client/http/HttpRequestTest.java index 53a3d8842..085b9f563 100644 --- a/google-http-client/src/test/java/com/google/api/client/http/HttpRequestTest.java +++ b/google-http-client/src/test/java/com/google/api/client/http/HttpRequestTest.java @@ -14,6 +14,13 @@ package com.google.api.client.http; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import com.google.api.client.testing.http.HttpTesting; import com.google.api.client.testing.http.MockHttpTransport; import com.google.api.client.testing.http.MockHttpUnsuccessfulResponseHandler; @@ -41,37 +48,39 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.logging.Level; -import junit.framework.Assert; -import junit.framework.TestCase; +import java.util.regex.Pattern; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link HttpRequest}. * * @author Yaniv Inbar */ -public class HttpRequestTest extends TestCase { +@RunWith(JUnit4.class) +public class HttpRequestTest { private static final ImmutableSet BASIC_METHODS = ImmutableSet.of(HttpMethods.GET, HttpMethods.PUT, HttpMethods.POST, HttpMethods.DELETE); private static final ImmutableSet OTHER_METHODS = ImmutableSet.of(HttpMethods.HEAD, HttpMethods.PATCH); - public HttpRequestTest(String name) { - super(name); - } - - @Override + @Before public void setUp() { // suppress logging warnings to the console HttpTransport.LOGGER.setLevel(java.util.logging.Level.SEVERE); } - @Override + @After public void tearDown() { // back to the standard logging level for console HttpTransport.LOGGER.setLevel(java.util.logging.Level.WARNING); } + @Test public void testNotSupportedByDefault() throws Exception { MockHttpTransport transport = new MockHttpTransport(); HttpRequest request = @@ -100,6 +109,7 @@ public void testNotSupportedByDefault() throws Exception { static class MockExecutor implements Executor { private Runnable runnable; + public void actuallyRun() { runnable.run(); } @@ -110,14 +120,13 @@ public void execute(Runnable command) { } @Deprecated - static private class MockBackOffPolicy implements BackOffPolicy { + private static class MockBackOffPolicy implements BackOffPolicy { int backOffCalls; int resetCalls; boolean returnBackOffStop; - MockBackOffPolicy() { - } + MockBackOffPolicy() {} public boolean isBackOffRequired(int statusCode) { switch (statusCode) { @@ -142,9 +151,7 @@ public long getNextBackOffMillis() { } } - /** - * Transport used for testing the redirection logic in HttpRequest. - */ + /** Transport used for testing the redirection logic in HttpRequest. */ static class RedirectTransport extends MockHttpTransport { int lowLevelExecCalls; @@ -154,31 +161,34 @@ static class RedirectTransport extends MockHttpTransport { int redirectStatusCode = HttpStatusCodes.STATUS_CODE_MOVED_PERMANENTLY; String[] expectedContent; - LowLevelHttpRequest retryableGetRequest = new MockLowLevelHttpRequest() { + LowLevelHttpRequest retryableGetRequest = + new MockLowLevelHttpRequest() { - @Override - public LowLevelHttpResponse execute() throws IOException { - if (expectedContent != null) { - assertEquals(String.valueOf(lowLevelExecCalls), expectedContent[lowLevelExecCalls], - getContentAsString()); - } - lowLevelExecCalls++; - if (infiniteRedirection || lowLevelExecCalls == 1) { - // Return redirect on only the first call. - // If infiniteRedirection is true then always return the redirect status code. - MockLowLevelHttpResponse response = new MockLowLevelHttpResponse(); - response.setStatusCode(redirectStatusCode); - if (!removeLocation) { - response.addHeader("Location", HttpTesting.SIMPLE_URL); + @Override + public LowLevelHttpResponse execute() throws IOException { + if (expectedContent != null) { + assertEquals( + String.valueOf(lowLevelExecCalls), + expectedContent[lowLevelExecCalls], + getContentAsString()); + } + lowLevelExecCalls++; + if (infiniteRedirection || lowLevelExecCalls == 1) { + // Return redirect on only the first call. + // If infiniteRedirection is true then always return the redirect status code. + MockLowLevelHttpResponse response = new MockLowLevelHttpResponse(); + response.setStatusCode(redirectStatusCode); + if (!removeLocation) { + response.addHeader("Location", HttpTesting.SIMPLE_URL); + } + return response; + } + // Return success on the second if infiniteRedirection is False. + MockLowLevelHttpResponse response = new MockLowLevelHttpResponse(); + response.setContent("{\"data\":{\"foo\":{\"v1\":{}}}}"); + return response; } - return response; - } - // Return success on the second if infiniteRedirection is False. - MockLowLevelHttpResponse response = new MockLowLevelHttpResponse(); - response.setContent("{\"data\":{\"foo\":{\"v1\":{}}}}"); - return response; - } - }; + }; @Override public LowLevelHttpRequest buildRequest(String method, String url) { @@ -190,15 +200,18 @@ private void setBackOffUnsuccessfulResponseHandler( HttpRequest request, BackOff backOff, final HttpUnsuccessfulResponseHandler handler) { final HttpBackOffUnsuccessfulResponseHandler backOffHandler = new HttpBackOffUnsuccessfulResponseHandler(backOff).setSleeper(new MockSleeper()); - request.setUnsuccessfulResponseHandler(new HttpUnsuccessfulResponseHandler() { - public boolean handleResponse( - HttpRequest request, HttpResponse response, boolean supportsRetry) throws IOException { - return handler.handleResponse(request, response, supportsRetry) - || backOffHandler.handleResponse(request, response, supportsRetry); - } - }); + request.setUnsuccessfulResponseHandler( + new HttpUnsuccessfulResponseHandler() { + public boolean handleResponse( + HttpRequest request, HttpResponse response, boolean supportsRetry) + throws IOException { + return handler.handleResponse(request, response, supportsRetry) + || backOffHandler.handleResponse(request, response, supportsRetry); + } + }); } + @Test public void test301Redirect() throws Exception { // Set up RedirectTransport to redirect on the first request and then return success. RedirectTransport fakeTransport = new RedirectTransport(); @@ -206,11 +219,12 @@ public void test301Redirect() throws Exception { fakeTransport.createRequestFactory().buildGetRequest(new GenericUrl("http://gmail.com")); HttpResponse resp = request.execute(); - Assert.assertEquals(200, resp.getStatusCode()); - Assert.assertEquals(2, fakeTransport.lowLevelExecCalls); + assertEquals(200, resp.getStatusCode()); + assertEquals(2, fakeTransport.lowLevelExecCalls); } @Deprecated + @Test public void test301RedirectWithUnsuccessfulResponseHandled() throws Exception { MockHttpUnsuccessfulResponseHandler handler = new MockHttpUnsuccessfulResponseHandler(true); MockBackOffPolicy backOffPolicy = new MockBackOffPolicy(); @@ -222,18 +236,19 @@ public void test301RedirectWithUnsuccessfulResponseHandled() throws Exception { request.setBackOffPolicy(backOffPolicy); HttpResponse resp = request.execute(); - Assert.assertEquals(200, resp.getStatusCode()); - Assert.assertEquals(2, fakeTransport.lowLevelExecCalls); + assertEquals(200, resp.getStatusCode()); + assertEquals(2, fakeTransport.lowLevelExecCalls); // Assert that the redirect logic was not invoked because the response handler could handle the // request. The request url should be the original http://gmail.com - Assert.assertEquals("http://gmail.com", request.getUrl().toString()); + assertEquals("http://gmail.com", request.getUrl().toString()); // Assert that the backoff policy was not invoked because the response handler could handle the // request. - Assert.assertEquals(1, backOffPolicy.resetCalls); - Assert.assertEquals(0, backOffPolicy.backOffCalls); - Assert.assertTrue(handler.isCalled()); + assertEquals(1, backOffPolicy.resetCalls); + assertEquals(0, backOffPolicy.backOffCalls); + assertTrue(handler.isCalled()); } + @Test public void test301RedirectWithBackOffUnsuccessfulResponseHandled() throws Exception { MockHttpUnsuccessfulResponseHandler handler = new MockHttpUnsuccessfulResponseHandler(true); // Set up RedirectTransport to redirect on the first request and then return success. @@ -246,14 +261,14 @@ public void test301RedirectWithBackOffUnsuccessfulResponseHandled() throws Excep HttpResponse resp = request.execute(); - Assert.assertEquals(200, resp.getStatusCode()); - Assert.assertEquals(2, fakeTransport.lowLevelExecCalls); + assertEquals(200, resp.getStatusCode()); + assertEquals(2, fakeTransport.lowLevelExecCalls); // Assert that the redirect logic was not invoked because the response handler could handle the // request. The request url should be the original http://gmail.com - Assert.assertEquals("http://gmail.com", request.getUrl().toString()); + assertEquals("http://gmail.com", request.getUrl().toString()); // Assert that the backoff was not invoked since the response handler could handle the request - Assert.assertEquals(0, backOff.getNumberOfTries()); - Assert.assertTrue(handler.isCalled()); + assertEquals(0, backOff.getNumberOfTries()); + assertTrue(handler.isCalled()); } @Deprecated @@ -269,17 +284,18 @@ public void test301RedirectWithUnsuccessfulResponseNotHandled() throws Exception request.setBackOffPolicy(backOffPolicy); HttpResponse resp = request.execute(); - Assert.assertEquals(200, resp.getStatusCode()); + assertEquals(200, resp.getStatusCode()); // Assert that the redirect logic was invoked because the response handler could not handle the // request. The request url should have changed from http://gmail.com to http://google.com - Assert.assertEquals(HttpTesting.SIMPLE_URL, request.getUrl().toString()); - Assert.assertEquals(2, fakeTransport.lowLevelExecCalls); + assertEquals(HttpTesting.SIMPLE_URL, request.getUrl().toString()); + assertEquals(2, fakeTransport.lowLevelExecCalls); // Assert that the backoff policy is never invoked (except to reset) because the response // handler returned false. - Assert.assertEquals(1, backOffPolicy.resetCalls); - Assert.assertEquals(0, backOffPolicy.backOffCalls); + assertEquals(1, backOffPolicy.resetCalls); + assertEquals(0, backOffPolicy.backOffCalls); } + @Test public void test301RedirectWithBackOffUnsuccessfulResponseNotHandled() throws Exception { // Create an Unsuccessful response handler that always returns false. MockHttpUnsuccessfulResponseHandler handler = new MockHttpUnsuccessfulResponseHandler(false); @@ -293,34 +309,39 @@ public void test301RedirectWithBackOffUnsuccessfulResponseNotHandled() throws Ex HttpResponse resp = request.execute(); - Assert.assertEquals(200, resp.getStatusCode()); + assertEquals(200, resp.getStatusCode()); // Assert that the redirect logic was invoked because the response handler could not handle the // request. The request url should have changed from http://gmail.com to http://google.com - Assert.assertEquals(HttpTesting.SIMPLE_URL, request.getUrl().toString()); - Assert.assertEquals(2, fakeTransport.lowLevelExecCalls); + assertEquals(HttpTesting.SIMPLE_URL, request.getUrl().toString()); + assertEquals(2, fakeTransport.lowLevelExecCalls); // Assert that the backoff was not invoked since it's not required for 3xx errors - Assert.assertEquals(0, backOff.getNumberOfTries()); + assertEquals(0, backOff.getNumberOfTries()); } + @Test public void test303Redirect() throws Exception { // Set up RedirectTransport to redirect on the first request and then return success. RedirectTransport fakeTransport = new RedirectTransport(); fakeTransport.redirectStatusCode = HttpStatusCodes.STATUS_CODE_SEE_OTHER; byte[] content = new byte[300]; Arrays.fill(content, (byte) ' '); - HttpRequest request = fakeTransport.createRequestFactory() - .buildPostRequest(new GenericUrl("http://gmail.com"), new ByteArrayContent(null, content)); + HttpRequest request = + fakeTransport + .createRequestFactory() + .buildPostRequest( + new GenericUrl("http://gmail.com"), new ByteArrayContent(null, content)); request.setRequestMethod(HttpMethods.POST); HttpResponse resp = request.execute(); - Assert.assertEquals(200, resp.getStatusCode()); - Assert.assertEquals(2, fakeTransport.lowLevelExecCalls); + assertEquals(200, resp.getStatusCode()); + assertEquals(2, fakeTransport.lowLevelExecCalls); // Assert that the method in the request was changed to a GET due to the 303. - Assert.assertEquals(HttpMethods.GET, request.getRequestMethod()); + assertEquals(HttpMethods.GET, request.getRequestMethod()); // Assert that the content is null, since GET requests don't support non-zero content-length - Assert.assertNull(request.getContent()); + assertNull(request.getContent()); } + @Test public void testInfiniteRedirects() throws Exception { // Set up RedirectTransport to cause infinite redirections. RedirectTransport fakeTransport = new RedirectTransport(); @@ -335,9 +356,10 @@ public void testInfiniteRedirects() throws Exception { // Should be called 1 more than the number of retries allowed (because the first request is not // counted as a retry). - Assert.assertEquals(request.getNumberOfRetries() + 1, fakeTransport.lowLevelExecCalls); + assertEquals(request.getNumberOfRetries() + 1, fakeTransport.lowLevelExecCalls); } + @Test public void testMissingLocationRedirect() throws Exception { // Set up RedirectTransport to set responses with missing location headers. RedirectTransport fakeTransport = new RedirectTransport(); @@ -350,10 +372,10 @@ public void testMissingLocationRedirect() throws Exception { } catch (HttpResponseException e) { } - Assert.assertEquals(1, fakeTransport.lowLevelExecCalls); + assertEquals(1, fakeTransport.lowLevelExecCalls); } - static private class FailThenSuccessBackoffTransport extends MockHttpTransport { + private static class FailThenSuccessBackoffTransport extends MockHttpTransport { public int lowLevelExecCalls; int errorStatusCode; @@ -364,26 +386,27 @@ protected FailThenSuccessBackoffTransport(int errorStatusCode, int callsBeforeSu this.callsBeforeSuccess = callsBeforeSuccess; } - public LowLevelHttpRequest retryableGetRequest = new MockLowLevelHttpRequest() { - - @Override - public LowLevelHttpResponse execute() { - lowLevelExecCalls++; + public LowLevelHttpRequest retryableGetRequest = + new MockLowLevelHttpRequest() { - if (lowLevelExecCalls <= callsBeforeSuccess) { - // Return failure on the first call - MockLowLevelHttpResponse response = new MockLowLevelHttpResponse(); - response.setContent("INVALID TOKEN"); - response.setStatusCode(errorStatusCode); - return response; - } - // Return success on the second - MockLowLevelHttpResponse response = new MockLowLevelHttpResponse(); - response.setContent("{\"data\":{\"foo\":{\"v1\":{}}}}"); - response.setStatusCode(200); - return response; - } - }; + @Override + public LowLevelHttpResponse execute() { + lowLevelExecCalls++; + + if (lowLevelExecCalls <= callsBeforeSuccess) { + // Return failure on the first call + MockLowLevelHttpResponse response = new MockLowLevelHttpResponse(); + response.setContent("INVALID TOKEN"); + response.setStatusCode(errorStatusCode); + return response; + } + // Return success on the second + MockLowLevelHttpResponse response = new MockLowLevelHttpResponse(); + response.setContent("{\"data\":{\"foo\":{\"v1\":{}}}}"); + response.setStatusCode(200); + return response; + } + }; @Override public LowLevelHttpRequest buildRequest(String method, String url) { @@ -391,7 +414,7 @@ public LowLevelHttpRequest buildRequest(String method, String url) { } } - static private class FailThenSuccessConnectionErrorTransport extends MockHttpTransport { + private static class FailThenSuccessConnectionErrorTransport extends MockHttpTransport { public int lowLevelExecCalls; int callsBeforeSuccess; @@ -423,22 +446,22 @@ public LowLevelHttpResponse execute() throws IOException { } } - static private class StatusCodesTransport extends MockHttpTransport { + private static class StatusCodesTransport extends MockHttpTransport { int statusCode = 200; - public StatusCodesTransport() { - } + public StatusCodesTransport() {} - public MockLowLevelHttpRequest retryableGetRequest = new MockLowLevelHttpRequest() { + public MockLowLevelHttpRequest retryableGetRequest = + new MockLowLevelHttpRequest() { - @Override - public LowLevelHttpResponse execute() throws IOException { - MockLowLevelHttpResponse response = new MockLowLevelHttpResponse(); - response.setStatusCode(statusCode); - return response; - } - }; + @Override + public LowLevelHttpResponse execute() throws IOException { + MockLowLevelHttpResponse response = new MockLowLevelHttpResponse(); + response.setStatusCode(statusCode); + return response; + } + }; @Override public LowLevelHttpRequest buildRequest(String method, String url) { @@ -446,6 +469,7 @@ public LowLevelHttpRequest buildRequest(String method, String url) { } } + @Test public void testHandleRedirect() throws Exception { StatusCodesTransport transport = new StatusCodesTransport(); HttpRequest req = @@ -500,6 +524,7 @@ private void subtestRedirect(int statusCode, boolean setLocation) throws Excepti } } + @Test public void testHandleRedirect_relativeLocation() throws IOException { subtestHandleRedirect_relativeLocation("http://some.org/a/b", "z", "http://some.org/a/z"); subtestHandleRedirect_relativeLocation("http://some.org/a/b", "z/", "http://some.org/a/z/"); @@ -519,6 +544,7 @@ public void subtestHandleRedirect_relativeLocation( } @Deprecated + @Test public void testExecuteErrorWithRetryEnabled() throws Exception { int callsBeforeSuccess = 3; FailThenSuccessConnectionErrorTransport fakeTransport = @@ -529,10 +555,11 @@ public void testExecuteErrorWithRetryEnabled() throws Exception { req.setNumberOfRetries(callsBeforeSuccess + 1); HttpResponse resp = req.execute(); - Assert.assertEquals(200, resp.getStatusCode()); - Assert.assertEquals(4, fakeTransport.lowLevelExecCalls); + assertEquals(200, resp.getStatusCode()); + assertEquals(4, fakeTransport.lowLevelExecCalls); } + @Test public void testExecuteErrorWithIOExceptionHandler() throws Exception { int callsBeforeSuccess = 3; FailThenSuccessConnectionErrorTransport fakeTransport = @@ -543,11 +570,12 @@ public void testExecuteErrorWithIOExceptionHandler() throws Exception { req.setNumberOfRetries(callsBeforeSuccess + 1); HttpResponse resp = req.execute(); - Assert.assertEquals(200, resp.getStatusCode()); - Assert.assertEquals(4, fakeTransport.lowLevelExecCalls); + assertEquals(200, resp.getStatusCode()); + assertEquals(4, fakeTransport.lowLevelExecCalls); } @Deprecated + @Test public void testExecuteErrorWithRetryEnabledBeyondRetryLimit() throws Exception { int callsBeforeSuccess = 11; FailThenSuccessConnectionErrorTransport fakeTransport = @@ -562,9 +590,10 @@ public void testExecuteErrorWithRetryEnabledBeyondRetryLimit() throws Exception } catch (IOException e) { // Expected } - Assert.assertEquals(callsBeforeSuccess, fakeTransport.lowLevelExecCalls); + assertEquals(callsBeforeSuccess, fakeTransport.lowLevelExecCalls); } + @Test public void testExecuteErrorWithIOExceptionHandlerBeyondRetryLimit() throws Exception { int callsBeforeSuccess = 11; FailThenSuccessConnectionErrorTransport fakeTransport = @@ -579,9 +608,10 @@ public void testExecuteErrorWithIOExceptionHandlerBeyondRetryLimit() throws Exce } catch (IOException e) { // Expected } - Assert.assertEquals(callsBeforeSuccess, fakeTransport.lowLevelExecCalls); + assertEquals(callsBeforeSuccess, fakeTransport.lowLevelExecCalls); } + @Test public void testExecuteErrorWithoutIOExceptionHandler() throws Exception { int callsBeforeSuccess = 3; FailThenSuccessConnectionErrorTransport fakeTransport = @@ -596,10 +626,11 @@ public void testExecuteErrorWithoutIOExceptionHandler() throws Exception { } catch (IOException e) { // Expected } - Assert.assertEquals(1, fakeTransport.lowLevelExecCalls); + assertEquals(1, fakeTransport.lowLevelExecCalls); } @Deprecated + @Test public void testUserAgentWithExecuteErrorAndRetryEnabled() throws Exception { int callsBeforeSuccess = 3; FailThenSuccessConnectionErrorTransport fakeTransport = @@ -610,12 +641,13 @@ public void testUserAgentWithExecuteErrorAndRetryEnabled() throws Exception { req.setNumberOfRetries(callsBeforeSuccess + 1); HttpResponse resp = req.execute(); - Assert.assertEquals(1, fakeTransport.userAgentHeader.size()); - Assert.assertEquals(HttpRequest.USER_AGENT_SUFFIX, fakeTransport.userAgentHeader.get(0)); - Assert.assertEquals(200, resp.getStatusCode()); - Assert.assertEquals(4, fakeTransport.lowLevelExecCalls); + assertEquals(1, fakeTransport.userAgentHeader.size()); + assertEquals(HttpRequest.USER_AGENT_SUFFIX, fakeTransport.userAgentHeader.get(0)); + assertEquals(200, resp.getStatusCode()); + assertEquals(4, fakeTransport.lowLevelExecCalls); } + @Test public void testUserAgentWithExecuteErrorAndIOExceptionHandler() throws Exception { int callsBeforeSuccess = 3; FailThenSuccessConnectionErrorTransport fakeTransport = @@ -626,12 +658,13 @@ public void testUserAgentWithExecuteErrorAndIOExceptionHandler() throws Exceptio req.setNumberOfRetries(callsBeforeSuccess + 1); HttpResponse resp = req.execute(); - Assert.assertEquals(1, fakeTransport.userAgentHeader.size()); - Assert.assertEquals(HttpRequest.USER_AGENT_SUFFIX, fakeTransport.userAgentHeader.get(0)); - Assert.assertEquals(200, resp.getStatusCode()); - Assert.assertEquals(4, fakeTransport.lowLevelExecCalls); + assertEquals(1, fakeTransport.userAgentHeader.size()); + assertEquals(HttpRequest.USER_AGENT_SUFFIX, fakeTransport.userAgentHeader.get(0)); + assertEquals(200, resp.getStatusCode()); + assertEquals(4, fakeTransport.lowLevelExecCalls); } + @Test public void testAbnormalResponseHandlerWithNoBackOff() throws Exception { FailThenSuccessBackoffTransport fakeTransport = new FailThenSuccessBackoffTransport(HttpStatusCodes.STATUS_CODE_UNAUTHORIZED, 1); @@ -642,12 +675,13 @@ public void testAbnormalResponseHandlerWithNoBackOff() throws Exception { req.setUnsuccessfulResponseHandler(handler); HttpResponse resp = req.execute(); - Assert.assertEquals(200, resp.getStatusCode()); - Assert.assertEquals(2, fakeTransport.lowLevelExecCalls); - Assert.assertTrue(handler.isCalled()); + assertEquals(200, resp.getStatusCode()); + assertEquals(2, fakeTransport.lowLevelExecCalls); + assertTrue(handler.isCalled()); } @Deprecated + @Test public void testAbnormalResponseHandlerWithBackOff() throws Exception { FailThenSuccessBackoffTransport fakeTransport = new FailThenSuccessBackoffTransport(HttpStatusCodes.STATUS_CODE_SERVER_ERROR, 1); @@ -660,13 +694,14 @@ public void testAbnormalResponseHandlerWithBackOff() throws Exception { req.setBackOffPolicy(backOffPolicy); HttpResponse resp = req.execute(); - Assert.assertEquals(200, resp.getStatusCode()); - Assert.assertEquals(2, fakeTransport.lowLevelExecCalls); - Assert.assertEquals(1, backOffPolicy.resetCalls); - Assert.assertEquals(0, backOffPolicy.backOffCalls); - Assert.assertTrue(handler.isCalled()); + assertEquals(200, resp.getStatusCode()); + assertEquals(2, fakeTransport.lowLevelExecCalls); + assertEquals(1, backOffPolicy.resetCalls); + assertEquals(0, backOffPolicy.backOffCalls); + assertTrue(handler.isCalled()); } + @Test public void testAbnormalResponseHandlerWithBackOffUnsuccessfulResponseHandler() throws Exception { FailThenSuccessBackoffTransport fakeTransport = new FailThenSuccessBackoffTransport(HttpStatusCodes.STATUS_CODE_SERVER_ERROR, 1); @@ -678,13 +713,14 @@ public void testAbnormalResponseHandlerWithBackOffUnsuccessfulResponseHandler() setBackOffUnsuccessfulResponseHandler(req, backOff, handler); HttpResponse resp = req.execute(); - Assert.assertEquals(200, resp.getStatusCode()); - Assert.assertEquals(2, fakeTransport.lowLevelExecCalls); - Assert.assertEquals(0, backOff.getNumberOfTries()); - Assert.assertTrue(handler.isCalled()); + assertEquals(200, resp.getStatusCode()); + assertEquals(2, fakeTransport.lowLevelExecCalls); + assertEquals(0, backOff.getNumberOfTries()); + assertTrue(handler.isCalled()); } @Deprecated + @Test public void testBackOffSingleCall() throws Exception { FailThenSuccessBackoffTransport fakeTransport = new FailThenSuccessBackoffTransport(HttpStatusCodes.STATUS_CODE_SERVER_ERROR, 1); @@ -697,13 +733,14 @@ public void testBackOffSingleCall() throws Exception { req.setBackOffPolicy(backOffPolicy); HttpResponse resp = req.execute(); - Assert.assertEquals(200, resp.getStatusCode()); - Assert.assertEquals(2, fakeTransport.lowLevelExecCalls); - Assert.assertEquals(1, backOffPolicy.resetCalls); - Assert.assertEquals(1, backOffPolicy.backOffCalls); - Assert.assertTrue(handler.isCalled()); + assertEquals(200, resp.getStatusCode()); + assertEquals(2, fakeTransport.lowLevelExecCalls); + assertEquals(1, backOffPolicy.resetCalls); + assertEquals(1, backOffPolicy.backOffCalls); + assertTrue(handler.isCalled()); } + @Test public void testBackOffUnsuccessfulResponseSingleCall() throws Exception { FailThenSuccessBackoffTransport fakeTransport = new FailThenSuccessBackoffTransport(HttpStatusCodes.STATUS_CODE_SERVER_ERROR, 1); @@ -715,17 +752,19 @@ public void testBackOffUnsuccessfulResponseSingleCall() throws Exception { setBackOffUnsuccessfulResponseHandler(req, backOff, handler); HttpResponse resp = req.execute(); - Assert.assertEquals(200, resp.getStatusCode()); - Assert.assertEquals(2, fakeTransport.lowLevelExecCalls); - Assert.assertEquals(1, backOff.getNumberOfTries()); - Assert.assertTrue(handler.isCalled()); + assertEquals(200, resp.getStatusCode()); + assertEquals(2, fakeTransport.lowLevelExecCalls); + assertEquals(1, backOff.getNumberOfTries()); + assertTrue(handler.isCalled()); } @Deprecated + @Test public void testBackOffMultipleCalls() throws Exception { int callsBeforeSuccess = 5; - FailThenSuccessBackoffTransport fakeTransport = new FailThenSuccessBackoffTransport( - HttpStatusCodes.STATUS_CODE_SERVER_ERROR, callsBeforeSuccess); + FailThenSuccessBackoffTransport fakeTransport = + new FailThenSuccessBackoffTransport( + HttpStatusCodes.STATUS_CODE_SERVER_ERROR, callsBeforeSuccess); MockHttpUnsuccessfulResponseHandler handler = new MockHttpUnsuccessfulResponseHandler(false); MockBackOffPolicy backOffPolicy = new MockBackOffPolicy(); @@ -735,17 +774,19 @@ public void testBackOffMultipleCalls() throws Exception { req.setBackOffPolicy(backOffPolicy); HttpResponse resp = req.execute(); - Assert.assertEquals(200, resp.getStatusCode()); - Assert.assertEquals(callsBeforeSuccess + 1, fakeTransport.lowLevelExecCalls); - Assert.assertEquals(1, backOffPolicy.resetCalls); - Assert.assertEquals(callsBeforeSuccess, backOffPolicy.backOffCalls); - Assert.assertTrue(handler.isCalled()); + assertEquals(200, resp.getStatusCode()); + assertEquals(callsBeforeSuccess + 1, fakeTransport.lowLevelExecCalls); + assertEquals(1, backOffPolicy.resetCalls); + assertEquals(callsBeforeSuccess, backOffPolicy.backOffCalls); + assertTrue(handler.isCalled()); } + @Test public void testBackOffUnsucessfulReponseMultipleCalls() throws Exception { int callsBeforeSuccess = 5; - FailThenSuccessBackoffTransport fakeTransport = new FailThenSuccessBackoffTransport( - HttpStatusCodes.STATUS_CODE_SERVER_ERROR, callsBeforeSuccess); + FailThenSuccessBackoffTransport fakeTransport = + new FailThenSuccessBackoffTransport( + HttpStatusCodes.STATUS_CODE_SERVER_ERROR, callsBeforeSuccess); MockHttpUnsuccessfulResponseHandler handler = new MockHttpUnsuccessfulResponseHandler(false); HttpRequest req = @@ -754,17 +795,19 @@ public void testBackOffUnsucessfulReponseMultipleCalls() throws Exception { setBackOffUnsuccessfulResponseHandler(req, backOff, handler); HttpResponse resp = req.execute(); - Assert.assertEquals(200, resp.getStatusCode()); - Assert.assertEquals(callsBeforeSuccess + 1, fakeTransport.lowLevelExecCalls); - Assert.assertEquals(callsBeforeSuccess, backOff.getNumberOfTries()); - Assert.assertTrue(handler.isCalled()); + assertEquals(200, resp.getStatusCode()); + assertEquals(callsBeforeSuccess + 1, fakeTransport.lowLevelExecCalls); + assertEquals(callsBeforeSuccess, backOff.getNumberOfTries()); + assertTrue(handler.isCalled()); } @Deprecated + @Test public void testBackOffCallsBeyondRetryLimit() throws Exception { int callsBeforeSuccess = 11; - FailThenSuccessBackoffTransport fakeTransport = new FailThenSuccessBackoffTransport( - HttpStatusCodes.STATUS_CODE_SERVER_ERROR, callsBeforeSuccess); + FailThenSuccessBackoffTransport fakeTransport = + new FailThenSuccessBackoffTransport( + HttpStatusCodes.STATUS_CODE_SERVER_ERROR, callsBeforeSuccess); MockHttpUnsuccessfulResponseHandler handler = new MockHttpUnsuccessfulResponseHandler(false); MockBackOffPolicy backOffPolicy = new MockBackOffPolicy(); @@ -778,16 +821,18 @@ public void testBackOffCallsBeyondRetryLimit() throws Exception { fail("expected HttpResponseException"); } catch (HttpResponseException e) { } - Assert.assertEquals(callsBeforeSuccess, fakeTransport.lowLevelExecCalls); - Assert.assertEquals(1, backOffPolicy.resetCalls); - Assert.assertEquals(callsBeforeSuccess - 1, backOffPolicy.backOffCalls); - Assert.assertTrue(handler.isCalled()); + assertEquals(callsBeforeSuccess, fakeTransport.lowLevelExecCalls); + assertEquals(1, backOffPolicy.resetCalls); + assertEquals(callsBeforeSuccess - 1, backOffPolicy.backOffCalls); + assertTrue(handler.isCalled()); } + @Test public void testBackOffUnsuccessfulReponseCallsBeyondRetryLimit() throws Exception { int callsBeforeSuccess = 11; - FailThenSuccessBackoffTransport fakeTransport = new FailThenSuccessBackoffTransport( - HttpStatusCodes.STATUS_CODE_SERVER_ERROR, callsBeforeSuccess); + FailThenSuccessBackoffTransport fakeTransport = + new FailThenSuccessBackoffTransport( + HttpStatusCodes.STATUS_CODE_SERVER_ERROR, callsBeforeSuccess); MockHttpUnsuccessfulResponseHandler handler = new MockHttpUnsuccessfulResponseHandler(false); HttpRequest req = @@ -800,12 +845,13 @@ public void testBackOffUnsuccessfulReponseCallsBeyondRetryLimit() throws Excepti fail("expected HttpResponseException"); } catch (HttpResponseException e) { } - Assert.assertEquals(callsBeforeSuccess, fakeTransport.lowLevelExecCalls); - Assert.assertEquals(callsBeforeSuccess - 1, backOff.getMaxTries()); - Assert.assertTrue(handler.isCalled()); + assertEquals(callsBeforeSuccess, fakeTransport.lowLevelExecCalls); + assertEquals(callsBeforeSuccess - 1, backOff.getMaxTries()); + assertTrue(handler.isCalled()); } @Deprecated + @Test public void testBackOffUnRecognizedStatusCode() throws Exception { FailThenSuccessBackoffTransport fakeTransport = new FailThenSuccessBackoffTransport(HttpStatusCodes.STATUS_CODE_UNAUTHORIZED, 1); @@ -821,13 +867,14 @@ public void testBackOffUnRecognizedStatusCode() throws Exception { } catch (HttpResponseException e) { } - Assert.assertEquals(1, fakeTransport.lowLevelExecCalls); - Assert.assertEquals(1, backOffPolicy.resetCalls); + assertEquals(1, fakeTransport.lowLevelExecCalls); + assertEquals(1, backOffPolicy.resetCalls); // The BackOffPolicy should not be called since it does not support 401 status codes. - Assert.assertEquals(0, backOffPolicy.backOffCalls); - Assert.assertTrue(handler.isCalled()); + assertEquals(0, backOffPolicy.backOffCalls); + assertTrue(handler.isCalled()); } + @Test public void testBackOffUnsuccessfulReponseUnRecognizedStatusCode() throws Exception { FailThenSuccessBackoffTransport fakeTransport = new FailThenSuccessBackoffTransport(HttpStatusCodes.STATUS_CODE_UNAUTHORIZED, 1); @@ -842,17 +889,19 @@ public void testBackOffUnsuccessfulReponseUnRecognizedStatusCode() throws Except } catch (HttpResponseException e) { } - Assert.assertEquals(1, fakeTransport.lowLevelExecCalls); + assertEquals(1, fakeTransport.lowLevelExecCalls); // The back-off should not be called since it does not support 401 status codes. - Assert.assertEquals(0, backOff.getNumberOfTries()); - Assert.assertTrue(handler.isCalled()); + assertEquals(0, backOff.getNumberOfTries()); + assertTrue(handler.isCalled()); } @Deprecated + @Test public void testBackOffStop() throws Exception { int callsBeforeSuccess = 5; - FailThenSuccessBackoffTransport fakeTransport = new FailThenSuccessBackoffTransport( - HttpStatusCodes.STATUS_CODE_SERVER_ERROR, callsBeforeSuccess); + FailThenSuccessBackoffTransport fakeTransport = + new FailThenSuccessBackoffTransport( + HttpStatusCodes.STATUS_CODE_SERVER_ERROR, callsBeforeSuccess); MockHttpUnsuccessfulResponseHandler handler = new MockHttpUnsuccessfulResponseHandler(false); MockBackOffPolicy backOffPolicy = new MockBackOffPolicy(); backOffPolicy.returnBackOffStop = true; @@ -866,18 +915,20 @@ public void testBackOffStop() throws Exception { } catch (HttpResponseException e) { } - Assert.assertEquals(1, fakeTransport.lowLevelExecCalls); - Assert.assertEquals(1, backOffPolicy.resetCalls); + assertEquals(1, fakeTransport.lowLevelExecCalls); + assertEquals(1, backOffPolicy.resetCalls); // The BackOffPolicy should be called only once and then it should return BackOffPolicy.STOP // should stop all back off retries. - Assert.assertEquals(1, backOffPolicy.backOffCalls); - Assert.assertTrue(handler.isCalled()); + assertEquals(1, backOffPolicy.backOffCalls); + assertTrue(handler.isCalled()); } + @Test public void testBackOffUnsucessfulResponseStop() throws Exception { int callsBeforeSuccess = 5; - FailThenSuccessBackoffTransport fakeTransport = new FailThenSuccessBackoffTransport( - HttpStatusCodes.STATUS_CODE_SERVER_ERROR, callsBeforeSuccess); + FailThenSuccessBackoffTransport fakeTransport = + new FailThenSuccessBackoffTransport( + HttpStatusCodes.STATUS_CODE_SERVER_ERROR, callsBeforeSuccess); MockHttpUnsuccessfulResponseHandler handler = new MockHttpUnsuccessfulResponseHandler(false); HttpRequest req = @@ -889,14 +940,13 @@ public void testBackOffUnsucessfulResponseStop() throws Exception { } catch (HttpResponseException e) { } - Assert.assertEquals(2, fakeTransport.lowLevelExecCalls); + assertEquals(2, fakeTransport.lowLevelExecCalls); // The back-off should be called only once, since the its max tries is set to 1 - Assert.assertEquals(1, backOff.getNumberOfTries()); - Assert.assertTrue(handler.isCalled()); + assertEquals(1, backOff.getNumberOfTries()); + assertTrue(handler.isCalled()); } public enum E { - @Value VALUE, @Value("other") @@ -905,28 +955,22 @@ public enum E { public static class MyHeaders extends HttpHeaders { - @Key - public String foo; + @Key public String foo; - @Key - Object objNum; + @Key Object objNum; - @Key - Object objList; + @Key Object objList; - @Key - List list; + @Key List list; - @Key - String[] r; + @Key String[] r; - @Key - E value; + @Key E value; - @Key - E otherValue; + @Key E otherValue; } + @Test public void testExecute_headerSerialization() throws Exception { // custom headers MyHeaders myHeaders = new MyHeaders(); @@ -942,12 +986,13 @@ public void testExecute_headerSerialization() throws Exception { myHeaders.otherValue = E.OTHER_VALUE; // execute request final MockLowLevelHttpRequest lowLevelRequest = new MockLowLevelHttpRequest(); - HttpTransport transport = new MockHttpTransport() { - @Override - public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { - return lowLevelRequest; - } - }; + HttpTransport transport = + new MockHttpTransport() { + @Override + public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { + return lowLevelRequest; + } + }; HttpRequest request = transport.createRequestFactory().buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL); request.setHeaders(myHeaders); @@ -958,13 +1003,15 @@ public LowLevelHttpRequest buildRequest(String method, String url) throws IOExce assertEquals(ImmutableList.of("a2", "b2", "c2"), lowLevelRequest.getHeaderValues("objlist")); assertEquals(ImmutableList.of("a1", "a2"), lowLevelRequest.getHeaderValues("r")); assertTrue(lowLevelRequest.getHeaderValues("accept-encoding").isEmpty()); - assertEquals(ImmutableList.of("foo " + HttpRequest.USER_AGENT_SUFFIX), + assertEquals( + ImmutableList.of("foo Google-HTTP-Java-Client/" + HttpRequest.VERSION + " (gzip)"), lowLevelRequest.getHeaderValues("user-agent")); assertEquals(ImmutableList.of("b"), lowLevelRequest.getHeaderValues("a")); assertEquals(ImmutableList.of("VALUE"), lowLevelRequest.getHeaderValues("value")); assertEquals(ImmutableList.of("other"), lowLevelRequest.getHeaderValues("othervalue")); } + @Test public void testGZipEncoding() throws Exception { class MyTransport extends MockHttpTransport { @@ -979,7 +1026,7 @@ public LowLevelHttpResponse execute() throws IOException { if (expectGZip) { assertEquals(HttpEncodingStreamingContent.class, getStreamingContent().getClass()); assertEquals("gzip", getContentEncoding()); - assertEquals(25, getContentLength()); + assertEquals(-1, getContentLength()); } else { assertFalse( getStreamingContent().getClass().equals(HttpEncodingStreamingContent.class)); @@ -997,9 +1044,14 @@ public LowLevelHttpResponse execute() throws IOException { MyTransport transport = new MyTransport(); byte[] content = new byte[300]; Arrays.fill(content, (byte) ' '); - HttpRequest request = transport.createRequestFactory().buildPostRequest( - HttpTesting.SIMPLE_GENERIC_URL, new ByteArrayContent( - new HttpMediaType("text/plain").setCharsetParameter(Charsets.UTF_8).build(), content)); + HttpRequest request = + transport + .createRequestFactory() + .buildPostRequest( + HttpTesting.SIMPLE_GENERIC_URL, + new ByteArrayContent( + new HttpMediaType("text/plain").setCharsetParameter(Charsets.UTF_8).build(), + content)); assertNull(request.getEncoding()); request.execute(); assertNull(request.getEncoding()); @@ -1009,6 +1061,7 @@ HttpTesting.SIMPLE_GENERIC_URL, new ByteArrayContent( request.execute(); } + @Test public void testContentLoggingLimitWithLoggingEnabledAndDisabled() throws Exception { class MyTransport extends MockHttpTransport { @@ -1036,8 +1089,11 @@ public LowLevelHttpResponse execute() throws IOException { // Create content of length 300. byte[] content = new byte[300]; Arrays.fill(content, (byte) ' '); - HttpRequest request = transport.createRequestFactory().buildPostRequest( - HttpTesting.SIMPLE_GENERIC_URL, new ByteArrayContent("text/html", content)); + HttpRequest request = + transport + .createRequestFactory() + .buildPostRequest( + HttpTesting.SIMPLE_GENERIC_URL, new ByteArrayContent("text/html", content)); // Assert logging is enabled by default. assertTrue(request.isLoggingEnabled()); @@ -1072,11 +1128,20 @@ public LowLevelHttpResponse execute() throws IOException { } } + @Test + public void testVersion() { + assertNotNull("version constant should not be null", HttpRequest.VERSION); + Pattern semverPattern = Pattern.compile("\\d+\\.\\d+\\.\\d+(-sp\\.\\d+)?(-SNAPSHOT)?"); + assertTrue(semverPattern.matcher(HttpRequest.VERSION).matches()); + } + + @Test public void testUserAgent() { assertTrue(HttpRequest.USER_AGENT_SUFFIX.contains("Google-HTTP-Java-Client")); assertTrue(HttpRequest.USER_AGENT_SUFFIX.contains("gzip")); } + @Test public void testExecute_headers() throws Exception { HttpTransport transport = new MockHttpTransport(); HttpRequest request = @@ -1086,6 +1151,7 @@ public void testExecute_headers() throws Exception { request.execute(); } + @Test public void testSuppressUserAgentSuffix() throws Exception { class MyTransport extends MockHttpTransport { String expectedUserAgent; @@ -1128,6 +1194,7 @@ public LowLevelHttpResponse execute() throws IOException { request.execute(); } + @Test public void testExecuteAsync() throws IOException, InterruptedException, ExecutionException, TimeoutException { MockExecutor mockExecutor = new MockExecutor(); @@ -1142,6 +1209,7 @@ public void testExecuteAsync() assertNotNull(futureResponse.get(10, TimeUnit.MILLISECONDS)); } + @Test public void testExecute_redirects() throws Exception { class MyTransport extends MockHttpTransport { int count = 1; @@ -1151,16 +1219,19 @@ public LowLevelHttpRequest buildRequest(String method, String url) throws IOExce // expect that it redirected to new URL every time using the count assertEquals(HttpTesting.SIMPLE_URL + "_" + count, url); count++; - return new MockLowLevelHttpRequest().setResponse( - new MockLowLevelHttpResponse().setStatusCode( - HttpStatusCodes.STATUS_CODE_MOVED_PERMANENTLY) - .setHeaderNames(Arrays.asList("Location")) - .setHeaderValues(Arrays.asList(HttpTesting.SIMPLE_URL + "_" + count))); + return new MockLowLevelHttpRequest() + .setResponse( + new MockLowLevelHttpResponse() + .setStatusCode(HttpStatusCodes.STATUS_CODE_MOVED_PERMANENTLY) + .setHeaderNames(Arrays.asList("Location")) + .setHeaderValues(Arrays.asList(HttpTesting.SIMPLE_URL + "_" + count))); } } MyTransport transport = new MyTransport(); - HttpRequest request = transport.createRequestFactory() - .buildGetRequest(new GenericUrl(HttpTesting.SIMPLE_URL + "_" + transport.count)); + HttpRequest request = + transport + .createRequestFactory() + .buildGetRequest(new GenericUrl(HttpTesting.SIMPLE_URL + "_" + transport.count)); try { request.execute(); fail("expected " + HttpResponseException.class); @@ -1169,41 +1240,51 @@ public LowLevelHttpRequest buildRequest(String method, String url) throws IOExce } } + @Test public void testExecute_redirectWithIncorrectContentRetryableSetting() throws Exception { // TODO(yanivi): any way we can warn user about this? RedirectTransport fakeTransport = new RedirectTransport(); String contentValue = "hello"; fakeTransport.expectedContent = new String[] {contentValue, ""}; byte[] bytes = StringUtils.getBytesUtf8(contentValue); - InputStreamContent content = new InputStreamContent( - new HttpMediaType("text/plain").setCharsetParameter(Charsets.UTF_8).build(), - new ByteArrayInputStream(bytes)); + InputStreamContent content = + new InputStreamContent( + new HttpMediaType("text/plain").setCharsetParameter(Charsets.UTF_8).build(), + new ByteArrayInputStream(bytes)); content.setRetrySupported(true); - HttpRequest request = fakeTransport.createRequestFactory() - .buildPostRequest(HttpTesting.SIMPLE_GENERIC_URL, content); + HttpRequest request = + fakeTransport + .createRequestFactory() + .buildPostRequest(HttpTesting.SIMPLE_GENERIC_URL, content); HttpResponse resp = request.execute(); assertEquals(200, resp.getStatusCode()); assertEquals(2, fakeTransport.lowLevelExecCalls); } + @Test public void testExecute_curlLogger() throws Exception { LogRecordingHandler recorder = new LogRecordingHandler(); HttpTransport.LOGGER.setLevel(Level.CONFIG); HttpTransport.LOGGER.addHandler(recorder); - new MockHttpTransport().createRequestFactory() - .buildGetRequest(new GenericUrl("http://google.com/#q=a'b'c")).execute(); + new MockHttpTransport() + .createRequestFactory() + .buildGetRequest(new GenericUrl("http://google.com/#q=a'b'c")) + .execute(); boolean found = false; for (String message : recorder.messages()) { if (message.startsWith("curl")) { found = true; - assertEquals("curl -v --compressed -H 'Accept-Encoding: gzip' -H 'User-Agent: " - + HttpRequest.USER_AGENT_SUFFIX + "' -- 'http://google.com/#q=a'\"'\"'b'\"'\"'c'", - message); + assertTrue(message.contains("curl -v --compressed -H 'Accept-Encoding: gzip'")); + assertTrue( + message.contains( + "-H 'User-Agent: Google-HTTP-Java-Client/" + HttpRequest.VERSION + " (gzip)'")); + assertTrue(message.contains("' -- 'http://google.com/#q=a'\"'\"'b'\"'\"'c'")); } } assertTrue(found); } + @Test public void testExecute_curlLoggerWithContentEncoding() throws Exception { LogRecordingHandler recorder = new LogRecordingHandler(); HttpTransport.LOGGER.setLevel(Level.CONFIG); @@ -1211,25 +1292,39 @@ public void testExecute_curlLoggerWithContentEncoding() throws Exception { String contentValue = "hello"; byte[] bytes = StringUtils.getBytesUtf8(contentValue); - InputStreamContent content = new InputStreamContent( - new HttpMediaType("text/plain").setCharsetParameter(Charsets.UTF_8).build(), - new ByteArrayInputStream(bytes)); + InputStreamContent content = + new InputStreamContent( + new HttpMediaType("text/plain").setCharsetParameter(Charsets.UTF_8).build(), + new ByteArrayInputStream(bytes)); - new MockHttpTransport().createRequestFactory() + new MockHttpTransport() + .createRequestFactory() .buildPostRequest(new GenericUrl("http://google.com/#q=a'b'c"), content) - .setEncoding(new GZipEncoding()).execute(); + .setEncoding(new GZipEncoding()) + .execute(); boolean found = false; - final String expectedCurlLog = "curl -v --compressed -X POST -H 'Accept-Encoding: gzip' " - + "-H 'User-Agent: " + HttpRequest.USER_AGENT_SUFFIX - + "' -H 'Content-Type: text/plain; charset=UTF-8' -H 'Content-Encoding: gzip' " - + "-d '@-' -- 'http://google.com/#q=a'\"'\"'b'\"'\"'c' << $$$"; for (String message : recorder.messages()) { if (message.startsWith("curl")) { found = true; - assertEquals(expectedCurlLog, message); + assertTrue(message.contains("curl -v --compressed -X POST -H 'Accept-Encoding: gzip'")); + assertTrue(message.contains("-H 'User-Agent: " + HttpRequest.USER_AGENT_SUFFIX + "'")); + assertTrue( + message.contains( + "-H 'Content-Type: text/plain; charset=UTF-8' -H 'Content-Encoding: gzip'")); + assertTrue(message.contains("-d '@-' -- 'http://google.com/#q=a'\"'\"'b'\"'\"'c' << $$$")); } } assertTrue(found); } + + @Test + public void testVersion_matchesAcceptablePatterns() throws Exception { + String acceptableVersionPattern = + "unknown-version|(?:\\d+\\.\\d+\\.\\d+(?:-.*?)?(?:-SNAPSHOT)?)"; + String version = HttpRequest.VERSION; + assertTrue( + String.format("the loaded version '%s' did not match the acceptable pattern", version), + version.matches(acceptableVersionPattern)); + } } diff --git a/google-http-client/src/test/java/com/google/api/client/http/HttpRequestTracingTest.java b/google-http-client/src/test/java/com/google/api/client/http/HttpRequestTracingTest.java new file mode 100644 index 000000000..528f5f309 --- /dev/null +++ b/google-http-client/src/test/java/com/google/api/client/http/HttpRequestTracingTest.java @@ -0,0 +1,165 @@ +/* + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.api.client.http; + +import static com.google.api.client.http.OpenCensusUtils.SPAN_NAME_HTTP_REQUEST_EXECUTE; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.google.api.client.testing.http.MockHttpTransport; +import com.google.api.client.testing.http.MockLowLevelHttpRequest; +import com.google.api.client.testing.http.MockLowLevelHttpResponse; +import io.opencensus.common.Functions; +import io.opencensus.testing.export.TestHandler; +import io.opencensus.trace.AttributeValue; +import io.opencensus.trace.MessageEvent; +import io.opencensus.trace.Status; +import io.opencensus.trace.Tracing; +import io.opencensus.trace.config.TraceParams; +import io.opencensus.trace.export.SpanData; +import io.opencensus.trace.samplers.Samplers; +import java.io.IOException; +import java.util.List; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class HttpRequestTracingTest { + private static final TestHandler testHandler = new TestHandler(); + + @Before + public void setupTestTracer() { + Tracing.getExportComponent().getSpanExporter().registerHandler("test", testHandler); + TraceParams params = + Tracing.getTraceConfig() + .getActiveTraceParams() + .toBuilder() + .setSampler(Samplers.alwaysSample()) + .build(); + Tracing.getTraceConfig().updateActiveTraceParams(params); + } + + @After + public void teardownTestTracer() { + Tracing.getExportComponent().getSpanExporter().unregisterHandler("test"); + } + + @Test(timeout = 20_000L) + public void executeCreatesSpan() throws IOException { + MockLowLevelHttpResponse mockResponse = new MockLowLevelHttpResponse().setStatusCode(200); + HttpTransport transport = + new MockHttpTransport.Builder().setLowLevelHttpResponse(mockResponse).build(); + HttpRequest request = + new HttpRequestFactory(transport, null) + .buildGetRequest(new GenericUrl("https://google.com/")); + request.execute(); + + // This call blocks - we set a timeout on this test to ensure we don't wait forever + List spans = testHandler.waitForExport(1); + assertEquals(1, spans.size()); + SpanData span = spans.get(0); + + // Ensure the span name is set + assertEquals(SPAN_NAME_HTTP_REQUEST_EXECUTE, span.getName()); + + // Ensure we have basic span attributes + assertAttributeEquals(span, "http.path", "/"); + assertAttributeEquals(span, "http.host", "google.com"); + assertAttributeEquals(span, "http.url", "https://google.com/"); + assertAttributeEquals(span, "http.method", "GET"); + assertAttributeEquals(span, "http.status_code", "200"); + + // Ensure we have a single annotation for starting the first attempt + assertEquals(1, span.getAnnotations().getEvents().size()); + + // Ensure we have 2 message events, SENT and RECEIVED + assertEquals(2, span.getMessageEvents().getEvents().size()); + assertEquals( + MessageEvent.Type.SENT, span.getMessageEvents().getEvents().get(0).getEvent().getType()); + assertEquals( + MessageEvent.Type.RECEIVED, + span.getMessageEvents().getEvents().get(1).getEvent().getType()); + + // Ensure we record the span status as OK + assertEquals(Status.OK, span.getStatus()); + } + + @Test(timeout = 20_000L) + public void executeExceptionCreatesSpan() throws IOException { + HttpTransport transport = + new MockHttpTransport.Builder() + .setLowLevelHttpRequest( + new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + throw new IOException("some IOException"); + } + }) + .build(); + HttpRequest request = + new HttpRequestFactory(transport, null) + .buildGetRequest(new GenericUrl("https://google.com/")); + + try { + request.execute(); + fail("expected to throw an IOException"); + } catch (IOException expected) { + } + + // This call blocks - we set a timeout on this test to ensure we don't wait forever + List spans = testHandler.waitForExport(1); + assertEquals(1, spans.size()); + SpanData span = spans.get(0); + + // Ensure the span name is set + assertEquals(SPAN_NAME_HTTP_REQUEST_EXECUTE, span.getName()); + + // Ensure we have basic span attributes + assertAttributeEquals(span, "http.path", "/"); + assertAttributeEquals(span, "http.host", "google.com"); + assertAttributeEquals(span, "http.url", "https://google.com/"); + assertAttributeEquals(span, "http.method", "GET"); + + // Ensure we have a single annotation for starting the first attempt + assertEquals(1, span.getAnnotations().getEvents().size()); + + // Ensure we have 2 message events, SENT and RECEIVED + assertEquals(1, span.getMessageEvents().getEvents().size()); + assertEquals( + MessageEvent.Type.SENT, span.getMessageEvents().getEvents().get(0).getEvent().getType()); + + // Ensure we record the span status as UNKNOWN + assertEquals(Status.UNKNOWN, span.getStatus()); + } + + void assertAttributeEquals(SpanData span, String attributeName, String expectedValue) { + Object attributeValue = span.getAttributes().getAttributeMap().get(attributeName); + assertNotNull("expected span to contain attribute: " + attributeName, attributeValue); + assertTrue(attributeValue instanceof AttributeValue); + String value = + ((AttributeValue) attributeValue) + .match( + Functions.returnToString(), + Functions.returnToString(), + Functions.returnToString(), + Functions.returnToString(), + Functions.returnNull()); + assertEquals(expectedValue, value); + } +} diff --git a/google-http-client/src/test/java/com/google/api/client/http/HttpResponseExceptionTest.java b/google-http-client/src/test/java/com/google/api/client/http/HttpResponseExceptionTest.java index 950c40d7e..3c4c2aa94 100644 --- a/google-http-client/src/test/java/com/google/api/client/http/HttpResponseExceptionTest.java +++ b/google-http-client/src/test/java/com/google/api/client/http/HttpResponseExceptionTest.java @@ -14,45 +14,59 @@ package com.google.api.client.http; +import static com.google.api.client.testing.http.HttpTesting.SIMPLE_GENERIC_URL; +import static com.google.api.client.util.StringUtils.LINE_SEPARATOR; +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + import com.google.api.client.http.HttpResponseException.Builder; -import com.google.api.client.testing.http.HttpTesting; import com.google.api.client.testing.http.MockHttpTransport; import com.google.api.client.testing.http.MockLowLevelHttpRequest; import com.google.api.client.testing.http.MockLowLevelHttpResponse; -import com.google.api.client.util.StringUtils; +import com.google.api.client.util.ExponentialBackOff; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutput; import java.io.ObjectOutputStream; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.function.ThrowingRunnable; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link HttpResponseException}. * * @author Yaniv Inbar */ -public class HttpResponseExceptionTest extends TestCase { +@RunWith(JUnit4.class) +public class HttpResponseExceptionTest { + @Test public void testConstructor() throws Exception { HttpTransport transport = new MockHttpTransport(); - HttpRequest request = - transport.createRequestFactory().buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL); + HttpRequest request = transport.createRequestFactory().buildGetRequest(SIMPLE_GENERIC_URL); HttpResponse response = request.execute(); HttpHeaders headers = response.getHeaders(); - HttpResponseException e = new HttpResponseException(response); - assertEquals("200", e.getMessage()); - assertNull(e.getContent()); - assertEquals(200, e.getStatusCode()); - assertNull(e.getStatusMessage()); - assertTrue(headers == e.getHeaders()); + HttpResponseException responseException = new HttpResponseException(response); + assertThat(responseException).hasMessageThat().isEqualTo("200\nGET " + SIMPLE_GENERIC_URL); + assertNull(responseException.getContent()); + assertEquals(200, responseException.getStatusCode()); + assertNull(responseException.getStatusMessage()); + assertTrue(headers == responseException.getHeaders()); } + @Test public void testBuilder() throws Exception { HttpHeaders headers = new HttpHeaders(); - Builder builder = new HttpResponseException.Builder(9, "statusMessage", headers).setMessage( - "message").setContent("content"); + Builder builder = + new HttpResponseException.Builder(9, "statusMessage", headers) + .setMessage("message") + .setContent("content"); assertEquals("message", builder.getMessage()); assertEquals("content", builder.getContent()); assertEquals(9, builder.getStatusCode()); @@ -66,137 +80,282 @@ public void testBuilder() throws Exception { assertTrue(headers == e.getHeaders()); } + @Test public void testConstructorWithStatusMessage() throws Exception { - HttpTransport transport = new MockHttpTransport() { - @Override - public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { - return new MockLowLevelHttpRequest() { + HttpTransport transport = + new MockHttpTransport() { @Override - public LowLevelHttpResponse execute() throws IOException { - MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); - result.setReasonPhrase("OK"); - return result; + public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { + return new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); + result.setReasonPhrase("OK"); + return result; + } + }; } }; - } - }; - HttpRequest request = - transport.createRequestFactory().buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL); + HttpRequest request = transport.createRequestFactory().buildGetRequest(SIMPLE_GENERIC_URL); HttpResponse response = request.execute(); - HttpResponseException e = new HttpResponseException(response); - assertEquals("OK", e.getStatusMessage()); + HttpResponseException responseException = new HttpResponseException(response); + assertEquals("OK", responseException.getStatusMessage()); } + @Test public void testConstructor_noStatusCode() throws Exception { - HttpTransport transport = new MockHttpTransport() { - @Override - public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { - return new MockLowLevelHttpRequest() { + HttpTransport transport = + new MockHttpTransport() { @Override - public LowLevelHttpResponse execute() throws IOException { - MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); - result.setStatusCode(0); - return result; + public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { + return new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); + result.setStatusCode(0); + return result; + } + }; } }; - } - }; - HttpRequest request = - transport.createRequestFactory().buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL); - try { - request.execute(); - fail(); - } catch (HttpResponseException e) { - assertEquals("", e.getMessage()); - } + final HttpRequest request = + transport.createRequestFactory().buildGetRequest(SIMPLE_GENERIC_URL); + HttpResponseException responseException = + assertThrows( + HttpResponseException.class, + new ThrowingRunnable() { + @Override + public void run() throws Throwable { + request.execute(); + } + }); + assertThat(responseException).hasMessageThat().isEqualTo("GET " + SIMPLE_GENERIC_URL); } + @Test public void testConstructor_messageButNoStatusCode() throws Exception { - HttpTransport transport = new MockHttpTransport() { - @Override - public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { - return new MockLowLevelHttpRequest() { + HttpTransport transport = + new MockHttpTransport() { @Override - public LowLevelHttpResponse execute() throws IOException { - MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); - result.setStatusCode(0); - result.setReasonPhrase("Foo"); - return result; + public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { + return new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); + result.setStatusCode(0); + result.setReasonPhrase("Foo"); + return result; + } + }; } }; - } - }; - HttpRequest request = - transport.createRequestFactory().buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL); - try { - request.execute(); - fail(); - } catch (HttpResponseException e) { - assertEquals("Foo", e.getMessage()); - } + final HttpRequest request = + transport.createRequestFactory().buildGetRequest(SIMPLE_GENERIC_URL); + HttpResponseException responseException = + assertThrows( + HttpResponseException.class, + new ThrowingRunnable() { + @Override + public void run() throws Throwable { + request.execute(); + } + }); + assertThat(responseException).hasMessageThat().isEqualTo("Foo\nGET " + SIMPLE_GENERIC_URL); } + @Test public void testComputeMessage() throws Exception { - HttpTransport transport = new MockHttpTransport() { - @Override - public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { - return new MockLowLevelHttpRequest() { + HttpTransport transport = + new MockHttpTransport() { @Override - public LowLevelHttpResponse execute() throws IOException { - MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); - result.setReasonPhrase("Foo"); - return result; + public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { + return new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); + result.setReasonPhrase("Foo"); + return result; + } + }; } }; - } - }; - HttpRequest request = - transport.createRequestFactory().buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL); + HttpRequest request = transport.createRequestFactory().buildGetRequest(SIMPLE_GENERIC_URL); HttpResponse response = request.execute(); - assertEquals("200 Foo", HttpResponseException.computeMessageBuffer(response).toString()); + assertThat(HttpResponseException.computeMessageBuffer(response).toString()) + .isEqualTo("200 Foo\nGET " + SIMPLE_GENERIC_URL); } + @Test public void testThrown() throws Exception { - HttpTransport transport = new MockHttpTransport() { - @Override - public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { - return new MockLowLevelHttpRequest() { + HttpTransport transport = + new MockHttpTransport() { + @Override + public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { + return new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); + result.setStatusCode(HttpStatusCodes.STATUS_CODE_NOT_FOUND); + result.setReasonPhrase("Not Found"); + result.setContent("Unable to find resource"); + return result; + } + }; + } + }; + final HttpRequest request = + transport.createRequestFactory().buildGetRequest(SIMPLE_GENERIC_URL); + HttpResponseException responseException = + assertThrows( + HttpResponseException.class, + new ThrowingRunnable() { + @Override + public void run() throws Throwable { + request.execute(); + } + }); + + assertThat(responseException) + .hasMessageThat() + .isEqualTo( + "404 Not Found\nGET " + + SIMPLE_GENERIC_URL + + LINE_SEPARATOR + + "Unable to find resource"); + // no retries expected + assertEquals(1, responseException.getAttemptCount()); + } + + @Test + public void testInvalidCharset() throws Exception { + HttpTransport transport = + new MockHttpTransport() { + @Override + public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { + return new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); + result.setStatusCode(HttpStatusCodes.STATUS_CODE_NOT_FOUND); + result.setReasonPhrase("Not Found"); + result.setContentType("text/plain; charset="); + result.setContent("Unable to find resource"); + return result; + } + }; + } + }; + final HttpRequest request = + transport.createRequestFactory().buildGetRequest(SIMPLE_GENERIC_URL); + HttpResponseException responseException = + assertThrows( + HttpResponseException.class, + new ThrowingRunnable() { + @Override + public void run() throws Throwable { + request.execute(); + } + }); + + assertThat(responseException) + .hasMessageThat() + .isEqualTo("404 Not Found\nGET " + SIMPLE_GENERIC_URL); + } + + @Test + public void testAttemptCountWithBackOff() throws Exception { + HttpTransport fakeTransport = + new MockHttpTransport() { + @Override + public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { + return new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); + result.setStatusCode(HttpStatusCodes.STATUS_CODE_SERVER_ERROR); + result.setReasonPhrase("Error"); + result.setContent("Unknown Error"); + return result; + } + }; + } + }; + ExponentialBackOff backoff = new ExponentialBackOff.Builder().build(); + final HttpRequest request = + fakeTransport.createRequestFactory().buildGetRequest(new GenericUrl("http://not/used")); + request.setUnsuccessfulResponseHandler( + new HttpBackOffUnsuccessfulResponseHandler(backoff) + .setBackOffRequired( + new HttpBackOffUnsuccessfulResponseHandler.BackOffRequired() { + public boolean isRequired(HttpResponse response) { + return true; + } + })); + request.setNumberOfRetries(1); + HttpResponseException responseException = + assertThrows( + HttpResponseException.class, + new ThrowingRunnable() { + @Override + public void run() throws Throwable { + request.execute(); + } + }); + + assertEquals(500, responseException.getStatusCode()); + // original request and 1 retry - total 2 + assertEquals(2, responseException.getAttemptCount()); + } + + @Test + public void testUnsupportedCharset() throws Exception { + HttpTransport transport = + new MockHttpTransport() { @Override - public LowLevelHttpResponse execute() throws IOException { - MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); - result.setStatusCode(HttpStatusCodes.STATUS_CODE_NOT_FOUND); - result.setReasonPhrase("Not Found"); - result.setContent("Unable to find resource"); - return result; + public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { + return new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); + result.setStatusCode(HttpStatusCodes.STATUS_CODE_NOT_FOUND); + result.setReasonPhrase("Not Found"); + result.setContentType("text/plain; charset=invalid-charset"); + result.setContent("Unable to find resource"); + return result; + } + }; } }; - } - }; - HttpRequest request = - transport.createRequestFactory().buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL); - try { - request.execute(); - fail(); - } catch (HttpResponseException e) { - assertEquals( - "404 Not Found" + StringUtils.LINE_SEPARATOR + "Unable to find resource", e.getMessage()); - } + final HttpRequest request = + transport.createRequestFactory().buildGetRequest(SIMPLE_GENERIC_URL); + HttpResponseException responseException = + assertThrows( + HttpResponseException.class, + new ThrowingRunnable() { + @Override + public void run() throws Throwable { + request.execute(); + } + }); + assertThat(responseException) + .hasMessageThat() + .isEqualTo("404 Not Found\nGET " + SIMPLE_GENERIC_URL); } + @Test public void testSerialization() throws Exception { HttpTransport transport = new MockHttpTransport(); - HttpRequest request = - transport.createRequestFactory().buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL); + HttpRequest request = transport.createRequestFactory().buildGetRequest(SIMPLE_GENERIC_URL); HttpResponse response = request.execute(); - HttpResponseException e = new HttpResponseException(response); + HttpResponseException responseException = new HttpResponseException(response); ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutput s = new ObjectOutputStream(out); - s.writeObject(e); + s.writeObject(responseException); ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); ObjectInputStream objectInput = new ObjectInputStream(in); HttpResponseException e2 = (HttpResponseException) objectInput.readObject(); - assertEquals(e.getMessage(), e2.getMessage()); - assertEquals(e.getStatusCode(), e2.getStatusCode()); + assertEquals(responseException.getMessage(), e2.getMessage()); + assertEquals(responseException.getStatusCode(), e2.getStatusCode()); assertNull(e2.getHeaders()); } } diff --git a/google-http-client/src/test/java/com/google/api/client/http/HttpResponseTest.java b/google-http-client/src/test/java/com/google/api/client/http/HttpResponseTest.java index 6f6835d88..9017b39a5 100644 --- a/google-http-client/src/test/java/com/google/api/client/http/HttpResponseTest.java +++ b/google-http-client/src/test/java/com/google/api/client/http/HttpResponseTest.java @@ -14,6 +14,13 @@ package com.google.api.client.http; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import com.google.api.client.json.Json; import com.google.api.client.testing.http.HttpTesting; import com.google.api.client.testing.http.MockHttpTransport; @@ -22,29 +29,34 @@ import com.google.api.client.testing.util.LogRecordingHandler; import com.google.api.client.testing.util.TestableByteArrayInputStream; import com.google.api.client.util.Key; +import com.google.common.io.ByteStreams; +import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.FilterInputStream; import java.io.IOException; +import java.io.InputStream; import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; import java.text.NumberFormat; import java.util.Arrays; +import java.util.Locale; import java.util.logging.Level; -import junit.framework.TestCase; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link HttpResponse}. * * @author Yaniv Inbar */ -public class HttpResponseTest extends TestCase { - - public HttpResponseTest() { - } - - public HttpResponseTest(String name) { - super(name); - } +@RunWith(JUnit4.class) +public class HttpResponseTest { + @Test public void testParseAsString_none() throws Exception { HttpTransport transport = new MockHttpTransport(); HttpRequest request = @@ -55,64 +67,216 @@ public void testParseAsString_none() throws Exception { private static final String SAMPLE = "123\u05D9\u05e0\u05D9\u05D1"; private static final String SAMPLE2 = "123abc"; - + private static final String JSON_SAMPLE = "{\"foo\": \"ßar\"}"; + private static final String ERROR_SAMPLE = + "{domain:'global',reason:'domainPolicy',message:'msg'}"; + private static final String VALID_CONTENT_TYPE = "text/plain"; + private static final String VALID_CONTENT_TYPE_WITH_PARAMS = + "application/vnd.com.google.datastore.entity+json; charset=utf-8; version=v1; q=0.9"; + private static final String VALID_CONTENT_TYPE_WITHOUT_CHARSET = "text/csv; version=v1; q=0.9"; + private static final String INVALID_CONTENT_TYPE = "!!!invalid!!!"; + private static final String JSON_CONTENT_TYPE = "application/json"; + + @Test public void testParseAsString_utf8() throws Exception { - HttpTransport transport = new MockHttpTransport() { - @Override - public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { - return new MockLowLevelHttpRequest() { + HttpTransport transport = + new MockHttpTransport() { @Override - public LowLevelHttpResponse execute() throws IOException { - MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); - result.setContentType(Json.MEDIA_TYPE); - result.setContent(SAMPLE); - return result; + public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { + return new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); + result.setContentType(Json.MEDIA_TYPE); + result.setContent(SAMPLE); + return result; + } + }; } }; - } - }; HttpRequest request = transport.createRequestFactory().buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL); HttpResponse response = request.execute(); assertEquals(SAMPLE, response.parseAsString()); + assertEquals("UTF-8", response.getContentCharset().name()); } + @Test public void testParseAsString_noContentType() throws Exception { - HttpTransport transport = new MockHttpTransport() { - @Override - public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { - return new MockLowLevelHttpRequest() { + HttpTransport transport = + new MockHttpTransport() { @Override - public LowLevelHttpResponse execute() throws IOException { - MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); - result.setContent(SAMPLE2); - return result; + public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { + return new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); + result.setContent(SAMPLE2); + return result; + } + }; + } + }; + HttpRequest request = + transport.createRequestFactory().buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL); + HttpResponse response = request.execute(); + assertEquals(SAMPLE2, response.parseAsString()); + assertEquals("ISO-8859-1", response.getContentCharset().name()); + } + + @Test + public void testParseAsString_validContentType() throws Exception { + HttpTransport transport = + new MockHttpTransport() { + @Override + public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { + return new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); + result.setContent(SAMPLE2); + result.setContentType(VALID_CONTENT_TYPE); + return result; + } + }; + } + }; + HttpRequest request = + transport.createRequestFactory().buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL); + + HttpResponse response = request.execute(); + assertEquals(SAMPLE2, response.parseAsString()); + assertEquals(VALID_CONTENT_TYPE, response.getContentType()); + assertNotNull(response.getMediaType()); + assertEquals("ISO-8859-1", response.getContentCharset().name()); + } + + @Test + public void testParseAsString_validContentTypeWithParams() throws Exception { + HttpTransport transport = + new MockHttpTransport() { + @Override + public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { + return new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); + result.setContent(SAMPLE2); + result.setContentType(VALID_CONTENT_TYPE_WITH_PARAMS); + return result; + } + }; } }; - } - }; HttpRequest request = transport.createRequestFactory().buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL); + HttpResponse response = request.execute(); assertEquals(SAMPLE2, response.parseAsString()); + assertEquals(VALID_CONTENT_TYPE_WITH_PARAMS, response.getContentType()); + assertNotNull(response.getMediaType()); + assertEquals("UTF-8", response.getContentCharset().name()); } + @Test + public void testParseAsString_invalidContentType() throws Exception { + HttpTransport transport = + new MockHttpTransport() { + @Override + public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { + return new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); + result.setContent(SAMPLE2); + result.setContentType(INVALID_CONTENT_TYPE); + return result; + } + }; + } + }; + HttpRequest request = + transport.createRequestFactory().buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL); + + HttpResponse response = request.execute(); + assertEquals(SAMPLE2, response.parseAsString()); + assertEquals(INVALID_CONTENT_TYPE, response.getContentType()); + assertNull(response.getMediaType()); + assertEquals("ISO-8859-1", response.getContentCharset().name()); + } + + @Test + public void testParseAsString_validContentTypeWithoutCharSetWithParams() throws Exception { + HttpTransport transport = + new MockHttpTransport() { + @Override + public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { + return new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); + result.setContent(SAMPLE2); + result.setContentType(VALID_CONTENT_TYPE_WITHOUT_CHARSET); + return result; + } + }; + } + }; + HttpRequest request = + transport.createRequestFactory().buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL); + + HttpResponse response = request.execute(); + assertEquals(SAMPLE2, response.parseAsString()); + assertEquals(VALID_CONTENT_TYPE_WITHOUT_CHARSET, response.getContentType()); + assertNotNull(response.getMediaType()); + assertEquals("UTF-8", response.getContentCharset().name()); + } + + @Test + public void testParseAsString_jsonContentType() throws IOException { + HttpTransport transport = + new MockHttpTransport() { + @Override + public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { + return new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); + result.setContent(JSON_SAMPLE); + result.setContentType(JSON_CONTENT_TYPE); + return result; + } + }; + } + }; + HttpRequest request = + transport.createRequestFactory().buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL); + + HttpResponse response = request.execute(); + assertEquals(JSON_SAMPLE, response.parseAsString()); + assertEquals(JSON_CONTENT_TYPE, response.getContentType()); + assertEquals("UTF-8", response.getContentCharset().name()); + } + + @Test public void testStatusCode_negative_dontThrowException() throws Exception { subtestStatusCode_negative(false); } + @Test public void testStatusCode_negative_throwException() throws Exception { subtestStatusCode_negative(true); } private void subtestStatusCode_negative(boolean throwExceptionOnExecuteError) throws Exception { - HttpTransport transport = new MockHttpTransport() { - @Override - public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { - return new MockLowLevelHttpRequest().setResponse( - new MockLowLevelHttpResponse().setStatusCode(-1)); - } - }; + HttpTransport transport = + new MockHttpTransport() { + @Override + public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { + return new MockLowLevelHttpRequest() + .setResponse(new MockLowLevelHttpResponse().setStatusCode(-1)); + } + }; HttpRequest request = transport.createRequestFactory().buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL); request.setThrowExceptionOnExecuteError(throwExceptionOnExecuteError); @@ -130,40 +294,39 @@ public LowLevelHttpRequest buildRequest(String method, String url) throws IOExce public static class MyHeaders extends HttpHeaders { - @Key - public String foo; + @Key public String foo; - @Key - public Object obj; + @Key public Object obj; - @Key - String[] r; + @Key String[] r; } static final String ETAG_VALUE = "\"abc\""; + @Test public void testHeaderParsing() throws Exception { - HttpTransport transport = new MockHttpTransport() { - @Override - public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { - return new MockLowLevelHttpRequest() { + HttpTransport transport = + new MockHttpTransport() { @Override - public LowLevelHttpResponse execute() throws IOException { - MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); - result.addHeader("accept", "value"); - result.addHeader("foo", "bar"); - result.addHeader("goo", "car"); - result.addHeader("hoo", "dar"); - result.addHeader("hoo", "far"); - result.addHeader("obj", "o"); - result.addHeader("r", "a1"); - result.addHeader("r", "a2"); - result.addHeader("ETAG", ETAG_VALUE); - return result; + public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { + return new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); + result.addHeader("accept", "value"); + result.addHeader("foo", "bar"); + result.addHeader("goo", "car"); + result.addHeader("hoo", "dar"); + result.addHeader("hoo", "far"); + result.addHeader("obj", "o"); + result.addHeader("r", "a1"); + result.addHeader("r", "a2"); + result.addHeader("ETAG", ETAG_VALUE); + return result; + } + }; } }; - } - }; HttpRequest request = transport.createRequestFactory().buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL); request.setResponseHeaders(new MyHeaders()); @@ -177,93 +340,113 @@ public LowLevelHttpResponse execute() throws IOException { assertEquals(ETAG_VALUE, response.getHeaders().getETag()); } + @Test public void testParseAs_noParser() throws Exception { try { - new MockHttpTransport().createRequestFactory() - .buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL).execute().parseAs(Object.class); + new MockHttpTransport() + .createRequestFactory() + .buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL) + .execute() + .parseAs(Object.class); fail("expected " + NullPointerException.class); } catch (NullPointerException e) { // expected } } + @Test public void testParseAs_classNoContent() throws Exception { final MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); - for (final int status : new int[] { - HttpStatusCodes.STATUS_CODE_NO_CONTENT, HttpStatusCodes.STATUS_CODE_NOT_MODIFIED, 102}) { - HttpTransport transport = new MockHttpTransport() { - @Override - public LowLevelHttpRequest buildRequest(String method, final String url) throws IOException { - return new MockLowLevelHttpRequest() { + for (final int status : + new int[] { + HttpStatusCodes.STATUS_CODE_NO_CONTENT, HttpStatusCodes.STATUS_CODE_NOT_MODIFIED, 102 + }) { + HttpTransport transport = + new MockHttpTransport() { @Override - public LowLevelHttpResponse execute() throws IOException { - result.setStatusCode(status); - result.setContentType(null); - result.setContent(new ByteArrayInputStream(new byte[0])); - return result; + public LowLevelHttpRequest buildRequest(String method, final String url) + throws IOException { + return new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + result.setStatusCode(status); + result.setContentType(null); + result.setContent(new ByteArrayInputStream(new byte[0])); + return result; + } + }; } }; - } - }; // Confirm that 'null' is returned when getting the response object of a // request with no message body. - Object parsed = transport.createRequestFactory() - .buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL) - .setThrowExceptionOnExecuteError(false) - .execute() - .parseAs(Object.class); + Object parsed = + transport + .createRequestFactory() + .buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL) + .setThrowExceptionOnExecuteError(false) + .execute() + .parseAs(Object.class); assertNull(parsed); } } + @Test public void testParseAs_typeNoContent() throws Exception { final MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); - for (final int status : new int[] { - HttpStatusCodes.STATUS_CODE_NO_CONTENT, HttpStatusCodes.STATUS_CODE_NOT_MODIFIED, 102}) { - HttpTransport transport = new MockHttpTransport() { - @Override - public LowLevelHttpRequest buildRequest(String method, final String url) throws IOException { - return new MockLowLevelHttpRequest() { + for (final int status : + new int[] { + HttpStatusCodes.STATUS_CODE_NO_CONTENT, HttpStatusCodes.STATUS_CODE_NOT_MODIFIED, 102 + }) { + HttpTransport transport = + new MockHttpTransport() { @Override - public LowLevelHttpResponse execute() throws IOException { - result.setStatusCode(status); - result.setContentType(null); - result.setContent(new ByteArrayInputStream(new byte[0])); - return result; + public LowLevelHttpRequest buildRequest(String method, final String url) + throws IOException { + return new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + result.setStatusCode(status); + result.setContentType(null); + result.setContent(new ByteArrayInputStream(new byte[0])); + return result; + } + }; } }; - } - }; // Confirm that 'null' is returned when getting the response object of a // request with no message body. - Object parsed = transport.createRequestFactory() - .buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL) - .setThrowExceptionOnExecuteError(false) - .execute() - .parseAs((Type) Object.class); + Object parsed = + transport + .createRequestFactory() + .buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL) + .setThrowExceptionOnExecuteError(false) + .execute() + .parseAs((Type) Object.class); assertNull(parsed); } } + @Test public void testDownload() throws Exception { - HttpTransport transport = new MockHttpTransport() { - @Override - public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { - return new MockLowLevelHttpRequest() { + HttpTransport transport = + new MockHttpTransport() { @Override - public LowLevelHttpResponse execute() throws IOException { - MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); - result.setContentType(Json.MEDIA_TYPE); - result.setContent(SAMPLE); - return result; + public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { + return new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); + result.setContentType(Json.MEDIA_TYPE); + result.setContent(SAMPLE); + return result; + } + }; } }; - } - }; HttpRequest request = transport.createRequestFactory().buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL); HttpResponse response = request.execute(); @@ -272,23 +455,24 @@ public LowLevelHttpResponse execute() throws IOException { assertEquals(SAMPLE, outputStream.toString("UTF-8")); } + @Test public void testDisconnectWithContent() throws Exception { - final MockLowLevelHttpResponse lowLevelHttpResponse = - new MockLowLevelHttpResponse(); + final MockLowLevelHttpResponse lowLevelHttpResponse = new MockLowLevelHttpResponse(); - HttpTransport transport = new MockHttpTransport() { - @Override - public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { - return new MockLowLevelHttpRequest() { + HttpTransport transport = + new MockHttpTransport() { @Override - public LowLevelHttpResponse execute() throws IOException { - lowLevelHttpResponse.setContentType(Json.MEDIA_TYPE); - lowLevelHttpResponse.setContent(SAMPLE); - return lowLevelHttpResponse; + public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { + return new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + lowLevelHttpResponse.setContentType(Json.MEDIA_TYPE); + lowLevelHttpResponse.setContent(SAMPLE); + return lowLevelHttpResponse; + } + }; } }; - } - }; HttpRequest request = transport.createRequestFactory().buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL); HttpResponse response = request.execute(); @@ -302,21 +486,22 @@ public LowLevelHttpResponse execute() throws IOException { assertTrue(content.isClosed()); } + @Test public void testDisconnectWithNoContent() throws Exception { - final MockLowLevelHttpResponse lowLevelHttpResponse = - new MockLowLevelHttpResponse(); + final MockLowLevelHttpResponse lowLevelHttpResponse = new MockLowLevelHttpResponse(); - HttpTransport transport = new MockHttpTransport() { - @Override - public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { - return new MockLowLevelHttpRequest() { + HttpTransport transport = + new MockHttpTransport() { @Override - public LowLevelHttpResponse execute() throws IOException { - return lowLevelHttpResponse; + public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { + return new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + return lowLevelHttpResponse; + } + }; } }; - } - }; HttpRequest request = transport.createRequestFactory().buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL); HttpResponse response = request.execute(); @@ -326,6 +511,7 @@ public LowLevelHttpResponse execute() throws IOException { assertTrue(lowLevelHttpResponse.isDisconnected()); } + @Test public void testContentLoggingLimitWithLoggingEnabledAndDisabled() throws Exception { subtestContentLoggingLimit("", 2, false); subtestContentLoggingLimit("A", 2, false); @@ -345,26 +531,38 @@ public void testContentLoggingLimitWithLoggingEnabledAndDisabled() throws Except Arrays.fill(a, 'x'); String big = new String(a); String formated18kInteger = NumberFormat.getInstance().format(18000); - subtestContentLoggingLimit(big, Integer.MAX_VALUE, true, String.format("Total: %s bytes", formated18kInteger), big); - subtestContentLoggingLimit(big, 4, true, String.format("Total: %s bytes (logging first 4 bytes)", formated18kInteger), "xxxx"); + subtestContentLoggingLimit( + big, Integer.MAX_VALUE, true, String.format("Total: %s bytes", formated18kInteger), big); + subtestContentLoggingLimit( + big, + 4, + true, + String.format("Total: %s bytes (logging first 4 bytes)", formated18kInteger), + "xxxx"); } - public void subtestContentLoggingLimit(final String content, int contentLoggingLimit, - boolean loggingEnabled, String... expectedMessages) throws Exception { - HttpTransport transport = new MockHttpTransport() { - @Override - public LowLevelHttpRequest buildRequest(String method, final String url) throws IOException { - return new MockLowLevelHttpRequest() { + public void subtestContentLoggingLimit( + final String content, + int contentLoggingLimit, + boolean loggingEnabled, + String... expectedMessages) + throws Exception { + HttpTransport transport = + new MockHttpTransport() { @Override - public LowLevelHttpResponse execute() throws IOException { - MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); - result.setContent(content); - result.setContentType("text/plain"); - return result; + public LowLevelHttpRequest buildRequest(String method, final String url) + throws IOException { + return new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); + result.setContent(content); + result.setContentType("text/plain"); + return result; + } + }; } }; - } - }; HttpTransport.LOGGER.setLevel(Level.CONFIG); HttpRequest request = @@ -380,24 +578,224 @@ public LowLevelHttpResponse execute() throws IOException { assertEquals(Arrays.asList(expectedMessages), recorder.messages()); } + @Test public void testGetContent_gzipNoContent() throws IOException { - HttpTransport transport = new MockHttpTransport() { - @Override - public LowLevelHttpRequest buildRequest(String method, final String url) throws IOException { - return new MockLowLevelHttpRequest() { + HttpTransport transport = + new MockHttpTransport() { + @Override + public LowLevelHttpRequest buildRequest(String method, final String url) + throws IOException { + return new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); + result.setContent(""); + result.setContentEncoding("gzip"); + result.setContentType("text/plain"); + return result; + } + }; + } + }; + HttpRequest request = + transport.createRequestFactory().buildHeadRequest(HttpTesting.SIMPLE_GENERIC_URL); + InputStream noContent = request.execute().getContent(); + assertNull(noContent); + } + + @Test + public void testGetContent_gzipEncoding_ReturnRawStream() throws IOException { + HttpTransport transport = + new MockHttpTransport() { @Override - public LowLevelHttpResponse execute() throws IOException { - MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); - result.setContent(""); - result.setContentEncoding("gzip"); - result.setContentType("text/plain"); - return result; + public LowLevelHttpRequest buildRequest(String method, final String url) + throws IOException { + return new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); + result.setContent(""); + result.setContentEncoding("gzip"); + result.setContentType("text/plain"); + return result; + } + }; } }; + HttpRequest request = + transport.createRequestFactory().buildHeadRequest(HttpTesting.SIMPLE_GENERIC_URL); + request.setResponseReturnRawInputStream(true); + assertFalse( + "it should not decompress stream", + request.execute().getContent() instanceof GZIPInputStream); + assertFalse( + "it should not buffer stream", + request.execute().getContent() instanceof BufferedInputStream); + } + + @Test + public void testGetContent_gzipEncoding_finishReading() throws IOException { + do_testGetContent_gzipEncoding_finishReading("gzip"); + } + + @Test + public void testGetContent_gzipEncoding_finishReadingWithUppercaseContentEncoding() + throws IOException { + do_testGetContent_gzipEncoding_finishReading("GZIP"); + } + + @Test + public void + testGetContent_gzipEncoding_finishReadingWithDifferentDefaultLocaleAndUppercaseContentEncoding() + throws IOException { + Locale originalDefaultLocale = Locale.getDefault(); + try { + Locale.setDefault(Locale.forLanguageTag("tr-TR")); + do_testGetContent_gzipEncoding_finishReading("GZIP"); + } finally { + Locale.setDefault(originalDefaultLocale); + } + } + + private void do_testGetContent_gzipEncoding_finishReading(String contentEncoding) + throws IOException { + byte[] dataToCompress = "abcd".getBytes(StandardCharsets.UTF_8); + byte[] mockBytes; + try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream(dataToCompress.length); + GZIPOutputStream zipStream = new GZIPOutputStream((byteStream))) { + zipStream.write(dataToCompress); + zipStream.close(); + + // GZIPInputStream uses a default buffer of 512B. Add enough content to exceed this + // limit, so that some content will be left in the connection. + for (int i = 0; i < 1024; i++) { + byteStream.write('7'); } - }; + mockBytes = byteStream.toByteArray(); + } + final MockLowLevelHttpResponse mockResponse = new MockLowLevelHttpResponse(); + mockResponse.setContent(mockBytes); + mockResponse.setContentEncoding(contentEncoding); + mockResponse.setContentType("text/plain"); + + HttpTransport transport = + new MockHttpTransport() { + @Override + public LowLevelHttpRequest buildRequest(String method, final String url) + throws IOException { + return new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + return mockResponse; + } + }; + } + }; + HttpRequest request = + transport.createRequestFactory().buildHeadRequest(HttpTesting.SIMPLE_GENERIC_URL); + HttpResponse response = request.execute(); + try (TestableByteArrayInputStream output = + (TestableByteArrayInputStream) mockResponse.getContent()) { + assertFalse(output.isClosed()); + assertEquals("abcd", response.parseAsString()); + assertTrue(output.isClosed()); + // The underlying stream should be fully consumed, even if gzip only returns some of it. + assertEquals(-1, output.read()); + } + } + + @Test + public void testGetContent_otherEncodingWithgzipInItsName_GzipIsNotUsed() throws IOException { + final MockLowLevelHttpResponse mockResponse = new MockLowLevelHttpResponse(); + mockResponse.setContent("abcd"); + mockResponse.setContentEncoding("otherEncodingWithgzipInItsName"); + mockResponse.setContentType("text/plain"); + + HttpTransport transport = + new MockHttpTransport() { + @Override + public LowLevelHttpRequest buildRequest(String method, final String url) + throws IOException { + return new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + return mockResponse; + } + }; + } + }; HttpRequest request = transport.createRequestFactory().buildHeadRequest(HttpTesting.SIMPLE_GENERIC_URL); - request.execute().getContent(); + // If gzip was used on this response, an exception would be thrown + HttpResponse response = request.execute(); + assertEquals("abcd", response.parseAsString()); + } + + @Test + public void testGetContent_bufferedContent() throws IOException { + HttpTransport transport = + new MockHttpTransport() { + @Override + public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { + return new MockLowLevelHttpRequest() { + @Override + public LowLevelHttpResponse execute() throws IOException { + // have to use gzip here because MockLowLevelHttpResponse.setContent() + // returns BufferedStream by itself, so test always success + byte[] dataToCompress = ERROR_SAMPLE.getBytes(StandardCharsets.UTF_8); + ByteArrayOutputStream content = new ByteArrayOutputStream(dataToCompress.length); + try (GZIPOutputStream zipStream = new GZIPOutputStream((content))) { + zipStream.write(dataToCompress); + } + + MockLowLevelHttpResponse result = new MockLowLevelHttpResponse(); + result.setStatusCode(403); + result.setContentType(JSON_CONTENT_TYPE); + result.setContentEncoding("gzip"); + result.setContent(content.toByteArray()); + + return result; + } + }; + } + }; + HttpRequest request = + transport + .createRequestFactory() + .buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL) + .setThrowExceptionOnExecuteError(false); + + HttpResponse response = request.execute(); + InputStream content = response.getContent(); + assertTrue(content.markSupported()); + + // inspect content like in HttpUnsuccessfulResponseHandler + try (RollbackInputStream is = new RollbackInputStream(content)) { + byte[] bytes = ByteStreams.toByteArray(is); + String text = new String(bytes, response.getContentCharset()); + assertEquals(ERROR_SAMPLE, text); + } + + // original response still parsable by HttpResponseException + HttpResponseException exception = new HttpResponseException(response); + assertEquals(exception.getStatusCode(), 403); + assertEquals(exception.getContent(), ERROR_SAMPLE); + } + + static class RollbackInputStream extends FilterInputStream { + private boolean closed; + + RollbackInputStream(InputStream in) { + super(in); + in.mark(8192); // big enough to keep most error messages + } + + @Override + public void close() throws IOException { + if (!closed) { + closed = true; + in.reset(); + } + } } } diff --git a/google-http-client/src/test/java/com/google/api/client/http/HttpStatusCodesTest.java b/google-http-client/src/test/java/com/google/api/client/http/HttpStatusCodesTest.java new file mode 100644 index 000000000..16a7c4bef --- /dev/null +++ b/google-http-client/src/test/java/com/google/api/client/http/HttpStatusCodesTest.java @@ -0,0 +1,43 @@ +/* + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.http; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests {@link HttpStatusCodes}. */ +@RunWith(JUnit4.class) +public class HttpStatusCodesTest { + + @Test + public void testIsRedirect_3xx() { + assertTrue(HttpStatusCodes.isRedirect(301)); + assertTrue(HttpStatusCodes.isRedirect(302)); + assertTrue(HttpStatusCodes.isRedirect(303)); + assertTrue(HttpStatusCodes.isRedirect(307)); + assertTrue(HttpStatusCodes.isRedirect(308)); + } + + @Test + public void testIsRedirect_non3xx() { + assertFalse(HttpStatusCodes.isRedirect(200)); + assertFalse(HttpStatusCodes.isRedirect(401)); + assertFalse(HttpStatusCodes.isRedirect(500)); + } +} diff --git a/google-http-client/src/test/java/com/google/api/client/http/MultipartContentTest.java b/google-http-client/src/test/java/com/google/api/client/http/MultipartContentTest.java index fd6c08b4b..198534f6f 100644 --- a/google-http-client/src/test/java/com/google/api/client/http/MultipartContentTest.java +++ b/google-http-client/src/test/java/com/google/api/client/http/MultipartContentTest.java @@ -14,39 +14,119 @@ package com.google.api.client.http; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + import com.google.api.client.json.Json; import com.google.api.client.util.StringUtils; import java.io.ByteArrayOutputStream; -import junit.framework.TestCase; +import java.nio.charset.StandardCharsets; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link MultipartContent}. * * @author Yaniv Inbar */ -public class MultipartContentTest extends TestCase { +@RunWith(JUnit4.class) +public class MultipartContentTest { + private static final String BOUNDARY = "__END_OF_PART__"; private static final String CRLF = "\r\n"; private static final String CONTENT_TYPE = Json.MEDIA_TYPE; - private static final String HEADERS = "Content-Length: 3" + CRLF - + "Content-Type: application/json; charset=UTF-8" + CRLF + "content-transfer-encoding: binary" - + CRLF; + private static final String HEADERS = headers("application/json; charset=UTF-8", "foo"); + + private static String headers(String contentType, String value) { + return "Content-Length: " + + value.length() + + CRLF + + "Content-Type: " + + contentType + + CRLF + + "content-transfer-encoding: binary" + + CRLF; + } + + @Test + public void testRandomContent() throws Exception { + MultipartContent content = new MultipartContent(); + String boundaryString = content.getBoundary(); + assertNotNull(boundaryString); + assertTrue(boundaryString.startsWith(BOUNDARY)); + assertTrue(boundaryString.endsWith("__")); + assertEquals("multipart/related; boundary=" + boundaryString, content.getType()); + + final String[][] VALUES = + new String[][] { + {"Hello world", "text/plain"}, + {"Hi", "application/xml"}, + {"{x:1,y:2}", "application/json"} + }; + StringBuilder expectedStringBuilder = new StringBuilder(); + for (String[] valueTypePair : VALUES) { + String contentValue = valueTypePair[0]; + String contentType = valueTypePair[1]; + content.addPart( + new MultipartContent.Part(ByteArrayContent.fromString(contentType, contentValue))); + expectedStringBuilder + .append("--") + .append(boundaryString) + .append(CRLF) + .append(headers(contentType, contentValue)) + .append(CRLF) + .append(contentValue) + .append(CRLF); + } + expectedStringBuilder.append("--").append(boundaryString).append("--").append(CRLF); + // write to string + ByteArrayOutputStream out = new ByteArrayOutputStream(); + content.writeTo(out); + String expectedContent = expectedStringBuilder.toString(); + assertEquals(expectedContent, out.toString(StandardCharsets.UTF_8.name())); + assertEquals(StringUtils.getBytesUtf8(expectedContent).length, content.getLength()); + } + @Test public void testContent() throws Exception { - subtestContent("--__END_OF_PART__--" + CRLF, null); + subtestContent("--" + BOUNDARY + "--" + CRLF, null); + subtestContent( + "--" + BOUNDARY + CRLF + HEADERS + CRLF + "foo" + CRLF + "--" + BOUNDARY + "--" + CRLF, + null, + "foo"); subtestContent( - "--__END_OF_PART__" + CRLF + HEADERS + CRLF + "foo" + CRLF + "--__END_OF_PART__--" + CRLF, - null, "foo"); - subtestContent("--__END_OF_PART__" + CRLF + HEADERS + CRLF + "foo" + CRLF + "--__END_OF_PART__" - + CRLF + HEADERS + CRLF + "bar" + CRLF + "--__END_OF_PART__--" + CRLF, null, "foo", "bar"); - subtestContent("--myboundary" + CRLF + HEADERS + CRLF + "foo" + CRLF + "--myboundary" + CRLF - + HEADERS + CRLF + "bar" + CRLF + "--myboundary--" + CRLF, "myboundary", "foo", "bar"); + "--" + BOUNDARY + CRLF + HEADERS + CRLF + "foo" + CRLF + "--" + BOUNDARY + CRLF + HEADERS + + CRLF + "bar" + CRLF + "--" + BOUNDARY + "--" + CRLF, + null, + "foo", + "bar"); + subtestContent( + "--myboundary" + + CRLF + + HEADERS + + CRLF + + "foo" + + CRLF + + "--myboundary" + + CRLF + + HEADERS + + CRLF + + "bar" + + CRLF + + "--myboundary--" + + CRLF, + "myboundary", + "foo", + "bar"); } private void subtestContent(String expectedContent, String boundaryString, String... contents) throws Exception { // multipart content - MultipartContent content = new MultipartContent(); + MultipartContent content = + new MultipartContent(boundaryString == null ? BOUNDARY : boundaryString); for (String contentValue : contents) { content.addPart( new MultipartContent.Part(ByteArrayContent.fromString(CONTENT_TYPE, contentValue))); @@ -57,9 +137,12 @@ private void subtestContent(String expectedContent, String boundaryString, Strin // write to string ByteArrayOutputStream out = new ByteArrayOutputStream(); content.writeTo(out); - assertEquals(expectedContent, out.toString()); + assertEquals(expectedContent, out.toString(StandardCharsets.UTF_8.name())); assertEquals(StringUtils.getBytesUtf8(expectedContent).length, content.getLength()); - assertEquals(boundaryString == null ? "multipart/related; boundary=__END_OF_PART__" : - "multipart/related; boundary=" + boundaryString, content.getType()); + assertEquals( + boundaryString == null + ? "multipart/related; boundary=" + BOUNDARY + : "multipart/related; boundary=" + boundaryString, + content.getType()); } } diff --git a/google-http-client/src/test/java/com/google/api/client/http/OpenCensusUtilsTest.java b/google-http-client/src/test/java/com/google/api/client/http/OpenCensusUtilsTest.java new file mode 100644 index 000000000..f29d6b986 --- /dev/null +++ b/google-http-client/src/test/java/com/google/api/client/http/OpenCensusUtilsTest.java @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2018 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.http; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import io.opencensus.trace.Annotation; +import io.opencensus.trace.AttributeValue; +import io.opencensus.trace.BlankSpan; +import io.opencensus.trace.EndSpanOptions; +import io.opencensus.trace.Link; +import io.opencensus.trace.MessageEvent; +import io.opencensus.trace.Span; +import io.opencensus.trace.SpanContext; +import io.opencensus.trace.Status; +import io.opencensus.trace.Tracer; +import io.opencensus.trace.propagation.TextFormat; +import java.util.List; +import java.util.Map; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Tests {@link OpenCensusUtils}. + * + * @author Hailong Wen + */ +@RunWith(JUnit4.class) +public class OpenCensusUtilsTest { + + TextFormat mockTextFormat; + TextFormat.Setter mockTextFormatSetter; + TextFormat originTextFormat; + TextFormat.Setter originTextFormatSetter; + Span mockSpan; + HttpHeaders headers; + Tracer tracer; + + @Before + public void setUp() { + mockTextFormat = + new TextFormat() { + @Override + public List fields() { + throw new UnsupportedOperationException("TextFormat.fields"); + } + + @Override + public void inject(SpanContext spanContext, C carrier, Setter setter) { + throw new UnsupportedOperationException("TextFormat.inject"); + } + + @Override + public SpanContext extract(C carrier, Getter getter) { + throw new UnsupportedOperationException("TextFormat.extract"); + } + }; + mockTextFormatSetter = + new TextFormat.Setter() { + @Override + public void put(HttpHeaders carrier, String key, String value) { + throw new UnsupportedOperationException("TextFormat.Setter.put"); + } + }; + headers = new HttpHeaders(); + tracer = OpenCensusUtils.getTracer(); + mockSpan = + new Span(tracer.getCurrentSpan().getContext(), null) { + + @Override + public void addAnnotation(String description, Map attributes) {} + + @Override + public void addAnnotation(Annotation annotation) {} + + @Override + public void addMessageEvent(MessageEvent event) { + throw new UnsupportedOperationException("Span.addMessageEvent"); + } + + @Override + public void addLink(Link link) {} + + @Override + public void end(EndSpanOptions options) {} + }; + originTextFormat = OpenCensusUtils.propagationTextFormat; + originTextFormatSetter = OpenCensusUtils.propagationTextFormatSetter; + } + + @After + public void tearDown() { + OpenCensusUtils.setPropagationTextFormat(originTextFormat); + OpenCensusUtils.setPropagationTextFormatSetter(originTextFormatSetter); + } + + @Test + public void testInitialization() { + assertNotNull(OpenCensusUtils.getTracer()); + assertNotNull(OpenCensusUtils.propagationTextFormat); + assertNotNull(OpenCensusUtils.propagationTextFormatSetter); + } + + @Test + public void testSetPropagationTextFormat() { + OpenCensusUtils.setPropagationTextFormat(mockTextFormat); + assertEquals(mockTextFormat, OpenCensusUtils.propagationTextFormat); + } + + @Test + public void testSetPropagationTextFormatSetter() { + OpenCensusUtils.setPropagationTextFormatSetter(mockTextFormatSetter); + assertEquals(mockTextFormatSetter, OpenCensusUtils.propagationTextFormatSetter); + } + + @Test + public void testPropagateTracingContextInjection() { + OpenCensusUtils.setPropagationTextFormat(mockTextFormat); + try { + OpenCensusUtils.propagateTracingContext(mockSpan, headers); + fail("expected " + UnsupportedOperationException.class); + } catch (UnsupportedOperationException e) { + assertEquals(e.getMessage(), "TextFormat.inject"); + } + } + + @Test + public void testPropagateTracingContextHeader() { + OpenCensusUtils.setPropagationTextFormatSetter(mockTextFormatSetter); + try { + OpenCensusUtils.propagateTracingContext(mockSpan, headers); + fail("expected " + UnsupportedOperationException.class); + } catch (UnsupportedOperationException e) { + assertEquals(e.getMessage(), "TextFormat.Setter.put"); + } + } + + @Test + public void testPropagateTracingContextNullSpan() { + OpenCensusUtils.setPropagationTextFormat(mockTextFormat); + try { + OpenCensusUtils.propagateTracingContext(null, headers); + fail("expected " + IllegalArgumentException.class); + } catch (IllegalArgumentException e) { + assertEquals(e.getMessage(), "span should not be null."); + } + } + + @Test + public void testPropagateTracingContextNullHeaders() { + OpenCensusUtils.setPropagationTextFormat(mockTextFormat); + try { + OpenCensusUtils.propagateTracingContext(mockSpan, null); + fail("expected " + IllegalArgumentException.class); + } catch (IllegalArgumentException e) { + assertEquals(e.getMessage(), "headers should not be null."); + } + } + + @Test + public void testPropagateTracingContextInvalidSpan() { + OpenCensusUtils.setPropagationTextFormat(mockTextFormat); + // No injection. No exceptions should be thrown. + OpenCensusUtils.propagateTracingContext(BlankSpan.INSTANCE, headers); + } + + @Test + public void testGetEndSpanOptionsNoResponse() { + EndSpanOptions expected = EndSpanOptions.builder().setStatus(Status.UNKNOWN).build(); + assertEquals(expected, OpenCensusUtils.getEndSpanOptions(null)); + } + + @Test + public void testGetEndSpanOptionsSuccess() { + EndSpanOptions expected = EndSpanOptions.builder().setStatus(Status.OK).build(); + assertEquals(expected, OpenCensusUtils.getEndSpanOptions(200)); + assertEquals(expected, OpenCensusUtils.getEndSpanOptions(201)); + assertEquals(expected, OpenCensusUtils.getEndSpanOptions(202)); + } + + @Test + public void testGetEndSpanOptionsBadRequest() { + EndSpanOptions expected = EndSpanOptions.builder().setStatus(Status.INVALID_ARGUMENT).build(); + assertEquals(expected, OpenCensusUtils.getEndSpanOptions(400)); + } + + @Test + public void testGetEndSpanOptionsUnauthorized() { + EndSpanOptions expected = EndSpanOptions.builder().setStatus(Status.UNAUTHENTICATED).build(); + assertEquals(expected, OpenCensusUtils.getEndSpanOptions(401)); + } + + @Test + public void testGetEndSpanOptionsForbidden() { + EndSpanOptions expected = EndSpanOptions.builder().setStatus(Status.PERMISSION_DENIED).build(); + assertEquals(expected, OpenCensusUtils.getEndSpanOptions(403)); + } + + @Test + public void testGetEndSpanOptionsNotFound() { + EndSpanOptions expected = EndSpanOptions.builder().setStatus(Status.NOT_FOUND).build(); + assertEquals(expected, OpenCensusUtils.getEndSpanOptions(404)); + } + + @Test + public void testGetEndSpanOptionsPreconditionFailed() { + EndSpanOptions expected = + EndSpanOptions.builder().setStatus(Status.FAILED_PRECONDITION).build(); + assertEquals(expected, OpenCensusUtils.getEndSpanOptions(412)); + } + + @Test + public void testGetEndSpanOptionsServerError() { + EndSpanOptions expected = EndSpanOptions.builder().setStatus(Status.UNAVAILABLE).build(); + assertEquals(expected, OpenCensusUtils.getEndSpanOptions(500)); + } + + @Test + public void testGetEndSpanOptionsOther() { + EndSpanOptions expected = EndSpanOptions.builder().setStatus(Status.UNKNOWN).build(); + // test some random unsupported statuses + assertEquals(expected, OpenCensusUtils.getEndSpanOptions(301)); + assertEquals(expected, OpenCensusUtils.getEndSpanOptions(402)); + assertEquals(expected, OpenCensusUtils.getEndSpanOptions(501)); + } + + @Test + public void testRecordMessageEventInNullSpan() { + try { + OpenCensusUtils.recordMessageEvent(null, 0, MessageEvent.Type.SENT); + fail("expected " + IllegalArgumentException.class); + } catch (IllegalArgumentException e) { + assertEquals(e.getMessage(), "span should not be null."); + } + } + + @Test + public void testRecordMessageEvent() { + try { + OpenCensusUtils.recordMessageEvent(mockSpan, 0, MessageEvent.Type.SENT); + fail("expected " + UnsupportedOperationException.class); + } catch (UnsupportedOperationException e) { + assertEquals(e.getMessage(), "Span.addMessageEvent"); + } + } +} diff --git a/google-http-client/src/test/java/com/google/api/client/http/UriTemplateTest.java b/google-http-client/src/test/java/com/google/api/client/http/UriTemplateTest.java index 2dfcc1f3d..9fc90ba85 100644 --- a/google-http-client/src/test/java/com/google/api/client/http/UriTemplateTest.java +++ b/google-http-client/src/test/java/com/google/api/client/http/UriTemplateTest.java @@ -14,6 +14,9 @@ package com.google.api.client.http; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import com.google.api.client.util.Value; import com.google.common.collect.Lists; import com.google.common.collect.Maps; @@ -21,14 +24,17 @@ import java.util.List; import java.util.Map; import java.util.SortedMap; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link UriTemplate}. * * @author Ravi Mistry */ -public class UriTemplateTest extends TestCase { +@RunWith(JUnit4.class) +public class UriTemplateTest { public void testExpandTemplates_initialization() { SortedMap map = Maps.newTreeMap(); @@ -39,6 +45,7 @@ public void testExpandTemplates_initialization() { assertEquals("/a/b/c", UriTemplate.expand("{/id*}", map, false)); } + @Test public void testExpandTemplates_basic() { SortedMap requestMap = Maps.newTreeMap(); requestMap.put("abc", "xyz"); @@ -48,7 +55,8 @@ public void testExpandTemplates_basic() { // Assert with addUnusedParamsAsQueryParams = false. assertEquals("foo/xyz/bar/123", UriTemplate.expand("foo/{abc}/bar/{def}", requestMap, false)); // Assert with addUnusedParamsAsQueryParams = true. - assertEquals("foo/xyz/bar/123?unused=unused%20parameter", + assertEquals( + "foo/xyz/bar/123?unused=unused%20parameter", UriTemplate.expand("foo/{abc}/bar/{def}", requestMap, true)); // Assert the map has not changed. assertEquals(3, requestMap.size()); @@ -57,14 +65,31 @@ public void testExpandTemplates_basic() { assertTrue(requestMap.containsKey("unused")); } + @Test + public void testExpandTemplates_basicEncodeValue() { + SortedMap requestMap = Maps.newTreeMap(); + requestMap.put("abc", "xyz;def"); + assertEquals(";abc=xyz%3Bdef", UriTemplate.expand("{;abc}", requestMap, false)); + assertEquals("xyz;def", UriTemplate.expand("{+abc}", requestMap, false)); + } + + @Test + public void testExpandTemplates_encodeSpace() { + SortedMap requestMap = Maps.newTreeMap(); + requestMap.put("abc", "xyz def"); + assertEquals(";abc=xyz%20def", UriTemplate.expand("{;abc}", requestMap, false)); + } + + @Test public void testExpandTemplates_noExpansionsWithQueryParams() { SortedMap requestMap = Maps.newTreeMap(); requestMap.put("abc", "xyz"); requestMap.put("def", "123"); - assertEquals("foo/xyz/bar/123?abc=xyz&def=123", - UriTemplate.expand("foo/xyz/bar/123", requestMap, true)); + assertEquals( + "foo/xyz/bar/123?abc=xyz&def=123", UriTemplate.expand("foo/xyz/bar/123", requestMap, true)); } + @Test public void testExpandTemplates_noExpansionsWithoutQueryParams() { SortedMap requestMap = Maps.newTreeMap(); requestMap.put("abc", "xyz"); @@ -72,12 +97,14 @@ public void testExpandTemplates_noExpansionsWithoutQueryParams() { assertEquals("foo/xyz/bar/123", UriTemplate.expand("foo/xyz/bar/123", requestMap, false)); } + @Test public void testExpandTemplates_missingParameter() { SortedMap requestMap = Maps.newTreeMap(); requestMap.put("abc", "xyz"); assertEquals("foo/xyz/bar/", UriTemplate.expand("foo/{abc}/bar/{def}", requestMap, true)); } + @Test public void testExpandTemplates_nullValue() { SortedMap requestMap = Maps.newTreeMap(); requestMap.put("abc", "xyz"); @@ -85,6 +112,7 @@ public void testExpandTemplates_nullValue() { assertEquals("foo/xyz/bar/", UriTemplate.expand("foo/{abc}/bar/{def}", requestMap, true)); } + @Test public void testExpandTemplates_emptyAndNullRequestMap() { SortedMap requestMap = Maps.newTreeMap(); assertEquals("foo//bar/", UriTemplate.expand("foo/{abc}/bar/{def}", requestMap, true)); @@ -97,24 +125,25 @@ private Iterable getListIterable() { // template, expected output. private static final String[][] LIST_TESTS = { - {"{d}", "red,green,blue"}, - {"{d*}", "red,green,blue"}, - {"{+d}", "red,green,blue"}, - {"{+d*}", "red,green,blue"}, - {"{#d}", "#red,green,blue"}, - {"{#d*}", "#red,green,blue"}, - {"X{.d}", "X.red,green,blue"}, - {"X{.d*}", "X.red.green.blue"}, - {"{/d}", "/red,green,blue"}, - {"{/d*}", "/red/green/blue"}, - {"{;d}", ";d=red,green,blue"}, - {"{;d*}", ";d=red;d=green;d=blue"}, - {"{?d}", "?d=red,green,blue"}, - {"{?d*}", "?d=red&d=green&d=blue"}, - {"{&d}", "&d=red,green,blue"}, - {"{&d*}", "&d=red&d=green&d=blue"}, + {"{d}", "red,green,blue"}, + {"{d*}", "red,green,blue"}, + {"{+d}", "red,green,blue"}, + {"{+d*}", "red,green,blue"}, + {"{#d}", "#red,green,blue"}, + {"{#d*}", "#red,green,blue"}, + {"X{.d}", "X.red,green,blue"}, + {"X{.d*}", "X.red.green.blue"}, + {"{/d}", "/red,green,blue"}, + {"{/d*}", "/red/green/blue"}, + {"{;d}", ";d=red,green,blue"}, + {"{;d*}", ";d=red;d=green;d=blue"}, + {"{?d}", "?d=red,green,blue"}, + {"{?d*}", "?d=red&d=green&d=blue"}, + {"{&d}", "&d=red,green,blue"}, + {"{&d*}", "&d=red&d=green&d=blue"}, }; + @Test public void testExpandTemplates_explodeIterator() { for (String[] test : LIST_TESTS) { SortedMap requestMap = Maps.newTreeMap(); @@ -123,6 +152,7 @@ public void testExpandTemplates_explodeIterator() { } } + @Test public void testExpandTemplates_explodeIterable() { for (String[] test : LIST_TESTS) { SortedMap requestMap = Maps.newTreeMap(); @@ -136,12 +166,14 @@ enum testEnum { ONE } + @Test public void testExpandTemplates_explodeEnum() { SortedMap requestMap = Maps.newTreeMap(); requestMap.put("d", testEnum.ONE); assertEquals(testEnum.ONE.toString(), UriTemplate.expand("{d}", requestMap, true)); } + @Test public void testExpandTemplates_missingCompositeParameter() { SortedMap requestMap = Maps.newTreeMap(); requestMap.put("abc", "xyz"); @@ -149,6 +181,7 @@ public void testExpandTemplates_missingCompositeParameter() { assertEquals("?abc=xyz", UriTemplate.expand("{d}", requestMap, true)); } + @Test public void testExpandTemplates_emptyListParameter() { SortedMap requestMap = Maps.newTreeMap(); requestMap.put("d", Lists.newArrayList()); @@ -165,24 +198,25 @@ private Map getMapParams() { // template, expected output. private static final String[][] MAP_TESTS = { - {"{d}", "semi,%3B,dot,.,comma,%2C"}, - {"{d*}", "semi=%3B,dot=.,comma=%2C"}, - {"{+d}", "semi,;,dot,.,comma,,"}, - {"{+d*}", "semi=;,dot=.,comma=,"}, - {"{#d}", "#semi,;,dot,.,comma,,"}, - {"{#d*}", "#semi=;,dot=.,comma=,"}, - {"X{.d}", "X.semi,%3B,dot,.,comma,%2C"}, - {"X{.d*}", "X.semi=%3B.dot=..comma=%2C"}, - {"{/d}", "/semi,%3B,dot,.,comma,%2C"}, - {"{/d*}", "/semi=%3B/dot=./comma=%2C"}, - {"{;d}", ";d=semi,%3B,dot,.,comma,%2C"}, - {"{;d*}", ";semi=%3B;dot=.;comma=%2C"}, - {"{?d}", "?d=semi,%3B,dot,.,comma,%2C"}, - {"{?d*}", "?semi=%3B&dot=.&comma=%2C"}, - {"{&d}", "&d=semi,%3B,dot,.,comma,%2C"}, - {"{&d*}", "&semi=%3B&dot=.&comma=%2C"}, + {"{d}", "semi,%3B,dot,.,comma,%2C"}, + {"{d*}", "semi=%3B,dot=.,comma=%2C"}, + {"{+d}", "semi,;,dot,.,comma,,"}, + {"{+d*}", "semi=;,dot=.,comma=,"}, + {"{#d}", "#semi,;,dot,.,comma,,"}, + {"{#d*}", "#semi=;,dot=.,comma=,"}, + {"X{.d}", "X.semi,%3B,dot,.,comma,%2C"}, + {"X{.d*}", "X.semi=%3B.dot=..comma=%2C"}, + {"{/d}", "/semi,%3B,dot,.,comma,%2C"}, + {"{/d*}", "/semi=%3B/dot=./comma=%2C"}, + {"{;d}", ";d=semi,%3B,dot,.,comma,%2C"}, + {"{;d*}", ";semi=%3B;dot=.;comma=%2C"}, + {"{?d}", "?d=semi,%3B,dot,.,comma,%2C"}, + {"{?d*}", "?semi=%3B&dot=.&comma=%2C"}, + {"{&d}", "&d=semi,%3B,dot,.,comma,%2C"}, + {"{&d*}", "&semi=%3B&dot=.&comma=%2C"}, }; + @Test public void testExpandTemplates_explodeMap() { for (String[] test : MAP_TESTS) { SortedMap requestMap = Maps.newTreeMap(); @@ -191,22 +225,26 @@ public void testExpandTemplates_explodeMap() { } } + @Test public void testExpandTemplates_emptyMapParameter() { SortedMap requestMap = Maps.newTreeMap(); requestMap.put("d", Maps.newTreeMap()); assertEquals("", UriTemplate.expand("{d}", requestMap, true)); } + @Test public void testExpandTemplates_unusedQueryParametersEncoding() { Map requestMap = Maps.newLinkedHashMap(); // Add unused params. requestMap.put("unused1", "abc!1234?"); requestMap.put("unused2", "56$7 8"); requestMap.put("unused3", "9=&/:@."); - assertEquals("?unused1=abc!1234?&unused2=56$7%208&unused3=9%3D%26/:@.", + assertEquals( + "?unused1=abc!1234?&unused2=56$7%208&unused3=9%3D%26/:@.", UriTemplate.expand("", requestMap, true)); } + @Test public void testExpandTemplates_unusedListQueryParameters() { Map requestMap = Maps.newLinkedHashMap(); // Add unused params. @@ -216,10 +254,12 @@ public void testExpandTemplates_unusedListQueryParameters() { requestMap.put("unused1", params); requestMap.put("unused2", "56$7 8"); requestMap.put("unused3", "9=&/:@."); - assertEquals("?unused1=value1&unused1=value2&unused2=56$7%208&unused3=9%3D%26/:@.", + assertEquals( + "?unused1=value1&unused1=value2&unused2=56$7%208&unused3=9%3D%26/:@.", UriTemplate.expand("", requestMap, true)); } + @Test public void testExpandTemplates_mixedBagParameters() { Map requestMap = Maps.newLinkedHashMap(); // Add list params. @@ -236,7 +276,7 @@ public void testExpandTemplates_mixedBagParameters() { requestMap.put("unused2", "unused=param"); assertEquals( "foo/xyz/red/green/blue&iterable=red&iterable=green&iterable=blue&map=semi,%3B,dot,.,comma" - + ",%2C&enum=ONE?unused1=unused%20param&unused2=unused%3Dparam", + + ",%2C&enum=ONE?unused1=unused%20param&unused2=unused%3Dparam", UriTemplate.expand("foo/{abc}{/iterator*}{&iterable*}{&map}{&enum}", requestMap, true)); // Assert the map has not changed. assertEquals(7, requestMap.size()); @@ -248,40 +288,48 @@ public void testExpandTemplates_mixedBagParameters() { assertTrue(requestMap.containsKey("unused2")); } + @Test public void testExpandTemplates_withBaseUrl() { SortedMap requestMap = Maps.newTreeMap(); requestMap.put("abc", "xyz"); requestMap.put("def", "123"); // Expand with URI template not starting with "/". - assertEquals("https://test/base/path/xyz/123/bar/", + assertEquals( + "https://test/base/path/xyz/123/bar/", UriTemplate.expand("https://test/base/path/", "{abc}/{def}/bar/", requestMap, true)); // Expand with URI template starting with "/". - assertEquals("https://test/xyz/123/bar/", + assertEquals( + "https://test/xyz/123/bar/", UriTemplate.expand("https://test/base/path/", "/{abc}/{def}/bar/", requestMap, true)); // Expand with URI template as a full URL. - assertEquals("http://test3/xyz/123/bar/", UriTemplate.expand("https://test/base/path/", - "http://test3/{abc}/{def}/bar/", requestMap, true)); + assertEquals( + "http://test3/xyz/123/bar/", + UriTemplate.expand( + "https://test/base/path/", "http://test3/{abc}/{def}/bar/", requestMap, true)); } + @Test public void testExpandNonReservedNonComposite() { SortedMap requestMap = Maps.newTreeMap(); requestMap.put("abc", "xyz"); requestMap.put("def", "a/b?c"); - assertEquals("foo/xyz/bar/a%2Fb%3Fc", - UriTemplate.expand("foo/{abc}/bar/{def}", requestMap, false)); - assertEquals("foo/xyz/bar/a/b?c", - UriTemplate.expand("foo/{abc}/bar/{+def}", requestMap, false)); + assertEquals( + "foo/xyz/bar/a%2Fb%3Fc", UriTemplate.expand("foo/{abc}/bar/{def}", requestMap, false)); + assertEquals( + "foo/xyz/bar/a/b?c", UriTemplate.expand("foo/{abc}/bar/{+def}", requestMap, false)); } + @Test public void testExpandSeveralTemplates() { - SortedMap map = Maps.newTreeMap(); - map.put("id", "a"); - map.put("uid", "b"); + SortedMap map = Maps.newTreeMap(); + map.put("id", "a"); + map.put("uid", "b"); - assertEquals("?id=a&uid=b", UriTemplate.expand("{?id,uid}", map, false)); + assertEquals("?id=a&uid=b", UriTemplate.expand("{?id,uid}", map, false)); } + @Test public void testExpandSeveralTemplatesUnusedParameterInMiddle() { SortedMap map = Maps.newTreeMap(); map.put("id", "a"); @@ -290,6 +338,7 @@ public void testExpandSeveralTemplatesUnusedParameterInMiddle() { assertEquals("?id=a&uid=b", UriTemplate.expand("{?id,foo,bar,uid}", map, false)); } + @Test public void testExpandSeveralTemplatesFirstParameterUnused() { SortedMap map = Maps.newTreeMap(); map.put("id", "a"); @@ -298,8 +347,90 @@ public void testExpandSeveralTemplatesFirstParameterUnused() { assertEquals("?id=a&uid=b", UriTemplate.expand("{?foo,id,uid}", map, false)); } + @Test public void testExpandSeveralTemplatesNoParametersUsed() { SortedMap map = Maps.newTreeMap(); assertEquals("", UriTemplate.expand("{?id,uid}", map, false)); } + + @Test + public void testExpandTemplates_reservedExpansion_mustNotEscapeReservedCharSet() { + + String reservedSet = ":/?#[]@!$&'()*+,;="; + + SortedMap requestMap = Maps.newTreeMap(); + requestMap.put("var", reservedSet); + + assertEquals( + "Reserved expansion must not escape chars from reserved set according to rfc6570#section-3.2.3", + reservedSet, + UriTemplate.expand("{+var}", requestMap, false)); + } + + @Test + public void testExpandTemplates_reservedExpansion_mustNotEscapeUnreservedCharSet() { + + String unReservedSet = "-._~"; + + SortedMap requestMap = Maps.newTreeMap(); + requestMap.put("var", unReservedSet); + + assertEquals( + "Reserved expansion must not escape chars from unreserved set according to rfc6570#section-3.2.3", + unReservedSet, + UriTemplate.expand("{+var}", requestMap, false)); + } + + @Test + // These tests are from the uri-template test suite + // https://github.com/uri-templates/uritemplate-test/blob/master/extended-tests.json + public void testExpandTemplates_reservedExpansion_alreadyEncodedInput() { + Map variables = Maps.newLinkedHashMap(); + variables.put("id", "admin%2F"); + assertEquals("admin%252F", UriTemplate.expand("{id}", variables, false)); + assertEquals("admin%2F", UriTemplate.expand("{+id}", variables, false)); + assertEquals("#admin%2F", UriTemplate.expand("{#id}", variables, false)); + } + + @Test + // These tests are from the uri-template test suite + // https://github.com/uri-templates/uritemplate-test/blob/master/extended-tests.json + public void testExpandTemplates_reservedExpansion_notEncodedInput() { + Map variables = Maps.newLinkedHashMap(); + variables.put("not_pct", "%foo"); + assertEquals("%25foo", UriTemplate.expand("{not_pct}", variables, false)); + assertEquals("%25foo", UriTemplate.expand("{+not_pct}", variables, false)); + assertEquals("#%25foo", UriTemplate.expand("{#not_pct}", variables, false)); + } + + @Test + // These tests are from the uri-template test suite + // https://github.com/uri-templates/uritemplate-test/blob/master/extended-tests.json + public void testExpandTemplates_reservedExpansion_listExpansionWithMixedEncodedInput() { + Map variables = Maps.newLinkedHashMap(); + variables.put("list", Arrays.asList("red%25", "%2Fgreen", "blue ")); + assertEquals("red%2525,%252Fgreen,blue%20", UriTemplate.expand("{list}", variables, false)); + assertEquals("red%25,%2Fgreen,blue%20", UriTemplate.expand("{+list}", variables, false)); + assertEquals("#red%25,%2Fgreen,blue%20", UriTemplate.expand("{#list}", variables, false)); + } + + @Test + // These tests are from the uri-template test suite + // https://github.com/uri-templates/uritemplate-test/blob/master/extended-tests.json with an + // additional map entry + public void testExpandTemplates_reservedExpansion_mapWithMixedEncodedInput() { + Map variables = Maps.newLinkedHashMap(); + Map keys = Maps.newLinkedHashMap(); + keys.put("key1", "val1%2F"); + keys.put("key2", "val2%2F"); + keys.put("key3", "val "); + variables.put("keys", keys); + assertEquals( + "key1,val1%252F,key2,val2%252F,key3,val%20", + UriTemplate.expand("{keys}", variables, false)); + assertEquals( + "key1,val1%2F,key2,val2%2F,key3,val%20", UriTemplate.expand("{+keys}", variables, false)); + assertEquals( + "#key1,val1%2F,key2,val2%2F,key3,val%20", UriTemplate.expand("{#keys}", variables, false)); + } } diff --git a/google-http-client/src/test/java/com/google/api/client/http/UrlEncodedContentTest.java b/google-http-client/src/test/java/com/google/api/client/http/UrlEncodedContentTest.java index 76e768404..ff607b333 100644 --- a/google-http-client/src/test/java/com/google/api/client/http/UrlEncodedContentTest.java +++ b/google-http-client/src/test/java/com/google/api/client/http/UrlEncodedContentTest.java @@ -14,6 +14,11 @@ package com.google.api.client.http; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import com.google.api.client.testing.http.HttpTesting; import com.google.api.client.testing.http.MockHttpTransport; import com.google.api.client.util.ArrayMap; @@ -23,45 +28,64 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link UrlEncodedContent}. * * @author Yaniv Inbar */ -public class UrlEncodedContentTest extends TestCase { +@RunWith(JUnit4.class) +public class UrlEncodedContentTest { + @Test public void testWriteTo() throws IOException { - subtestWriteTo("a=x", ArrayMap.of("a", "x")); - subtestWriteTo("noval", ArrayMap.of("noval", "")); + subtestWriteTo("a=x", ArrayMap.of("a", "x"), false); + subtestWriteTo("noval", ArrayMap.of("noval", ""), false); subtestWriteTo( - "multi=a&multi=b&multi=c", ArrayMap.of("multi", Arrays.asList("a", "b", "c"))); + "multi=a&multi=b&multi=c", ArrayMap.of("multi", Arrays.asList("a", "b", "c")), false); subtestWriteTo( - "multi=a&multi=b&multi=c", ArrayMap.of("multi", new String[] {"a", "b", "c"})); + "multi=a&multi=b&multi=c", ArrayMap.of("multi", new String[] {"a", "b", "c"}), false); // https://github.com/googleapis/google-http-java-client/issues/202 final Map params = new LinkedHashMap(); params.put("username", "un"); params.put("password", "password123;{}"); - subtestWriteTo("username=un&password=password123%3B%7B%7D", params); + subtestWriteTo("username=un&password=password123%3B%7B%7D", params, false); + subtestWriteTo("additionkey=add%2Btion", ArrayMap.of("additionkey", "add+tion"), false); + + subtestWriteTo("a=x", ArrayMap.of("a", "x"), true); + subtestWriteTo("noval", ArrayMap.of("noval", ""), true); + subtestWriteTo( + "multi=a&multi=b&multi=c", ArrayMap.of("multi", Arrays.asList("a", "b", "c")), true); + subtestWriteTo( + "multi=a&multi=b&multi=c", ArrayMap.of("multi", new String[] {"a", "b", "c"}), true); + subtestWriteTo("username=un&password=password123;%7B%7D", params, true); + subtestWriteTo("additionkey=add%2Btion", ArrayMap.of("additionkey", "add+tion"), true); } - private void subtestWriteTo(String expected, Object data) throws IOException { - UrlEncodedContent content = new UrlEncodedContent(data); + private void subtestWriteTo(String expected, Object data, boolean useEscapeUriPathEncoding) + throws IOException { + UrlEncodedContent content = new UrlEncodedContent(data, useEscapeUriPathEncoding); ByteArrayOutputStream out = new ByteArrayOutputStream(); content.writeTo(out); assertEquals(expected, out.toString()); } + @Test public void testGetContent() throws Exception { - HttpRequest request = new MockHttpTransport().createRequestFactory() - .buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL); + HttpRequest request = + new MockHttpTransport() + .createRequestFactory() + .buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL); UrlEncodedContent content = UrlEncodedContent.getContent(request); assertNotNull(content); assertTrue(content.getData() instanceof Map); assertEquals(content, UrlEncodedContent.getContent(request)); } + @Test public void testGetData() { try { new UrlEncodedContent(null); diff --git a/google-http-client/src/test/java/com/google/api/client/http/UrlEncodedParserTest.java b/google-http-client/src/test/java/com/google/api/client/http/UrlEncodedParserTest.java index 7659e4551..e4beed966 100644 --- a/google-http-client/src/test/java/com/google/api/client/http/UrlEncodedParserTest.java +++ b/google-http-client/src/test/java/com/google/api/client/http/UrlEncodedParserTest.java @@ -14,6 +14,10 @@ package com.google.api.client.http; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + import com.google.api.client.util.ArrayMap; import com.google.api.client.util.GenericData; import com.google.api.client.util.Key; @@ -25,54 +29,46 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link UrlEncodedParser}. * * @author Yaniv Inbar */ -public class UrlEncodedParserTest extends TestCase { - - public UrlEncodedParserTest() { - } - - public UrlEncodedParserTest(String name) { - super(name); - } +@RunWith(JUnit4.class) +public class UrlEncodedParserTest { public static class Simple { - @Key - Void v; + @Key Void v; - @Key - String a; + @Key String a; - @Key - String b; + @Key String b; - @Key - String c; + @Key String c; - @Key - List q; + @Key List q; - @Key - String[] r; + @Key String[] r; - @Key - Object o; + @Key Object o; @Override public boolean equals(Object obj) { Simple other = (Simple) obj; - return Objects.equal(a, other.a) && Objects.equal(b, other.b) && Objects.equal(c, other.c) - && Objects.equal(q, other.q) && Arrays.equals(r, other.r) && Objects.equal(o, other.o); + return Objects.equal(a, other.a) + && Objects.equal(b, other.b) + && Objects.equal(c, other.c) + && Objects.equal(q, other.q) + && Arrays.equals(r, other.r) + && Objects.equal(o, other.o); } - public Simple() { - } + public Simple() {} @Override public String toString() { @@ -88,20 +84,15 @@ public String toString() { } public static class Generic extends GenericData { - @Key - String a; + @Key String a; - @Key - String b; + @Key String b; - @Key - String c; + @Key String c; - @Key - List q; + @Key List q; - @Key - Object o; + @Key Object o; @Override public Generic set(String fieldName, Object value) { @@ -109,6 +100,7 @@ public Generic set(String fieldName, Object value) { } } + @Test public void testParse_simple() { Simple actual = new Simple(); UrlEncodedParser.parse( @@ -124,6 +116,7 @@ public void testParse_simple() { assertNull(expected.v); } + @Test public void testParse_generic() { Generic actual = new Generic(); UrlEncodedParser.parse("p=4&q=1&a=x&p=3&b=y&c=z&d=v&q=2&p=5&o=object", actual); @@ -138,6 +131,7 @@ public void testParse_generic() { assertEquals(ArrayList.class, actual.get("d").getClass()); } + @Test public void testParse_map() { ArrayMap actual = new ArrayMap(); UrlEncodedParser.parse("p=4&q=1&a=x&p=3&b=y&c=z&d=v&q=2&p=5&noval1&noval2=", actual); @@ -154,6 +148,7 @@ public void testParse_map() { assertEquals(ArrayList.class, actual.get("a").getClass()); } + @Test public void testParse_encoding() { ArrayMap actual = new ArrayMap(); UrlEncodedParser.parse("q=%20", actual); @@ -162,6 +157,7 @@ public void testParse_encoding() { assertEquals(expected, actual); } + @Test public void testParse_null() { ArrayMap actual = new ArrayMap(); UrlEncodedParser.parse((String) null, actual); @@ -169,7 +165,6 @@ public void testParse_null() { } public enum E { - @Value VALUE, @Value("other") @@ -177,10 +172,8 @@ public enum E { } public static class EnumValue extends GenericData { - @Key - public E value; - @Key - public E otherValue; + @Key public E value; + @Key public E otherValue; @Override public EnumValue set(String fieldName, Object value) { @@ -190,6 +183,7 @@ public EnumValue set(String fieldName, Object value) { static final String ENUM_VALUE = "otherValue=other&value=VALUE"; + @Test public void testParse_enum() throws IOException { EnumValue actual = new EnumValue(); UrlEncodedParser.parse(ENUM_VALUE, actual); diff --git a/google-http-client/src/test/java/com/google/api/client/http/apache/ApacheHttpTransportTest.java b/google-http-client/src/test/java/com/google/api/client/http/apache/ApacheHttpTransportTest.java deleted file mode 100644 index 21aa27357..000000000 --- a/google-http-client/src/test/java/com/google/api/client/http/apache/ApacheHttpTransportTest.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2011 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package com.google.api.client.http.apache; - -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import com.google.api.client.util.ByteArrayStreamingContent; -import com.google.api.client.util.StringUtils; -import junit.framework.TestCase; -import org.apache.http.HttpResponse; -import org.apache.http.HttpVersion; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.client.params.ClientPNames; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.impl.client.DefaultHttpRequestRetryHandler; -import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; -import org.apache.http.params.CoreConnectionPNames; -import org.apache.http.params.HttpParams; -import org.apache.http.params.HttpProtocolParams; - -/** - * Tests {@link ApacheHttpTransport}. - * - * @author Yaniv Inbar - */ -public class ApacheHttpTransportTest extends TestCase { - - public void testApacheHttpTransport() { - ApacheHttpTransport transport = new ApacheHttpTransport(); - DefaultHttpClient httpClient = (DefaultHttpClient) transport.getHttpClient(); - checkDefaultHttpClient(httpClient); - checkHttpClient(httpClient); - } - - public void testApacheHttpTransportWithParam() { - ApacheHttpTransport transport = new ApacheHttpTransport(new DefaultHttpClient()); - checkHttpClient(transport.getHttpClient()); - } - - public void testNewDefaultHttpClient() { - checkDefaultHttpClient(ApacheHttpTransport.newDefaultHttpClient()); - } - - public void testRequestsWithContent() throws Exception { - HttpClient mockClient = mock(HttpClient.class); - HttpResponse mockResponse = mock(HttpResponse.class); - when(mockClient.execute(any(HttpUriRequest.class))).thenReturn(mockResponse); - - ApacheHttpTransport transport = new ApacheHttpTransport(mockClient); - - // Test GET. - subtestUnsupportedRequestsWithContent( - transport.buildRequest("GET", "http://www.test.url"), "GET"); - // Test DELETE. - subtestUnsupportedRequestsWithContent( - transport.buildRequest("DELETE", "http://www.test.url"), "DELETE"); - // Test HEAD. - subtestUnsupportedRequestsWithContent( - transport.buildRequest("HEAD", "http://www.test.url"), "HEAD"); - - // Test PUT. - execute(transport.buildRequest("PUT", "http://www.test.url")); - // Test POST. - execute(transport.buildRequest("POST", "http://www.test.url")); - // Test PATCH. - execute(transport.buildRequest("PATCH", "http://www.test.url")); - } - - private void subtestUnsupportedRequestsWithContent(ApacheHttpRequest request, String method) - throws Exception { - try { - execute(request); - fail("expected " + IllegalArgumentException.class); - } catch (IllegalArgumentException e) { - // expected - assertEquals(e.getMessage(), - "Apache HTTP client does not support " + method + " requests with content."); - } - } - - private void execute(ApacheHttpRequest request) throws Exception { - byte[] bytes = StringUtils.getBytesUtf8("abc"); - request.setStreamingContent(new ByteArrayStreamingContent(bytes)); - request.setContentType("text/html"); - request.setContentLength(bytes.length); - request.execute(); - } - - private void checkDefaultHttpClient(DefaultHttpClient client) { - HttpParams params = client.getParams(); - assertTrue(client.getConnectionManager() instanceof ThreadSafeClientConnManager); - assertEquals(8192, params.getIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, -1)); - DefaultHttpRequestRetryHandler retryHandler = - (DefaultHttpRequestRetryHandler) client.getHttpRequestRetryHandler(); - assertEquals(0, retryHandler.getRetryCount()); - assertFalse(retryHandler.isRequestSentRetryEnabled()); - } - - private void checkHttpClient(HttpClient client) { - HttpParams params = client.getParams(); - assertFalse(params.getBooleanParameter(ClientPNames.HANDLE_REDIRECTS, true)); - assertEquals(HttpVersion.HTTP_1_1, HttpProtocolParams.getVersion(params)); - } -} diff --git a/google-http-client/src/test/java/com/google/api/client/http/javanet/NetHttpRequestTest.java b/google-http-client/src/test/java/com/google/api/client/http/javanet/NetHttpRequestTest.java new file mode 100644 index 000000000..9599507a2 --- /dev/null +++ b/google-http-client/src/test/java/com/google/api/client/http/javanet/NetHttpRequestTest.java @@ -0,0 +1,252 @@ +package com.google.api.client.http.javanet; + +import static org.junit.Assert.*; + +import com.google.api.client.http.ByteArrayContent; +import com.google.api.client.http.HttpContent; +import com.google.api.client.http.InputStreamContent; +import com.google.api.client.http.LowLevelHttpResponse; +import com.google.api.client.http.javanet.NetHttpRequest.OutputWriter; +import com.google.api.client.testing.http.HttpTesting; +import com.google.api.client.testing.http.javanet.MockHttpURLConnection; +import com.google.api.client.util.StreamingContent; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeoutException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class NetHttpRequestTest { + + static class SleepingOutputWriter implements OutputWriter { + private long sleepTimeInMs; + + public SleepingOutputWriter(long sleepTimeInMs) { + this.sleepTimeInMs = sleepTimeInMs; + } + + @Override + public void write(OutputStream outputStream, StreamingContent content) throws IOException { + try { + Thread.sleep(sleepTimeInMs); + } catch (InterruptedException e) { + throw new IOException("sleep interrupted", e); + } + } + } + + @Test + public void testHangingWrite() throws InterruptedException { + Thread thread = + new Thread() { + @Override + public void run() { + try { + postWithTimeout(0); + } catch (IOException e) { + // expected to be interrupted + assertEquals(e.getCause().getClass(), InterruptedException.class); + return; + } catch (Exception e) { + fail(); + } + fail("should be interrupted before here"); + } + }; + + thread.start(); + Thread.sleep(1000); + assertTrue(thread.isAlive()); + thread.interrupt(); + } + + @Test(timeout = 1000) + public void testOutputStreamWriteTimeout() throws Exception { + try { + postWithTimeout(100); + fail("should have timed out"); + } catch (IOException e) { + assertEquals(e.getCause().getClass(), TimeoutException.class); + } catch (Exception e) { + fail("Expected an IOException not a " + e.getCause().getClass().getName()); + } + } + + private static void postWithTimeout(int timeout) throws Exception { + MockHttpURLConnection connection = new MockHttpURLConnection(new URL(HttpTesting.SIMPLE_URL)); + connection.setRequestMethod("POST"); + NetHttpRequest request = new NetHttpRequest(connection); + InputStream is = NetHttpRequestTest.class.getClassLoader().getResourceAsStream("file.txt"); + HttpContent content = new InputStreamContent("text/plain", is); + request.setStreamingContent(content); + request.setWriteTimeout(timeout); + request.execute(new SleepingOutputWriter(5000L)); + } + + @Test + public void testInterruptedWriteWithResponse() throws Exception { + MockHttpURLConnection connection = + new MockHttpURLConnection(new URL(HttpTesting.SIMPLE_URL)) { + @Override + public OutputStream getOutputStream() throws IOException { + return new OutputStream() { + @Override + public void write(int b) throws IOException { + throw new IOException("Error writing request body to server"); + } + }; + } + }; + connection.setResponseCode(401); + connection.setRequestMethod("POST"); + NetHttpRequest request = new NetHttpRequest(connection); + InputStream is = NetHttpRequestTest.class.getClassLoader().getResourceAsStream("file.txt"); + HttpContent content = new InputStreamContent("text/plain", is); + request.setStreamingContent(content); + + LowLevelHttpResponse response = request.execute(); + assertEquals(401, response.getStatusCode()); + } + + @Test + public void testInterruptedWriteWithoutResponse() throws Exception { + MockHttpURLConnection connection = + new MockHttpURLConnection(new URL(HttpTesting.SIMPLE_URL)) { + @Override + public OutputStream getOutputStream() throws IOException { + return new OutputStream() { + @Override + public void write(int b) throws IOException { + throw new IOException("Error writing request body to server"); + } + }; + } + }; + connection.setRequestMethod("POST"); + NetHttpRequest request = new NetHttpRequest(connection); + InputStream is = NetHttpRequestTest.class.getClassLoader().getResourceAsStream("file.txt"); + HttpContent content = new InputStreamContent("text/plain", is); + request.setStreamingContent(content); + + try { + request.execute(); + fail("Expected to throw an IOException"); + } catch (IOException e) { + assertEquals("Error writing request body to server", e.getMessage()); + } + } + + @Test + public void testInterruptedWriteErrorOnResponse() throws Exception { + MockHttpURLConnection connection = + new MockHttpURLConnection(new URL(HttpTesting.SIMPLE_URL)) { + @Override + public OutputStream getOutputStream() throws IOException { + return new OutputStream() { + @Override + public void write(int b) throws IOException { + throw new IOException("Error writing request body to server"); + } + }; + } + + @Override + public int getResponseCode() throws IOException { + throw new IOException("Error parsing response code"); + } + }; + connection.setRequestMethod("POST"); + NetHttpRequest request = new NetHttpRequest(connection); + InputStream is = NetHttpRequestTest.class.getClassLoader().getResourceAsStream("file.txt"); + HttpContent content = new InputStreamContent("text/plain", is); + request.setStreamingContent(content); + + try { + request.execute(); + fail("Expected to throw an IOException"); + } catch (IOException e) { + assertEquals("Error writing request body to server", e.getMessage()); + } + } + + @Test + public void testErrorOnClose() throws Exception { + MockHttpURLConnection connection = + new MockHttpURLConnection(new URL(HttpTesting.SIMPLE_URL)) { + @Override + public OutputStream getOutputStream() throws IOException { + return new OutputStream() { + @Override + public void write(int b) throws IOException { + return; + } + + @Override + public void close() throws IOException { + throw new IOException("Error during close"); + } + }; + } + }; + connection.setRequestMethod("POST"); + NetHttpRequest request = new NetHttpRequest(connection); + InputStream is = NetHttpRequestTest.class.getClassLoader().getResourceAsStream("file.txt"); + HttpContent content = new InputStreamContent("text/plain", is); + request.setStreamingContent(content); + + try { + request.execute(); + fail("Expected to throw an IOException"); + } catch (IOException e) { + assertEquals("Error during close", e.getMessage()); + } + } + + @Test + public void testChunkedLengthSet() throws Exception { + MockHttpURLConnection connection = new MockHttpURLConnection(new URL(HttpTesting.SIMPLE_URL)); + connection.setRequestMethod("POST"); + NetHttpRequest request = new NetHttpRequest(connection); + InputStream is = NetHttpRequestTest.class.getClassLoader().getResourceAsStream("file.txt"); + HttpContent content = new InputStreamContent("text/plain", is); + request.setStreamingContent(content); + request.setContentEncoding("gzip"); + request.execute(); + + assertEquals(4096, connection.getChunkLength()); + assertNull(request.getRequestProperty("Content-Length")); + } + + @Test + public void testChunkedLengthNotSet() throws Exception { + MockHttpURLConnection connection = new MockHttpURLConnection(new URL(HttpTesting.SIMPLE_URL)); + connection.setRequestMethod("POST"); + NetHttpRequest request = new NetHttpRequest(connection); + HttpContent content = + new ByteArrayContent("text/plain", "sample".getBytes(StandardCharsets.UTF_8)); + request.setStreamingContent(content); + request.setContentLength(content.getLength()); + request.execute(); + + assertEquals(connection.getChunkLength(), -1); + assertEquals("6", request.getRequestProperty("Content-Length")); + } + + // see https://github.com/googleapis/google-http-java-client/issues/1471 for more details + @Test + public void testDeleteSetsContentLengthToZeroWithoutContent() throws Exception { + MockHttpURLConnection connection = new MockHttpURLConnection(new URL(HttpTesting.SIMPLE_URL)); + connection.setRequestMethod("DELETE"); + NetHttpRequest request = new NetHttpRequest(connection); + request.execute(); + + assertTrue(connection.doOutputCalled()); + assertTrue(connection.isSetFixedLengthStreamingModeLongCalled()); + assertFalse(connection.isSetFixedLengthStreamingModeIntCalled()); + } +} diff --git a/google-http-client/src/test/java/com/google/api/client/http/javanet/NetHttpResponseTest.java b/google-http-client/src/test/java/com/google/api/client/http/javanet/NetHttpResponseTest.java index 866928d16..f82c4b25f 100644 --- a/google-http-client/src/test/java/com/google/api/client/http/javanet/NetHttpResponseTest.java +++ b/google-http-client/src/test/java/com/google/api/client/http/javanet/NetHttpResponseTest.java @@ -14,20 +14,25 @@ package com.google.api.client.http.javanet; +import static org.junit.Assert.assertEquals; + import com.google.api.client.testing.http.javanet.MockHttpURLConnection; import com.google.api.client.util.StringUtils; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link NetHttpResponse}. * * @author Yaniv Inbar */ -public class NetHttpResponseTest extends TestCase { +@RunWith(JUnit4.class) +public class NetHttpResponseTest { private static final String VALID_RESPONSE = "This is a valid response."; private static final String ERROR_RESPONSE = "This is an error response."; @@ -39,10 +44,13 @@ public void testGetStatusCode() throws IOException { } public void subtestGetStatusCode(int expectedCode, int responseCode) throws IOException { - assertEquals(expectedCode, new NetHttpResponse( - new MockHttpURLConnection(null).setResponseCode(responseCode)).getStatusCode()); + assertEquals( + expectedCode, + new NetHttpResponse(new MockHttpURLConnection(null).setResponseCode(responseCode)) + .getStatusCode()); } + @Test public void testGetContent() throws IOException { subtestGetContent(0); subtestGetContent(200); @@ -61,9 +69,12 @@ public void testGetContent() throws IOException { public void subtestGetContent(int responseCode) throws IOException { NetHttpResponse response = - new NetHttpResponse(new MockHttpURLConnection(null).setResponseCode(responseCode) - .setInputStream(new ByteArrayInputStream(StringUtils.getBytesUtf8(VALID_RESPONSE))) - .setErrorStream(new ByteArrayInputStream(StringUtils.getBytesUtf8(ERROR_RESPONSE)))); + new NetHttpResponse( + new MockHttpURLConnection(null) + .setResponseCode(responseCode) + .setInputStream(new ByteArrayInputStream(StringUtils.getBytesUtf8(VALID_RESPONSE))) + .setErrorStream( + new ByteArrayInputStream(StringUtils.getBytesUtf8(ERROR_RESPONSE)))); InputStream is = response.getContent(); byte[] buf = new byte[100]; int bytes = 0, n = 0; @@ -79,9 +90,12 @@ public void subtestGetContent(int responseCode) throws IOException { public void subtestGetContentWithShortRead(int responseCode) throws IOException { NetHttpResponse response = - new NetHttpResponse(new MockHttpURLConnection(null).setResponseCode(responseCode) - .setInputStream(new ByteArrayInputStream(StringUtils.getBytesUtf8(VALID_RESPONSE))) - .setErrorStream(new ByteArrayInputStream(StringUtils.getBytesUtf8(ERROR_RESPONSE)))); + new NetHttpResponse( + new MockHttpURLConnection(null) + .setResponseCode(responseCode) + .setInputStream(new ByteArrayInputStream(StringUtils.getBytesUtf8(VALID_RESPONSE))) + .setErrorStream( + new ByteArrayInputStream(StringUtils.getBytesUtf8(ERROR_RESPONSE)))); InputStream is = response.getContent(); byte[] buf = new byte[100]; int bytes = 0, b = 0; @@ -95,11 +109,13 @@ public void subtestGetContentWithShortRead(int responseCode) throws IOException } } + @Test public void testSkippingBytes() throws IOException { - MockHttpURLConnection connection = new MockHttpURLConnection(null) - .setResponseCode(200) - .setInputStream(new ByteArrayInputStream(StringUtils.getBytesUtf8("0123456789"))) - .addHeader("Content-Length", "10"); + MockHttpURLConnection connection = + new MockHttpURLConnection(null) + .setResponseCode(200) + .setInputStream(new ByteArrayInputStream(StringUtils.getBytesUtf8("0123456789"))) + .addHeader("Content-Length", "10"); NetHttpResponse response = new NetHttpResponse(connection); InputStream is = response.getContent(); // read 1 byte, then skip 9 (to EOF) diff --git a/google-http-client/src/test/java/com/google/api/client/http/javanet/NetHttpTransportTest.java b/google-http-client/src/test/java/com/google/api/client/http/javanet/NetHttpTransportTest.java index 56e65c01b..87c5337c6 100644 --- a/google-http-client/src/test/java/com/google/api/client/http/javanet/NetHttpTransportTest.java +++ b/google-http-client/src/test/java/com/google/api/client/http/javanet/NetHttpTransportTest.java @@ -14,27 +14,74 @@ package com.google.api.client.http.javanet; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.google.api.client.http.GenericUrl; +import com.google.api.client.http.HttpTransport; import com.google.api.client.testing.http.HttpTesting; import com.google.api.client.testing.http.javanet.MockHttpURLConnection; import com.google.api.client.util.ByteArrayStreamingContent; import com.google.api.client.util.StringUtils; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.net.HttpURLConnection; +import java.net.InetSocketAddress; import java.net.URL; -import junit.framework.TestCase; +import java.security.KeyStore; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link NetHttpTransport}. * * @author Yaniv Inbar */ -public class NetHttpTransportTest extends TestCase { +@RunWith(JUnit4.class) +public class NetHttpTransportTest { + + private static final String[] METHODS = { + "GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE" + }; + + public void testNotMtlsWithoutClientCert() throws Exception { + KeyStore trustStore = KeyStore.getInstance("JKS"); + + NetHttpTransport transport = + new NetHttpTransport.Builder().trustCertificates(trustStore).build(); + assertFalse(transport.isMtls()); + } + + @Test + public void testIsMtlsWithClientCert() throws Exception { + KeyStore trustStore = KeyStore.getInstance("JKS"); + KeyStore keyStore = KeyStore.getInstance("PKCS12"); - private static final String[] METHODS = - {"GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE"}; + // Load client certificate and private key from secret.p12 file. + keyStore.load( + this.getClass() + .getClassLoader() + .getResourceAsStream("com/google/api/client/util/secret.p12"), + "notasecret".toCharArray()); + NetHttpTransport transport = + new NetHttpTransport.Builder() + .trustCertificates(trustStore, keyStore, "notasecret") + .build(); + assertTrue(transport.isMtls()); + } + + @Test public void testExecute_mock() throws Exception { for (String method : METHODS) { boolean isPutOrPost = method.equals("PUT") || method.equals("POST"); @@ -59,14 +106,15 @@ public void testExecute_mock() throws Exception { } } - + @Test public void testExecute_methodUnchanged() throws Exception { String body = "Arbitrary body"; byte[] buf = StringUtils.getBytesUtf8(body); for (String method : METHODS) { - HttpURLConnection connection = new MockHttpURLConnection(new URL(HttpTesting.SIMPLE_URL)) - .setResponseCode(200) - .setInputStream(new ByteArrayInputStream(buf)); + HttpURLConnection connection = + new MockHttpURLConnection(new URL(HttpTesting.SIMPLE_URL)) + .setResponseCode(200) + .setInputStream(new ByteArrayInputStream(buf)); connection.setRequestMethod(method); NetHttpRequest request = new NetHttpRequest(connection); setContent(request, "text/html", ""); @@ -75,15 +123,15 @@ public void testExecute_methodUnchanged() throws Exception { } } + @Test public void testAbruptTerminationIsNoticedWithContentLength() throws Exception { - String incompleteBody = "" - + "Fixed size body test.\r\n" - + "Incomplete response."; + String incompleteBody = "" + "Fixed size body test.\r\n" + "Incomplete response."; byte[] buf = StringUtils.getBytesUtf8(incompleteBody); - MockHttpURLConnection connection = new MockHttpURLConnection(new URL(HttpTesting.SIMPLE_URL)) - .setResponseCode(200) - .addHeader("Content-Length", "205") - .setInputStream(new ByteArrayInputStream(buf)); + MockHttpURLConnection connection = + new MockHttpURLConnection(new URL(HttpTesting.SIMPLE_URL)) + .setResponseCode(200) + .addHeader("Content-Length", "205") + .setInputStream(new ByteArrayInputStream(buf)); connection.setRequestMethod("GET"); NetHttpRequest request = new NetHttpRequest(connection); setContent(request, null, ""); @@ -101,15 +149,15 @@ public void testAbruptTerminationIsNoticedWithContentLength() throws Exception { assertTrue(thrown); } + @Test public void testAbruptTerminationIsNoticedWithContentLengthWithReadToBuf() throws Exception { - String incompleteBody = "" - + "Fixed size body test.\r\n" - + "Incomplete response."; + String incompleteBody = "" + "Fixed size body test.\r\n" + "Incomplete response."; byte[] buf = StringUtils.getBytesUtf8(incompleteBody); - MockHttpURLConnection connection = new MockHttpURLConnection(new URL(HttpTesting.SIMPLE_URL)) - .setResponseCode(200) - .addHeader("Content-Length", "205") - .setInputStream(new ByteArrayInputStream(buf)); + MockHttpURLConnection connection = + new MockHttpURLConnection(new URL(HttpTesting.SIMPLE_URL)) + .setResponseCode(200) + .addHeader("Content-Length", "205") + .setInputStream(new ByteArrayInputStream(buf)); connection.setRequestMethod("GET"); NetHttpRequest request = new NetHttpRequest(connection); setContent(request, null, ""); @@ -133,4 +181,64 @@ private void setContent(NetHttpRequest request, String type, String value) throw request.setContentType(type); request.setContentLength(bytes.length); } + + static class FakeServer implements AutoCloseable { + private final HttpServer server; + private final ExecutorService executorService; + + public FakeServer(HttpHandler httpHandler) throws IOException { + this.server = HttpServer.create(new InetSocketAddress(0), 0); + this.executorService = Executors.newFixedThreadPool(1); + server.setExecutor(this.executorService); + server.createContext("/", httpHandler); + server.start(); + } + + public int getPort() { + return server.getAddress().getPort(); + } + + @Override + public void close() { + this.server.stop(0); + this.executorService.shutdownNow(); + } + } + + @Test(timeout = 10_000L) + public void testDisconnectShouldNotWaitToReadResponse() throws IOException { + // This handler waits for 100s before returning writing content. The test should + // timeout if disconnect waits for the response before closing the connection. + final HttpHandler handler = + new HttpHandler() { + @Override + public void handle(HttpExchange httpExchange) throws IOException { + byte[] response = httpExchange.getRequestURI().toString().getBytes(); + httpExchange.sendResponseHeaders(200, response.length); + try (OutputStream out = httpExchange.getResponseBody()) { + byte[] firstByte = new byte[] {0x01}; + out.write(firstByte); + out.flush(); + + // Sleep for longer than the test timeout + try { + Thread.sleep(100_000); + } catch (InterruptedException e) { + throw new IOException("interrupted", e); + } + out.write(response); + } + } + }; + + try (FakeServer server = new FakeServer(handler)) { + HttpTransport transport = new NetHttpTransport(); + GenericUrl testUrl = new GenericUrl("http://localhost/foo//bar"); + testUrl.setPort(server.getPort()); + com.google.api.client.http.HttpResponse response = + transport.createRequestFactory().buildGetRequest(testUrl).execute(); + // disconnect should not wait to read the entire content + response.disconnect(); + } + } } diff --git a/google-http-client/src/test/java/com/google/api/client/json/GenericJsonTest.java b/google-http-client/src/test/java/com/google/api/client/json/GenericJsonTest.java index 662e6e129..cfa2efcb3 100644 --- a/google-http-client/src/test/java/com/google/api/client/json/GenericJsonTest.java +++ b/google-http-client/src/test/java/com/google/api/client/json/GenericJsonTest.java @@ -14,18 +14,24 @@ package com.google.api.client.json; -import junit.framework.TestCase; +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link GenericJson}. * * @author Yaniv Inbar */ -public class GenericJsonTest extends TestCase { +@RunWith(JUnit4.class) +public class GenericJsonTest { + @Test public void testToString_noFactory() { GenericJson data = new GenericJson(); data.put("a", "b"); - assertEquals("{a=b}", data.toString()); + assertEquals("GenericData{classInfo=[], {a=b}}", data.toString()); } } diff --git a/google-http-client/src/test/java/com/google/api/client/json/JsonObjectParserTest.java b/google-http-client/src/test/java/com/google/api/client/json/JsonObjectParserTest.java index b5d88adc8..394468553 100644 --- a/google-http-client/src/test/java/com/google/api/client/json/JsonObjectParserTest.java +++ b/google-http-client/src/test/java/com/google/api/client/json/JsonObjectParserTest.java @@ -14,17 +14,22 @@ package com.google.api.client.json; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import com.google.api.client.testing.json.MockJsonFactory; +import com.google.api.client.testing.json.MockJsonParser; import com.google.common.base.Charsets; import java.io.ByteArrayInputStream; +import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.io.StringReader; import java.lang.reflect.Type; import java.nio.charset.Charset; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests for the {@link JsonObjectParser} class. @@ -32,45 +37,61 @@ * @author Matthias Linder (mlinder) * @since 1.10 */ -public class JsonObjectParserTest extends TestCase { +@RunWith(JUnit4.class) +public class JsonObjectParserTest { + @Test public void testConstructor_null() { try { new JsonObjectParser((JsonFactory) null); fail("Did not throw NullPointerException"); - } catch (NullPointerException expected) {} + } catch (NullPointerException expected) { + } } + @Test public void testParse_InputStream() throws Exception { - InputStream in = new ByteArrayInputStream(new byte[256]); - Charset utf8 = Charsets.UTF_8; - Type type = Integer[].class; + InputStream in = new ByteArrayInputStream(new byte[0]); Integer[] parsed = new Integer[1]; - JsonParser mockJsonParser = mock(JsonParser.class); - when(mockJsonParser.parse(type, true)).thenReturn(parsed); - - JsonFactory mockJsonFactory = mock(JsonFactory.class); - when(mockJsonFactory.createJsonParser(in, utf8)).thenReturn(mockJsonParser); - // Test the JsonObjectParser - JsonObjectParser jop = new JsonObjectParser(mockJsonFactory); - assertEquals(parsed, jop.parseAndClose(in, utf8, type)); + JsonObjectParser jop = new JsonObjectParser(setUpMockJsonFactory(Integer[].class, parsed)); + assertEquals(parsed, jop.parseAndClose(in, Charsets.UTF_8, Integer[].class)); } + @Test public void testParse_Reader() throws Exception { Reader in = new StringReader("something"); - Type type = Integer[].class; Integer[] parsed = new Integer[1]; - JsonParser mockJsonParser = mock(JsonParser.class); - when(mockJsonParser.parse(type, true)).thenReturn(parsed); + // Test the JsonObjectParser + JsonObjectParser jop = new JsonObjectParser(setUpMockJsonFactory(Integer[].class, parsed)); + assertEquals(parsed, jop.parseAndClose(in, Integer[].class)); + } + + // Mockito.mock() on JsonFactory and JsonParser fails with Java 17, so set them up manually. + private static final JsonFactory setUpMockJsonFactory( + final Class clazz, final T parsedResult) { + final MockJsonParser jsonParser = + new MockJsonParser(null) { + @Override + public Object parse(Type dataType, boolean close) throws IOException { + assertEquals(clazz, dataType); + return parsedResult; + } + }; - JsonFactory mockJsonFactory = mock(JsonFactory.class); - when(mockJsonFactory.createJsonParser(in)).thenReturn(mockJsonParser); + return new MockJsonFactory() { + @Override + public JsonParser createJsonParser(Reader in) throws IOException { + return jsonParser; + } - // Test the JsonObjectParser - JsonObjectParser jop = new JsonObjectParser(mockJsonFactory); - assertEquals(parsed, jop.parseAndClose(in, type)); + @Override + public JsonParser createJsonParser(InputStream in, Charset charset) throws IOException { + assertEquals(Charsets.UTF_8, charset); + return jsonParser; + } + }; } } diff --git a/google-http-client/src/test/java/com/google/api/client/json/JsonParserTest.java b/google-http-client/src/test/java/com/google/api/client/json/JsonParserTest.java index 726671f9c..ffedade88 100644 --- a/google-http-client/src/test/java/com/google/api/client/json/JsonParserTest.java +++ b/google-http-client/src/test/java/com/google/api/client/json/JsonParserTest.java @@ -14,19 +14,24 @@ package com.google.api.client.json; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import com.google.api.client.testing.json.MockJsonFactory; import com.google.api.client.testing.json.MockJsonParser; - -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link JsonParser}. * * @author Yaniv Inbar */ -public class JsonParserTest extends TestCase { +@RunWith(JUnit4.class) +public class JsonParserTest { + @Test public void testParseAndClose_noInput() throws Exception { MockJsonParser parser = (MockJsonParser) new MockJsonFactory().createJsonParser(""); try { @@ -37,6 +42,7 @@ public void testParseAndClose_noInput() throws Exception { } } + @Test public void testParseAndClose_noInputVoid() throws Exception { MockJsonParser parser = (MockJsonParser) new MockJsonFactory().createJsonParser(""); parser.parseAndClose(Void.class); diff --git a/google-http-client/src/test/java/com/google/api/client/json/webtoken/JsonWebSignatureTest.java b/google-http-client/src/test/java/com/google/api/client/json/webtoken/JsonWebSignatureTest.java index b26237569..94a688162 100644 --- a/google-http-client/src/test/java/com/google/api/client/json/webtoken/JsonWebSignatureTest.java +++ b/google-http-client/src/test/java/com/google/api/client/json/webtoken/JsonWebSignatureTest.java @@ -17,45 +17,150 @@ import com.google.api.client.testing.json.MockJsonFactory; import com.google.api.client.testing.json.webtoken.TestCertificates; import com.google.api.client.testing.util.SecurityTestUtils; +import com.google.api.client.util.Base64; +import com.google.api.client.util.StringUtils; +import java.io.IOException; +import java.math.BigInteger; +import java.security.AlgorithmParameters; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPrivateKey; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPublicKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.InvalidParameterSpecException; +import java.util.ArrayList; +import java.util.List; import javax.net.ssl.X509TrustManager; -import junit.framework.TestCase; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link JsonWebSignature}. * * @author Yaniv Inbar */ -public class JsonWebSignatureTest extends TestCase { +@RunWith(JUnit4.class) +public class JsonWebSignatureTest { + @Test public void testSign() throws Exception { JsonWebSignature.Header header = new JsonWebSignature.Header(); header.setAlgorithm("RS256"); header.setType("JWT"); JsonWebToken.Payload payload = new JsonWebToken.Payload(); - payload.setIssuer("issuer") - .setAudience("audience").setIssuedAtTimeSeconds(0L).setExpirationTimeSeconds(3600L); + payload + .setIssuer("issuer") + .setAudience("audience") + .setIssuedAtTimeSeconds(0L) + .setExpirationTimeSeconds(3600L); RSAPrivateKey privateKey = SecurityTestUtils.newRsaPrivateKey(); - assertEquals( + Assert.assertEquals( "..kDmKaHNYByLmqAi9ROeLcFmZM7W_emsceKvDZiEGAo-ineCunC6_Nb0HEpAuzIidV-LYTMHS3BvI49KFz9gi6hI3" - + "ZndDL5EzplpFJo1ZclVk1_hLn94P2OTAkZ4ydsTfus6Bl98EbCkInpF_2t5Fr8OaHxCZCDdDU7W5DSnOsx4", + + "ZndDL5EzplpFJo1ZclVk1_hLn94P2OTAkZ4ydsTfus6Bl98EbCkInpF_2t5Fr8OaHxCZCDdDU7W5DSnOsx4", JsonWebSignature.signUsingRsaSha256(privateKey, new MockJsonFactory(), header, payload)); } - private X509Certificate verifyX509WithCaCert(TestCertificates.CertData caCert) throws Exception { + private X509Certificate verifyX509WithCaCert(TestCertificates.CertData caCert) + throws IOException, GeneralSecurityException { JsonWebSignature signature = TestCertificates.getJsonWebSignature(); X509TrustManager trustManager = caCert.getTrustManager(); return signature.verifySignature(trustManager); } + @Test + public void testImmutableSignatureBytes() throws IOException { + JsonWebSignature signature = TestCertificates.getJsonWebSignature(); + byte[] bytes = signature.getSignatureBytes(); + bytes[0] = (byte) (bytes[0] + 1); + byte[] bytes2 = signature.getSignatureBytes(); + Assert.assertNotEquals(bytes2[0], bytes[0]); + } + + @Test + public void testImmutableSignedContentBytes() throws IOException { + JsonWebSignature signature = TestCertificates.getJsonWebSignature(); + byte[] bytes = signature.getSignedContentBytes(); + bytes[0] = (byte) (bytes[0] + 1); + byte[] bytes2 = signature.getSignedContentBytes(); + Assert.assertNotEquals(bytes2[0], bytes[0]); + } + + @Test + public void testImmutableCertificates() throws IOException { + JsonWebSignature signature = TestCertificates.getJsonWebSignature(); + List certificates = signature.getHeader().getX509Certificates(); + certificates.set(0, "foo"); + Assert.assertNotEquals("foo", signature.getHeader().getX509Certificates().get(0)); + } + + @Test + public void testImmutableCritical() throws IOException { + JsonWebSignature signature = TestCertificates.getJsonWebSignature(); + List critical = new ArrayList<>(); + signature.getHeader().setCritical(critical); + critical.add("bar"); + Assert.assertNull(signature.getHeader().getCritical()); + } + + @Test + public void testCriticalNullForNone() throws IOException { + JsonWebSignature signature = TestCertificates.getJsonWebSignature(); + Assert.assertNull(signature.getHeader().getCritical()); + } + + @Test public void testVerifyX509() throws Exception { X509Certificate signatureCert = verifyX509WithCaCert(TestCertificates.CA_CERT); - assertNotNull(signatureCert); - assertTrue(signatureCert.getSubjectDN().getName().startsWith("CN=foo.bar.com")); + Assert.assertNotNull(signatureCert); + Assert.assertTrue(signatureCert.getSubjectDN().getName().startsWith("CN=foo.bar.com")); } + @Test public void testVerifyX509WrongCa() throws Exception { - assertNull(verifyX509WithCaCert(TestCertificates.BOGUS_CA_CERT)); + Assert.assertNull(verifyX509WithCaCert(TestCertificates.BOGUS_CA_CERT)); + } + + private static final String ES256_CONTENT = + "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Im1wZjBEQSJ9.eyJhdWQiOiIvcHJvamVjdHMvNjUyNTYyNzc2Nzk4L2FwcHMvY2xvdWQtc2FtcGxlcy10ZXN0cy1waHAtaWFwIiwiZW1haWwiOiJjaGluZ29yQGdvb2dsZS5jb20iLCJleHAiOjE1ODQwNDc2MTcsImdvb2dsZSI6eyJhY2Nlc3NfbGV2ZWxzIjpbImFjY2Vzc1BvbGljaWVzLzUxODU1MTI4MDkyNC9hY2Nlc3NMZXZlbHMvcmVjZW50U2VjdXJlQ29ubmVjdERhdGEiLCJhY2Nlc3NQb2xpY2llcy81MTg1NTEyODA5MjQvYWNjZXNzTGV2ZWxzL3Rlc3ROb09wIiwiYWNjZXNzUG9saWNpZXMvNTE4NTUxMjgwOTI0L2FjY2Vzc0xldmVscy9ldmFwb3JhdGlvblFhRGF0YUZ1bGx5VHJ1c3RlZCJdfSwiaGQiOiJnb29nbGUuY29tIiwiaWF0IjoxNTg0MDQ3MDE3LCJpc3MiOiJodHRwczovL2Nsb3VkLmdvb2dsZS5jb20vaWFwIiwic3ViIjoiYWNjb3VudHMuZ29vZ2xlLmNvbToxMTIxODE3MTI3NzEyMDE5NzI4OTEifQ"; + private static final String ES256_SIGNATURE = + "yKNtdFY5EKkRboYNexBdfugzLhC3VuGyFcuFYA8kgpxMqfyxa41zkML68hYKrWu2kOBTUW95UnbGpsIi_u1fiA"; + + // x, y values for keyId "mpf0DA" from https://www.gstatic.com/iap/verify/public_key-jwk + private static final String GOOGLE_ES256_X = "fHEdeT3a6KaC1kbwov73ZwB_SiUHEyKQwUUtMCEn0aI"; + private static final String GOOGLE_ES256_Y = "QWOjwPhInNuPlqjxLQyhveXpWqOFcQPhZ3t-koMNbZI"; + + private PublicKey buildEs256PublicKey(String x, String y) + throws NoSuchAlgorithmException, InvalidParameterSpecException, InvalidKeySpecException { + AlgorithmParameters parameters = AlgorithmParameters.getInstance("EC"); + parameters.init(new ECGenParameterSpec("secp256r1")); + ECPublicKeySpec ecPublicKeySpec = + new ECPublicKeySpec( + new ECPoint( + new BigInteger(1, Base64.decodeBase64(x)), + new BigInteger(1, Base64.decodeBase64(y))), + parameters.getParameterSpec(ECParameterSpec.class)); + KeyFactory keyFactory = KeyFactory.getInstance("EC"); + return keyFactory.generatePublic(ecPublicKeySpec); + } + + @Test + public void testVerifyES256() throws Exception { + PublicKey publicKey = buildEs256PublicKey(GOOGLE_ES256_X, GOOGLE_ES256_Y); + JsonWebSignature.Header header = new JsonWebSignature.Header(); + header.setAlgorithm("ES256"); + JsonWebSignature.Payload payload = new JsonWebToken.Payload(); + byte[] signatureBytes = Base64.decodeBase64(ES256_SIGNATURE); + byte[] signedContentBytes = StringUtils.getBytesUtf8(ES256_CONTENT); + JsonWebSignature jsonWebSignature = + new JsonWebSignature(header, payload, signatureBytes, signedContentBytes); + Assert.assertTrue(jsonWebSignature.verifySignature(publicKey)); } } diff --git a/google-http-client/src/test/java/com/google/api/client/testing/http/FixedClockTest.java b/google-http-client/src/test/java/com/google/api/client/testing/http/FixedClockTest.java index e384d91e0..ccc441fac 100644 --- a/google-http-client/src/test/java/com/google/api/client/testing/http/FixedClockTest.java +++ b/google-http-client/src/test/java/com/google/api/client/testing/http/FixedClockTest.java @@ -14,16 +14,21 @@ package com.google.api.client.testing.http; -import junit.framework.TestCase; +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests for the {@link FixedClock}. + * * @author mlinder@google.com (Matthias Linder) */ -public class FixedClockTest extends TestCase { - /** - * Tests that the {@link FixedClock#currentTimeMillis()} method will return the mocked values. - */ +@RunWith(JUnit4.class) +public class FixedClockTest { + /** Tests that the {@link FixedClock#currentTimeMillis()} method will return the mocked values. */ + @Test public void testCurrentTimeMillis() { // Check that the initial value is set properly. FixedClock clock = new FixedClock(100); diff --git a/google-http-client/src/test/java/com/google/api/client/testing/http/MockHttpTransportTest.java b/google-http-client/src/test/java/com/google/api/client/testing/http/MockHttpTransportTest.java index 474150471..a78f127da 100644 --- a/google-http-client/src/test/java/com/google/api/client/testing/http/MockHttpTransportTest.java +++ b/google-http-client/src/test/java/com/google/api/client/testing/http/MockHttpTransportTest.java @@ -19,18 +19,21 @@ import com.google.api.client.http.GenericUrl; import com.google.api.client.http.HttpRequest; import com.google.api.client.http.HttpRequestFactory; - -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link MockHttpTransport}. * * @author Paweł Zuzelski */ -public final class MockHttpTransportTest extends TestCase { +@RunWith(JUnit4.class) +public final class MockHttpTransportTest { // The purpose of this test is to verify, that the actual lowLevelHttpRequest used is preserved // so that the content of the request can be verified in tests. - public void testBuildGetRequest_preservesLoLevelHttpRequest() throws Exception { + @Test + public void testBuildGetRequest_preservesLowLevelHttpRequest() throws Exception { MockHttpTransport httpTransport = new MockHttpTransport(); GenericUrl url = new GenericUrl("http://example.org"); HttpRequestFactory requestFactory = httpTransport.createRequestFactory(); diff --git a/google-http-client/src/test/java/com/google/api/client/testing/http/MockLowLevelHttpRequestTest.java b/google-http-client/src/test/java/com/google/api/client/testing/http/MockLowLevelHttpRequestTest.java index 537e20bad..144ff1c73 100644 --- a/google-http-client/src/test/java/com/google/api/client/testing/http/MockLowLevelHttpRequestTest.java +++ b/google-http-client/src/test/java/com/google/api/client/testing/http/MockLowLevelHttpRequestTest.java @@ -14,18 +14,23 @@ package com.google.api.client.testing.http; +import static org.junit.Assert.assertEquals; + import com.google.api.client.util.ByteArrayStreamingContent; import com.google.api.client.util.StringUtils; - -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link MockLowLevelHttpRequest}. * * @author Yaniv Inbar */ -public class MockLowLevelHttpRequestTest extends TestCase { +@RunWith(JUnit4.class) +public class MockLowLevelHttpRequestTest { + @Test public void testGetContentAsString() throws Exception { subtestGetContentAsString("", null); subtestGetContentAsString("hello", "hello"); diff --git a/google-http-client/src/test/java/com/google/api/client/testing/http/javanet/MockHttpUrlConnectionTest.java b/google-http-client/src/test/java/com/google/api/client/testing/http/javanet/MockHttpUrlConnectionTest.java index 6f54f1975..5a64c1f79 100644 --- a/google-http-client/src/test/java/com/google/api/client/testing/http/javanet/MockHttpUrlConnectionTest.java +++ b/google-http-client/src/test/java/com/google/api/client/testing/http/javanet/MockHttpUrlConnectionTest.java @@ -14,6 +14,9 @@ package com.google.api.client.testing.http.javanet; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + import com.google.api.client.testing.http.HttpTesting; import com.google.api.client.util.StringUtils; import java.io.ByteArrayInputStream; @@ -23,50 +26,54 @@ import java.util.Arrays; import java.util.List; import java.util.Map; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; -/** - * Tests {@link MockHttpURLConnection}. - */ -public class MockHttpUrlConnectionTest extends TestCase { +/** Tests {@link MockHttpURLConnection}. */ +@RunWith(JUnit4.class) +public class MockHttpUrlConnectionTest { private static final String RESPONSE_BODY = "body"; private static final String HEADER_NAME = "Custom-Header"; public void testSetGetHeaders() throws IOException { - MockHttpURLConnection connection = new MockHttpURLConnection(new URL(HttpTesting.SIMPLE_URL)); - connection.addHeader(HEADER_NAME, "100"); - assertEquals("100", connection.getHeaderField(HEADER_NAME)); + MockHttpURLConnection connection = new MockHttpURLConnection(new URL(HttpTesting.SIMPLE_URL)); + connection.addHeader(HEADER_NAME, "100"); + assertEquals("100", connection.getHeaderField(HEADER_NAME)); } + @Test public void testSetGetMultipleHeaders() throws IOException { - MockHttpURLConnection connection = new MockHttpURLConnection(new URL(HttpTesting.SIMPLE_URL)); - List values = Arrays.asList("value1", "value2", "value3"); - for (String value : values) { - connection.addHeader(HEADER_NAME, value); - } - Map> headers = connection.getHeaderFields(); - assertEquals(3, headers.get(HEADER_NAME).size()); - for (int i = 0; i < 3; i++) { - assertEquals(values.get(i), headers.get(HEADER_NAME).get(i)); - } + MockHttpURLConnection connection = new MockHttpURLConnection(new URL(HttpTesting.SIMPLE_URL)); + List values = Arrays.asList("value1", "value2", "value3"); + for (String value : values) { + connection.addHeader(HEADER_NAME, value); + } + Map> headers = connection.getHeaderFields(); + assertEquals(3, headers.get(HEADER_NAME).size()); + for (int i = 0; i < 3; i++) { + assertEquals(values.get(i), headers.get(HEADER_NAME).get(i)); + } } + @Test public void testGetNonExistingHeader() throws IOException { - MockHttpURLConnection connection = new MockHttpURLConnection(new URL(HttpTesting.SIMPLE_URL)); - assertNull(connection.getHeaderField(HEADER_NAME)); + MockHttpURLConnection connection = new MockHttpURLConnection(new URL(HttpTesting.SIMPLE_URL)); + assertNull(connection.getHeaderField(HEADER_NAME)); } + @Test public void testSetInputStreamAndInputStreamImmutable() throws IOException { - MockHttpURLConnection connection = new MockHttpURLConnection(new URL(HttpTesting.SIMPLE_URL)); - connection.setInputStream(new ByteArrayInputStream(StringUtils.getBytesUtf8(RESPONSE_BODY))); - connection.setInputStream(new ByteArrayInputStream(StringUtils.getBytesUtf8("override"))); - byte[] buf = new byte[10]; - InputStream in = connection.getInputStream(); - int n = 0, bytes = 0; - while ((n = in.read(buf)) != -1) { - bytes += n; - } - assertEquals(RESPONSE_BODY, new String(buf, 0, bytes)); + MockHttpURLConnection connection = new MockHttpURLConnection(new URL(HttpTesting.SIMPLE_URL)); + connection.setInputStream(new ByteArrayInputStream(StringUtils.getBytesUtf8(RESPONSE_BODY))); + connection.setInputStream(new ByteArrayInputStream(StringUtils.getBytesUtf8("override"))); + byte[] buf = new byte[10]; + InputStream in = connection.getInputStream(); + int n = 0, bytes = 0; + while ((n = in.read(buf)) != -1) { + bytes += n; + } + assertEquals(RESPONSE_BODY, new String(buf, 0, bytes)); } } diff --git a/google-http-client/src/test/java/com/google/api/client/testing/util/MockBackOffTest.java b/google-http-client/src/test/java/com/google/api/client/testing/util/MockBackOffTest.java index 127edb1b0..8260c2082 100644 --- a/google-http-client/src/test/java/com/google/api/client/testing/util/MockBackOffTest.java +++ b/google-http-client/src/test/java/com/google/api/client/testing/util/MockBackOffTest.java @@ -14,17 +14,23 @@ package com.google.api.client.testing.util; +import static org.junit.Assert.assertEquals; + import com.google.api.client.util.BackOff; import java.io.IOException; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link MockBackOff}. * * @author Yaniv Inbar */ -public class MockBackOffTest extends TestCase { +@RunWith(JUnit4.class) +public class MockBackOffTest { + @Test public void testNextBackOffMillis() throws IOException { subtestNextBackOffMillis(0, new MockBackOff()); subtestNextBackOffMillis(BackOff.STOP, new MockBackOff().setBackOffMillis(BackOff.STOP)); @@ -37,5 +43,4 @@ public void subtestNextBackOffMillis(long expectedValue, BackOff backOffPolicy) assertEquals(expectedValue, backOffPolicy.nextBackOffMillis()); } } - } diff --git a/google-http-client/src/test/java/com/google/api/client/util/ArrayMapTest.java b/google-http-client/src/test/java/com/google/api/client/util/ArrayMapTest.java index db1f1e435..8aa50280c 100644 --- a/google-http-client/src/test/java/com/google/api/client/util/ArrayMapTest.java +++ b/google-http-client/src/test/java/com/google/api/client/util/ArrayMapTest.java @@ -14,30 +14,31 @@ package com.google.api.client.util; -import junit.framework.TestCase; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.util.Iterator; import java.util.Map; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link ArrayMap}. * * @author Yaniv Inbar */ -public class ArrayMapTest extends TestCase { - - public ArrayMapTest() { - } - - public ArrayMapTest(String testName) { - super(testName); - } +@RunWith(JUnit4.class) +public class ArrayMapTest { + @Test public void testOf_zero() { ArrayMap map = ArrayMap.of(); assertTrue(map.isEmpty()); } + @Test public void testOf_one() { ArrayMap map = ArrayMap.of("a", 1); assertEquals(1, map.size()); @@ -45,6 +46,7 @@ public void testOf_one() { assertEquals((Integer) 1, map.getValue(0)); } + @Test public void testOf_two() { ArrayMap map = ArrayMap.of("a", 1, "b", 2); assertEquals(2, map.size()); @@ -54,30 +56,35 @@ public void testOf_two() { assertEquals((Integer) 2, map.getValue(1)); } + @Test public void testRemove1() { ArrayMap map = ArrayMap.of("a", 1, "b", 2); map.remove("b"); assertEquals(ArrayMap.of("a", 1), map); } + @Test public void testRemove2() { ArrayMap map = ArrayMap.of("a", 1, "b", 2); map.remove("a"); assertEquals(ArrayMap.of("b", 2), map); } + @Test public void testRemove3() { ArrayMap map = ArrayMap.of("a", 1); map.remove("a"); assertEquals(ArrayMap.of(), map); } + @Test public void testRemove4() { ArrayMap map = ArrayMap.of("a", 1, "b", 2, "c", 3); map.remove("b"); assertEquals(ArrayMap.of("a", 1, "c", 3), map); } + @Test public void testClone_changingEntrySet() { ArrayMap map = ArrayMap.of(); assertEquals("{}", map.toString()); @@ -86,6 +93,7 @@ public void testClone_changingEntrySet() { assertEquals("{foo=bar}", clone.toString()); } + @Test public void testSet() { ArrayMap map = ArrayMap.of(); map.set(0, "a", 1); @@ -104,6 +112,7 @@ public void testSet() { } } + @Test public void testHashCode() { ArrayMap map = ArrayMap.of(); map.set(0, "a", null); @@ -113,6 +122,7 @@ public void testHashCode() { assertTrue(map.hashCode() > 0); } + @Test public void testIteratorRemove1() { ArrayMap map = new ArrayMap(); map.put("a", "a"); @@ -128,6 +138,7 @@ public void testIteratorRemove1() { assertEquals(0, map.size()); } + @Test public void testIteratorRemove2() { ArrayMap map = new ArrayMap(); map.put("a", "a"); @@ -145,6 +156,7 @@ public void testIteratorRemove2() { assertEquals("c", map.get("c")); } + @Test public void testIteratorRemove3() { ArrayMap map = new ArrayMap(); map.put("a", "a"); diff --git a/google-http-client/src/test/java/com/google/api/client/util/BackOffTest.java b/google-http-client/src/test/java/com/google/api/client/util/BackOffTest.java index b88dd780b..9e462f58b 100644 --- a/google-http-client/src/test/java/com/google/api/client/util/BackOffTest.java +++ b/google-http-client/src/test/java/com/google/api/client/util/BackOffTest.java @@ -14,16 +14,22 @@ package com.google.api.client.util; +import static org.junit.Assert.assertEquals; + import java.io.IOException; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link BackOff}. * * @author Yaniv Inbar */ -public class BackOffTest extends TestCase { +@RunWith(JUnit4.class) +public class BackOffTest { + @Test public void testNextBackOffMillis() throws IOException { subtestNextBackOffMillis(0, BackOff.ZERO_BACKOFF); subtestNextBackOffMillis(BackOff.STOP, BackOff.STOP_BACKOFF); diff --git a/google-http-client/src/test/java/com/google/api/client/util/BackOffUtilsTest.java b/google-http-client/src/test/java/com/google/api/client/util/BackOffUtilsTest.java index fad0b82f9..dc07b2cf6 100644 --- a/google-http-client/src/test/java/com/google/api/client/util/BackOffUtilsTest.java +++ b/google-http-client/src/test/java/com/google/api/client/util/BackOffUtilsTest.java @@ -14,18 +14,23 @@ package com.google.api.client.util; +import static org.junit.Assert.assertEquals; + import com.google.api.client.testing.util.MockBackOff; import com.google.api.client.testing.util.MockSleeper; - -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link BackOffUtils}. * * @author Yaniv Inbar */ -public class BackOffUtilsTest extends TestCase { +@RunWith(JUnit4.class) +public class BackOffUtilsTest { + @Test public void testNext() throws Exception { subtestNext(7); subtestNext(0); diff --git a/google-http-client/src/test/java/com/google/api/client/util/Base64Test.java b/google-http-client/src/test/java/com/google/api/client/util/Base64Test.java new file mode 100644 index 000000000..cfc8e937f --- /dev/null +++ b/google-http-client/src/test/java/com/google/api/client/util/Base64Test.java @@ -0,0 +1,153 @@ +/* + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.util; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import java.nio.charset.StandardCharsets; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Tests {@link Base64}. + * + * @author Jeff Ching + */ +@RunWith(JUnit4.class) +public class Base64Test { + + @Test + public void test_decodeBase64_withPadding() { + String encoded = "Zm9vOmJhcg=="; + assertEquals("foo:bar", new String(Base64.decodeBase64(encoded), StandardCharsets.UTF_8)); + } + + @Test + public void test_decodeBase64_withoutPadding() { + String encoded = "Zm9vOmJhcg"; + assertEquals("foo:bar", new String(Base64.decodeBase64(encoded), StandardCharsets.UTF_8)); + } + + @Test + public void test_decodeBase64_withTrailingWhitespace() { + // Some internal use cases append extra space characters that apache-commons base64 decoding + // previously handled. + String encoded = "Zm9vOmJhcg==\r\n"; + assertEquals("foo:bar", new String(Base64.decodeBase64(encoded), StandardCharsets.UTF_8)); + } + + @Test + public void test_decodeBase64_withNullBytes_shouldReturnNull() { + byte[] encoded = null; + assertNull(Base64.decodeBase64(encoded)); + } + + @Test + public void test_decodeBase64_withNull_shouldReturnNull() { + String encoded = null; + assertNull(Base64.decodeBase64(encoded)); + } + + @Test + public void test_encodeBase64URLSafeString_withNull_shouldReturnNull() { + assertNull(Base64.encodeBase64URLSafeString(null)); + } + + @Test + public void test_encodeBase64URLSafe_withNull_shouldReturnNull() { + assertNull(Base64.encodeBase64URLSafe(null)); + } + + @Test + public void test_encodeBase64_withNull_shouldReturnNull() { + assertNull(Base64.encodeBase64(null)); + } + + @Test + public void test_decodeBase64_newline_character_invalid_length() { + // The RFC 4648 (https://datatracker.ietf.org/doc/html/rfc4648#section-3.3) states that a + // specification referring to the Base64 encoding may state that it ignores characters outside + // the base alphabet. + + // In Base64 encoding, 3 characters (24 bits) are converted to 4 of 6-bits, each of which is + // converted to a byte (a character). + // Base64encode("abc") => "YWJj" (4 characters) + // Base64encode("def") => "ZGVm" (4 characters) + // Adding a new line character between them. This should be discarded. + String encodedString = "YWJj\nZGVm"; + + // This is a reference implementation by Apache Commons Codec. It discards the new line + // characters. + // assertEquals( + // "abcdef", + // new String( + // org.apache.commons.codec.binary.Base64.decodeBase64(encodedString), + // StandardCharsets.UTF_8)); + + // This is our implementation. Before the + // https://github.com/googleapis/google-http-java-client/pull/1941/, it was throwing + // IllegalArgumentException("Invalid length 9"). + assertEquals("abcdef", new String(Base64.decodeBase64(encodedString), StandardCharsets.UTF_8)); + } + + @Test + public void test_decodeBase64_newline_character() { + // In Base64 encoding, 2 characters (16 bits) are converted to 3 of 6-bits plus the padding + // character ('="). + // Base64encode("ab") => "YWI=" (3 characters + padding character) + // Adding a new line character that should be discarded between them + String encodedString = "YW\nI="; + + // This is a reference implementation by Apache Commons Codec. It discards the new line + // characters. + // assertEquals( + // "ab", + // new String( + // org.apache.commons.codec.binary.Base64.decodeBase64(encodedString), + // StandardCharsets.UTF_8)); + + // This is our implementation. Before the fix + // https://github.com/googleapis/google-http-java-client/pull/1941/, it was throwing + // IllegalArgumentException("Unrecognized character: 0xa"). + assertEquals("ab", new String(Base64.decodeBase64(encodedString), StandardCharsets.UTF_8)); + } + + @Test + public void test_decodeBase64_plus_and_newline_characters() { + // The plus sign is 62 in the Base64 table. So it's a valid character in encoded strings. + // https://datatracker.ietf.org/doc/html/rfc4648#section-4 + String encodedString = "+\nw=="; + + byte[] actual = Base64.decodeBase64(encodedString); + // Before the fix https://github.com/googleapis/google-http-java-client/pull/1941/, it was + // throwing IllegalArgumentException("Unrecognized character: +"). + assertThat(actual).isEqualTo(new byte[] {(byte) 0xfb}); + } + + @Test + public void test_decodeBase64_slash_and_newline_characters() { + // The slash sign is 63 in the Base64 table. So it's a valid character in encoded strings. + // https://datatracker.ietf.org/doc/html/rfc4648#section-4 + String encodedString = "/\nw=="; + + byte[] actual = Base64.decodeBase64(encodedString); + // Before the fix https://github.com/googleapis/google-http-java-client/pull/1941/, it was + // throwing IllegalArgumentException("Unrecognized character: /"). + assertThat(actual).isEqualTo(new byte[] {(byte) 0xff}); + } +} diff --git a/google-http-client/src/test/java/com/google/api/client/util/ClassInfoTest.java b/google-http-client/src/test/java/com/google/api/client/util/ClassInfoTest.java index c7ec1c7af..d5e51d120 100644 --- a/google-http-client/src/test/java/com/google/api/client/util/ClassInfoTest.java +++ b/google-http-client/src/test/java/com/google/api/client/util/ClassInfoTest.java @@ -14,33 +14,43 @@ package com.google.api.client.util; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import java.util.Arrays; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link ClassInfo}. * * @author Yaniv Inbar */ -public class ClassInfoTest extends TestCase { +@RunWith(JUnit4.class) +public class ClassInfoTest { public enum E { - @Value VALUE, @Value("other") OTHER_VALUE, @NullValue - NULL, IGNORED_VALUE + NULL, + IGNORED_VALUE } + @Test public void testIsEnum() { assertTrue(ClassInfo.of(E.class).isEnum()); assertFalse(ClassInfo.of(String.class).isEnum()); } + @Test public void testGetFieldInfo_enum() throws Exception { ClassInfo classInfo = ClassInfo.of(E.class); assertEquals(E.class.getField("NULL"), classInfo.getFieldInfo(null).getField()); @@ -49,10 +59,11 @@ public void testGetFieldInfo_enum() throws Exception { } public class A { - @Key - String b; + @Key String b; + @Key("oc") String c; + String d; @Key("AbC") @@ -60,8 +71,7 @@ public class A { } public class B extends A { - @Key - String e; + @Key String e; } public class C extends B { @@ -70,27 +80,31 @@ public class C extends B { } public class A1 { - @Key - String foo; + @Key String foo; + @Key("foo") String foo2; } + @Test public void testNames() { assertEquals(ImmutableList.of("AbC", "b", "oc"), ClassInfo.of(A.class).names); assertEquals(ImmutableList.of("AbC", "b", "e", "oc"), ClassInfo.of(B.class).names); } + @Test public void testNames_ignoreCase() { assertEquals(ImmutableList.of("abc", "b", "oc"), ClassInfo.of(A.class, true).names); assertEquals(ImmutableList.of("abc", "b", "e", "oc"), ClassInfo.of(B.class, true).names); } + @Test public void testNames_enum() { ClassInfo classInfo = ClassInfo.of(E.class); assertEquals(Lists.newArrayList(Arrays.asList(null, "VALUE", "other")), classInfo.names); } + @Test public void testOf() { try { ClassInfo.of(A1.class, true); diff --git a/google-http-client/src/test/java/com/google/api/client/util/ClockTest.java b/google-http-client/src/test/java/com/google/api/client/util/ClockTest.java index 64acd1993..e87a4fc61 100644 --- a/google-http-client/src/test/java/com/google/api/client/util/ClockTest.java +++ b/google-http-client/src/test/java/com/google/api/client/util/ClockTest.java @@ -14,16 +14,22 @@ package com.google.api.client.util; -import junit.framework.TestCase; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests for the {@link Clock}. + * * @author mlinder@google.com (Matthias Linder) */ -public class ClockTest extends TestCase { - /** - * Tests that the Clock.SYSTEM.currentTimeMillis() method returns useful values. - */ +@RunWith(JUnit4.class) +public class ClockTest { + /** Tests that the Clock.SYSTEM.currentTimeMillis() method returns useful values. */ + @Test public void testSystemClock() { assertNotNull(Clock.SYSTEM); diff --git a/google-http-client/src/test/java/com/google/api/client/util/DataMapTest.java b/google-http-client/src/test/java/com/google/api/client/util/DataMapTest.java index 30d5eba0d..45f07d5c2 100644 --- a/google-http-client/src/test/java/com/google/api/client/util/DataMapTest.java +++ b/google-http-client/src/test/java/com/google/api/client/util/DataMapTest.java @@ -14,27 +14,34 @@ package com.google.api.client.util; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import java.util.Iterator; import java.util.Map; -import junit.framework.TestCase; +import org.checkerframework.checker.units.qual.A; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link DataMap}. * * @author Yaniv Inbar */ -public class DataMapTest extends TestCase { +@RunWith(JUnit4.class) +public class DataMapTest { static class A { - @Key - String r; - @Key - String s; - @Key - String t; + @Key String r; + @Key String s; + @Key String t; } + @Test public void testSizeAndIsEmpty() { A a = new A(); DataMap map = new DataMap(a, false); @@ -51,6 +58,7 @@ public void testSizeAndIsEmpty() { assertFalse(map.isEmpty()); } + @Test public void testIterator() { A a = new A(); a.s = "value"; @@ -65,6 +73,7 @@ public void testIterator() { assertFalse(iterator.hasNext()); } + @Test public void testValues() { A a = new A(); a.r = "r"; @@ -80,6 +89,7 @@ public void testValues() { assertEquals(ImmutableList.of(), Lists.newArrayList(map.values())); } + @Test public void testKeys() { A a = new A(); a.r = "r"; @@ -95,6 +105,7 @@ public void testKeys() { assertEquals(ImmutableList.of(), Lists.newArrayList(map.keySet())); } + @Test public void testClear() { A a = new A(); a.r = "r"; @@ -106,6 +117,7 @@ public void testClear() { assertTrue(map.isEmpty()); } + @Test public void testGetKeyAndContainsKey() { A a = new A(); a.r = "rv"; @@ -118,6 +130,7 @@ public void testGetKeyAndContainsKey() { assertTrue(map.containsKey("r")); } + @Test public void testPut() { A a = new A(); a.r = "rv"; diff --git a/google-http-client/src/test/java/com/google/api/client/util/DataTest.java b/google-http-client/src/test/java/com/google/api/client/util/DataTest.java index 75ebc160b..1d2946f25 100644 --- a/google-http-client/src/test/java/com/google/api/client/util/DataTest.java +++ b/google-http-client/src/test/java/com/google/api/client/util/DataTest.java @@ -14,6 +14,14 @@ package com.google.api.client.util; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.google.common.collect.ImmutableMap; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; @@ -38,15 +46,19 @@ import java.util.TreeMap; import java.util.TreeSet; import java.util.concurrent.ConcurrentMap; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link Data}. * * @author Yaniv Inbar */ -public class DataTest extends TestCase { +@RunWith(JUnit4.class) +public class DataTest { + @Test public void testNullOf() { assertEquals("java.lang.Object", Data.nullOf(Object.class).getClass().getName()); assertEquals("java.lang.String", Data.nullOf(String.class).getClass().getName()); @@ -68,6 +80,14 @@ public void testNullOf() { } } + @Test + public void testNullOfTemplateTypes() { + String nullValue = Data.nullOf(String.class); + Map nullField = ImmutableMap.of("v", nullValue); + assertEquals(nullValue, nullField.get("v")); + } + + @Test public void testIsNull() { assertTrue(Data.isNull(Data.NULL_BOOLEAN)); assertTrue(Data.isNull(Data.NULL_STRING)); @@ -95,6 +115,7 @@ public void testIsNull() { assertFalse(Data.isNull(BigInteger.ZERO)); } + @Test public void testClone_array() { String[] orig = new String[] {"a", "b", "c"}; String[] result = Data.clone(orig); @@ -102,6 +123,7 @@ public void testClone_array() { assertTrue(Arrays.equals(orig, result)); } + @Test public void testClone_intArray() { int[] orig = new int[] {1, 2, 3}; int[] result = Data.clone(orig); @@ -109,12 +131,14 @@ public void testClone_intArray() { assertTrue(Arrays.equals(orig, result)); } + @Test public void testClone_arrayMap() { ArrayMap map = ArrayMap.of(); map.add("a", 1); assertEquals(map, Data.clone(map)); } + @Test public void testClone_ArraysAsList() { { List orig = Arrays.asList("a", "b", "c", new ArrayList()); @@ -132,6 +156,7 @@ public void testClone_ArraysAsList() { } } + @Test public void testNewCollectionInstance() throws Exception { assertEquals(ArrayList.class, Data.newCollectionInstance(null).getClass()); assertEquals(ArrayList.class, Data.newCollectionInstance(String[].class).getClass()); @@ -166,6 +191,7 @@ public void testNewCollectionInstance() throws Exception { } } + @Test public void testNewMapInstance() { assertEquals(ArrayMap.class, Data.newMapInstance(Object.class).getClass()); assertEquals(ArrayMap.class, Data.newMapInstance(Map.class).getClass()); @@ -189,12 +215,14 @@ public void testNewMapInstance() { } } + @Test public void testIsPrimitive() { assertFalse(Data.isPrimitive(null)); assertTrue(Data.isPrimitive(int.class)); assertTrue(Data.isPrimitive(Integer.class)); } + @Test public void testParsePrimitiveValue() { assertNull(Data.parsePrimitiveValue(Boolean.class, null)); assertEquals("abc", Data.parsePrimitiveValue(null, "abc")); @@ -203,29 +231,41 @@ public void testParsePrimitiveValue() { assertEquals('a', Data.parsePrimitiveValue(Character.class, "a")); assertEquals(true, Data.parsePrimitiveValue(boolean.class, "true")); assertEquals(true, Data.parsePrimitiveValue(Boolean.class, "true")); - assertEquals(new Byte(Byte.MAX_VALUE), + assertEquals( + new Byte(Byte.MAX_VALUE), Data.parsePrimitiveValue(Byte.class, String.valueOf(Byte.MAX_VALUE))); - assertEquals(new Byte(Byte.MAX_VALUE), + assertEquals( + new Byte(Byte.MAX_VALUE), Data.parsePrimitiveValue(byte.class, String.valueOf(Byte.MAX_VALUE))); - assertEquals(new Short(Short.MAX_VALUE), + assertEquals( + new Short(Short.MAX_VALUE), Data.parsePrimitiveValue(Short.class, String.valueOf(Short.MAX_VALUE))); - assertEquals(new Short(Short.MAX_VALUE), + assertEquals( + new Short(Short.MAX_VALUE), Data.parsePrimitiveValue(short.class, String.valueOf(Short.MAX_VALUE))); - assertEquals(new Integer(Integer.MAX_VALUE), + assertEquals( + new Integer(Integer.MAX_VALUE), Data.parsePrimitiveValue(Integer.class, String.valueOf(Integer.MAX_VALUE))); - assertEquals(new Integer(Integer.MAX_VALUE), + assertEquals( + new Integer(Integer.MAX_VALUE), Data.parsePrimitiveValue(int.class, String.valueOf(Integer.MAX_VALUE))); - assertEquals(new Long(Long.MAX_VALUE), + assertEquals( + new Long(Long.MAX_VALUE), Data.parsePrimitiveValue(Long.class, String.valueOf(Long.MAX_VALUE))); - assertEquals(new Long(Long.MAX_VALUE), + assertEquals( + new Long(Long.MAX_VALUE), Data.parsePrimitiveValue(long.class, String.valueOf(Long.MAX_VALUE))); - assertEquals(new Float(Float.MAX_VALUE), + assertEquals( + new Float(Float.MAX_VALUE), Data.parsePrimitiveValue(Float.class, String.valueOf(Float.MAX_VALUE))); - assertEquals(new Float(Float.MAX_VALUE), + assertEquals( + new Float(Float.MAX_VALUE), Data.parsePrimitiveValue(float.class, String.valueOf(Float.MAX_VALUE))); - assertEquals(new Double(Double.MAX_VALUE), + assertEquals( + new Double(Double.MAX_VALUE), Data.parsePrimitiveValue(Double.class, String.valueOf(Double.MAX_VALUE))); - assertEquals(new Double(Double.MAX_VALUE), + assertEquals( + new Double(Double.MAX_VALUE), Data.parsePrimitiveValue(double.class, String.valueOf(Double.MAX_VALUE))); BigInteger bigint = BigInteger.valueOf(Long.MAX_VALUE); assertEquals( @@ -242,6 +282,26 @@ public void testParsePrimitiveValue() { // expected } assertNull(Data.parsePrimitiveValue(Void.class, "abc")); + assertNull(Data.parsePrimitiveValue(Enum.class, null)); + } + + private enum MyEnum { + A("a"); + private final String s; + + MyEnum(String s) { + this.s = s; + } + } + + @Test + public void testParsePrimitiveValueWithUnknownEnum() { + try { + Data.parsePrimitiveValue(MyEnum.class, "foo"); + fail("expected " + IllegalArgumentException.class); + } catch (IllegalArgumentException e) { + // expected + } } static class Resolve { @@ -249,57 +309,46 @@ static class Resolve { public X x; } - static class IntegerResolve extends Resolve { - } + static class IntegerResolve extends Resolve {} - static class MedResolve extends Resolve { - } + static class MedResolve extends Resolve {} - static class DoubleResolve extends MedResolve { - } + static class DoubleResolve extends MedResolve {} - static class Med2Resolve extends MedResolve { - } + static class Med2Resolve extends MedResolve {} - static class LongResolve extends Med2Resolve { - } + static class LongResolve extends Med2Resolve {} - static class ArrayResolve extends Resolve { - } + static class ArrayResolve extends Resolve {} - static class ParameterizedResolve extends Resolve, Integer> { - } + static class ParameterizedResolve extends Resolve, Integer> {} - static class MedXResolve extends Resolve { - } + static class MedXResolve extends Resolve {} + @Test public void testResolveWildcardTypeOrTypeVariable() throws Exception { // t TypeVariable tTypeVar = (TypeVariable) Resolve.class.getField("t").getGenericType(); - assertEquals( - Number.class, resolveWildcardTypeOrTypeVariable(new Object().getClass(), tTypeVar)); - assertEquals(Number.class, - resolveWildcardTypeOrTypeVariable(new Resolve().getClass(), tTypeVar)); - assertEquals(Integer.class, - resolveWildcardTypeOrTypeVariable(new IntegerResolve().getClass(), tTypeVar)); - assertEquals( - Long.class, resolveWildcardTypeOrTypeVariable(new LongResolve().getClass(), tTypeVar)); - assertEquals( - Double.class, resolveWildcardTypeOrTypeVariable(new DoubleResolve().getClass(), tTypeVar)); + assertEquals(Number.class, resolveWildcardTypeOrTypeVariable(Object.class, tTypeVar)); + assertEquals(Number.class, resolveWildcardTypeOrTypeVariable(Resolve.class, tTypeVar)); + assertEquals(Integer.class, resolveWildcardTypeOrTypeVariable(IntegerResolve.class, tTypeVar)); + assertEquals(Long.class, resolveWildcardTypeOrTypeVariable(LongResolve.class, tTypeVar)); + assertEquals(Double.class, resolveWildcardTypeOrTypeVariable(DoubleResolve.class, tTypeVar)); // partially resolved - assertEquals(Number.class, - resolveWildcardTypeOrTypeVariable(new MedResolve().getClass(), tTypeVar)); + assertEquals(Number.class, resolveWildcardTypeOrTypeVariable(MedResolve.class, tTypeVar)); // x TypeVariable xTypeVar = (TypeVariable) Resolve.class.getField("x").getGenericType(); + assertEquals(Object.class, resolveWildcardTypeOrTypeVariable(Object.class, xTypeVar)); assertEquals( - Object.class, resolveWildcardTypeOrTypeVariable(new Object().getClass(), xTypeVar)); - assertEquals(Boolean.class, Types.getArrayComponentType( - resolveWildcardTypeOrTypeVariable(new ArrayResolve().getClass(), xTypeVar))); + Boolean.class, + Types.getArrayComponentType( + resolveWildcardTypeOrTypeVariable(ArrayResolve.class, xTypeVar))); assertEquals( - Collection.class, Types.getRawClass((ParameterizedType) resolveWildcardTypeOrTypeVariable( - new ParameterizedResolve().getClass(), xTypeVar))); - assertEquals(Number.class, resolveWildcardTypeOrTypeVariable( - new MedXResolve().getClass(), xTypeVar)); + Collection.class, + Types.getRawClass( + (ParameterizedType) + resolveWildcardTypeOrTypeVariable(ParameterizedResolve.class, xTypeVar))); + assertEquals(Number.class, resolveWildcardTypeOrTypeVariable(MedXResolve.class, xTypeVar)); } private static Type resolveWildcardTypeOrTypeVariable( diff --git a/google-http-client/src/test/java/com/google/api/client/util/DateTimeTest.java b/google-http-client/src/test/java/com/google/api/client/util/DateTimeTest.java index 7313d92ce..66c0b7171 100644 --- a/google-http-client/src/test/java/com/google/api/client/util/DateTimeTest.java +++ b/google-http-client/src/test/java/com/google/api/client/util/DateTimeTest.java @@ -14,85 +14,96 @@ package com.google.api.client.util; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.google.api.client.util.DateTime.SecondsAndNanos; import java.util.Date; import java.util.TimeZone; -import junit.framework.TestCase; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link DateTime}. * * @author Yaniv Inbar */ -public class DateTimeTest extends TestCase { +@RunWith(JUnit4.class) +public class DateTimeTest { private TimeZone originalTimeZone; - public DateTimeTest() { - } - - public DateTimeTest(String testName) { - super(testName); - } - - @Override - protected void setUp() throws Exception { + @Before + public void setUp() throws Exception { originalTimeZone = TimeZone.getDefault(); } - @Override - protected void tearDown() throws Exception { + @Before + public void tearDown() throws Exception { TimeZone.setDefault(originalTimeZone); } + @Test public void testToStringRfc3339() { TimeZone.setDefault(TimeZone.getTimeZone("GMT-4")); - assertEquals("Check with explicit Date and Timezone.", + assertEquals( + "Check with explicit Date and Timezone.", "2012-11-06T12:10:44.000-08:00", new DateTime(new Date(1352232644000L), TimeZone.getTimeZone("GMT-8")).toStringRfc3339()); - assertEquals("Check with explicit Date but no explicit Timezone.", + assertEquals( + "Check with explicit Date but no explicit Timezone.", "2012-11-06T16:10:44.000-04:00", new DateTime(new Date(1352232644000L)).toStringRfc3339()); - assertEquals("Check with explicit Date and Timezone-Shift.", + assertEquals( + "Check with explicit Date and Timezone-Shift.", "2012-11-06T17:10:44.000-03:00", new DateTime(1352232644000L, -180).toStringRfc3339()); - assertEquals("Check with explicit Date and Zulu Timezone Offset.", + assertEquals( + "Check with explicit Date and Zulu Timezone Offset.", "2012-11-06T20:10:44.000Z", new DateTime(1352232644000L, 0).toStringRfc3339()); TimeZone.setDefault(TimeZone.getTimeZone("UTC")); - assertEquals("Check with explicit Date but no explicit Timezone.", + assertEquals( + "Check with explicit Date but no explicit Timezone.", "2012-11-06T20:10:44.000Z", new DateTime(new Date(1352232644000L)).toStringRfc3339()); } + @Test public void testToStringRfc3339_dateOnly() { - for (String timeZoneString : new String[]{"GMT-4", "UTC", "UTC-7"}) { + for (String timeZoneString : new String[] {"GMT-4", "UTC", "UTC-7"}) { TimeZone.setDefault(TimeZone.getTimeZone(timeZoneString)); - assertEquals( - "2012-11-06", - new DateTime(true, 1352232644000L, 1).toStringRfc3339()); - assertEquals( - "2012-11-06", - new DateTime(true, 1352232644000L, null).toStringRfc3339()); + assertEquals("2012-11-06", new DateTime(true, 1352232644000L, 1).toStringRfc3339()); + assertEquals("2012-11-06", new DateTime(true, 1352232644000L, null).toStringRfc3339()); assertEquals("2000-01-01", new DateTime("2000-01-01").toStringRfc3339()); } } + @Test public void testEquals() throws InterruptedException { - assertFalse("Check equals with two different tz specified.", + assertFalse( + "Check equals with two different tz specified.", new DateTime(1234567890L).equals(new DateTime(1234567890L, 120))); - assertTrue("Check equals with two identical tz specified.", + assertTrue( + "Check equals with two identical tz specified.", new DateTime(1234567890L, -240).equals(new DateTime(1234567890L, -240))); - assertFalse("Check equals with two different tz specified.", + assertFalse( + "Check equals with two different tz specified.", new DateTime(1234567890L, 60).equals(new DateTime(1234567890L, 240))); assertFalse("Check not equal.", new DateTime(1234567890L).equals(new DateTime(9876543210L))); - assertFalse("Check not equal with tz.", + assertFalse( + "Check not equal with tz.", new DateTime(1234567890L, 120).equals(new DateTime(9876543210L, 120))); assertFalse( "Check not equal with Date.", new DateTime(1234567890L).equals(new Date(9876543210L))); @@ -103,6 +114,7 @@ public void testEquals() throws InterruptedException { assertEquals(dateTime1, dateTime2); } + @Test public void testParseRfc3339() { expectExceptionForParseRfc3339(""); expectExceptionForParseRfc3339("abc"); @@ -129,14 +141,122 @@ public void testParseRfc3339() { assertEquals(0, value.getValue() % 100); // From the RFC3339 Standard - assertEquals(DateTime.parseRfc3339("1996-12-19T16:39:57-08:00").getValue(), + assertEquals( + DateTime.parseRfc3339("1996-12-19T16:39:57-08:00").getValue(), DateTime.parseRfc3339("1996-12-20T00:39:57Z").getValue()); // from Section 5.8 Examples - assertEquals(DateTime.parseRfc3339("1990-12-31T23:59:60Z").getValue(), + assertEquals( + DateTime.parseRfc3339("1990-12-31T23:59:60Z").getValue(), DateTime.parseRfc3339("1990-12-31T15:59:60-08:00").getValue()); // from Section 5.8 Examples - assertEquals(DateTime.parseRfc3339("2007-06-01t18:50:00-04:00").getValue(), + assertEquals( + DateTime.parseRfc3339("2007-06-01t18:50:00-04:00").getValue(), DateTime.parseRfc3339("2007-06-01t22:50:00Z").getValue()); // from Section 4.2 Local Offsets + + // Test truncating beyond millisecond precision. + assertEquals( + DateTime.parseRfc3339( + "2018-12-31T23:59:59.999999999Z"), // This value would be rounded up prior to version + // 1.30.2 + DateTime.parseRfc3339("2018-12-31T23:59:59.999Z")); + assertEquals( + DateTime.parseRfc3339( + "2018-12-31T23:59:59.9999Z"), // This value would be truncated prior to version 1.30.2 + DateTime.parseRfc3339("2018-12-31T23:59:59.999Z")); + + // The beginning of Gregorian Calendar + assertEquals( + -12219287774877L, // Result from Joda time's Instant.parse + DateTime.parseRfc3339("1582-10-15T01:23:45.123Z").getValue()); + } + + /** + * The following test values have been generated and verified using the {@link DateTimeFormatter} + * in Java 8. + * + *
    +   * Timestamp                           |   Seconds     |   Nanos
    +   * 2018-03-01T10:11:12.999Z            |   1519899072  |   999000000
    +   * 2018-10-28T02:00:00+02:00           |   1540684800  |   0
    +   * 2018-10-28T03:00:00+01:00           |   1540692000  |   0
    +   * 2018-01-01T00:00:00.000000001Z      |   1514764800  |   1
    +   * 2018-10-28T02:00:00Z                |   1540692000  |   0
    +   * 2018-12-31T23:59:59.999999999Z      |   1546300799  |   999999999
    +   * 2018-03-01T10:11:12.9999Z           |   1519899072  |   999900000
    +   * 2018-03-01T10:11:12.000000001Z      |   1519899072  |   1
    +   * 2018-03-01T10:11:12.100000000Z      |   1519899072  |   100000000
    +   * 2018-03-01T10:11:12.100000001Z      |   1519899072  |   100000001
    +   * 2018-03-01T10:11:12-10:00           |   1519935072  |   0
    +   * 2018-03-01T10:11:12.999999999Z      |   1519899072  |   999999999
    +   * 2018-03-01T10:11:12-12:00           |   1519942272  |   0
    +   * 2018-10-28T03:00:00Z                |   1540695600  |   0
    +   * 2018-10-28T02:30:00Z                |   1540693800  |   0
    +   * 2018-03-01T10:11:12.123Z            |   1519899072  |   123000000
    +   * 2018-10-28T02:30:00+02:00           |   1540686600  |   0
    +   * 2018-03-01T10:11:12.123456789Z      |   1519899072  |   123456789
    +   * 2018-03-01T10:11:12.1000Z           |   1519899072  |   100000000
    +   * 
    + */ + @Test + public void testParseRfc3339ToSecondsAndNanos() { + assertParsedRfc3339( + "2018-03-01T10:11:12.999Z", SecondsAndNanos.ofSecondsAndNanos(1519899072L, 999000000)); + assertParsedRfc3339( + "2018-10-28T02:00:00+02:00", SecondsAndNanos.ofSecondsAndNanos(1540684800L, 0)); + assertParsedRfc3339( + "2018-10-28T03:00:00+01:00", SecondsAndNanos.ofSecondsAndNanos(1540692000L, 0)); + assertParsedRfc3339( + "2018-01-01T00:00:00.000000001Z", SecondsAndNanos.ofSecondsAndNanos(1514764800L, 1)); + assertParsedRfc3339("2018-10-28T02:00:00Z", SecondsAndNanos.ofSecondsAndNanos(1540692000L, 0)); + assertParsedRfc3339( + "2018-12-31T23:59:59.999999999Z", + SecondsAndNanos.ofSecondsAndNanos(1546300799L, 999999999)); + assertParsedRfc3339( + "2018-03-01T10:11:12.9999Z", SecondsAndNanos.ofSecondsAndNanos(1519899072L, 999900000)); + assertParsedRfc3339( + "2018-03-01T10:11:12.000000001Z", SecondsAndNanos.ofSecondsAndNanos(1519899072L, 1)); + assertParsedRfc3339( + "2018-03-01T10:11:12.100000000Z", + SecondsAndNanos.ofSecondsAndNanos(1519899072L, 100000000)); + assertParsedRfc3339( + "2018-03-01T10:11:12.100000001Z", + SecondsAndNanos.ofSecondsAndNanos(1519899072L, 100000001)); + assertParsedRfc3339( + "2018-03-01T10:11:12-10:00", SecondsAndNanos.ofSecondsAndNanos(1519935072L, 0)); + assertParsedRfc3339( + "2018-03-01T10:11:12.999999999Z", + SecondsAndNanos.ofSecondsAndNanos(1519899072L, 999999999)); + assertParsedRfc3339( + "2018-03-01T10:11:12-12:00", SecondsAndNanos.ofSecondsAndNanos(1519942272L, 0)); + assertParsedRfc3339("2018-10-28T03:00:00Z", SecondsAndNanos.ofSecondsAndNanos(1540695600L, 0)); + assertParsedRfc3339("2018-10-28T02:30:00Z", SecondsAndNanos.ofSecondsAndNanos(1540693800L, 0)); + assertParsedRfc3339( + "2018-03-01T10:11:12.123Z", SecondsAndNanos.ofSecondsAndNanos(1519899072L, 123000000)); + assertParsedRfc3339( + "2018-10-28T02:30:00+02:00", SecondsAndNanos.ofSecondsAndNanos(1540686600L, 0)); + assertParsedRfc3339( + "2018-03-01T10:11:12.123456789Z", + SecondsAndNanos.ofSecondsAndNanos(1519899072L, 123456789)); + assertParsedRfc3339( + "2018-03-01T10:11:12.1000Z", SecondsAndNanos.ofSecondsAndNanos(1519899072L, 100000000)); + } + + @Test + public void testEpoch() { + assertParsedRfc3339("1970-01-01T00:00:00.000Z", SecondsAndNanos.ofSecondsAndNanos(0, 0)); + } + + @Test + public void testOneSecondBeforeEpoch() { + assertParsedRfc3339("1969-12-31T23:59:59.000Z", SecondsAndNanos.ofSecondsAndNanos(-1, 0)); + } + + private static void assertParsedRfc3339(String input, SecondsAndNanos expected) { + SecondsAndNanos actual = DateTime.parseRfc3339ToSecondsAndNanos(input); + assertEquals( + "Seconds for " + input + " do not match", expected.getSeconds(), actual.getSeconds()); + assertEquals("Nanos for " + input + " do not match", expected.getNanos(), actual.getNanos()); } + @Test public void testParseAndFormatRfc3339() { // .12 becomes .120 String input = "1996-12-19T16:39:57.12-08:00"; @@ -146,14 +266,14 @@ public void testParseAndFormatRfc3339() { assertEquals(expected, output); // Truncated to milliseconds. - input = "1996-12-19T16:39:57.123456789-08:00"; + input = "1996-12-19T16:39:57.123456789-08:00"; expected = "1996-12-19T16:39:57.123-08:00"; dt = DateTime.parseRfc3339(input); output = dt.toStringRfc3339(); assertEquals(expected, output); } - private void expectExceptionForParseRfc3339(String input) { + private static void expectExceptionForParseRfc3339(String input) { try { DateTime.parseRfc3339(input); fail("expected NumberFormatException"); diff --git a/google-http-client/src/test/java/com/google/api/client/util/ExponentialBackOffTest.java b/google-http-client/src/test/java/com/google/api/client/util/ExponentialBackOffTest.java index 8561e5a4c..194adbb2c 100644 --- a/google-http-client/src/test/java/com/google/api/client/util/ExponentialBackOffTest.java +++ b/google-http-client/src/test/java/com/google/api/client/util/ExponentialBackOffTest.java @@ -14,46 +14,56 @@ package com.google.api.client.util; -import junit.framework.TestCase; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link ExponentialBackOff}. * * @author Ravi Mistry */ -public class ExponentialBackOffTest extends TestCase { - - public ExponentialBackOffTest(String name) { - super(name); - } +@RunWith(JUnit4.class) +public class ExponentialBackOffTest { + @Test public void testConstructor() { ExponentialBackOff backOffPolicy = new ExponentialBackOff(); - assertEquals(ExponentialBackOff.DEFAULT_INITIAL_INTERVAL_MILLIS, + assertEquals( + ExponentialBackOff.DEFAULT_INITIAL_INTERVAL_MILLIS, backOffPolicy.getInitialIntervalMillis()); - assertEquals(ExponentialBackOff.DEFAULT_INITIAL_INTERVAL_MILLIS, + assertEquals( + ExponentialBackOff.DEFAULT_INITIAL_INTERVAL_MILLIS, backOffPolicy.getCurrentIntervalMillis()); - assertEquals(ExponentialBackOff.DEFAULT_RANDOMIZATION_FACTOR, - backOffPolicy.getRandomizationFactor()); - assertEquals(ExponentialBackOff.DEFAULT_MULTIPLIER, backOffPolicy.getMultiplier()); + assertEquals( + ExponentialBackOff.DEFAULT_RANDOMIZATION_FACTOR, backOffPolicy.getRandomizationFactor(), 0); + assertEquals(ExponentialBackOff.DEFAULT_MULTIPLIER, backOffPolicy.getMultiplier(), 0); assertEquals( ExponentialBackOff.DEFAULT_MAX_INTERVAL_MILLIS, backOffPolicy.getMaxIntervalMillis()); - assertEquals(ExponentialBackOff.DEFAULT_MAX_ELAPSED_TIME_MILLIS, + assertEquals( + ExponentialBackOff.DEFAULT_MAX_ELAPSED_TIME_MILLIS, backOffPolicy.getMaxElapsedTimeMillis()); } + @Test public void testBuilder() { ExponentialBackOff backOffPolicy = new ExponentialBackOff.Builder().build(); - assertEquals(ExponentialBackOff.DEFAULT_INITIAL_INTERVAL_MILLIS, + assertEquals( + ExponentialBackOff.DEFAULT_INITIAL_INTERVAL_MILLIS, backOffPolicy.getInitialIntervalMillis()); - assertEquals(ExponentialBackOff.DEFAULT_INITIAL_INTERVAL_MILLIS, + assertEquals( + ExponentialBackOff.DEFAULT_INITIAL_INTERVAL_MILLIS, backOffPolicy.getCurrentIntervalMillis()); - assertEquals(ExponentialBackOff.DEFAULT_RANDOMIZATION_FACTOR, - backOffPolicy.getRandomizationFactor()); - assertEquals(ExponentialBackOff.DEFAULT_MULTIPLIER, backOffPolicy.getMultiplier()); + assertEquals( + ExponentialBackOff.DEFAULT_RANDOMIZATION_FACTOR, backOffPolicy.getRandomizationFactor(), 0); + assertEquals(ExponentialBackOff.DEFAULT_MULTIPLIER, backOffPolicy.getMultiplier(), 0); assertEquals( ExponentialBackOff.DEFAULT_MAX_INTERVAL_MILLIS, backOffPolicy.getMaxIntervalMillis()); - assertEquals(ExponentialBackOff.DEFAULT_MAX_ELAPSED_TIME_MILLIS, + assertEquals( + ExponentialBackOff.DEFAULT_MAX_ELAPSED_TIME_MILLIS, backOffPolicy.getMaxElapsedTimeMillis()); int testInitialInterval = 1; @@ -62,21 +72,23 @@ public void testBuilder() { int testMaxInterval = 10; int testMaxElapsedTime = 900000; - backOffPolicy = new ExponentialBackOff.Builder() - .setInitialIntervalMillis(testInitialInterval) - .setRandomizationFactor(testRandomizationFactor) - .setMultiplier(testMultiplier) - .setMaxIntervalMillis(testMaxInterval) - .setMaxElapsedTimeMillis(testMaxElapsedTime) - .build(); + backOffPolicy = + new ExponentialBackOff.Builder() + .setInitialIntervalMillis(testInitialInterval) + .setRandomizationFactor(testRandomizationFactor) + .setMultiplier(testMultiplier) + .setMaxIntervalMillis(testMaxInterval) + .setMaxElapsedTimeMillis(testMaxElapsedTime) + .build(); assertEquals(testInitialInterval, backOffPolicy.getInitialIntervalMillis()); assertEquals(testInitialInterval, backOffPolicy.getCurrentIntervalMillis()); - assertEquals(testRandomizationFactor, backOffPolicy.getRandomizationFactor()); - assertEquals(testMultiplier, backOffPolicy.getMultiplier()); + assertEquals(testRandomizationFactor, backOffPolicy.getRandomizationFactor(), 0); + assertEquals(testMultiplier, backOffPolicy.getMultiplier(), 0); assertEquals(testMaxInterval, backOffPolicy.getMaxIntervalMillis()); assertEquals(testMaxElapsedTime, backOffPolicy.getMaxElapsedTimeMillis()); } + @Test public void testBackOff() throws Exception { int testInitialInterval = 500; double testRandomizationFactor = 0.1; @@ -84,13 +96,14 @@ public void testBackOff() throws Exception { int testMaxInterval = 5000; int testMaxElapsedTime = 900000; - ExponentialBackOff backOffPolicy = new ExponentialBackOff.Builder() - .setInitialIntervalMillis(testInitialInterval) - .setRandomizationFactor(testRandomizationFactor) - .setMultiplier(testMultiplier) - .setMaxIntervalMillis(testMaxInterval) - .setMaxElapsedTimeMillis(testMaxElapsedTime) - .build(); + ExponentialBackOff backOffPolicy = + new ExponentialBackOff.Builder() + .setInitialIntervalMillis(testInitialInterval) + .setRandomizationFactor(testRandomizationFactor) + .setMultiplier(testMultiplier) + .setMaxIntervalMillis(testMaxInterval) + .setMaxElapsedTimeMillis(testMaxElapsedTime) + .build(); int[] expectedResults = {500, 1000, 2000, 4000, 5000, 5000, 5000, 5000, 5000, 5000}; for (int expected : expectedResults) { assertEquals(expected, backOffPolicy.getCurrentIntervalMillis()); @@ -102,6 +115,7 @@ public void testBackOff() throws Exception { } } + @Test public void testGetRandomizedInterval() { // 33% chance of being 1. assertEquals(1, ExponentialBackOff.getRandomValueFromInterval(0.5, 0, 2)); @@ -119,8 +133,7 @@ static class MyNanoClock implements NanoClock { private int i = 0; private long startSeconds; - MyNanoClock() { - } + MyNanoClock() {} MyNanoClock(long startSeconds) { this.startSeconds = startSeconds; @@ -131,6 +144,7 @@ public long nanoTime() { } } + @Test public void testGetElapsedTimeMillis() { ExponentialBackOff backOffPolicy = new ExponentialBackOff.Builder().setNanoClock(new MyNanoClock()).build(); @@ -138,6 +152,7 @@ public void testGetElapsedTimeMillis() { assertEquals("elapsedTimeMillis=" + elapsedTimeMillis, 1000, elapsedTimeMillis); } + @Test public void testMaxElapsedTime() throws Exception { ExponentialBackOff backOffPolicy = new ExponentialBackOff.Builder().setNanoClock(new MyNanoClock(10000)).build(); @@ -148,15 +163,17 @@ public void testMaxElapsedTime() throws Exception { assertEquals(BackOff.STOP, backOffPolicy.nextBackOffMillis()); } + @Test public void testBackOffOverflow() throws Exception { int testInitialInterval = Integer.MAX_VALUE / 2; double testMultiplier = 2.1; int testMaxInterval = Integer.MAX_VALUE; - ExponentialBackOff backOffPolicy = new ExponentialBackOff.Builder() - .setInitialIntervalMillis(testInitialInterval) - .setMultiplier(testMultiplier) - .setMaxIntervalMillis(testMaxInterval) - .build(); + ExponentialBackOff backOffPolicy = + new ExponentialBackOff.Builder() + .setInitialIntervalMillis(testInitialInterval) + .setMultiplier(testMultiplier) + .setMaxIntervalMillis(testMaxInterval) + .build(); backOffPolicy.nextBackOffMillis(); // Assert that when an overflow is possible the current interval is set to the max interval. assertEquals(testMaxInterval, backOffPolicy.getCurrentIntervalMillis()); diff --git a/google-http-client/src/test/java/com/google/api/client/util/FieldInfoTest.java b/google-http-client/src/test/java/com/google/api/client/util/FieldInfoTest.java index 4f62d4455..03e9b3c00 100644 --- a/google-http-client/src/test/java/com/google/api/client/util/FieldInfoTest.java +++ b/google-http-client/src/test/java/com/google/api/client/util/FieldInfoTest.java @@ -14,26 +14,33 @@ package com.google.api.client.util; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; -import junit.framework.TestCase; +import com.google.api.client.json.GenericJson; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link FieldInfo}. * * @author Yaniv Inbar */ -public class FieldInfoTest extends TestCase { +@RunWith(JUnit4.class) +public class FieldInfoTest { public enum E { - @Value VALUE, @Value("other") OTHER_VALUE, @NullValue - NULL, IGNORED_VALUE + NULL, + IGNORED_VALUE } + @Test public void testOf_enum() throws Exception { assertEquals(E.class.getField("VALUE"), FieldInfo.of(E.VALUE).getField()); assertEquals(E.class.getField("OTHER_VALUE"), FieldInfo.of(E.OTHER_VALUE).getField()); @@ -45,9 +52,43 @@ public void testOf_enum() throws Exception { } } + @Test public void testEnumValue() { assertEquals(E.VALUE, FieldInfo.of(E.VALUE).enumValue()); assertEquals(E.OTHER_VALUE, FieldInfo.of(E.OTHER_VALUE).enumValue()); assertEquals(E.NULL, FieldInfo.of(E.NULL).enumValue()); } + + public static final class Data extends GenericJson { + @Key String passcode; + @Key String passCode; + + public Data setPasscode(String passcode) { + this.passcode = passcode; + return this; + } + + public Data setPassCode(String passCode) { + this.passCode = passCode; + return this; + } + } + + @Test + public void testSetValueCaseSensitivityPriority() { + Data data = new Data(); + data.setPasscode("pass1"); + data.setPassCode("pass2"); + data.set("passCode", "passX"); + + assertEquals(data.passcode, "pass1"); + assertEquals(data.passCode, "passX"); + + data.setPasscode("pass1"); + data.setPassCode("pass2"); + data.set("passcode", "passX"); + + assertEquals(data.passcode, "passX"); + assertEquals(data.passCode, "pass2"); + } } diff --git a/google-http-client/src/test/java/com/google/api/client/util/GenericDataTest.java b/google-http-client/src/test/java/com/google/api/client/util/GenericDataTest.java index 91d319561..3b823d411 100644 --- a/google-http-client/src/test/java/com/google/api/client/util/GenericDataTest.java +++ b/google-http-client/src/test/java/com/google/api/client/util/GenericDataTest.java @@ -14,17 +14,27 @@ package com.google.api.client.util; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertTrue; + import com.google.api.client.util.GenericData.Flags; +import java.util.ArrayList; import java.util.EnumSet; -import junit.framework.Assert; -import junit.framework.TestCase; +import java.util.List; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link GenericData}. * * @author Yaniv Inbar */ -public class GenericDataTest extends TestCase { +@RunWith(JUnit4.class) +public class GenericDataTest { private class MyData extends GenericData { public MyData() { super(EnumSet.of(Flags.IGNORE_CASE)); @@ -32,17 +42,87 @@ public MyData() { @Key("FieldA") public String fieldA; + + @Key("FieldB") + public List fieldB; + + public void setFieldB(String fieldB) { + this.fieldB = Lists.newArrayList(); + this.fieldB.add(fieldB); + } + + public void setFieldB(List fieldB) { + this.fieldB = fieldB; + } + } + + private class GenericData1 extends GenericData { + public GenericData1() { + super(EnumSet.of(Flags.IGNORE_CASE)); + } + + @Key("FieldA") + public String fieldA; + } + + private class GenericData2 extends GenericData { + public GenericData2() { + super(EnumSet.of(Flags.IGNORE_CASE)); + } + + @Key("FieldA") + public String fieldA; + } + + @Test + public void testEquals_Symmetric() { + GenericData actual = new GenericData1(); + actual.set("fieldA", "bar"); + GenericData expected = new GenericData2(); + // Test that objects are equal. + expected.set("fieldA", "bar"); + assertNotSame(expected, actual); + assertTrue(expected.equals(expected) && actual.equals(actual)); + // Test that objects not are equal. + expected.set("fieldA", "far"); + assertFalse(expected.equals(actual) || actual.equals(expected)); + assertFalse(expected.hashCode() == actual.hashCode()); + } + + @Test + public void testEquals_SymmetricWithSameClass() { + GenericData actual = new MyData(); + actual.set("fieldA", "bar"); + GenericData expected = new MyData(); + // Test that objects are equal. + expected.set("fieldA", "bar"); + assertNotSame(expected, actual); + assertTrue(expected.equals(expected) && actual.equals(actual)); + assertTrue(expected.hashCode() == expected.hashCode()); } + @Test + public void testNotEquals_SymmetricWithSameClass() { + GenericData actual = new MyData(); + actual.set("fieldA", "bar"); + GenericData expected = new MyData(); + // Test that objects are not equal. + expected.set("fieldA", "far"); + assertNotSame(expected, actual); + assertFalse(expected.equals(actual) || actual.equals(expected)); + assertFalse(expected.hashCode() == actual.hashCode()); + } + @Test public void testClone_changingEntrySet() { GenericData data = new GenericData(); - assertEquals("{}", data.toString()); + assertEquals("GenericData{classInfo=[], {}}", data.toString()); GenericData clone = data.clone(); clone.set("foo", "bar"); - assertEquals("{foo=bar}", clone.toString()); + assertEquals("GenericData{classInfo=[], {foo=bar}}", clone.toString()); } + @Test public void testSetIgnoreCase_unknownKey() { GenericData data = new GenericData(EnumSet.of(Flags.IGNORE_CASE)); data.set("Foobar", "oldValue"); @@ -55,6 +135,7 @@ public void testSetIgnoreCase_unknownKey() { assertEquals(1, data.getUnknownKeys().size()); } + @Test public void testSetIgnoreCase_class() { MyData data = new MyData(); data.set("FIELDA", "someValue"); @@ -62,6 +143,7 @@ public void testSetIgnoreCase_class() { assertEquals(0, data.getUnknownKeys().size()); } + @Test public void testPutIgnoreCase_class() { MyData data = new MyData(); data.fieldA = "123"; @@ -70,12 +152,14 @@ public void testPutIgnoreCase_class() { assertEquals(0, data.getUnknownKeys().size()); } + @Test public void testGetIgnoreCase_class() { MyData data = new MyData(); data.fieldA = "someValue"; assertEquals("someValue", data.get("FIELDA")); } + @Test public void testRemoveIgnoreCase_class() { MyData data = new MyData(); data.fieldA = "someValue"; @@ -86,6 +170,7 @@ public void testRemoveIgnoreCase_class() { } } + @Test public void testPutIgnoreCase_unknownKey() { GenericData data = new GenericData(EnumSet.of(Flags.IGNORE_CASE)); assertEquals(null, data.put("fooBAR", "oldValue")); @@ -98,6 +183,7 @@ public void testPutIgnoreCase_unknownKey() { assertEquals(1, data.getUnknownKeys().size()); } + @Test public void testGetIgnoreCase_unknownKey() { GenericData data = new GenericData(EnumSet.of(Flags.IGNORE_CASE)); data.set("One", 1); @@ -109,6 +195,7 @@ public void testGetIgnoreCase_unknownKey() { assertEquals(null, data.get("unknownKey")); } + @Test public void testRemoveIgnoreCase_unknownKey() { GenericData data = new GenericData(EnumSet.of(Flags.IGNORE_CASE)); data.set("One", 1); @@ -119,4 +206,15 @@ public void testRemoveIgnoreCase_unknownKey() { assertEquals(2, data.remove("TESTA")); assertEquals(null, data.remove("TESTA")); } + + @Test + public void testPutShouldUseSetter() { + MyData data = new MyData(); + data.put("fieldB", "value1"); + assertEquals("value1", data.fieldB.get(0)); + List list = new ArrayList<>(); + list.add("value2"); + data.put("fieldB", list); + assertEquals(list, data.fieldB); + } } diff --git a/google-http-client/src/test/java/com/google/api/client/util/IOUtilsTest.java b/google-http-client/src/test/java/com/google/api/client/util/IOUtilsTest.java index f38c8dac7..c3c8acd17 100644 --- a/google-http-client/src/test/java/com/google/api/client/util/IOUtilsTest.java +++ b/google-http-client/src/test/java/com/google/api/client/util/IOUtilsTest.java @@ -14,16 +14,25 @@ package com.google.api.client.util; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + import java.io.File; import java.io.IOException; -import junit.framework.TestCase; +import java.nio.file.Files; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link IOUtils}. * * @author Yaniv Inbar */ -public class IOUtilsTest extends TestCase { +@RunWith(JUnit4.class) +public class IOUtilsTest { static final String VALUE = "abc"; @@ -32,33 +41,26 @@ public void testSerialize() throws IOException { assertEquals(VALUE, IOUtils.deserialize(bytes)); } + @Test public void testDeserialize() throws IOException { assertNull(IOUtils.deserialize((byte[]) null)); } + @Test public void testIsSymbolicLink_false() throws IOException { File file = File.createTempFile("tmp", null); file.deleteOnExit(); assertFalse(IOUtils.isSymbolicLink(file)); } - public void testIsSymbolicLink_true() throws IOException, InterruptedException { + @Test + public void testIsSymbolicLink_true() throws IOException { File file = File.createTempFile("tmp", null); file.deleteOnExit(); File file2 = new File(file.getCanonicalPath() + "2"); file2.deleteOnExit(); - try { - Process process = Runtime.getRuntime() - .exec(new String[] {"ln", "-s", file.getCanonicalPath(), file2.getCanonicalPath()}); - process.waitFor(); - process.destroy(); - } catch (IOException e) { - // ignore because ln command may not be defined - return; - } - // multiple versions of jdk6 cannot detect the symbolic link. Consider support best-effort on - // jdk6 - boolean jdk6 = System.getProperty("java.version").startsWith("1.6.0_"); - assertTrue(IOUtils.isSymbolicLink(file2) || jdk6); + Files.createSymbolicLink(file2.toPath(), file.toPath()); + + assertTrue(IOUtils.isSymbolicLink(file2)); } } diff --git a/google-http-client/src/test/java/com/google/api/client/util/LoggingStreamingContentTest.java b/google-http-client/src/test/java/com/google/api/client/util/LoggingStreamingContentTest.java index 2a353466a..6efbf19dd 100644 --- a/google-http-client/src/test/java/com/google/api/client/util/LoggingStreamingContentTest.java +++ b/google-http-client/src/test/java/com/google/api/client/util/LoggingStreamingContentTest.java @@ -14,19 +14,25 @@ package com.google.api.client.util; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + import com.google.api.client.testing.util.LogRecordingHandler; import java.io.ByteArrayOutputStream; import java.util.Arrays; import java.util.logging.Level; import java.util.logging.Logger; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link LoggingStreamingContent}. * * @author Yaniv Inbar */ -public class LoggingStreamingContentTest extends TestCase { +@RunWith(JUnit4.class) +public class LoggingStreamingContentTest { static final Logger LOGGER = Logger.getLogger(LoggingStreamingContentTest.class.getName()); @@ -34,12 +40,12 @@ public class LoggingStreamingContentTest extends TestCase { new byte[] {49, 50, 51, -41, -103, -41, -96, -41, -103, -41, -111}; private static final String SAMPLE = "123\u05D9\u05e0\u05D9\u05D1"; - /** - * Test method for {@link LoggingStreamingContent#writeTo(java.io.OutputStream)}. - */ + /** Test method for {@link LoggingStreamingContent#writeTo(java.io.OutputStream)}. */ + @Test public void testWriteTo() throws Exception { - LoggingStreamingContent logContent = new LoggingStreamingContent( - new ByteArrayStreamingContent(SAMPLE_UTF8), LOGGER, Level.CONFIG, Integer.MAX_VALUE); + LoggingStreamingContent logContent = + new LoggingStreamingContent( + new ByteArrayStreamingContent(SAMPLE_UTF8), LOGGER, Level.CONFIG, Integer.MAX_VALUE); ByteArrayOutputStream out = new ByteArrayOutputStream(); LOGGER.setLevel(Level.CONFIG); LogRecordingHandler recorder = new LogRecordingHandler(); @@ -49,6 +55,7 @@ public void testWriteTo() throws Exception { assertEquals(Arrays.asList("Total: 11 bytes", SAMPLE), recorder.messages()); } + @Test public void testContentLoggingLimit() throws Exception { LOGGER.setLevel(Level.CONFIG); @@ -56,16 +63,21 @@ public void testContentLoggingLimit() throws Exception { LogRecordingHandler recorder = new LogRecordingHandler(); LOGGER.addHandler(recorder); ByteArrayOutputStream out = new ByteArrayOutputStream(); - LoggingStreamingContent logContent = new LoggingStreamingContent( - new ByteArrayStreamingContent(SAMPLE_UTF8), LOGGER, Level.CONFIG, SAMPLE_UTF8.length); + LoggingStreamingContent logContent = + new LoggingStreamingContent( + new ByteArrayStreamingContent(SAMPLE_UTF8), LOGGER, Level.CONFIG, SAMPLE_UTF8.length); logContent.writeTo(out); assertEquals(Arrays.asList("Total: 11 bytes", SAMPLE), recorder.messages()); // Set the content logging limit to be less than the length of the content. recorder = new LogRecordingHandler(); LOGGER.addHandler(recorder); - logContent = new LoggingStreamingContent( - new ByteArrayStreamingContent(SAMPLE_UTF8), LOGGER, Level.CONFIG, SAMPLE_UTF8.length - 1); + logContent = + new LoggingStreamingContent( + new ByteArrayStreamingContent(SAMPLE_UTF8), + LOGGER, + Level.CONFIG, + SAMPLE_UTF8.length - 1); logContent.writeTo(new ByteArrayOutputStream()); assertEquals( Arrays.asList("Total: 11 bytes (logging first 10 bytes)", "123\u05D9\u05e0\u05D9\ufffd"), @@ -74,23 +86,26 @@ public void testContentLoggingLimit() throws Exception { // Set the content logging limit to 0 to disable content logging. recorder = new LogRecordingHandler(); LOGGER.addHandler(recorder); - logContent = new LoggingStreamingContent( - new ByteArrayStreamingContent(SAMPLE_UTF8), LOGGER, Level.CONFIG, 0); + logContent = + new LoggingStreamingContent( + new ByteArrayStreamingContent(SAMPLE_UTF8), LOGGER, Level.CONFIG, 0); logContent.writeTo(new ByteArrayOutputStream()); assertEquals(Arrays.asList("Total: 11 bytes"), recorder.messages()); // writeTo should behave as expected even if content length is specified to be -1. recorder = new LogRecordingHandler(); LOGGER.addHandler(recorder); - logContent = new LoggingStreamingContent( - new ByteArrayStreamingContent(SAMPLE_UTF8), LOGGER, Level.CONFIG, SAMPLE_UTF8.length); + logContent = + new LoggingStreamingContent( + new ByteArrayStreamingContent(SAMPLE_UTF8), LOGGER, Level.CONFIG, SAMPLE_UTF8.length); logContent.writeTo(new ByteArrayOutputStream()); assertEquals(Arrays.asList("Total: 11 bytes", SAMPLE), recorder.messages()); // Assert that an exception is thrown if content logging limit < 0. try { - logContent = new LoggingStreamingContent( - new ByteArrayStreamingContent(SAMPLE_UTF8), LOGGER, Level.CONFIG, -1); + logContent = + new LoggingStreamingContent( + new ByteArrayStreamingContent(SAMPLE_UTF8), LOGGER, Level.CONFIG, -1); logContent.writeTo(new ByteArrayOutputStream()); fail("Expected: " + IllegalArgumentException.class); } catch (IllegalArgumentException e) { diff --git a/google-http-client/src/test/java/com/google/api/client/util/NanoClockTest.java b/google-http-client/src/test/java/com/google/api/client/util/NanoClockTest.java index 383da3f10..681fdd017 100644 --- a/google-http-client/src/test/java/com/google/api/client/util/NanoClockTest.java +++ b/google-http-client/src/test/java/com/google/api/client/util/NanoClockTest.java @@ -14,15 +14,22 @@ package com.google.api.client.util; -import junit.framework.TestCase; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link NanoClock}. * * @author Yaniv Inbar */ -public class NanoClockTest extends TestCase { +@RunWith(JUnit4.class) +public class NanoClockTest { + @Test public void testSystemClock() { assertNotNull(NanoClock.SYSTEM); diff --git a/google-http-client/src/test/java/com/google/api/client/util/ObjectsTest.java b/google-http-client/src/test/java/com/google/api/client/util/ObjectsTest.java index b2b1b8372..e4a36d4d7 100644 --- a/google-http-client/src/test/java/com/google/api/client/util/ObjectsTest.java +++ b/google-http-client/src/test/java/com/google/api/client/util/ObjectsTest.java @@ -14,36 +14,42 @@ package com.google.api.client.util; -import junit.framework.TestCase; +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link Objects}. * * @author Yaniv Inbar */ -public class ObjectsTest extends TestCase { +@RunWith(JUnit4.class) +public class ObjectsTest { + @Test public void testToStringHelper() { String toTest = Objects.toStringHelper(new TestClass()).add("hello", "world").toString(); assertEquals("TestClass{hello=world}", toTest); } + @Test public void testConstructor_innerClass() { String toTest = Objects.toStringHelper(new TestClass()).toString(); assertEquals("TestClass{}", toTest); } + @Test public void testToString_oneIntegerField() { - String toTest = Objects.toStringHelper(new TestClass()) - .add("field1", new Integer(42)) - .toString(); + String toTest = + Objects.toStringHelper(new TestClass()).add("field1", Integer.valueOf(42)).toString(); assertEquals("TestClass{field1=42}", toTest); } + @Test public void testToStringOmitNullValues_oneField() { - String toTest = Objects.toStringHelper(new TestClass()) - .omitNullValues() - .add("field1", null) - .toString(); + String toTest = + Objects.toStringHelper(new TestClass()).omitNullValues().add("field1", null).toString(); assertEquals("TestClass{}", toTest); } diff --git a/google-http-client/src/test/java/com/google/api/client/util/PemReaderTest.java b/google-http-client/src/test/java/com/google/api/client/util/PemReaderTest.java index 8a1d01e5f..80d9dc171 100644 --- a/google-http-client/src/test/java/com/google/api/client/util/PemReaderTest.java +++ b/google-http-client/src/test/java/com/google/api/client/util/PemReaderTest.java @@ -16,49 +16,54 @@ import java.io.InputStream; import java.io.InputStreamReader; -import junit.framework.TestCase; import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link PemReader}. * * @author Yaniv Inbar */ -public class PemReaderTest extends TestCase { +@RunWith(JUnit4.class) +public class PemReaderTest { - private static final byte[] EXPECTED_BYTES = {48, -126, 2, 117, 2, 1, 0, 48, 13, 6, 9, 42, -122, - 72, -122, -9, 13, 1, 1, 1, 5, 0, 4, -126, 2, 95, 48, -126, 2, 91, 2, 1, 0, 2, -127, -127, 0, - -67, 82, 117, -113, 35, 77, 69, 84, -20, -18, 63, 94, -74, 75, 97, 60, 52, 56, -35, 101, 87, - -93, 16, 68, 102, -93, 20, 49, 66, -88, 61, -123, 119, -37, 80, 24, -75, -68, 33, -5, 113, -9, - 7, -41, 123, 92, -18, 105, -24, 73, 74, 2, -36, -56, 44, 90, -51, -4, -119, -96, -35, -47, -120, - -29, 65, -118, 16, 106, 89, -114, -99, -34, 84, 10, -86, 72, 44, 69, 51, 89, -111, -47, 73, 89, - -36, -27, -1, -96, -79, -124, -81, 108, -94, 56, 118, -89, 74, -9, -73, 39, 109, -65, -40, -80, - -85, -105, -18, -71, 98, 47, -76, -32, -19, 79, -33, 4, -126, -104, 97, -99, -60, 0, -86, 66, - -88, 23, 124, -43, 2, 3, 1, 0, 1, 2, -127, -128, 121, -44, 31, 92, 93, -18, 50, -120, 100, -13, - 39, -118, 78, 42, -111, -58, -55, 32, 50, -80, 45, 69, -4, -120, -41, -73, 103, -98, 15, 115, - -18, 42, -2, 38, -2, 18, -8, -105, -71, 18, 114, -110, -15, -45, -29, 73, -71, 14, 35, -15, 77, - -108, 43, -7, 16, 57, -38, -58, 0, -42, -87, 7, 86, 91, 49, -112, 10, -2, -83, 49, -104, -123, - 51, 116, -46, 3, -120, 100, -88, 77, 113, -115, -38, 97, 31, 118, -63, 41, 67, -11, -30, -65, - 73, 114, -123, -128, 65, 60, 47, -54, -30, -58, 8, -92, 119, 28, -98, 20, -111, 65, 70, 101, 88, - -78, -40, -108, 78, 92, 20, -46, -126, -11, -66, -10, -37, -87, -115, -67, 2, 65, 0, -5, 116, - -123, -16, -115, 32, -35, 36, 81, -125, -128, -10, 55, 75, -73, 30, -62, 19, -116, -110, 24, - -61, -33, -28, -93, -63, -69, 51, -35, -14, -36, -75, 127, 22, -123, -101, -45, -63, -20, 30, - -6, 85, -108, 24, -104, 119, 22, -53, -54, -45, -27, 120, -24, 44, -111, -21, -104, 101, -75, - 102, -13, -2, -120, 59, 2, 65, 0, -64, -66, 114, -88, 106, -77, -50, 25, -66, 98, 37, -7, 42, - 70, -80, 56, 126, 87, -11, 76, -23, 95, -5, 95, -82, -88, -125, -119, -9, -113, 121, -10, 57, - 36, 37, -26, -12, -126, -1, -45, -78, 16, -25, -101, -81, -37, 90, 127, -75, -112, -100, -91, - 65, -94, -78, -128, 40, -77, -64, -4, 1, 103, -50, 47, 2, 64, 52, 14, -21, -85, -31, -117, -20, - 60, -104, -93, -95, 15, 88, 99, 84, -122, 9, -88, 2, 114, 60, -82, 80, -84, 5, 59, 22, -122, - -90, 108, -95, 68, -14, 10, -73, -98, -117, 56, -102, -87, -49, 41, -24, 127, 47, 17, 120, -90, - -72, 87, 38, 42, -31, -26, 88, 79, 110, 61, -96, 80, -80, 51, 2, 1, 2, 64, 17, 56, -13, 69, -39, - 66, -9, -57, -107, 27, 112, 9, 51, -99, -35, 97, 46, -24, -19, 34, 82, 56, 33, 94, 11, 93, 67, - 99, -80, -101, 65, 106, -98, -16, 123, -14, -121, 38, -83, 117, 93, 19, -27, -98, 35, -72, -107, - -3, -109, 91, -72, -93, -117, -103, -34, 25, 85, -119, -70, 84, -54, 75, 92, 65, 2, 64, 30, -82, - 75, 100, -32, 123, 48, 18, -75, 26, 80, 109, 108, -3, -33, -110, -127, -49, 30, -4, -93, 30, 4, - 73, 85, -8, -36, 70, -123, -59, -124, 121, -101, 95, 28, -55, -1, -23, 83, -91, 111, 54, 60, - -40, -100, -81, 11, -45, -118, 11, -51, 0, -28, 32, 4, 85, 69, 122, 111, 110, 100, -86, -73, - 46}; + private static final byte[] EXPECTED_BYTES = { + 48, -126, 2, 117, 2, 1, 0, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 1, 5, 0, 4, -126, 2, + 95, 48, -126, 2, 91, 2, 1, 0, 2, -127, -127, 0, -67, 82, 117, -113, 35, 77, 69, 84, -20, -18, + 63, 94, -74, 75, 97, 60, 52, 56, -35, 101, 87, -93, 16, 68, 102, -93, 20, 49, 66, -88, 61, -123, + 119, -37, 80, 24, -75, -68, 33, -5, 113, -9, 7, -41, 123, 92, -18, 105, -24, 73, 74, 2, -36, + -56, 44, 90, -51, -4, -119, -96, -35, -47, -120, -29, 65, -118, 16, 106, 89, -114, -99, -34, 84, + 10, -86, 72, 44, 69, 51, 89, -111, -47, 73, 89, -36, -27, -1, -96, -79, -124, -81, 108, -94, 56, + 118, -89, 74, -9, -73, 39, 109, -65, -40, -80, -85, -105, -18, -71, 98, 47, -76, -32, -19, 79, + -33, 4, -126, -104, 97, -99, -60, 0, -86, 66, -88, 23, 124, -43, 2, 3, 1, 0, 1, 2, -127, -128, + 121, -44, 31, 92, 93, -18, 50, -120, 100, -13, 39, -118, 78, 42, -111, -58, -55, 32, 50, -80, + 45, 69, -4, -120, -41, -73, 103, -98, 15, 115, -18, 42, -2, 38, -2, 18, -8, -105, -71, 18, 114, + -110, -15, -45, -29, 73, -71, 14, 35, -15, 77, -108, 43, -7, 16, 57, -38, -58, 0, -42, -87, 7, + 86, 91, 49, -112, 10, -2, -83, 49, -104, -123, 51, 116, -46, 3, -120, 100, -88, 77, 113, -115, + -38, 97, 31, 118, -63, 41, 67, -11, -30, -65, 73, 114, -123, -128, 65, 60, 47, -54, -30, -58, 8, + -92, 119, 28, -98, 20, -111, 65, 70, 101, 88, -78, -40, -108, 78, 92, 20, -46, -126, -11, -66, + -10, -37, -87, -115, -67, 2, 65, 0, -5, 116, -123, -16, -115, 32, -35, 36, 81, -125, -128, -10, + 55, 75, -73, 30, -62, 19, -116, -110, 24, -61, -33, -28, -93, -63, -69, 51, -35, -14, -36, -75, + 127, 22, -123, -101, -45, -63, -20, 30, -6, 85, -108, 24, -104, 119, 22, -53, -54, -45, -27, + 120, -24, 44, -111, -21, -104, 101, -75, 102, -13, -2, -120, 59, 2, 65, 0, -64, -66, 114, -88, + 106, -77, -50, 25, -66, 98, 37, -7, 42, 70, -80, 56, 126, 87, -11, 76, -23, 95, -5, 95, -82, + -88, -125, -119, -9, -113, 121, -10, 57, 36, 37, -26, -12, -126, -1, -45, -78, 16, -25, -101, + -81, -37, 90, 127, -75, -112, -100, -91, 65, -94, -78, -128, 40, -77, -64, -4, 1, 103, -50, 47, + 2, 64, 52, 14, -21, -85, -31, -117, -20, 60, -104, -93, -95, 15, 88, 99, 84, -122, 9, -88, 2, + 114, 60, -82, 80, -84, 5, 59, 22, -122, -90, 108, -95, 68, -14, 10, -73, -98, -117, 56, -102, + -87, -49, 41, -24, 127, 47, 17, 120, -90, -72, 87, 38, 42, -31, -26, 88, 79, 110, 61, -96, 80, + -80, 51, 2, 1, 2, 64, 17, 56, -13, 69, -39, 66, -9, -57, -107, 27, 112, 9, 51, -99, -35, 97, 46, + -24, -19, 34, 82, 56, 33, 94, 11, 93, 67, 99, -80, -101, 65, 106, -98, -16, 123, -14, -121, 38, + -83, 117, 93, 19, -27, -98, 35, -72, -107, -3, -109, 91, -72, -93, -117, -103, -34, 25, 85, + -119, -70, 84, -54, 75, 92, 65, 2, 64, 30, -82, 75, 100, -32, 123, 48, 18, -75, 26, 80, 109, + 108, -3, -33, -110, -127, -49, 30, -4, -93, 30, 4, 73, 85, -8, -36, 70, -123, -59, -124, 121, + -101, 95, 28, -55, -1, -23, 83, -91, 111, 54, 60, -40, -100, -81, 11, -45, -118, 11, -51, 0, + -28, 32, 4, 85, 69, 122, 111, 110, 100, -86, -73, 46 + }; + @Test public void testReadFirstSectionAndClose() throws Exception { InputStream stream = getClass().getClassLoader().getResourceAsStream("com/google/api/client/util/secret.pem"); diff --git a/google-http-client/src/test/java/com/google/api/client/util/SecurityUtilsTest.java b/google-http-client/src/test/java/com/google/api/client/util/SecurityUtilsTest.java index aa3eab20a..611f2dad0 100644 --- a/google-http-client/src/test/java/com/google/api/client/util/SecurityUtilsTest.java +++ b/google-http-client/src/test/java/com/google/api/client/util/SecurityUtilsTest.java @@ -14,118 +14,138 @@ package com.google.api.client.util; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import com.google.api.client.testing.json.webtoken.TestCertificates; import com.google.api.client.testing.util.SecurityTestUtils; import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.security.KeyStore; import java.security.PrivateKey; import java.security.Signature; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPublicKey; import java.util.ArrayList; import javax.net.ssl.X509TrustManager; -import junit.framework.TestCase; import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link SecurityUtils}. * * @author Yaniv Inbar */ -public class SecurityUtilsTest extends TestCase { - - private static final byte[] ENCODED = {48, -126, 2, 117, 2, 1, 0, 48, 13, 6, 9, 42, -122, 72, - -122, -9, 13, 1, 1, 1, 5, 0, 4, -126, 2, 95, 48, -126, 2, 91, 2, 1, 0, 2, -127, -127, 0, -67, - 82, 117, -113, 35, 77, 69, 84, -20, -18, 63, 94, -74, 75, 97, 60, 52, 56, -35, 101, 87, -93, 16, - 68, 102, -93, 20, 49, 66, -88, 61, -123, 119, -37, 80, 24, -75, -68, 33, -5, 113, -9, 7, -41, - 123, 92, -18, 105, -24, 73, 74, 2, -36, -56, 44, 90, -51, -4, -119, -96, -35, -47, -120, -29, - 65, -118, 16, 106, 89, -114, -99, -34, 84, 10, -86, 72, 44, 69, 51, 89, -111, -47, 73, 89, -36, - -27, -1, -96, -79, -124, -81, 108, -94, 56, 118, -89, 74, -9, -73, 39, 109, -65, -40, -80, -85, - -105, -18, -71, 98, 47, -76, -32, -19, 79, -33, 4, -126, -104, 97, -99, -60, 0, -86, 66, -88, - 23, 124, -43, 2, 3, 1, 0, 1, 2, -127, -128, 121, -44, 31, 92, 93, -18, 50, -120, 100, -13, 39, - -118, 78, 42, -111, -58, -55, 32, 50, -80, 45, 69, -4, -120, -41, -73, 103, -98, 15, 115, -18, - 42, -2, 38, -2, 18, -8, -105, -71, 18, 114, -110, -15, -45, -29, 73, -71, 14, 35, -15, 77, -108, - 43, -7, 16, 57, -38, -58, 0, -42, -87, 7, 86, 91, 49, -112, 10, -2, -83, 49, -104, -123, 51, - 116, -46, 3, -120, 100, -88, 77, 113, -115, -38, 97, 31, 118, -63, 41, 67, -11, -30, -65, 73, - 114, -123, -128, 65, 60, 47, -54, -30, -58, 8, -92, 119, 28, -98, 20, -111, 65, 70, 101, 88, - -78, -40, -108, 78, 92, 20, -46, -126, -11, -66, -10, -37, -87, -115, -67, 2, 65, 0, -5, 116, - -123, -16, -115, 32, -35, 36, 81, -125, -128, -10, 55, 75, -73, 30, -62, 19, -116, -110, 24, - -61, -33, -28, -93, -63, -69, 51, -35, -14, -36, -75, 127, 22, -123, -101, -45, -63, -20, 30, - -6, 85, -108, 24, -104, 119, 22, -53, -54, -45, -27, 120, -24, 44, -111, -21, -104, 101, -75, - 102, -13, -2, -120, 59, 2, 65, 0, -64, -66, 114, -88, 106, -77, -50, 25, -66, 98, 37, -7, 42, - 70, -80, 56, 126, 87, -11, 76, -23, 95, -5, 95, -82, -88, -125, -119, -9, -113, 121, -10, 57, - 36, 37, -26, -12, -126, -1, -45, -78, 16, -25, -101, -81, -37, 90, 127, -75, -112, -100, -91, - 65, -94, -78, -128, 40, -77, -64, -4, 1, 103, -50, 47, 2, 64, 52, 14, -21, -85, -31, -117, -20, - 60, -104, -93, -95, 15, 88, 99, 84, -122, 9, -88, 2, 114, 60, -82, 80, -84, 5, 59, 22, -122, - -90, 108, -95, 68, -14, 10, -73, -98, -117, 56, -102, -87, -49, 41, -24, 127, 47, 17, 120, -90, - -72, 87, 38, 42, -31, -26, 88, 79, 110, 61, -96, 80, -80, 51, 2, 1, 2, 64, 17, 56, -13, 69, -39, - 66, -9, -57, -107, 27, 112, 9, 51, -99, -35, 97, 46, -24, -19, 34, 82, 56, 33, 94, 11, 93, 67, - 99, -80, -101, 65, 106, -98, -16, 123, -14, -121, 38, -83, 117, 93, 19, -27, -98, 35, -72, -107, - -3, -109, 91, -72, -93, -117, -103, -34, 25, 85, -119, -70, 84, -54, 75, 92, 65, 2, 64, 30, -82, - 75, 100, -32, 123, 48, 18, -75, 26, 80, 109, 108, -3, -33, -110, -127, -49, 30, -4, -93, 30, 4, - 73, 85, -8, -36, 70, -123, -59, -124, 121, -101, 95, 28, -55, -1, -23, 83, -91, 111, 54, 60, - -40, -100, -81, 11, -45, -118, 11, -51, 0, -28, 32, 4, 85, 69, 122, 111, 110, 100, -86, -73, - 46}; - - private static final byte[] SIGNED = {55, -20, -102, 62, -2, 115, -3, 103, -30, -45, 87, -128, - -124, 39, -45, -125, -30, 19, -103, 85, -117, 99, 36, 44, 106, -94, -33, 51, 10, 62, 98, 81, 6, - 55, 26, -123, -48, -78, 40, -83, 38, 114, 18, 30, 12, 103, -77, 18, -10, -93, 126, -10, -99, 44, - 123, -57, -98, -13, 40, 86, 90, 91, 4, -127, -62, 10, -95, -26, 34, -23, 1, 3, 57, -70, -68, - -74, 18, -107, -39, -85, 16, 87, 60, 91, -71, 65, -43, -121, 116, -28, -75, 94, -68, -60, -24, - 83, 113, -41, -47, -35, 11, 107, 117, -22, -112, 9, -14, 126, -90, 107, 63, -106, -118, -91, - -97, -128, 31, -108, -100, 102, 0, -40, 25, 53, -85, -112, 51, -61}; +@RunWith(JUnit4.class) +public class SecurityUtilsTest { + + private static final byte[] ENCODED = { + 48, -126, 2, 117, 2, 1, 0, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 1, 5, 0, 4, -126, 2, + 95, 48, -126, 2, 91, 2, 1, 0, 2, -127, -127, 0, -67, 82, 117, -113, 35, 77, 69, 84, -20, -18, + 63, 94, -74, 75, 97, 60, 52, 56, -35, 101, 87, -93, 16, 68, 102, -93, 20, 49, 66, -88, 61, -123, + 119, -37, 80, 24, -75, -68, 33, -5, 113, -9, 7, -41, 123, 92, -18, 105, -24, 73, 74, 2, -36, + -56, 44, 90, -51, -4, -119, -96, -35, -47, -120, -29, 65, -118, 16, 106, 89, -114, -99, -34, 84, + 10, -86, 72, 44, 69, 51, 89, -111, -47, 73, 89, -36, -27, -1, -96, -79, -124, -81, 108, -94, 56, + 118, -89, 74, -9, -73, 39, 109, -65, -40, -80, -85, -105, -18, -71, 98, 47, -76, -32, -19, 79, + -33, 4, -126, -104, 97, -99, -60, 0, -86, 66, -88, 23, 124, -43, 2, 3, 1, 0, 1, 2, -127, -128, + 121, -44, 31, 92, 93, -18, 50, -120, 100, -13, 39, -118, 78, 42, -111, -58, -55, 32, 50, -80, + 45, 69, -4, -120, -41, -73, 103, -98, 15, 115, -18, 42, -2, 38, -2, 18, -8, -105, -71, 18, 114, + -110, -15, -45, -29, 73, -71, 14, 35, -15, 77, -108, 43, -7, 16, 57, -38, -58, 0, -42, -87, 7, + 86, 91, 49, -112, 10, -2, -83, 49, -104, -123, 51, 116, -46, 3, -120, 100, -88, 77, 113, -115, + -38, 97, 31, 118, -63, 41, 67, -11, -30, -65, 73, 114, -123, -128, 65, 60, 47, -54, -30, -58, 8, + -92, 119, 28, -98, 20, -111, 65, 70, 101, 88, -78, -40, -108, 78, 92, 20, -46, -126, -11, -66, + -10, -37, -87, -115, -67, 2, 65, 0, -5, 116, -123, -16, -115, 32, -35, 36, 81, -125, -128, -10, + 55, 75, -73, 30, -62, 19, -116, -110, 24, -61, -33, -28, -93, -63, -69, 51, -35, -14, -36, -75, + 127, 22, -123, -101, -45, -63, -20, 30, -6, 85, -108, 24, -104, 119, 22, -53, -54, -45, -27, + 120, -24, 44, -111, -21, -104, 101, -75, 102, -13, -2, -120, 59, 2, 65, 0, -64, -66, 114, -88, + 106, -77, -50, 25, -66, 98, 37, -7, 42, 70, -80, 56, 126, 87, -11, 76, -23, 95, -5, 95, -82, + -88, -125, -119, -9, -113, 121, -10, 57, 36, 37, -26, -12, -126, -1, -45, -78, 16, -25, -101, + -81, -37, 90, 127, -75, -112, -100, -91, 65, -94, -78, -128, 40, -77, -64, -4, 1, 103, -50, 47, + 2, 64, 52, 14, -21, -85, -31, -117, -20, 60, -104, -93, -95, 15, 88, 99, 84, -122, 9, -88, 2, + 114, 60, -82, 80, -84, 5, 59, 22, -122, -90, 108, -95, 68, -14, 10, -73, -98, -117, 56, -102, + -87, -49, 41, -24, 127, 47, 17, 120, -90, -72, 87, 38, 42, -31, -26, 88, 79, 110, 61, -96, 80, + -80, 51, 2, 1, 2, 64, 17, 56, -13, 69, -39, 66, -9, -57, -107, 27, 112, 9, 51, -99, -35, 97, 46, + -24, -19, 34, 82, 56, 33, 94, 11, 93, 67, 99, -80, -101, 65, 106, -98, -16, 123, -14, -121, 38, + -83, 117, 93, 19, -27, -98, 35, -72, -107, -3, -109, 91, -72, -93, -117, -103, -34, 25, 85, + -119, -70, 84, -54, 75, 92, 65, 2, 64, 30, -82, 75, 100, -32, 123, 48, 18, -75, 26, 80, 109, + 108, -3, -33, -110, -127, -49, 30, -4, -93, 30, 4, 73, 85, -8, -36, 70, -123, -59, -124, 121, + -101, 95, 28, -55, -1, -23, 83, -91, 111, 54, 60, -40, -100, -81, 11, -45, -118, 11, -51, 0, + -28, 32, 4, 85, 69, 122, 111, 110, 100, -86, -73, 46 + }; + + private static final byte[] SIGNED = { + 55, -20, -102, 62, -2, 115, -3, 103, -30, -45, 87, -128, -124, 39, -45, -125, -30, 19, -103, 85, + -117, 99, 36, 44, 106, -94, -33, 51, 10, 62, 98, 81, 6, 55, 26, -123, -48, -78, 40, -83, 38, + 114, 18, 30, 12, 103, -77, 18, -10, -93, 126, -10, -99, 44, 123, -57, -98, -13, 40, 86, 90, 91, + 4, -127, -62, 10, -95, -26, 34, -23, 1, 3, 57, -70, -68, -74, 18, -107, -39, -85, 16, 87, 60, + 91, -71, 65, -43, -121, 116, -28, -75, 94, -68, -60, -24, 83, 113, -41, -47, -35, 11, 107, 117, + -22, -112, 9, -14, 126, -90, 107, 63, -106, -118, -91, -97, -128, 31, -108, -100, 102, 0, -40, + 25, 53, -85, -112, 51, -61 + }; private static final byte[] CONTENT_BYTES = StringUtils.getBytesUtf8("hello world"); private static final String SECRET_P12_BASE64 = "MIIGgAIBAzCCBjoGCSqGSIb3DQEHAaCCBisEggYnMIIGIzCCAygGCSqGSIb3DQEHAaCCAxkEggMV" - + "MIIDETCCAw0GCyqGSIb3DQEMCgECoIICsjCCAq4wKAYKKoZIhvcNAQwBAzAaBBTfraKzYbHQ1S+9" - + "Og5GtCQccMoZgAICBAAEggKAqYQ5X3GaQyBXepYj7EskFZ3bXYJkXv+OYIZQmzwWEMa13G7ve7BY" - + "yQ5SVWYlJYDpg2wDAp++PFE6nqGTzSe3Fw+HcbCiUDdY2nHdcDG5WS54ZEzQ8iJ2GaUzpGDQkVTX" - + "2mNp979ftks5n991kI056BXBxLXjQI06GTLJCu6e9snx7ow2hwJ4drNgfC3A6pENnMKl//O/QYxJ" - + "lqVkq9Y4xMUQYzFugzQNbN/8Z3ml6IaWTnWMaquFuGHSi6Ci98roj575M8oIVbI7HV8+bm5fYPoC" - + "8+Au9wmWgjdwI5ZkyIgQwBxMuTfL47xDaVBzhrXT+iX1dhI8Yh2E/vEpGf7D5/0jHJZe2f+II56n" - + "jfvgAwXarCP/XPViFtkfg59/NWgAB8KDxfOnWZiq9Yakw9SDr0fHEJAOw/7g9/hySZzkE69vpHNl" - + "2e5DJoSLNgHGkPMBJL5cDVaDvJm++JRsBsVP4DflPAMErp3wSbQoep6h7yyK2hLMFkwDetoaOdcM" - + "e+JV6rzjbCrfEWg8563oJy119USDbgG+4wbVFIWH5TFYE7hY+aQQZH9nI3h69IDHidpQ4llaVQkA" - + "sFMBGhr5oKzbbrf4qi5hdm2R7UMMNsNJTQPXhfY6yaD6PLUWbYJ1fyBzPK26dVVlnqvACyik0QcG" - + "UQMP5pgEZWey1bbQj6b9a+4iumSlXM3KOQco/nqx4zkPDskr9+V67eOULudiQm9rBevC/sH/dAMD" - + "9aeiFqiQI8/9qFATvUhXkk/UzQyIw5kL1TtOj0gZ+c7GyrFCf9BYa7S4ywymFz6Bwq5UMs+vjqMz" - + "6JckkNfds4YN21piFlnCnIorz+9wFME610UpLCsj1zFIMCMGCSqGSIb3DQEJFDEWHhQAcAByAGkA" - + "dgBhAHQAZQBrAGUAeTAhBgkqhkiG9w0BCRUxFAQSVGltZSAxMzU0Mzc4MjQ1MzA1MIIC8wYJKoZI" - + "hvcNAQcGoIIC5DCCAuACAQAwggLZBgkqhkiG9w0BBwEwKAYKKoZIhvcNAQwBBjAaBBSUpExQ6kOI" - + "8VuFs0MRfku3GddfmAICBACAggKg8NTeaId96ftUgJNvk7kcbAjf/1Gl3+nRJphNrU0VAQ1C2zyU" - + "85La3PuqRhEpgzQBp8vFydDqbPWorevxQuprG8W5vkDyB/CE4ZNJ/Vo55L8bZAlWKIPEKoH4GAhS" - + "gKmp8o/FWjuTs4OshOe32U0/d0WjeT3BG9xuGzLxNH9HvPTi8obMe8JZWYT/K0j26WeDrdbR8bZR" - + "nMg5aNZCbyuk42XuYUyXcA9/g4iVy0AuFEXm9qengkPGQ8dWYSdA4oGBzVxD32JIjm3BkwTgI84g" - + "wA5kvq1X4R9MxeHdMMafbf5H7j3MeSQBKoUgLFPp7ZWHcuEIF6eE0vqmobMT81SqQajUncludgfF" - + "UY7ykFwEZFbCZu+a9ueDt3HfBlrzBTMI2pYDJlm/0uDfukPRQ1Nk+PgyKLo8gxEB7Q9TSQQ4SeaB" - + "k22fOJ5QFH1go7kzPbbR/9GkUIYphscyVEYcztsHCDeIW6ajwzQYdtnDhSwKhPZTCFKm5oUIZ5kb" - + "+ilCQh12Mu6F9FyXiO+vWe8zVu0oBoS7xUUGNBZmkyUTzfUZ2ZuwWs6KxHryATIGCkG64evSrYqH" - + "nxuImCfA08ToVVeIHnOQk8jzgRdyifEs4nJxrWf9Ipn0ZlwOpEM4LBmBJDRiaOERP9YBTANAwKEk" - + "T6wt03nIg5Af7+/144cTedx5lGvjNW397ZFrWABpYr6WAlxd8IzVXn/4eCTun0yIsb3EcIkQN5es" - + "t4ao2eQz6gmalGRmXLKdPu2aa1XbGzv3yxNY7ldCf2W20nlxxpqJ9SsNFdorVnWiVNe/1tylNuaf" - + "2MsCs4xlHiD0A3MOrvgUc4aY9N52Ab/dd0VYGH5cZpoBB9G1LL8+LqIoM8dkFxrNg5AgKTk8O91D" - + "22RFKkRCWD/bMD0wITAJBgUrDgMCGgUABBTypWwWM5JDub1RzIXkRwfD7oQ9XwQUbgGuCBGKiU1C" - + "YAqwa61lyj/OG90CAgQA"; + + "MIIDETCCAw0GCyqGSIb3DQEMCgECoIICsjCCAq4wKAYKKoZIhvcNAQwBAzAaBBTfraKzYbHQ1S+9" + + "Og5GtCQccMoZgAICBAAEggKAqYQ5X3GaQyBXepYj7EskFZ3bXYJkXv+OYIZQmzwWEMa13G7ve7BY" + + "yQ5SVWYlJYDpg2wDAp++PFE6nqGTzSe3Fw+HcbCiUDdY2nHdcDG5WS54ZEzQ8iJ2GaUzpGDQkVTX" + + "2mNp979ftks5n991kI056BXBxLXjQI06GTLJCu6e9snx7ow2hwJ4drNgfC3A6pENnMKl//O/QYxJ" + + "lqVkq9Y4xMUQYzFugzQNbN/8Z3ml6IaWTnWMaquFuGHSi6Ci98roj575M8oIVbI7HV8+bm5fYPoC" + + "8+Au9wmWgjdwI5ZkyIgQwBxMuTfL47xDaVBzhrXT+iX1dhI8Yh2E/vEpGf7D5/0jHJZe2f+II56n" + + "jfvgAwXarCP/XPViFtkfg59/NWgAB8KDxfOnWZiq9Yakw9SDr0fHEJAOw/7g9/hySZzkE69vpHNl" + + "2e5DJoSLNgHGkPMBJL5cDVaDvJm++JRsBsVP4DflPAMErp3wSbQoep6h7yyK2hLMFkwDetoaOdcM" + + "e+JV6rzjbCrfEWg8563oJy119USDbgG+4wbVFIWH5TFYE7hY+aQQZH9nI3h69IDHidpQ4llaVQkA" + + "sFMBGhr5oKzbbrf4qi5hdm2R7UMMNsNJTQPXhfY6yaD6PLUWbYJ1fyBzPK26dVVlnqvACyik0QcG" + + "UQMP5pgEZWey1bbQj6b9a+4iumSlXM3KOQco/nqx4zkPDskr9+V67eOULudiQm9rBevC/sH/dAMD" + + "9aeiFqiQI8/9qFATvUhXkk/UzQyIw5kL1TtOj0gZ+c7GyrFCf9BYa7S4ywymFz6Bwq5UMs+vjqMz" + + "6JckkNfds4YN21piFlnCnIorz+9wFME610UpLCsj1zFIMCMGCSqGSIb3DQEJFDEWHhQAcAByAGkA" + + "dgBhAHQAZQBrAGUAeTAhBgkqhkiG9w0BCRUxFAQSVGltZSAxMzU0Mzc4MjQ1MzA1MIIC8wYJKoZI" + + "hvcNAQcGoIIC5DCCAuACAQAwggLZBgkqhkiG9w0BBwEwKAYKKoZIhvcNAQwBBjAaBBSUpExQ6kOI" + + "8VuFs0MRfku3GddfmAICBACAggKg8NTeaId96ftUgJNvk7kcbAjf/1Gl3+nRJphNrU0VAQ1C2zyU" + + "85La3PuqRhEpgzQBp8vFydDqbPWorevxQuprG8W5vkDyB/CE4ZNJ/Vo55L8bZAlWKIPEKoH4GAhS" + + "gKmp8o/FWjuTs4OshOe32U0/d0WjeT3BG9xuGzLxNH9HvPTi8obMe8JZWYT/K0j26WeDrdbR8bZR" + + "nMg5aNZCbyuk42XuYUyXcA9/g4iVy0AuFEXm9qengkPGQ8dWYSdA4oGBzVxD32JIjm3BkwTgI84g" + + "wA5kvq1X4R9MxeHdMMafbf5H7j3MeSQBKoUgLFPp7ZWHcuEIF6eE0vqmobMT81SqQajUncludgfF" + + "UY7ykFwEZFbCZu+a9ueDt3HfBlrzBTMI2pYDJlm/0uDfukPRQ1Nk+PgyKLo8gxEB7Q9TSQQ4SeaB" + + "k22fOJ5QFH1go7kzPbbR/9GkUIYphscyVEYcztsHCDeIW6ajwzQYdtnDhSwKhPZTCFKm5oUIZ5kb" + + "+ilCQh12Mu6F9FyXiO+vWe8zVu0oBoS7xUUGNBZmkyUTzfUZ2ZuwWs6KxHryATIGCkG64evSrYqH" + + "nxuImCfA08ToVVeIHnOQk8jzgRdyifEs4nJxrWf9Ipn0ZlwOpEM4LBmBJDRiaOERP9YBTANAwKEk" + + "T6wt03nIg5Af7+/144cTedx5lGvjNW397ZFrWABpYr6WAlxd8IzVXn/4eCTun0yIsb3EcIkQN5es" + + "t4ao2eQz6gmalGRmXLKdPu2aa1XbGzv3yxNY7ldCf2W20nlxxpqJ9SsNFdorVnWiVNe/1tylNuaf" + + "2MsCs4xlHiD0A3MOrvgUc4aY9N52Ab/dd0VYGH5cZpoBB9G1LL8+LqIoM8dkFxrNg5AgKTk8O91D" + + "22RFKkRCWD/bMD0wITAJBgUrDgMCGgUABBTypWwWM5JDub1RzIXkRwfD7oQ9XwQUbgGuCBGKiU1C" + + "YAqwa61lyj/OG90CAgQA"; + @Test public void testLoadPrivateKeyFromKeyStore() throws Exception { byte[] secretP12 = Base64.decodeBase64(SECRET_P12_BASE64); ByteArrayInputStream stream = new ByteArrayInputStream(secretP12); - PrivateKey privateKey = SecurityUtils.loadPrivateKeyFromKeyStore( - SecurityUtils.getPkcs12KeyStore(), stream, "notasecret", "privateKey", "notasecret"); + PrivateKey privateKey = + SecurityUtils.loadPrivateKeyFromKeyStore( + SecurityUtils.getPkcs12KeyStore(), stream, "notasecret", "privateKey", "notasecret"); assertEquals("RSA", privateKey.getAlgorithm()); assertEquals("PKCS#8", privateKey.getFormat()); byte[] actualEncoded = privateKey.getEncoded(); Assert.assertArrayEquals(ENCODED, actualEncoded); } + @Test public void testSign() throws Exception { - byte[] actualSigned = SecurityUtils.sign( - SecurityUtils.getSha256WithRsaSignatureAlgorithm(), SecurityTestUtils.newRsaPrivateKey(), - CONTENT_BYTES); + byte[] actualSigned = + SecurityUtils.sign( + SecurityUtils.getSha256WithRsaSignatureAlgorithm(), + SecurityTestUtils.newRsaPrivateKey(), + CONTENT_BYTES); Assert.assertArrayEquals(SIGNED, actualSigned); } + @Test public void testVerify() throws Exception { Signature signatureAlgorithm = SecurityUtils.getSha256WithRsaSignatureAlgorithm(); RSAPublicKey publicKey = SecurityTestUtils.newRsaPublicKey(); @@ -143,15 +163,63 @@ public X509Certificate verifyX509(TestCertificates.CertData caCert) throws Excep ArrayList certChain = new ArrayList(); certChain.add(TestCertificates.FOO_BAR_COM_CERT.getBase64Der()); certChain.add(TestCertificates.CA_CERT.getBase64Der()); - return SecurityUtils.verify(signatureAlgorithm, trustManager, certChain, signature, - data.getBytes("UTF-8")); + return SecurityUtils.verify( + signatureAlgorithm, trustManager, certChain, signature, data.getBytes("UTF-8")); } + @Test public void testVerifyX509() throws Exception { assertNotNull(verifyX509(TestCertificates.CA_CERT)); } + @Test public void testVerifyX509WrongCa() throws Exception { assertNull(verifyX509(TestCertificates.BOGUS_CA_CERT)); } + + @Test + public void testCreateMtlsKeyStoreNoCert() throws Exception { + final InputStream certMissing = + getClass() + .getClassLoader() + .getResourceAsStream("com/google/api/client/util/privateKey.pem"); + + boolean thrown = false; + try { + SecurityUtils.createMtlsKeyStore(certMissing); + fail("should have thrown"); + } catch (IllegalArgumentException e) { + assertTrue(e.getMessage().contains("certificate is missing from certAndKey string")); + thrown = true; + } + assertTrue("should have caught an IllegalArgumentException", thrown); + } + + @Test + public void testCreateMtlsKeyStoreNoPrivateKey() throws Exception { + final InputStream privateKeyMissing = + getClass().getClassLoader().getResourceAsStream("com/google/api/client/util/cert.pem"); + + boolean thrown = false; + try { + SecurityUtils.createMtlsKeyStore(privateKeyMissing); + fail("should have thrown"); + } catch (IllegalArgumentException e) { + assertTrue(e.getMessage().contains("private key is missing from certAndKey string")); + thrown = true; + } + assertTrue("should have caught an IllegalArgumentException", thrown); + } + + @Test + public void testCreateMtlsKeyStoreSuccess() throws Exception { + InputStream certAndKey = + getClass() + .getClassLoader() + .getResourceAsStream("com/google/api/client/util/mtlsCertAndKey.pem"); + + KeyStore mtlsKeyStore = SecurityUtils.createMtlsKeyStore(certAndKey); + + assertEquals(1, mtlsKeyStore.size()); + } } diff --git a/google-http-client/src/test/java/com/google/api/client/util/StringUtilsTest.java b/google-http-client/src/test/java/com/google/api/client/util/StringUtilsTest.java index 0f7887197..5e0601b27 100644 --- a/google-http-client/src/test/java/com/google/api/client/util/StringUtilsTest.java +++ b/google-http-client/src/test/java/com/google/api/client/util/StringUtilsTest.java @@ -14,33 +14,49 @@ package com.google.api.client.util; -import junit.framework.TestCase; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link StringUtils}. * * @author Yaniv Inbar */ -public class StringUtilsTest extends TestCase { +@RunWith(JUnit4.class) +public class StringUtilsTest { private static final byte[] SAMPLE_UTF8 = new byte[] {49, 50, 51, -41, -103, -41, -96, -41, -103, -41, -111}; private static final String SAMPLE = "123\u05D9\u05e0\u05D9\u05D1"; - public StringUtilsTest(String testName) { - super(testName); - } - + @Test public void testLineSeparator() { assertNotNull(StringUtils.LINE_SEPARATOR); } + @Test public void testToBytesUtf8() { Assert.assertArrayEquals(SAMPLE_UTF8, StringUtils.getBytesUtf8(SAMPLE)); } + @Test + public void testToBytesUtf8Null() { + assertNull(StringUtils.getBytesUtf8(null)); + } + + @Test public void testFromBytesUtf8() { assertEquals(SAMPLE, StringUtils.newStringUtf8(SAMPLE_UTF8)); } + + @Test + public void testFromBytesUtf8Null() { + assertNull(StringUtils.newStringUtf8(null)); + } } diff --git a/google-http-client/src/test/java/com/google/api/client/util/TypesTest.java b/google-http-client/src/test/java/com/google/api/client/util/TypesTest.java index f325633f1..595854e22 100644 --- a/google-http-client/src/test/java/com/google/api/client/util/TypesTest.java +++ b/google-http-client/src/test/java/com/google/api/client/util/TypesTest.java @@ -14,6 +14,11 @@ package com.google.api.client.util; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -33,15 +38,19 @@ import java.util.Stack; import java.util.TreeMap; import java.util.Vector; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests {@link Types}. * * @author Yaniv Inbar */ -public class TypesTest extends TestCase { +@RunWith(JUnit4.class) +public class TypesTest { + @Test public void testIsAssignableToOrFrom() { assertTrue(Types.isAssignableToOrFrom(String.class, Object.class)); assertTrue(Types.isAssignableToOrFrom(String.class, String.class)); @@ -49,9 +58,9 @@ public void testIsAssignableToOrFrom() { assertFalse(Types.isAssignableToOrFrom(String.class, List.class)); } - static class Foo { - } + static class Foo {} + @Test public void testNewInstance() { assertEquals(Object.class, Types.newInstance(Object.class).getClass()); assertEquals(String.class, Types.newInstance(String.class).getClass()); @@ -74,8 +83,7 @@ public void testNewInstance() { } @SuppressWarnings("serial") - static class IntegerList extends ArrayList { - } + static class IntegerList extends ArrayList {} static class WildcardBounds { public Collection any; @@ -83,6 +91,7 @@ static class WildcardBounds { public Collection lower; } + @Test public void testGetBound() throws Exception { subtestGetBound(Object.class, "any"); subtestGetBound(Number.class, "upper"); @@ -101,46 +110,44 @@ static class Resolve { public X x; } - static class IntegerResolve extends Resolve { - } + static class IntegerResolve extends Resolve {} - static class MedResolve extends Resolve { - } + static class MedResolve extends Resolve {} - static class DoubleResolve extends MedResolve { - } + static class DoubleResolve extends MedResolve {} - static class Med2Resolve extends MedResolve { - } + static class Med2Resolve extends MedResolve {} - static class LongResolve extends Med2Resolve { - } + static class LongResolve extends Med2Resolve {} - static class ArrayResolve extends Resolve { - } + static class ArrayResolve extends Resolve {} - static class ParameterizedResolve extends Resolve, Integer> { - } + static class ParameterizedResolve extends Resolve, Integer> {} + @Test public void testResolveTypeVariable() throws Exception { // t TypeVariable tTypeVar = (TypeVariable) Resolve.class.getField("t").getGenericType(); - assertNull(resolveTypeVariable(new Object().getClass(), tTypeVar)); - assertNull(resolveTypeVariable(new Resolve().getClass(), tTypeVar)); - assertEquals(Integer.class, resolveTypeVariable(new IntegerResolve().getClass(), tTypeVar)); - assertEquals(Long.class, resolveTypeVariable(new LongResolve().getClass(), tTypeVar)); - assertEquals(Double.class, resolveTypeVariable(new DoubleResolve().getClass(), tTypeVar)); + assertNull(resolveTypeVariable(Object.class, tTypeVar)); + assertNull(resolveTypeVariable(Resolve.class, tTypeVar)); + assertEquals(Integer.class, resolveTypeVariable(IntegerResolve.class, tTypeVar)); + assertEquals(Long.class, resolveTypeVariable(LongResolve.class, tTypeVar)); + assertEquals(Double.class, resolveTypeVariable(DoubleResolve.class, tTypeVar)); // partially resolved - assertEquals(MedResolve.class, - ((TypeVariable) resolveTypeVariable(new MedResolve().getClass(), tTypeVar)) + assertEquals( + MedResolve.class, + ((TypeVariable) resolveTypeVariable(MedResolve.class, tTypeVar)) .getGenericDeclaration()); // x TypeVariable xTypeVar = (TypeVariable) Resolve.class.getField("x").getGenericType(); - assertNull(resolveTypeVariable(new Object().getClass(), xTypeVar)); - assertEquals(Boolean.class, - Types.getArrayComponentType(resolveTypeVariable(new ArrayResolve().getClass(), xTypeVar))); - assertEquals(Collection.class, Types.getRawClass( - (ParameterizedType) resolveTypeVariable(new ParameterizedResolve().getClass(), xTypeVar))); + assertNull(resolveTypeVariable(Object.class, xTypeVar)); + assertEquals( + Boolean.class, + Types.getArrayComponentType(resolveTypeVariable(ArrayResolve.class, xTypeVar))); + assertEquals( + Collection.class, + Types.getRawClass( + (ParameterizedType) resolveTypeVariable(ParameterizedResolve.class, xTypeVar))); } private static Type resolveTypeVariable(Type context, TypeVariable typeVariable) { @@ -150,8 +157,10 @@ private static Type resolveTypeVariable(Type context, TypeVariable typeVariab public class A { public Iterable i; public ArrayList a; + @SuppressWarnings({"unchecked", "rawtypes"}) public ArrayList aNoType; + public Stack wild; public Vector arr; public Vector tarr; @@ -160,30 +169,39 @@ public class A { public ArrayList atv; } - public class B extends A { - } + public class B extends A {} + @Test public void testGetIterableParameter() throws Exception { - assertEquals("T", + assertEquals( + "T", ((TypeVariable) Types.getIterableParameter(A.class.getField("tv").getGenericType())) .getName()); - assertEquals("T", + assertEquals( + "T", ((TypeVariable) Types.getIterableParameter(A.class.getField("atv").getGenericType())) .getName()); assertEquals(String.class, Types.getIterableParameter(A.class.getField("i").getGenericType())); assertEquals(String.class, Types.getIterableParameter(A.class.getField("a").getGenericType())); - assertEquals("E", + assertEquals( + "E", ((TypeVariable) Types.getIterableParameter(A.class.getField("aNoType").getGenericType())) .getName()); - assertEquals(Integer.class, Types.getArrayComponentType( - Types.getIterableParameter(A.class.getField("arr").getGenericType()))); - assertEquals("T", + assertEquals( + Integer.class, + Types.getArrayComponentType( + Types.getIterableParameter(A.class.getField("arr").getGenericType()))); + assertEquals( + "T", ((GenericArrayType) Types.getIterableParameter(A.class.getField("tarr").getGenericType())) - .getGenericComponentType().toString()); - assertEquals(ArrayList.class, + .getGenericComponentType() + .toString()); + assertEquals( + ArrayList.class, ((ParameterizedType) Types.getIterableParameter(A.class.getField("list").getGenericType())) .getRawType()); - assertEquals(Number.class, + assertEquals( + Number.class, ((WildcardType) Types.getIterableParameter(A.class.getField("wild").getGenericType())) .getUpperBounds()[0]); } @@ -191,8 +209,10 @@ public void testGetIterableParameter() throws Exception { public class C { public Map i; public ArrayMap a; + @SuppressWarnings({"unchecked", "rawtypes"}) public ArrayMap aNoType; + public TreeMap wild; public Vector arr; public HashMap tarr; @@ -201,33 +221,44 @@ public class C { public ArrayMap atv; } - public class D extends C { - } + public class D extends C {} + @Test public void testGetMapParameter() throws Exception { - assertEquals("T", + assertEquals( + "T", ((TypeVariable) Types.getMapValueParameter(C.class.getField("tv").getGenericType())) .getName()); - assertEquals("T", + assertEquals( + "T", ((TypeVariable) Types.getMapValueParameter(C.class.getField("atv").getGenericType())) .getName()); assertEquals(String.class, Types.getMapValueParameter(C.class.getField("i").getGenericType())); assertEquals(String.class, Types.getMapValueParameter(C.class.getField("a").getGenericType())); - assertEquals("V", + assertEquals( + "V", ((TypeVariable) Types.getMapValueParameter(C.class.getField("aNoType").getGenericType())) .getName()); - assertEquals(Integer.class, Types.getArrayComponentType( - Types.getIterableParameter(A.class.getField("arr").getGenericType()))); - assertEquals("T", ((GenericArrayType) Types.getMapValueParameter( - C.class.getField("tarr").getGenericType())).getGenericComponentType().toString()); - assertEquals(ArrayList.class, + assertEquals( + Integer.class, + Types.getArrayComponentType( + Types.getIterableParameter(A.class.getField("arr").getGenericType()))); + assertEquals( + "T", + ((GenericArrayType) Types.getMapValueParameter(C.class.getField("tarr").getGenericType())) + .getGenericComponentType() + .toString()); + assertEquals( + ArrayList.class, ((ParameterizedType) Types.getMapValueParameter(C.class.getField("list").getGenericType())) .getRawType()); - assertEquals(Number.class, + assertEquals( + Number.class, ((WildcardType) Types.getMapValueParameter(C.class.getField("wild").getGenericType())) .getUpperBounds()[0]); } + @Test public void testIterableOf() { List list = ImmutableList.of("a"); assertEquals(list, Types.iterableOf(list)); @@ -235,12 +266,16 @@ public void testIterableOf() { assertTrue(Iterables.elementsEqual(ImmutableList.of(1), Types.iterableOf(new int[] {1}))); } + @Test public void testToArray() { assertTrue( - Arrays.equals(new String[] {"a", "b"}, + Arrays.equals( + new String[] {"a", "b"}, (String[]) Types.toArray(ImmutableList.of("a", "b"), String.class))); - assertTrue(Arrays.equals( - new Integer[] {1, 2}, (Integer[]) Types.toArray(ImmutableList.of(1, 2), Integer.class))); + assertTrue( + Arrays.equals( + new Integer[] {1, 2}, + (Integer[]) Types.toArray(ImmutableList.of(1, 2), Integer.class))); assertTrue( Arrays.equals(new int[] {1, 2}, (int[]) Types.toArray(ImmutableList.of(1, 2), int.class))); int[][] arr = (int[][]) Types.toArray(ImmutableList.of(new int[] {1, 2}), int[].class); diff --git a/google-http-client/src/test/java/com/google/api/client/util/escape/CharEscapersTest.java b/google-http-client/src/test/java/com/google/api/client/util/escape/CharEscapersTest.java new file mode 100644 index 000000000..340607f2a --- /dev/null +++ b/google-http-client/src/test/java/com/google/api/client/util/escape/CharEscapersTest.java @@ -0,0 +1,57 @@ +/* + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.util.escape; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class CharEscapersTest { + + @Test + public void testDecodeUriPath() { + subtestDecodeUriPath(null, null); + subtestDecodeUriPath("", ""); + subtestDecodeUriPath("abc", "abc"); + subtestDecodeUriPath("a+b%2Bc", "a+b+c"); + subtestDecodeUriPath("Go%3D%23%2F%25%26%20?%3Co%3Egle", "Go=#/%& ?gle"); + } + + private void subtestDecodeUriPath(String input, String expected) { + String actual = CharEscapers.decodeUriPath(input); + assertEquals(expected, actual); + } + + @Test + public void testDecodeUri_IllegalArgumentException() { + subtestDecodeUri_IllegalArgumentException("abc%-1abc"); + subtestDecodeUri_IllegalArgumentException("%JJ"); + subtestDecodeUri_IllegalArgumentException("abc%0"); + } + + private void subtestDecodeUri_IllegalArgumentException(String input) { + try { + CharEscapers.decodeUriPath(input); + fail(); + } catch (IllegalArgumentException expected) { + assertNotNull(expected.getMessage()); + } + } +} diff --git a/google-http-client/src/test/java/com/google/api/client/util/escape/PercentEncodedEscaperTest.java b/google-http-client/src/test/java/com/google/api/client/util/escape/PercentEncodedEscaperTest.java new file mode 100644 index 000000000..9eff5c0f7 --- /dev/null +++ b/google-http-client/src/test/java/com/google/api/client/util/escape/PercentEncodedEscaperTest.java @@ -0,0 +1,32 @@ +package com.google.api.client.util.escape; + +import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class PercentEncodedEscaperTest extends TestCase { + @Test + public void testEscape() { + PercentEncodedEscaper escaper = + new PercentEncodedEscaper( + new PercentEscaper(PercentEscaper.SAFE_PLUS_RESERVED_CHARS_URLENCODER)); + String input = "Hello%20World+/?#[]"; + + String actual = escaper.escape(input); + assertEquals(input, actual); // No change expected since it's already percent-encoded + } + + @Test + public void testEscapeEncode() { + PercentEncodedEscaper escaper = + new PercentEncodedEscaper( + new PercentEscaper(PercentEscaper.SAFE_PLUS_RESERVED_CHARS_URLENCODER)); + String input = "Hello World%"; + String expected = "Hello%20World%25"; + + String actual = escaper.escape(input); + assertEquals(expected, actual); + } +} diff --git a/google-http-client/src/test/java/com/google/api/client/util/escape/PercentEscaperTest.java b/google-http-client/src/test/java/com/google/api/client/util/escape/PercentEscaperTest.java new file mode 100644 index 000000000..e38462ca1 --- /dev/null +++ b/google-http-client/src/test/java/com/google/api/client/util/escape/PercentEscaperTest.java @@ -0,0 +1,39 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.api.client.util.escape; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class PercentEscaperTest { + + @Test + public void testEscapeSpace() { + PercentEscaper escaper = + new PercentEscaper(PercentEscaper.SAFE_PLUS_RESERVED_CHARS_URLENCODER, false); + String actual = escaper.escape("Hello there"); + Assert.assertEquals("Hello%20there", actual); + } + + @Test + public void testEscapeSpaceDefault() { + PercentEscaper escaper = new PercentEscaper(PercentEscaper.SAFE_PLUS_RESERVED_CHARS_URLENCODER); + String actual = escaper.escape("Hello there"); + Assert.assertEquals("Hello%20there", actual); + } +} diff --git a/google-http-client/src/test/resources/META-INF/native-image/com.google.http-client/google-http-client/native-image.properties b/google-http-client/src/test/resources/META-INF/native-image/com.google.http-client/google-http-client/native-image.properties new file mode 100644 index 000000000..be831a62d --- /dev/null +++ b/google-http-client/src/test/resources/META-INF/native-image/com.google.http-client/google-http-client/native-image.properties @@ -0,0 +1,4 @@ +Args=--initialize-at-build-time=com.google.api.client.util.StringUtils \ +--initialize-at-build-time=com.google.common.collect.RegularImmutableSet \ +--initialize-at-build-time=org.junit.runner.RunWith \ +--initialize-at-build-time=org.junit.runners.model.FrameworkField \ \ No newline at end of file diff --git a/google-http-client/src/test/resources/META-INF/native-image/com.google.http-client/google-http-client/reflect-config.json b/google-http-client/src/test/resources/META-INF/native-image/com.google.http-client/google-http-client/reflect-config.json new file mode 100644 index 000000000..41fade5e2 --- /dev/null +++ b/google-http-client/src/test/resources/META-INF/native-image/com.google.http-client/google-http-client/reflect-config.json @@ -0,0 +1,244 @@ +[ + { + "name": "com.google.api.client.util.FieldInfoTest$E", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.util.FieldInfoTest$Data", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.util.DataMapTest$A", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.util.ClassInfoTest$E", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.http.UrlEncodedParserTest$Generic", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.http.UrlEncodedParserTest$Simple", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.http.UrlEncodedParserTest$E", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.http.UrlEncodedParserTest$EnumValue", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.http.HttpRequestTest$MyHeaders", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.http.HttpHeadersTest$SlugHeaders", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.http.HttpHeadersTest$V", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.http.HttpHeadersTest$MyHeaders", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.http.javanet.NetHttpRequestTest$SleepingOutputWriter", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.util.ClassInfoTest$A", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.util.ClassInfoTest$B", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.util.ClassInfoTest$C", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.util.ClassInfoTest$A1", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true +}, + { + "name": "com.google.api.client.util.ClassInfoTest$E", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.http.GenericUrlTest$FieldTypesUrl", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.http.GenericUrlTest$TestUrl", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.http.UriTemplateTest$testEnum", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.util.GenericDataTest$MyData", + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredFields": true, + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.util.GenericDataTest$GenericData1", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.util.GenericDataTest$GenericData2", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.util.TypesTest$WildcardBounds", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.util.TypesTest$Foo", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "name": "com.google.api.client.util.TypesTest$IntegerList", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.util.TypesTest$Resolve", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.util.TypesTest$IntegerResolve", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.util.TypesTest$IntegerResolve", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.http.HttpRequestTest$E", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.http.HttpResponseTest$MyHeaders", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true + }, + { + "name": "com.google.api.client.http.javanet.NetHttpTransportTest$FakeServer", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allDeclaredFields": true + } +] \ No newline at end of file diff --git a/google-http-client/src/test/resources/META-INF/native-image/com.google.http-client/google-http-client/resource-config.json b/google-http-client/src/test/resources/META-INF/native-image/com.google.http-client/google-http-client/resource-config.json new file mode 100644 index 000000000..2d7f5b77c --- /dev/null +++ b/google-http-client/src/test/resources/META-INF/native-image/com.google.http-client/google-http-client/resource-config.json @@ -0,0 +1,10 @@ +{ + "resources":[ + {"pattern":"\\Qcom/google/api/client/util/privateKey.pem\\E"}, + {"pattern":"\\Qcom/google/api/client/util/cert.pem\\E"}, + {"pattern":"\\Qcom/google/api/client/util/mtlsCertAndKey.pem\\E"}, + {"pattern": "\\Qfile.txt\\E"}, + {"pattern": "\\Qcom/google/api/client/util/secret.pem\\E"}, + {"pattern": "\\Qcom/google/api/client/util/secret.p12\\E"}], + "bundles":[] +} \ No newline at end of file diff --git a/google-http-client/src/test/resources/com/google/api/client/util/cert.pem b/google-http-client/src/test/resources/com/google/api/client/util/cert.pem new file mode 100644 index 000000000..56e1319bf --- /dev/null +++ b/google-http-client/src/test/resources/com/google/api/client/util/cert.pem @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICGzCCAYSgAwIBAgIIWrt6xtmHPs4wDQYJKoZIhvcNAQEFBQAwMzExMC8GA1UE +AxMoMTAwOTEyMDcyNjg3OC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbTAeFw0x +MjEyMDExNjEwNDRaFw0yMjExMjkxNjEwNDRaMDMxMTAvBgNVBAMTKDEwMDkxMjA3 +MjY4NzguYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20wgZ8wDQYJKoZIhvcNAQEB +BQADgY0AMIGJAoGBAL1SdY8jTUVU7O4/XrZLYTw0ON1lV6MQRGajFDFCqD2Fd9tQ +GLW8Iftx9wfXe1zuaehJSgLcyCxazfyJoN3RiONBihBqWY6d3lQKqkgsRTNZkdFJ +Wdzl/6CxhK9sojh2p0r3tydtv9iwq5fuuWIvtODtT98EgphhncQAqkKoF3zVAgMB +AAGjODA2MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQM +MAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEBBQUAA4GBAD8XQEqzGePa9VrvtEGpf+R4 +fkxKbcYAzqYq202nKu0kfjhIYkYSBj6gi348YaxE64yu60TVl42l5HThmswUheW4 +uQIaq36JvwvsDP5Zoj5BgiNSnDAFQp+jJFBRUA5vooJKgKgMDf/r/DCOsbO6VJF1 +kWwa9n19NFiV0z3m6isj +-----END CERTIFICATE----- \ No newline at end of file diff --git a/google-http-client/src/test/resources/com/google/api/client/util/mtlsCertAndKey.pem b/google-http-client/src/test/resources/com/google/api/client/util/mtlsCertAndKey.pem new file mode 100644 index 000000000..d6c045125 --- /dev/null +++ b/google-http-client/src/test/resources/com/google/api/client/util/mtlsCertAndKey.pem @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIICGzCCAYSgAwIBAgIIWrt6xtmHPs4wDQYJKoZIhvcNAQEFBQAwMzExMC8GA1UE +AxMoMTAwOTEyMDcyNjg3OC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbTAeFw0x +MjEyMDExNjEwNDRaFw0yMjExMjkxNjEwNDRaMDMxMTAvBgNVBAMTKDEwMDkxMjA3 +MjY4NzguYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20wgZ8wDQYJKoZIhvcNAQEB +BQADgY0AMIGJAoGBAL1SdY8jTUVU7O4/XrZLYTw0ON1lV6MQRGajFDFCqD2Fd9tQ +GLW8Iftx9wfXe1zuaehJSgLcyCxazfyJoN3RiONBihBqWY6d3lQKqkgsRTNZkdFJ +Wdzl/6CxhK9sojh2p0r3tydtv9iwq5fuuWIvtODtT98EgphhncQAqkKoF3zVAgMB +AAGjODA2MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQM +MAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEBBQUAA4GBAD8XQEqzGePa9VrvtEGpf+R4 +fkxKbcYAzqYq202nKu0kfjhIYkYSBj6gi348YaxE64yu60TVl42l5HThmswUheW4 +uQIaq36JvwvsDP5Zoj5BgiNSnDAFQp+jJFBRUA5vooJKgKgMDf/r/DCOsbO6VJF1 +kWwa9n19NFiV0z3m6isj +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAL1SdY8jTUVU7O4/ +XrZLYTw0ON1lV6MQRGajFDFCqD2Fd9tQGLW8Iftx9wfXe1zuaehJSgLcyCxazfyJ +oN3RiONBihBqWY6d3lQKqkgsRTNZkdFJWdzl/6CxhK9sojh2p0r3tydtv9iwq5fu +uWIvtODtT98EgphhncQAqkKoF3zVAgMBAAECgYB51B9cXe4yiGTzJ4pOKpHGySAy +sC1F/IjXt2eeD3PuKv4m/hL4l7kScpLx0+NJuQ4j8U2UK/kQOdrGANapB1ZbMZAK +/q0xmIUzdNIDiGSoTXGN2mEfdsEpQ/Xiv0lyhYBBPC/K4sYIpHccnhSRQUZlWLLY +lE5cFNKC9b7226mNvQJBAPt0hfCNIN0kUYOA9jdLtx7CE4ySGMPf5KPBuzPd8ty1 +fxaFm9PB7B76VZQYmHcWy8rT5XjoLJHrmGW1ZvP+iDsCQQDAvnKoarPOGb5iJfkq +RrA4flf1TOlf+1+uqIOJ94959jkkJeb0gv/TshDnm6/bWn+1kJylQaKygCizwPwB +Z84vAkA0Duur4YvsPJijoQ9YY1SGCagCcjyuUKwFOxaGpmyhRPIKt56LOJqpzyno +fy8ReKa4VyYq4eZYT249oFCwMwIBAkAROPNF2UL3x5UbcAkznd1hLujtIlI4IV4L +XUNjsJtBap7we/KHJq11XRPlniO4lf2TW7iji5neGVWJulTKS1xBAkAerktk4Hsw +ErUaUG1s/d+Sgc8e/KMeBElV+NxGhcWEeZtfHMn/6VOlbzY82JyvC9OKC80A5CAE +VUV6b25kqrcu +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/google-http-client/src/test/resources/com/google/api/client/util/privateKey.pem b/google-http-client/src/test/resources/com/google/api/client/util/privateKey.pem new file mode 100644 index 000000000..dd13e1c09 --- /dev/null +++ b/google-http-client/src/test/resources/com/google/api/client/util/privateKey.pem @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAL1SdY8jTUVU7O4/ +XrZLYTw0ON1lV6MQRGajFDFCqD2Fd9tQGLW8Iftx9wfXe1zuaehJSgLcyCxazfyJ +oN3RiONBihBqWY6d3lQKqkgsRTNZkdFJWdzl/6CxhK9sojh2p0r3tydtv9iwq5fu +uWIvtODtT98EgphhncQAqkKoF3zVAgMBAAECgYB51B9cXe4yiGTzJ4pOKpHGySAy +sC1F/IjXt2eeD3PuKv4m/hL4l7kScpLx0+NJuQ4j8U2UK/kQOdrGANapB1ZbMZAK +/q0xmIUzdNIDiGSoTXGN2mEfdsEpQ/Xiv0lyhYBBPC/K4sYIpHccnhSRQUZlWLLY +lE5cFNKC9b7226mNvQJBAPt0hfCNIN0kUYOA9jdLtx7CE4ySGMPf5KPBuzPd8ty1 +fxaFm9PB7B76VZQYmHcWy8rT5XjoLJHrmGW1ZvP+iDsCQQDAvnKoarPOGb5iJfkq +RrA4flf1TOlf+1+uqIOJ94959jkkJeb0gv/TshDnm6/bWn+1kJylQaKygCizwPwB +Z84vAkA0Duur4YvsPJijoQ9YY1SGCagCcjyuUKwFOxaGpmyhRPIKt56LOJqpzyno +fy8ReKa4VyYq4eZYT249oFCwMwIBAkAROPNF2UL3x5UbcAkznd1hLujtIlI4IV4L +XUNjsJtBap7we/KHJq11XRPlniO4lf2TW7iji5neGVWJulTKS1xBAkAerktk4Hsw +ErUaUG1s/d+Sgc8e/KMeBElV+NxGhcWEeZtfHMn/6VOlbzY82JyvC9OKC80A5CAE +VUV6b25kqrcu +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/google-http-client/src/test/resources/file.txt b/google-http-client/src/test/resources/file.txt new file mode 100644 index 000000000..1065c29e7 --- /dev/null +++ b/google-http-client/src/test/resources/file.txt @@ -0,0 +1 @@ +some sample file diff --git a/pom.xml b/pom.xml index 0b710eb0d..8ec5aa94d 100644 --- a/pom.xml +++ b/pom.xml @@ -2,16 +2,12 @@ 4.0.0 - - org.sonatype.oss - oss-parent - 7 - com.google.http-client google-http-client-parent - 1.26.1-SNAPSHOT + 2.0.3-SNAPSHOT pom Parent for the Google HTTP Client Library for Java + Google HTTP Client Library for Java https://github.com/googleapis/google-http-java-client @@ -22,10 +18,6 @@ 2011 - - 3.5.4 - - scm:git:https://github.com/googleapis/google-http-java-client.git scm:git:git@github.com:googleapis/google-http-java-client.git @@ -34,9 +26,17 @@ Google - http://www.google.com/ + https://www.google.com/ + + + chingor + Jeff Ching + chingor@google.com + + + The Apache Software License, Version 2.0 @@ -61,19 +61,19 @@ google-http-client-assembly google-http-client-appengine google-http-client-android + google-http-client-apache-v2 + google-http-client-apache-v5 google-http-client-protobuf google-http-client-gson - google-http-client-jackson google-http-client-jackson2 - google-http-client-jdo + google-http-client-xml google-http-client-findbugs google-http-client-test samples/dailymotion-simple-cmdline-sample - samples/googleplus-simple-cmdline-sample - google-http-client-xml + google-http-client-bom @@ -88,12 +88,14 @@ Central Repository https://repo.maven.apache.org/maven2 - - protoc-plugin - https://dl.bintray.com/sergei-ivanov/maven/ - + + com.google.cloud + native-image-shared-config + 1.17.0 + + - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.6 - true - - ossrh - https://oss.sonatype.org/ - false - - maven-assembly-plugin - 3.1.0 + 3.7.1 maven-compiler-plugin - 2.3.2 + 3.13.0 - 1.6 - 1.6 + 1.7 + 1.7 - + + maven-deploy-plugin + 3.1.3 + org.apache.maven.plugins maven-source-plugin - 2.2.1 + 3.3.1 attach-sources @@ -314,7 +319,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.9.1 + 3.11.2 attach-javadocs @@ -327,7 +332,7 @@ org.apache.maven.plugins maven-jar-plugin - 3.0.2 + 3.4.2 @@ -338,20 +343,21 @@ maven-surefire-plugin - 2.19.1 + ${project.surefire.version} -Xmx1024m sponge_log + org.apache.maven.plugins maven-checkstyle-plugin - 2.6 + 3.6.0 org.codehaus.mojo findbugs-maven-plugin - 2.5.2 + 3.0.5 org.codehaus.mojo @@ -361,25 +367,77 @@ org.codehaus.mojo animal-sniffer-maven-plugin - 1.9 + 1.24 org.apache.maven.plugins maven-project-info-reports-plugin - 2.7 + 3.6.2 - org.datanucleus - maven-datanucleus-plugin - ${project.datanucleus-maven-plugin.version} + org.apache.maven.plugins + maven-site-plugin + 3.21.0 + + + org.apache.maven.plugins + maven-dependency-plugin + 3.8.1 + + + org.apache.maven.plugins + maven-resources-plugin + 3.3.1 + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.5.0 + + + org.apache.maven.plugins + maven-source-plugin + 3.3.1 + + + attach-sources + + jar-no-fork + + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.5.0 + + enforce-maven + + enforce + + + + + [3.5.2,4.0.0) + + + [1.7,) + + + + + + + maven-javadoc-plugin - -Xdoclint:none + none + 7 @@ -390,13 +448,10 @@ site - http://download.oracle.com/javase/6/docs/api/ - http://cloud.google.com/appengine/docs/java/javadoc - https://jar-download.com/artifacts/org.codehaus.jackson/jackson-core-asl/${project.jackson-core-asl.version}/documentation - http://fasterxml.github.com/jackson-core/javadoc/${project.jackson-core2.version}/ - https://www.javadoc.io/doc/com.google.code.gson/gson/${project.gson.version} + https://download.oracle.com/javase/7/docs/api/ + https://cloud.google.com/appengine/docs/java/javadoc + https://static.javadoc.io/doc/com.google.code.gson/gson/${project.gson.version} https://google.github.io/guava/releases/${project.guava.version}/api/docs/ - https://commons.apache.org/proper/commons-codec/archives/${project.commons-codec.version}/apidocs/ Google HTTP Client Library for Java ${project.version} com.google.api.client.findbugs:com.google.api.client.test.:com.google.api.services @@ -422,18 +477,10 @@ google-http-client-gson com.google.api.client.json.gson* - - google-http-client-jackson - com.google.api.client.json.jackson.* - google-http-client-jackson2 com.google.api.client.json.jackson2.* - - google-http-client-jdo - com.google.api.client.extensions.jdo* - google-http-client-xml com.google.api.client.xml*:com.google.api.client.http.xml* @@ -444,22 +491,6 @@ - - org.apache.maven.plugins - maven-checkstyle-plugin - - checkstyle.xml - true - ${basedir}/../checkstyle-suppressions.xml - - - - - check - - - - org.codehaus.mojo findbugs-maven-plugin @@ -469,51 +500,43 @@ com.google.http-client google-http-client-findbugs - ${project.version} + ${project.http-client.version} + - org.codehaus.mojo - clirr-maven-plugin - - 1.19.0 - ${basedir}/../clirr-ignored-differences.xml - true - + maven-project-info-reports-plugin + 3.6.2 - check + dependencies + package - org.codehaus.mojo - animal-sniffer-maven-plugin + com.coveo + fmt-maven-plugin + 2.9 - - org.codehaus.mojo.signature - java16 - 1.0 - + + true - - - - check - - - - - - org.sonatype.plugins - nexus-staging-maven-plugin + + + com.google.googlejavaformat + google-java-format + 1.7 + + + - 1.9.64 + 2.0.3-SNAPSHOT + 2.0.32 UTF-8 3.0.2 - 2.1 - 1.9.13 - 2.9.6 - 2.6.1 - 20.0 + 2.11.0 + 2.18.2 + 3.21.12 + + 33.4.8-android 1.1.4c - 1.1.1 - 1.10 - 4.5.5 - 2.3-eb - 3.2.2 - 3.2.1 - 3.2.1 - 4.0.3 + 4.5.14 + 4.4.16 + 5.3.1 + 5.2.5 + 0.31.1 + .. + 3.5.2 + false + + clirr-compatibility-check + + + + [1.8,) + + + + + org.codehaus.mojo + clirr-maven-plugin + + clirr-ignored-differences.xml + true + + + + + check + + + + + + + + + animal-sniffer + + + [1.7,) + + + + + org.codehaus.mojo + animal-sniffer-maven-plugin + + + java7 + + check + + + + org.codehaus.mojo.signature + java17 + 1.0 + + + + + android + + check + + + + net.sf.androidscents.signature + android-api-level-19 + 4.4.2_r4 + + + + + + + + + + java21 + + [21,) + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.11.2 + + 1.8 + false + + + + attach-javadocs + + jar + + + + + + + + + + native-tests + + + + org.apache.maven.plugins + maven-surefire-plugin + ${surefire.version} + + + + **/*Test + + + + + + + + Windows + + + windows + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + + + + + + release-sign-artifacts @@ -557,7 +732,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.6 + 3.2.7 sign-artifacts @@ -577,5 +752,93 @@ + + + + root-directory + + + checkstyle-suppressions.xml + + + + . + + + + + + checkstyle-tests + + [1.8,) + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + com.puppycrawl.tools + checkstyle + 9.3 + + + + checkstyle.xml + true + checkstyle-suppressions.xml + + + + + check + + + + + + + + + + docFX + + + + docFX + + + + + ${project.build.directory}/docfx-yml + ${project.artifactId} + com\.google\.api\.client\.findbugs:com\.google\.api\.client\.test:com\.google\.api\.services + 8 + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.11.2 + + com.microsoft.doclet.DocFxDoclet + false + + + -outputpath ${outputpath} + -projectname ${projectname} + -excludepackages ${excludePackages}: + + none + protected + true + ${source} + + + + + diff --git a/renovate.json b/renovate.json new file mode 100644 index 000000000..bc479bbe5 --- /dev/null +++ b/renovate.json @@ -0,0 +1,98 @@ +{ + "extends": [ + "config:recommended", + ":separateMajorReleases", + ":combinePatchMinorReleases", + ":ignoreUnstable", + ":prImmediately", + ":updateNotScheduled", + ":automergeDisabled", + ":ignoreModulesAndTests", + ":maintainLockFilesDisabled" + ], + "ignorePaths": [ + ".kokoro/requirements.txt" + ], + "customManagers": [ + { + "customType": "regex", + "fileMatch": [ + "^.kokoro/presubmit/graalvm-native.*.cfg$" + ], + "matchStrings": ["docker_image: \"us-docker.pkg.dev/java-graalvm-ci-prod/graalvm-integration-testing/graalvm.*:(?.*?)\""], + "depNameTemplate": "com.google.cloud:native-image-shared-config", + "datasourceTemplate": "maven" + } + ], + "packageRules": [ + { + "matchPackagePatterns": [ + "^com.google.guava:" + ], + "versioning": "docker", + "enabled": false + }, + { + "matchPackagePatterns": [ + "*" + ], + "semanticCommitType": "deps", + "semanticCommitScope": null + }, + { + "matchPackagePatterns": [ + "^org.apache.maven", + "^org.jacoco:", + "^org.codehaus.mojo:", + "^org.sonatype.plugins:", + "^com.coveo:", + "^com.google.cloud:native-image-shared-config" + ], + "semanticCommitType": "build", + "semanticCommitScope": "deps", + "enabled": true + }, + { + "matchPackagePatterns": [ + "^com.google.http-client:google-http-client", + "^com.google.cloud:libraries-bom", + "^com.google.cloud.samples:shared-configuration" + ], + "semanticCommitType": "chore", + "semanticCommitScope": "deps" + }, + { + "matchPackagePatterns": [ + "^junit:junit", + "^com.google.truth:truth", + "^org.mockito:mockito-core", + "^org.objenesis:objenesis", + "^com.google.cloud:google-cloud-conformance-tests" + ], + "semanticCommitType": "test", + "semanticCommitScope": "deps" + }, + { + "matchPackagePatterns": [ + "^com.google.cloud:google-cloud-" + ], + "ignoreUnstable": false + }, + { + "matchPackagePatterns": [ + "^com.fasterxml.jackson.core" + ], + "groupName": "jackson dependencies" + }, + { + "semanticCommitType": "deps", + "groupName": "gRPC dependencies", + "matchPackageNames": [ + "/^io.grpc/" + ], + "enabled": false + } + ], + "semanticCommits": "enabled", + "dependencyDashboard": true +} diff --git a/samples/checkstyle.xml b/samples/checkstyle.xml deleted file mode 100644 index 66cd0c954..000000000 --- a/samples/checkstyle.xml +++ /dev/null @@ -1,336 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/samples/dailymotion-simple-cmdline-sample/.classpath b/samples/dailymotion-simple-cmdline-sample/.classpath deleted file mode 100644 index f3de25032..000000000 --- a/samples/dailymotion-simple-cmdline-sample/.classpath +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - diff --git a/samples/dailymotion-simple-cmdline-sample/.project b/samples/dailymotion-simple-cmdline-sample/.project deleted file mode 100644 index 1d87e91e0..000000000 --- a/samples/dailymotion-simple-cmdline-sample/.project +++ /dev/null @@ -1,23 +0,0 @@ - - - dailymotion-simple-cmdline-sample - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.m2e.core.maven2Nature - - diff --git a/samples/dailymotion-simple-cmdline-sample/.settings/org.eclipse.jdt.core.prefs b/samples/dailymotion-simple-cmdline-sample/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 652b81273..000000000 --- a/samples/dailymotion-simple-cmdline-sample/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,380 +0,0 @@ -#Fri Nov 04 10:10:39 EDT 2011 -eclipse.preferences.version=1 -org.eclipse.jdt.core.codeComplete.argumentPrefixes= -org.eclipse.jdt.core.codeComplete.argumentSuffixes= -org.eclipse.jdt.core.codeComplete.fieldPrefixes= -org.eclipse.jdt.core.codeComplete.fieldSuffixes= -org.eclipse.jdt.core.codeComplete.localPrefixes= -org.eclipse.jdt.core.codeComplete.localSuffixes= -org.eclipse.jdt.core.codeComplete.staticFieldPrefixes= -org.eclipse.jdt.core.codeComplete.staticFieldSuffixes= -org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes= -org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes= -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.6 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=ignore -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.autoboxing=ignore -org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning -org.eclipse.jdt.core.compiler.problem.deadCode=warning -org.eclipse.jdt.core.compiler.problem.deprecation=warning -org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled -org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=enabled -org.eclipse.jdt.core.compiler.problem.discouragedReference=ignore -org.eclipse.jdt.core.compiler.problem.emptyStatement=warning -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning -org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled -org.eclipse.jdt.core.compiler.problem.fieldHiding=warning -org.eclipse.jdt.core.compiler.problem.finalParameterBound=ignore -org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning -org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning -org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore -org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning -org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore -org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning -org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning -org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore -org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning -org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning -org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore -org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning -org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning -org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore -org.eclipse.jdt.core.compiler.problem.nullReference=warning -org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning -org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore -org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning -org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore -org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning -org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning -org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore -org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled -org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning -org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled -org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=warning -org.eclipse.jdt.core.compiler.problem.typeParameterHiding=ignore -org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning -org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore -org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore -org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning -org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning -org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled -org.eclipse.jdt.core.compiler.problem.unusedImport=warning -org.eclipse.jdt.core.compiler.problem.unusedLabel=warning -org.eclipse.jdt.core.compiler.problem.unusedLocal=warning -org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore -org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled -org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled -org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled -org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning -org.eclipse.jdt.core.compiler.problem.unusedWarningToken=ignore -org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning -org.eclipse.jdt.core.compiler.source=1.6 -org.eclipse.jdt.core.formatter.align_type_members_on_columns=false -org.eclipse.jdt.core.formatter.alignment_for_annotations_on_local_variable=569 -org.eclipse.jdt.core.formatter.alignment_for_annotations_on_member=569 -org.eclipse.jdt.core.formatter.alignment_for_annotations_on_package_declaration=569 -org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter=24 -org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type_declaration=569 -org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 -org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=16 -org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 -org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 -org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 -org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 -org.eclipse.jdt.core.formatter.alignment_for_assignment=16 -org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 -org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 -org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=16 -org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16|5|48 -org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16|5|80 -org.eclipse.jdt.core.formatter.alignment_for_field_declaration=16 -org.eclipse.jdt.core.formatter.alignment_for_for_statement=16 -org.eclipse.jdt.core.formatter.alignment_for_generic_type_arguments=16 -org.eclipse.jdt.core.formatter.alignment_for_local_variable_declaration=16 -org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 -org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 -org.eclipse.jdt.core.formatter.alignment_for_new_anonymous_class=0 -org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16|5|80 -org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16|5|80 -org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 -org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 -org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16|4|49 -org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16|4|48 -org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16|4|48 -org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16|4|48 -org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 -org.eclipse.jdt.core.formatter.blank_lines_after_package=1 -org.eclipse.jdt.core.formatter.blank_lines_before_field=0 -org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 -org.eclipse.jdt.core.formatter.blank_lines_before_imports=0 -org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0 -org.eclipse.jdt.core.formatter.blank_lines_before_method=1 -org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 -org.eclipse.jdt.core.formatter.blank_lines_before_package=0 -org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 -org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2 -org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line -org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false -org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false -org.eclipse.jdt.core.formatter.comment.format_block_comments=true -org.eclipse.jdt.core.formatter.comment.format_header=true -org.eclipse.jdt.core.formatter.comment.format_html=true -org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true -org.eclipse.jdt.core.formatter.comment.format_line_comments=true -org.eclipse.jdt.core.formatter.comment.format_source_code=false -org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false -org.eclipse.jdt.core.formatter.comment.indent_root_tags=true -org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert -org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert -org.eclipse.jdt.core.formatter.comment.line_length=100 -org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true -org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true -org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false -org.eclipse.jdt.core.formatter.comment_new_line_at_start_of_html_paragraph=true -org.eclipse.jdt.core.formatter.compact_else_if=true -org.eclipse.jdt.core.formatter.continuation_indentation=2 -org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 -org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off -org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on -org.eclipse.jdt.core.formatter.force_if_else_statement_brace=true -org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false -org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true -org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true -org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true -org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true -org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true -org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true -org.eclipse.jdt.core.formatter.indent_empty_lines=false -org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true -org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true -org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true -org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true -org.eclipse.jdt.core.formatter.indentation.size=4 -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert -org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert -org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert -org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert -org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert -org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert -org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert -org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert -org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert -org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert -org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert -org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert -org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert -org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert -org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert -org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert -org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert -org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert -org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert -org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert -org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert -org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert -org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert -org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert -org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert -org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert -org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert -org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert -org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert -org.eclipse.jdt.core.formatter.join_lines_in_comments=true -org.eclipse.jdt.core.formatter.join_wrapped_lines=true -org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false -org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false -org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=true -org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false -org.eclipse.jdt.core.formatter.lineSplit=100 -org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false -org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false -org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 -org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3 -org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false -org.eclipse.jdt.core.formatter.sort_local_variable_annotations=false -org.eclipse.jdt.core.formatter.sort_member_annotations=false -org.eclipse.jdt.core.formatter.sort_package_annotations=false -org.eclipse.jdt.core.formatter.sort_parameter_annotations=false -org.eclipse.jdt.core.formatter.sort_type_annotations=false -org.eclipse.jdt.core.formatter.tabulation.char=space -org.eclipse.jdt.core.formatter.tabulation.size=2 -org.eclipse.jdt.core.formatter.use_on_off_tags=false -org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false -org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true -org.eclipse.jdt.core.formatter.wrap_comment_inline_tags=false -org.eclipse.jdt.core.formatter.wrap_non_simple_local_variable_annotation=true -org.eclipse.jdt.core.formatter.wrap_non_simple_member_annotation=true -org.eclipse.jdt.core.formatter.wrap_non_simple_package_annotation=true -org.eclipse.jdt.core.formatter.wrap_non_simple_parameter_annotation=false -org.eclipse.jdt.core.formatter.wrap_non_simple_type_annotation=true -org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true -org.eclipse.jdt.core.formatter.wrap_prefer_two_fragments=false diff --git a/samples/dailymotion-simple-cmdline-sample/.settings/org.eclipse.jdt.ui.prefs b/samples/dailymotion-simple-cmdline-sample/.settings/org.eclipse.jdt.ui.prefs deleted file mode 100644 index c8c84d429..000000000 --- a/samples/dailymotion-simple-cmdline-sample/.settings/org.eclipse.jdt.ui.prefs +++ /dev/null @@ -1,117 +0,0 @@ -cleanup.add_default_serial_version_id=true -cleanup.add_generated_serial_version_id=false -cleanup.add_missing_annotations=true -cleanup.add_missing_deprecated_annotations=true -cleanup.add_missing_methods=false -cleanup.add_missing_nls_tags=false -cleanup.add_missing_override_annotations=true -cleanup.add_serial_version_id=true -cleanup.always_use_blocks=true -cleanup.always_use_parentheses_in_expressions=false -cleanup.always_use_this_for_non_static_field_access=false -cleanup.always_use_this_for_non_static_method_access=false -cleanup.convert_to_enhanced_for_loop=false -cleanup.correct_indentation=true -cleanup.format_source_code=true -cleanup.format_source_code_changes_only=false -cleanup.make_local_variable_final=true -cleanup.make_parameters_final=false -cleanup.make_private_fields_final=true -cleanup.make_type_abstract_if_missing_method=false -cleanup.make_variable_declarations_final=false -cleanup.never_use_blocks=false -cleanup.never_use_parentheses_in_expressions=true -cleanup.organize_imports=true -cleanup.qualify_static_field_accesses_with_declaring_class=false -cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true -cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true -cleanup.qualify_static_member_accesses_with_declaring_class=true -cleanup.qualify_static_method_accesses_with_declaring_class=false -cleanup.remove_private_constructors=true -cleanup.remove_trailing_whitespaces=true -cleanup.remove_trailing_whitespaces_all=true -cleanup.remove_trailing_whitespaces_ignore_empty=false -cleanup.remove_unnecessary_casts=true -cleanup.remove_unnecessary_nls_tags=true -cleanup.remove_unused_imports=true -cleanup.remove_unused_local_variables=false -cleanup.remove_unused_private_fields=true -cleanup.remove_unused_private_members=false -cleanup.remove_unused_private_methods=true -cleanup.remove_unused_private_types=true -cleanup.sort_members=false -cleanup.sort_members_all=false -cleanup.use_blocks=true -cleanup.use_blocks_only_for_return_and_throw=false -cleanup.use_parentheses_in_expressions=true -cleanup.use_this_for_non_static_field_access=true -cleanup.use_this_for_non_static_field_access_only_if_necessary=true -cleanup.use_this_for_non_static_method_access=true -cleanup.use_this_for_non_static_method_access_only_if_necessary=true -cleanup_profile=_google-api-java-client -cleanup_settings_version=2 -eclipse.preferences.version=1 -editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true -formatter_profile=_google-api-java-client 100 -formatter_settings_version=12 -org.eclipse.jdt.ui.exception.name=exception -org.eclipse.jdt.ui.gettersetter.use.is=false -org.eclipse.jdt.ui.ignorelowercasenames=true -org.eclipse.jdt.ui.importorder=com;org;;java;javax; -org.eclipse.jdt.ui.javadoc=true -org.eclipse.jdt.ui.keywordthis=false -org.eclipse.jdt.ui.ondemandthreshold=999 -org.eclipse.jdt.ui.overrideannotation=true -org.eclipse.jdt.ui.staticondemandthreshold=999 -org.eclipse.jdt.ui.text.custom_code_templates= -sp_cleanup.add_default_serial_version_id=true -sp_cleanup.add_generated_serial_version_id=false -sp_cleanup.add_missing_annotations=true -sp_cleanup.add_missing_deprecated_annotations=true -sp_cleanup.add_missing_methods=false -sp_cleanup.add_missing_nls_tags=false -sp_cleanup.add_missing_override_annotations=true -sp_cleanup.add_serial_version_id=false -sp_cleanup.always_use_blocks=true -sp_cleanup.always_use_parentheses_in_expressions=false -sp_cleanup.always_use_this_for_non_static_field_access=false -sp_cleanup.always_use_this_for_non_static_method_access=false -sp_cleanup.convert_to_enhanced_for_loop=false -sp_cleanup.correct_indentation=false -sp_cleanup.format_source_code=true -sp_cleanup.format_source_code_changes_only=false -sp_cleanup.make_local_variable_final=false -sp_cleanup.make_parameters_final=false -sp_cleanup.make_private_fields_final=true -sp_cleanup.make_type_abstract_if_missing_method=false -sp_cleanup.make_variable_declarations_final=true -sp_cleanup.never_use_blocks=false -sp_cleanup.never_use_parentheses_in_expressions=true -sp_cleanup.on_save_use_additional_actions=false -sp_cleanup.organize_imports=true -sp_cleanup.qualify_static_field_accesses_with_declaring_class=false -sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true -sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true -sp_cleanup.qualify_static_member_accesses_with_declaring_class=false -sp_cleanup.qualify_static_method_accesses_with_declaring_class=false -sp_cleanup.remove_private_constructors=true -sp_cleanup.remove_trailing_whitespaces=false -sp_cleanup.remove_trailing_whitespaces_all=true -sp_cleanup.remove_trailing_whitespaces_ignore_empty=false -sp_cleanup.remove_unnecessary_casts=true -sp_cleanup.remove_unnecessary_nls_tags=false -sp_cleanup.remove_unused_imports=false -sp_cleanup.remove_unused_local_variables=false -sp_cleanup.remove_unused_private_fields=true -sp_cleanup.remove_unused_private_members=false -sp_cleanup.remove_unused_private_methods=true -sp_cleanup.remove_unused_private_types=true -sp_cleanup.sort_members=false -sp_cleanup.sort_members_all=false -sp_cleanup.use_blocks=false -sp_cleanup.use_blocks_only_for_return_and_throw=false -sp_cleanup.use_parentheses_in_expressions=false -sp_cleanup.use_this_for_non_static_field_access=false -sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true -sp_cleanup.use_this_for_non_static_method_access=false -sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true diff --git a/samples/dailymotion-simple-cmdline-sample/DailyMotionSample.launch b/samples/dailymotion-simple-cmdline-sample/DailyMotionSample.launch deleted file mode 100644 index e273fce30..000000000 --- a/samples/dailymotion-simple-cmdline-sample/DailyMotionSample.launch +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/samples/dailymotion-simple-cmdline-sample/instructions.html b/samples/dailymotion-simple-cmdline-sample/instructions.html index b3ef6e311..59f93bcc1 100644 --- a/samples/dailymotion-simple-cmdline-sample/instructions.html +++ b/samples/dailymotion-simple-cmdline-sample/instructions.html @@ -14,23 +14,19 @@

    Browse Online

    Checkout Instructions

    -

    Prerequisites: install Java 6, Mercurial and Maven. You may need to set +

    Prerequisites: install Java 7 or later, git, + and Maven. You may need to set your JAVA_HOME.

    -
    cd [someDirectory]
    -git clone https://github.com/googleapis/google-http-java-client
    -cd google-http-java-client-samples/dailymotion-simple-cmdline-sample
    -mvn compile
    -mvn -q exec:java
    +
    $ git clone https://github.com/googleapis/google-http-java-client
    +$ cd google-http-java-client-samples/dailymotion-simple-cmdline-sample
    +$ mvn compile
    +$ mvn -q exec:java
    -

    Setup Project in Eclipse 3.5/3.6

    +

    Setup Project in Eclipse

    -

    Prerequisites: install Eclipse, -the Mercurial plugin, and the Maven -plugin.

    +

    Prerequisites: install Eclipse IDE +and the M2Eclipse plugin.

    • Setup Eclipse Preferences @@ -38,8 +34,8 @@

      Setup Project in Eclipse 3.5/3.6

    • Window > Preferences... (or on Mac, Eclipse > Preferences...)
    • Select Maven
        -
      • check on "Download Artifact Sources"
      • -
      • check on "Download Artifact JavaDoc"
      • +
      • Check "Download Artifact Sources"
      • +
      • Check "Download Artifact JavaDoc"
    @@ -47,10 +43,10 @@

    Setup Project in Eclipse 3.5/3.6

  • Import dailymotion-simple-cmdline-sample project
    • File > Import...
    • -
    • Select "General > Existing Project into Workspace" and click +
    • Select "Maven > Existing Maven Project" and click "Next"
    • -
    • Click "Browse" next to "Select root directory", find [someDirectory]/google-http-java-client-samples/dailymotion-simple-cmdline-sample - and click "Next"
    • +
    • Click "Browse" next to "Select root directory", find + google-http-java-client/samples/dailymotion-simple-cmdline-sample
    • Click "Finish"
  • @@ -58,8 +54,7 @@

    Setup Project in Eclipse 3.5/3.6

    • Right-click on project dailymotion-simple-cmdline-sample
    • Run As > Java Application
    • -
    • If asked, type "DailyMotionSample" and click OK
    • -
    • To enabled logging: +
    • To enable logging:
      • Run > Run Configurations...
      • Click on "Java Application > DailyMotionSample"
      • diff --git a/samples/dailymotion-simple-cmdline-sample/pom.xml b/samples/dailymotion-simple-cmdline-sample/pom.xml index ca0ffb08e..2341d4c98 100644 --- a/samples/dailymotion-simple-cmdline-sample/pom.xml +++ b/samples/dailymotion-simple-cmdline-sample/pom.xml @@ -4,18 +4,29 @@ com.google.http-client google-http-client-parent - 1.26.1-SNAPSHOT + 2.0.3-SNAPSHOT ../../pom.xml dailymotion-simple-cmdline-sample Simple example for the Dailymotion API. + + + + org.sonatype.plugins + nexus-staging-maven-plugin + + true + + + + org.codehaus.mojo exec-maven-plugin - 1.1 + 3.5.0 @@ -33,26 +44,9 @@ - - maven-checkstyle-plugin - 2.6 - - ../checkstyle.xml - true - false - - - - - check - - - - org.codehaus.mojo findbugs-maven-plugin - 2.3.2 ../../findbugs-exclude.xml false @@ -68,19 +62,10 @@ org.apache.maven.plugins maven-deploy-plugin - 2.5 true - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.6 - - true - - ${project.artifactId}-${project.version} @@ -97,4 +82,41 @@ UTF-8 + + + + + checkstyle-tests + + [1.8,) + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + com.puppycrawl.tools + checkstyle + 9.3 + + + + ../../checkstyle.xml + true + ../../checkstyle-suppressions.xml + + + + + check + + + + + + + + diff --git a/samples/dailymotion-simple-cmdline-sample/src/main/java/com/google/api/services/samples/dailymotion/cmdline/simple/DailyMotionSample.java b/samples/dailymotion-simple-cmdline-sample/src/main/java/com/google/api/services/samples/dailymotion/cmdline/simple/DailyMotionSample.java index 8fb16c04a..2e8e08128 100644 --- a/samples/dailymotion-simple-cmdline-sample/src/main/java/com/google/api/services/samples/dailymotion/cmdline/simple/DailyMotionSample.java +++ b/samples/dailymotion-simple-cmdline-sample/src/main/java/com/google/api/services/samples/dailymotion/cmdline/simple/DailyMotionSample.java @@ -1,11 +1,11 @@ /* * Copyright (c) 2011 Google Inc. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under @@ -25,13 +25,12 @@ import com.google.api.client.json.JsonObjectParser; import com.google.api.client.json.jackson2.JacksonFactory; import com.google.api.client.util.Key; - import java.util.List; /** * Simple example for the Dailymotion * Graph API. - * + * * @author Yaniv Inbar */ public class DailyMotionSample { @@ -41,8 +40,7 @@ public class DailyMotionSample { /** Represents a video feed. */ public static class VideoFeed { - @Key - public List