diff --git a/.asf.yaml b/.asf.yaml
index 16e358f62f..23fccc71e8 100644
--- a/.asf.yaml
+++ b/.asf.yaml
@@ -24,7 +24,9 @@ github:
squash: true
merge: false
rebase: false
- autolink_jira: RATIS
+ autolink_jira:
+ - HDDS
+ - RATIS
notifications:
commits: commits@ratis.apache.org
diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml
new file mode 100644
index 0000000000..1655d60148
--- /dev/null
+++ b/.github/workflows/check.yaml
@@ -0,0 +1,203 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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 reusable workflow executes a single check from `dev-support/checks/`.
+# Before and after the check, it performs various steps based on workflow inputs.
+
+name: ci-check
+
+on:
+ workflow_call:
+ inputs:
+ # REQUIRED
+ script:
+ type: string
+ description: "Test script to run from dev-support/checks, without .sh extension"
+ required: true
+
+ # OPTIONAL (ordered alphabetically)
+ java-version:
+ type: string
+ description: "Java version to set up (default: 8)"
+ default: '8'
+ required: false
+
+ needs-binary-tarball:
+ type: boolean
+ description: "Whether to download Ratis binary tarball created by build (default: no)"
+ default: false
+ required: false
+
+ needs-maven-repo:
+ type: boolean
+ description: "Whether to download Ratis jars created by build (default: no)"
+ default: false
+ required: false
+
+ needs-source-tarball:
+ type: boolean
+ description: "Whether to download Ratis source tarball created by build (default: no)"
+ default: false
+ required: false
+
+ runner:
+ type: string
+ description: "GitHub Actions runner to use"
+ default: 'ubuntu-24.04'
+ required: false
+
+ script-args:
+ type: string
+ description: "Arguments for the test script"
+ default: ''
+ required: false
+
+ split:
+ type: string
+ description: "Name of split for matrix jobs, only used in display name"
+ default: ''
+ required: false
+
+ timeout-minutes:
+ type: number
+ description: "Job timeout in minutes (default: 30)"
+ default: 30
+ required: false
+
+env:
+ MAVEN_ARGS: --batch-mode --show-version
+ MAVEN_OPTS: -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.http.retryHandler.class=standard -Dmaven.wagon.http.retryHandler.count=3
+ WITH_COVERAGE: ${{ github.event_name == 'push' }}
+
+jobs:
+ check:
+ name: ${{ (inputs.split && format('{0} ({1})', inputs.script, inputs.split)) || inputs.script }}
+ runs-on: ${{ inputs.runner }}
+ timeout-minutes: ${{ inputs.timeout-minutes }}
+ steps:
+ - name: Checkout project
+ if: ${{ !inputs.needs-source-tarball }}
+ uses: actions/checkout@v4
+
+ - name: Download source tarball
+ if: ${{ inputs.needs-source-tarball }}
+ uses: actions/download-artifact@v4
+ with:
+ name: ratis-src
+
+ - name: Extract source tarball
+ if: ${{ inputs.needs-source-tarball }}
+ run: |
+ tar --strip-components 1 -xzvf ratis*-src.tar.gz
+
+ - name: Create cache for Maven dependencies
+ if: ${{ inputs.script == 'build' }}
+ uses: actions/cache@v4
+ with:
+ path: |
+ ~/.m2/repository/*/*/*
+ !~/.m2/repository/org/apache/ratis
+ key: maven-repo-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ maven-repo-
+
+ - name: Restore cache for Maven dependencies
+ if: ${{ inputs.script != 'build' }}
+ uses: actions/cache/restore@v4
+ with:
+ path: |
+ ~/.m2/repository/*/*/*
+ !~/.m2/repository/org/apache/ratis
+ key: maven-repo-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ maven-repo-
+
+ - name: Download Maven repo
+ id: download-maven-repo
+ if: ${{ inputs.needs-maven-repo }}
+ uses: actions/download-artifact@v4
+ with:
+ name: maven-repo
+ path: |
+ ~/.m2/repository/org/apache/ratis
+
+ - name: Download binary tarball
+ if: ${{ inputs.needs-binary-tarball }}
+ uses: actions/download-artifact@v4
+ with:
+ name: ratis-bin
+
+ - name: Extract binary tarball
+ if: ${{ inputs.needs-binary-tarball }}
+ run: |
+ mkdir -p ratis-assembly/target
+ tar xzvf ratis-*-bin.tar.gz -C ratis-assembly/target
+
+ - name: Setup java ${{ inputs.java-version }}
+ if: ${{ inputs.java-version }}
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin'
+ java-version: ${{ inputs.java-version }}
+
+ - name: Execute tests
+ run: |
+ dev-support/checks/${{ inputs.script }}.sh ${{ inputs.script-args }}
+ env:
+ DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
+
+ - name: Summary of failures
+ if: ${{ failure() }}
+ run: |
+ if [[ -s "target/${{ inputs.script }}/summary.txt" ]]; then
+ cat target/${{ inputs.script }}/summary.txt
+ fi
+
+ - name: Archive build results
+ if: ${{ !cancelled() }}
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ (inputs.split && format('{0}-{1}', inputs.script, inputs.split)) || inputs.script }}
+ path: target/${{ inputs.script }}
+ continue-on-error: true
+
+ # The following steps are hard-coded to be run only for 'build' check,
+ # to avoid the need for 3 more inputs.
+ - name: Store binaries for tests
+ if: ${{ inputs.script == 'build' && !cancelled() }}
+ uses: actions/upload-artifact@v4
+ with:
+ name: ratis-bin
+ path: |
+ ratis-assembly/target/ratis-assembly-*-bin.tar.gz
+ retention-days: 1
+
+ - name: Store source tarball for compilation
+ if: ${{ inputs.script == 'build' && !cancelled() }}
+ uses: actions/upload-artifact@v4
+ with:
+ name: ratis-src
+ path: |
+ ratis-assembly/target/ratis-assembly-*-src.tar.gz
+ retention-days: 1
+
+ - name: Store Maven repo for tests
+ if: ${{ inputs.script == 'build' && !cancelled() }}
+ uses: actions/upload-artifact@v4
+ with:
+ name: maven-repo
+ path: |
+ ~/.m2/repository/org/apache/ratis
+ retention-days: 1
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
new file mode 100644
index 0000000000..20704f5ea9
--- /dev/null
+++ b/.github/workflows/ci.yaml
@@ -0,0 +1,150 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+name: CI
+
+on:
+ workflow_call:
+ inputs:
+ ref:
+ type: string
+ description: Ratis git ref (branch, tag or commit hash)
+ default: ''
+ required: false
+
+jobs:
+ build:
+ uses: ./.github/workflows/check.yaml
+ with:
+ script: build
+ script-args: -Prelease
+ timeout-minutes: 30
+ secrets: inherit
+
+ compile:
+ needs:
+ - build
+ strategy:
+ matrix:
+ java: [ 11, 17, 21 ]
+ fail-fast: false
+ uses: ./.github/workflows/check.yaml
+ with:
+ java-version: ${{ matrix.java }}
+ needs-source-tarball: true
+ script: compile
+ script-args: -Djavac.version=${{ matrix.java }}
+ split: ${{ matrix.java }}
+ timeout-minutes: 30
+ secrets: inherit
+
+ release:
+ uses: ./.github/workflows/check.yaml
+ with:
+ script: release
+ timeout-minutes: 30
+ secrets: inherit
+
+ repro:
+ needs:
+ - build
+ uses: ./.github/workflows/check.yaml
+ with:
+ needs-maven-repo: true
+ script: repro
+ script-args: -Prelease
+ timeout-minutes: 30
+ secrets: inherit
+
+ basic:
+ strategy:
+ matrix:
+ check:
+ - author
+ - checkstyle
+ - findbugs
+ - rat
+ fail-fast: false
+ uses: ./.github/workflows/check.yaml
+ with:
+ script: ${{ matrix.check }}
+ timeout-minutes: 30
+ secrets: inherit
+
+ unit:
+ strategy:
+ matrix:
+ profile:
+ - grpc
+ - server
+ - misc
+ - flaky
+ fail-fast: false
+ uses: ./.github/workflows/check.yaml
+ with:
+ script: unit
+ script-args: -P${{ matrix.profile }}-tests
+ split: ${{ matrix.profile }}
+ timeout-minutes: 60
+ secrets: inherit
+
+ coverage:
+ needs:
+ - build
+ - unit
+ runs-on: ubuntu-24.04
+ timeout-minutes: 30
+ if: github.event_name != 'pull_request'
+ steps:
+ - name: Checkout project
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ - name: Cache for maven dependencies
+ uses: actions/cache/restore@v4
+ with:
+ path: |
+ ~/.m2/repository
+ !~/.m2/repository/org/apache/ratis
+ key: maven-repo-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ maven-repo-
+ - name: Setup java 17
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin'
+ java-version: 17
+ - name: Download artifacts
+ uses: actions/download-artifact@v4
+ with:
+ path: target/artifacts
+ - name: Untar binaries
+ run: |
+ mkdir -p ratis-assembly/target
+ tar xzvf target/artifacts/ratis-bin/ratis-assembly-*.tar.gz -C ratis-assembly/target
+ - name: Calculate combined coverage
+ run: ./dev-support/checks/coverage.sh
+ - name: Upload coverage to Sonar
+ if: github.repository == 'apache/ratis'
+ run: ./dev-support/checks/sonar.sh
+ env:
+ SONAR_TOKEN: ${{ secrets.SONARCLOUD_TOKEN }}
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ - name: Archive build results
+ uses: actions/upload-artifact@v4
+ if: always()
+ with:
+ name: ${{ github.job }}
+ path: target/${{ github.job }}
diff --git a/.github/workflows/post-commit.yaml b/.github/workflows/post-commit.yaml
new file mode 100644
index 0000000000..583eb7bcf5
--- /dev/null
+++ b/.github/workflows/post-commit.yaml
@@ -0,0 +1,33 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+name: build-branch
+
+on:
+ push:
+ branches-ignore:
+ - 'dependabot/**'
+ tags:
+ - '**'
+ pull_request:
+
+concurrency:
+ group: ci-${{ github.event.pull_request.number || github.sha }}
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
+
+jobs:
+ CI:
+ uses: ./.github/workflows/ci.yaml
+ secrets: inherit
diff --git a/.github/workflows/post-commit.yml b/.github/workflows/post-commit.yml
deleted file mode 100644
index 9978caacf8..0000000000
--- a/.github/workflows/post-commit.yml
+++ /dev/null
@@ -1,265 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements. See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-name: build-branch
-on:
- - push
- - pull_request
-env:
- WITH_COVERAGE: true
-jobs:
- build:
- runs-on: ubuntu-20.04
- steps:
- - name: Checkout project
- uses: actions/checkout@v3
- - name: Cache for maven dependencies
- uses: actions/cache@v3
- with:
- path: |
- ~/.m2/repository
- !~/.m2/repository/org/apache/ratis
- key: maven-repo-${{ hashFiles('**/pom.xml') }}
- restore-keys: |
- maven-repo-
- - name: Setup java
- uses: actions/setup-java@v3
- with:
- distribution: 'temurin'
- java-version: 8
- - name: Run a full build
- run: ./dev-support/checks/build.sh -Prelease assembly:single
- - name: Store binaries for tests
- uses: actions/upload-artifact@v3
- with:
- name: ratis-bin
- path: |
- ratis-assembly/target/apache-ratis-*.tar.gz
- !ratis-assembly/target/apache-ratis-*-src.tar.gz
- retention-days: 1
- - name: Store source tarball for compilation
- uses: actions/upload-artifact@v3
- with:
- name: ratis-src
- path: ratis-assembly/target/apache-ratis-*-src.tar.gz
- retention-days: 1
- compile:
- needs:
- - build
- runs-on: ubuntu-20.04
- strategy:
- matrix:
- java: [ 11 ]
- fail-fast: false
- steps:
- - name: Download source tarball
- uses: actions/download-artifact@v3
- with:
- name: ratis-src
- - name: Untar sources
- run: |
- tar --strip-components 1 -xzvf apache-ratis-*-src.tar.gz
- - name: Cache for maven dependencies
- uses: actions/cache/restore@v3
- with:
- path: |
- ~/.m2/repository
- !~/.m2/repository/org/apache/ratis
- key: maven-repo-${{ hashFiles('**/pom.xml') }}
- restore-keys: |
- maven-repo-
- - name: Setup java
- uses: actions/setup-java@v3
- with:
- distribution: 'temurin'
- java-version: ${{ matrix.java }}
- - name: Run a full build
- run: ./dev-support/checks/build.sh
- rat:
- name: rat
- runs-on: ubuntu-20.04
- steps:
- - name: Checkout project
- uses: actions/checkout@v3
- - name: Cache for maven dependencies
- uses: actions/cache/restore@v3
- with:
- path: |
- ~/.m2/repository
- !~/.m2/repository/org/apache/ratis
- key: maven-repo-${{ hashFiles('**/pom.xml') }}
- restore-keys: |
- maven-repo-
- - name: Run tests
- run: ./dev-support/checks/rat.sh
- - name: Upload results
- uses: actions/upload-artifact@v3
- if: always()
- with:
- name: rat
- path: target/rat
- author:
- name: author
- runs-on: ubuntu-20.04
- steps:
- - name: Checkout project
- uses: actions/checkout@v3
- - name: Run tests
- run: ./dev-support/checks/author.sh
- - name: Upload results
- uses: actions/upload-artifact@v3
- if: always()
- with:
- name: author
- path: target/author
- unit:
- name: unit
- runs-on: ubuntu-20.04
- strategy:
- matrix:
- profile:
- - grpc
- - server
- - misc
- fail-fast: false
- steps:
- # TEMPORARY WHILE GITHUB FIXES https://github.com/actions/virtual-environments/issues/3185
- - name: Add the current IP address, long hostname and short hostname record to /etc/hosts file
- run: |
- echo -e "$(ip addr show eth0 | grep "inet\b" | awk '{print $2}' | cut -d/ -f1)\t$(hostname -f) $(hostname -s)" | sudo tee -a /etc/hosts
- # REMOVE CODE ABOVE WHEN ISSUE IS ADDRESSED!
- - name: Checkout project
- uses: actions/checkout@v3
- - name: Cache for maven dependencies
- uses: actions/cache/restore@v3
- with:
- path: |
- ~/.m2/repository
- !~/.m2/repository/org/apache/ratis
- key: maven-repo-${{ hashFiles('**/pom.xml') }}
- restore-keys: |
- maven-repo-
- - name: Setup java
- uses: actions/setup-java@v3
- with:
- distribution: 'temurin'
- java-version: 8
- - name: Run tests
- run: ./dev-support/checks/unit.sh -P${{ matrix.profile }}-tests
- - name: Summary of failures
- run: cat target/${{ github.job }}/summary.txt
- if: ${{ !cancelled() }}
- - name: Upload results
- uses: actions/upload-artifact@v3
- if: ${{ !cancelled() }}
- with:
- name: unit-${{ matrix.profile }}
- path: target/unit
- checkstyle:
- name: checkstyle
- runs-on: ubuntu-20.04
- steps:
- - name: Checkout project
- uses: actions/checkout@v3
- - name: Cache for maven dependencies
- uses: actions/cache/restore@v3
- with:
- path: |
- ~/.m2/repository
- !~/.m2/repository/org/apache/ratis
- key: maven-repo-${{ hashFiles('**/pom.xml') }}
- restore-keys: |
- maven-repo-
- - name: Run tests
- run: ./dev-support/checks/checkstyle.sh
- - name: Upload results
- uses: actions/upload-artifact@v3
- if: always()
- with:
- name: checkstyle
- path: target/checkstyle
- findbugs:
- name: findbugs
- runs-on: ubuntu-20.04
- steps:
- - name: Setup java
- uses: actions/setup-java@v3
- with:
- distribution: 'temurin'
- java-version: 8
- - name: Checkout project
- uses: actions/checkout@v3
- - name: Cache for maven dependencies
- uses: actions/cache/restore@v3
- with:
- path: |
- ~/.m2/repository
- !~/.m2/repository/org/apache/ratis
- key: maven-repo-${{ hashFiles('**/pom.xml') }}
- restore-keys: |
- maven-repo-
- - name: Run tests
- run: ./dev-support/checks/findbugs.sh
- - name: Upload results
- uses: actions/upload-artifact@v3
- if: always()
- with:
- name: findbugs
- path: target/findbugs
- coverage:
- needs:
- - build
- - unit
- runs-on: ubuntu-20.04
- if: (github.repository == 'apache/ratis' || github.repository == 'apache/incubator-ratis') && github.event_name != 'pull_request'
- steps:
- - name: Checkout project
- uses: actions/checkout@v3
- with:
- fetch-depth: 0
- - name: Cache for maven dependencies
- uses: actions/cache/restore@v3
- with:
- path: |
- ~/.m2/repository
- !~/.m2/repository/org/apache/ratis
- key: maven-repo-${{ hashFiles('**/pom.xml') }}
- restore-keys: |
- maven-repo-
- - name: Setup java 17
- uses: actions/setup-java@v3
- with:
- distribution: 'temurin'
- java-version: 17
- - name: Download artifacts
- uses: actions/download-artifact@v3
- with:
- path: target/artifacts
- - name: Untar binaries
- run: |
- mkdir -p ratis-assembly/target
- tar xzvf target/artifacts/ratis-bin/apache-ratis*.tar.gz -C ratis-assembly/target
- - name: Calculate combined coverage
- run: ./dev-support/checks/coverage.sh
- - name: Upload coverage to Sonar
- run: ./dev-support/checks/sonar.sh
- env:
- SONAR_TOKEN: ${{ secrets.SONARCLOUD_TOKEN }}
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- - name: Archive build results
- uses: actions/upload-artifact@v3
- if: always()
- with:
- name: ${{ github.job }}
- path: target/${{ github.job }}
diff --git a/.github/workflows/repeat-test.yml b/.github/workflows/repeat-test.yaml
similarity index 91%
rename from .github/workflows/repeat-test.yml
rename to .github/workflows/repeat-test.yaml
index 86150d2598..66fec90f16 100644
--- a/.github/workflows/repeat-test.yml
+++ b/.github/workflows/repeat-test.yaml
@@ -50,7 +50,7 @@ env:
run-name: ${{ github.event_name == 'workflow_dispatch' && format('{0}#{1}[{2}]-{3}x{4}', inputs.test-class, inputs.test-method, inputs.ref, inputs.splits, inputs.iterations) || '' }}
jobs:
prepare:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-24.04
outputs:
matrix: ${{ steps.generate.outputs.matrix }}
test-spec: ${{ steps.test-spec.outputs.test-spec }}
@@ -79,7 +79,7 @@ jobs:
if: ${{ always() }}
needs:
- prepare
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-24.04
env:
TEST_SPEC: ${{ needs.prepare.outputs.test-spec }}
strategy:
@@ -87,11 +87,11 @@ jobs:
split: ${{ fromJson(needs.prepare.outputs.matrix) }}
fail-fast: ${{ fromJson(github.event.inputs.fail-fast) }}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.ref }}
- name: Cache for maven dependencies
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: |
~/.m2/repository
@@ -100,7 +100,7 @@ jobs:
restore-keys: |
maven-repo-
- name: Setup java
- uses: actions/setup-java@v3
+ uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 8
@@ -113,18 +113,18 @@ jobs:
run: dev-support/checks/_summary.sh target/unit/summary.txt
if: ${{ !cancelled() }}
- name: Archive build results
- uses: actions/upload-artifact@v3
- if: always()
+ uses: actions/upload-artifact@v4
+ if: ${{ failure() }}
with:
- name: result-${{ env.TEST_CLASS }}-split-${{ matrix.split }}
+ name: result-${{ github.run_number }}-${{ github.run_id }}-split-${{ matrix.split }}
path: target/unit
count-failures:
- if: ${{ always() }}
+ if: ${{ failure() }}
needs: test
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-24.04
steps:
- name: Download build results
- uses: actions/download-artifact@v3
+ uses: actions/download-artifact@v4
- name: Count failures
run: |
failures=$(find . -name 'summary.txt' | grep -v 'iteration' | xargs grep -v 'exit code: 0' | wc -l)
diff --git a/.gitignore b/.gitignore
index 9379453102..ecaf1f2df9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,7 @@
.hugo_build.lock
.idea
.classpath
+.mvn/.develocity/
.project
.settings
target
diff --git a/.mvn/develocity.xml b/.mvn/develocity.xml
new file mode 100644
index 0000000000..3bef395946
--- /dev/null
+++ b/.mvn/develocity.xml
@@ -0,0 +1,53 @@
+
+
+
+ ratis
+
+ https://develocity.apache.org
+ false
+
+
+
+ true
+ true
+ true
+ false
+
+ #{isFalse(env['GITHUB_ACTIONS'])}
+
+
+
+
+ #{{'0.0.0.0'}}
+
+
+
+
+ #{isFalse(env['GITHUB_ACTIONS'])}
+
+
+ false
+
+
+
diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml
new file mode 100644
index 0000000000..8ceede33b9
--- /dev/null
+++ b/.mvn/extensions.xml
@@ -0,0 +1,34 @@
+
+
+
+
+ com.gradle
+ develocity-maven-extension
+ 1.22.2
+
+
+ com.gradle
+ common-custom-user-data-maven-extension
+ 2.0.1
+
+
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
index 08ea486aa5..d58dfb70ba 100644
--- a/.mvn/wrapper/maven-wrapper.properties
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -14,5 +14,6 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.0/apache-maven-3.9.0-bin.zip
-wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar
+wrapperVersion=3.3.2
+distributionType=only-script
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
diff --git a/dev-support/checks/build.sh b/dev-support/checks/build.sh
index 6add1ae605..ee8d3f3d7e 100755
--- a/dev-support/checks/build.sh
+++ b/dev-support/checks/build.sh
@@ -20,7 +20,7 @@ source "${DIR}/../find_maven.sh"
: ${WITH_COVERAGE:="false"}
-MAVEN_OPTIONS='-V -B -Dmaven.javadoc.skip=true -DskipTests --no-transfer-progress'
+MAVEN_OPTIONS='-V -B -Dmaven.javadoc.skip=true -DskipTests'
if [[ "${WITH_COVERAGE}" != "true" ]]; then
MAVEN_OPTIONS="${MAVEN_OPTIONS} -Djacoco.skip"
diff --git a/dev-support/checks/checkstyle.sh b/dev-support/checks/checkstyle.sh
index a2ee427380..cb06fdaacd 100755
--- a/dev-support/checks/checkstyle.sh
+++ b/dev-support/checks/checkstyle.sh
@@ -23,7 +23,7 @@ REPORT_DIR=${OUTPUT_DIR:-"$DIR/../../target/checkstyle"}
mkdir -p "$REPORT_DIR"
REPORT_FILE="$REPORT_DIR/summary.txt"
-MAVEN_OPTIONS='-B -fae --no-transfer-progress -Dcheckstyle.failOnViolation=false'
+MAVEN_OPTIONS='-B -fae -Dcheckstyle.failOnViolation=false'
declare -i rc
${MVN} ${MAVEN_OPTIONS} checkstyle:check | tee "${REPORT_DIR}/output.log"
diff --git a/dev-support/checks/shellcheck.sh b/dev-support/checks/compile.sh
similarity index 65%
rename from dev-support/checks/shellcheck.sh
rename to dev-support/checks/compile.sh
index feb307a2db..fba4394dec 100755
--- a/dev-support/checks/shellcheck.sh
+++ b/dev-support/checks/compile.sh
@@ -16,22 +16,16 @@
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
cd "$DIR/../.." || exit 1
-REPORT_DIR=${OUTPUT_DIR:-"$DIR/../../target/shellcheck"}
-mkdir -p "$REPORT_DIR"
-REPORT_FILE="$REPORT_DIR/summary.txt"
+source "${DIR}/../find_maven.sh"
-echo "" > "$OUTPUT_FILE"
-if [[ "$(uname -s)" = "Darwin" ]]; then
- find . -type f -perm '-500'
-else
- find . -type f -executable
-fi \
- | grep -v -e target/ -e node_modules/ -e '\.\(ico\|py\|yml\)$' \
- | xargs -n1 shellcheck \
- | tee "$REPORT_FILE"
+: ${WITH_COVERAGE:="false"}
-wc -l "$REPORT_FILE" | awk '{print $1}'> "$REPORT_DIR/failures"
+MAVEN_OPTIONS='-V -B -Dmaven.javadoc.skip=true -DskipTests'
-if [[ -s "${REPORT_FILE}" ]]; then
- exit 1
+if [[ "${WITH_COVERAGE}" != "true" ]]; then
+ MAVEN_OPTIONS="${MAVEN_OPTIONS} -Djacoco.skip"
fi
+
+export MAVEN_OPTS="-Xmx4096m"
+${MVN} ${MAVEN_OPTIONS} clean verify "$@"
+exit $?
diff --git a/dev-support/checks/coverage.sh b/dev-support/checks/coverage.sh
index a2fab9b32a..ff0aef1a48 100755
--- a/dev-support/checks/coverage.sh
+++ b/dev-support/checks/coverage.sh
@@ -29,7 +29,7 @@ mkdir -p "$REPORT_DIR"
JACOCO_VERSION=$(${MVN} help:evaluate -Dexpression=jacoco.version -q -DforceStdout)
#Install jacoco cli
-${MVN} --non-recursive --no-transfer-progress \
+${MVN} --non-recursive \
org.apache.maven.plugins:maven-dependency-plugin:3.6.1:copy \
-Dartifact=org.jacoco:org.jacoco.cli:${JACOCO_VERSION}:jar:nodeps
diff --git a/dev-support/checks/findbugs.sh b/dev-support/checks/findbugs.sh
index 17c669b8d5..3a063b3fa1 100755
--- a/dev-support/checks/findbugs.sh
+++ b/dev-support/checks/findbugs.sh
@@ -20,7 +20,7 @@ source "${DIR}/../find_maven.sh"
: ${WITH_COVERAGE:="false"}
-MAVEN_OPTIONS='-B -fae --no-transfer-progress'
+MAVEN_OPTIONS='-B -fae'
if ! type unionBugs >/dev/null 2>&1 || ! type convertXmlToText >/dev/null 2>&1; then
#shellcheck disable=SC2086
diff --git a/dev-support/checks/rat.sh b/dev-support/checks/rat.sh
index 34d8a25854..9b55878eff 100755
--- a/dev-support/checks/rat.sh
+++ b/dev-support/checks/rat.sh
@@ -23,7 +23,7 @@ mkdir -p "$REPORT_DIR"
REPORT_FILE="$REPORT_DIR/summary.txt"
-${MVN} -B -fn --no-transfer-progress org.apache.rat:apache-rat-plugin:0.13:check
+${MVN} -B -fn org.apache.rat:apache-rat-plugin:0.13:check
cd "$DIR/../.." || exit 1
diff --git a/dev-support/checks/release.sh b/dev-support/checks/release.sh
new file mode 100755
index 0000000000..1297b36b0c
--- /dev/null
+++ b/dev-support/checks/release.sh
@@ -0,0 +1,49 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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 -e -u -o pipefail
+
+# This script tests the local part of the release process. It does not publish anything.
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
+cd "$DIR/../.." || exit 1
+
+: "${RATISVERSION:="0.0.1"}"
+: "${RC:="-ci-test"}"
+: "${STAGING_REPO_DIR:="/tmp/ratis.staging-repo"}"
+: "${SVNDISTDIR:="/tmp/ratis.svn"}"
+: "${USERID:="ratis-ci-not-for-release"}"
+
+MVN_REPO_DIR="${HOME}/.m2/repository"
+
+mkdir -p "${SVNDISTDIR}"
+
+if [[ -z "${CODESIGNINGKEY:-}" ]]; then
+ gpg --batch --passphrase '' --pinentry-mode loopback --quick-generate-key "${USERID}" rsa4096 default 1d
+ CODESIGNINGKEY=$(gpg --list-keys --with-colons "${USERID}" | grep '^pub:' | cut -f5 -d:)
+fi
+
+git config user.email || git config user.email 'test@example.com'
+git config user.name || git config user.name 'Test User'
+
+export CODESIGNINGKEY MVN_REPO_DIR RATISVERSION RC SVNDISTDIR
+
+export MAVEN_ARGS="--batch-mode"
+
+dev-support/make_rc.sh 1-prepare-src
+dev-support/make_rc.sh 2-verify-bin
+dev-support/make_rc.sh 3-publish-mvn -DaltDeploymentRepository="local::default::file://${STAGING_REPO_DIR}"
+dev-support/make_rc.sh 4-assembly
diff --git a/dev-support/checks/repro.sh b/dev-support/checks/repro.sh
new file mode 100755
index 0000000000..88941bc286
--- /dev/null
+++ b/dev-support/checks/repro.sh
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
+cd "$DIR/../.." || exit 1
+
+source "${DIR}/../find_maven.sh"
+
+: ${WITH_COVERAGE:="false"}
+
+MAVEN_OPTIONS='-V -B -Dmaven.javadoc.skip=true -DskipTests'
+
+if [[ "${WITH_COVERAGE}" != "true" ]]; then
+ MAVEN_OPTIONS="${MAVEN_OPTIONS} -Djacoco.skip"
+fi
+
+export MAVEN_OPTS="-Xmx4096m"
+${MVN} ${MAVEN_OPTIONS} clean verify artifact:compare "$@"
+exit $?
diff --git a/dev-support/checks/sonar.sh b/dev-support/checks/sonar.sh
index 55a46cfec7..55edbdaaea 100755
--- a/dev-support/checks/sonar.sh
+++ b/dev-support/checks/sonar.sh
@@ -23,7 +23,7 @@ if [ ! "$SONAR_TOKEN" ]; then
exit 1
fi
-${MVN} -B verify -DskipShade -DskipTests --no-transfer-progress \
- org.sonarsource.scanner.maven:sonar-maven-plugin:3.6.0.1398:sonar \
+${MVN} -B verify -DskipShade -DskipTests \
+ sonar:sonar \
-Dsonar.coverage.jacoco.xmlReportPaths="$(pwd)/target/coverage/all.xml" \
-Dsonar.host.url=https://sonarcloud.io -Dsonar.organization=apache -Dsonar.projectKey=apache-ratis
diff --git a/dev-support/checks/unit.sh b/dev-support/checks/unit.sh
index f7a4f3017e..c0369898e7 100755
--- a/dev-support/checks/unit.sh
+++ b/dev-support/checks/unit.sh
@@ -34,7 +34,11 @@ REPORT_DIR=${OUTPUT_DIR:-"$DIR/../../target/unit"}
mkdir -p "$REPORT_DIR"
export MAVEN_OPTS="-Xmx4096m"
-MAVEN_OPTIONS='-V -B --no-transfer-progress'
+MAVEN_OPTIONS='-V -B'
+
+if [[ "$@" =~ "-Pflaky-tests" ]]; then
+ MAVEN_OPTIONS="${MAVEN_OPTIONS} -Dsurefire.rerunFailingTestsCount=5 -Dsurefire.timeout=1200"
+fi
if [[ "${FAIL_FAST}" == "true" ]]; then
MAVEN_OPTIONS="${MAVEN_OPTIONS} --fail-fast -Dsurefire.skipAfterFailureCount=1"
@@ -65,6 +69,12 @@ for i in $(seq 1 ${ITERATIONS}); do
fi
if [[ ${ITERATIONS} -gt 1 ]]; then
+ if ! grep -q "Running .*Test" "${REPORT_DIR}/output.log"; then
+ echo "No tests were run" >> "${REPORT_DIR}/summary.txt"
+ irc=1
+ FAIL_FAST=true
+ fi
+
if [[ ${irc} == 0 ]]; then
rm -fr "${REPORT_DIR}"
fi
diff --git a/dev-support/make_rc.sh b/dev-support/make_rc.sh
index b5bec51dde..c6a93a6d9d 100755
--- a/dev-support/make_rc.sh
+++ b/dev-support/make_rc.sh
@@ -91,12 +91,12 @@ mvnFun() {
MAVEN_OPTS="${mvnopts}" ${MVN} -Dmaven.repo.local="${repodir}" "$@"
}
-prepare-src() {
+1-prepare-src() {
cd "$projectdir"
git reset --hard
git clean -fdx
mvnFun versions:set -DnewVersion="$RATISVERSION"
- git commit -a -m "Change version for the version $RATISVERSION $RC"
+ git commit --allow-empty -a -m "Change version for the version $RATISVERSION $RC"
git config user.signingkey "${CODESIGNINGKEY}"
git tag -s -m "Release $RATISVERSION $RC" ratis-"${RATISVERSION}${RC}"
@@ -106,69 +106,72 @@ prepare-src() {
#grep -r SNAPSHOT --include=pom.xml
- mvnFun clean install assembly:single -DskipTests=true -Prelease -Papache-release -Dgpg.keyname="${CODESIGNINGKEY}"
+ mvnFun clean install -DskipTests=true -Prelease -Papache-release -Dgpg.keyname="${CODESIGNINGKEY}"
}
-prepare-bin() {
+2-verify-bin() {
echo "Cleaning up workingdir $WORKINGDIR"
rm -rf "$WORKINGDIR"
mkdir -p "$WORKINGDIR"
cd "$WORKINGDIR"
- tar zvxf "$projectdir/ratis-assembly/target/apache-ratis-${RATISVERSION}-src.tar.gz"
+ tar zvxf "$projectdir/ratis-assembly/target/ratis-assembly-${RATISVERSION}-src.tar.gz"
mv "apache-ratis-${RATISVERSION}-src" "apache-ratis-${RATISVERSION}"
cd "apache-ratis-${RATISVERSION}"
- mvnFun clean install assembly:single -DskipTests=true -Prelease -Papache-release -Dgpg.keyname="${CODESIGNINGKEY}"
+ mvnFun clean verify -DskipTests=true -Prelease -Papache-release -Dgpg.keyname="${CODESIGNINGKEY}" "$@"
}
-assembly() {
+3-publish-mvn() {
+ cd "$projectdir"
+ mvnFun verify artifact:compare deploy:deploy -DdeployAtEnd=true -DskipTests=true -Prelease -Papache-release -Dgpg.keyname="${CODESIGNINGKEY}" "$@"
+}
+
+4-assembly() {
cd "$SVNDISTDIR"
RCDIR="$SVNDISTDIR/${RATISVERSION}/${RC#-}"
mkdir -p "$RCDIR"
cd "$RCDIR"
- cp "$WORKINGDIR/apache-ratis-${RATISVERSION}/ratis-assembly/target/apache-ratis-${RATISVERSION}-bin.tar.gz" "apache-ratis-${RATISVERSION}-bin.tar.gz"
- cp "$projectdir/ratis-assembly/target/apache-ratis-${RATISVERSION}-src.tar.gz" "apache-ratis-${RATISVERSION}-src.tar.gz"
+ cp "$projectdir/ratis-assembly/target/ratis-assembly-${RATISVERSION}-bin.tar.gz" "apache-ratis-${RATISVERSION}-bin.tar.gz"
+ cp "$projectdir/ratis-assembly/target/ratis-assembly-${RATISVERSION}-src.tar.gz" "apache-ratis-${RATISVERSION}-src.tar.gz"
for i in *.tar.gz; do gpg -u "${CODESIGNINGKEY}" --armor --output "${i}.asc" --detach-sig "${i}"; done
for i in *.tar.gz; do gpg --print-md SHA512 "${i}" > "${i}.sha512"; done
for i in *.tar.gz; do gpg --print-mds "${i}" > "${i}.mds"; done
cd "$SVNDISTDIR"
- svn add "${RATISVERSION}" || svn add "${RATISVERSION}/${RC#-}"
+ # skip svn add in CI
+ if [[ -z "${CI:-}" ]]; then
+ svn add "${RATISVERSION}" || svn add "${RATISVERSION}/${RC#-}"
+ fi
}
-publish-git(){
+5-publish-git(){
cd "$projectdir"
git push apache "ratis-${RATISVERSION}${RC}"
}
-publish-svn() {
+6-publish-svn() {
cd "${SVNDISTDIR}"
svn commit -m "Publish proposed version of the next Ratis release ${RATISVERSION}${RC}"
}
-publish-mvn(){
- cd "$projectdir"
- mvnFun -X clean deploy assembly:single -DskipTests=true -Prelease -Papache-release -Dgpg.keyname="${CODESIGNINGKEY}"
-}
-
-if [ "$#" -ne 1 ]; then
+if [ "$#" -lt 1 ]; then
cat << EOF
-Please choose from available phases (eg. make_rc.sh prepare-src):
+Please choose from available phases (eg. make_rc.sh 1-prepare-src):
- 1. prepare-src: This is the first step. It modifies the mvn version, creates the git tag and
+ 1-prepare-src: This is the first step. It modifies the mvn version, creates the git tag and
builds the project to create the source artifacts.
IT INCLUDES A GIT RESET + CLEAN. ALL THE LOCAL CHANGES WILL BE LOST!
- 2. prepare-bin: The source artifact is copied to the $WORKINGDIR and the binary artifact is created from the source.
+ 2-verify-bin: The source artifact is copied to the $WORKINGDIR and the binary artifact is created from the source.
This is an additional check as the the released source artifact should be enough to build the whole project.
- 3. assembly : This step copies all the required artifacts to the svn directory and ($SVNDISTDIR) creates the signatures/checksum files.
+ 3-publish-mvn: Performs the final build, and uploads the artifacts to the maven staging repository
- 4. publish-git: The first remote step, only do it if everything is fine. It pushes the rc tag to the repository.
+ 4-assembly: This step copies all the required artifacts to the svn directory and ($SVNDISTDIR) creates the signatures/checksum files.
- 5. publish-svn: Uploads the artifacts to the apache dev staging area to start the vote.
+ 5-publish-git: Only do it if everything is fine. It pushes the rc tag to the repository.
- 6. publish-mvn: Uploads the artifacts to the maven staging repository
+ 6-publish-svn: Uploads the artifacts to the apache dev staging area to start the vote.
The next steps of the release process are not scripted:
@@ -189,5 +192,7 @@ The next steps of the release process are not scripted:
EOF
else
set -x
- eval "$1"
+ func="$1"
+ shift
+ eval "$func" "$@"
fi
diff --git a/mvnw b/mvnw
index 8d937f4c14..19529ddf8c 100755
--- a/mvnw
+++ b/mvnw
@@ -19,290 +19,241 @@
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
-# Apache Maven Wrapper startup batch script, version 3.2.0
-#
-# Required ENV vars:
-# ------------------
-# JAVA_HOME - location of a JDK home dir
+# Apache Maven Wrapper startup batch script, version 3.3.2
#
# Optional ENV vars
# -----------------
-# MAVEN_OPTS - parameters passed to the Java VM when running Maven
-# e.g. to debug Maven itself, use
-# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
-# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# JAVA_HOME - location of a JDK home dir, required when download maven via java source
+# MVNW_REPOURL - repo url base for downloading maven distribution
+# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
+# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
# ----------------------------------------------------------------------------
-if [ -z "$MAVEN_SKIP_RC" ] ; then
-
- if [ -f /usr/local/etc/mavenrc ] ; then
- . /usr/local/etc/mavenrc
- fi
-
- if [ -f /etc/mavenrc ] ; then
- . /etc/mavenrc
- fi
-
- if [ -f "$HOME/.mavenrc" ] ; then
- . "$HOME/.mavenrc"
- fi
-
-fi
+set -euf
+[ "${MVNW_VERBOSE-}" != debug ] || set -x
-# OS specific support. $var _must_ be set to either true or false.
-cygwin=false;
-darwin=false;
-mingw=false
+# OS specific support.
+native_path() { printf %s\\n "$1"; }
case "$(uname)" in
- CYGWIN*) cygwin=true ;;
- MINGW*) mingw=true;;
- Darwin*) darwin=true
- # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
- # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
- if [ -z "$JAVA_HOME" ]; then
- if [ -x "/usr/libexec/java_home" ]; then
- JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME
- else
- JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
- fi
- fi
- ;;
+CYGWIN* | MINGW*)
+ [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
+ native_path() { cygpath --path --windows "$1"; }
+ ;;
esac
-if [ -z "$JAVA_HOME" ] ; then
- if [ -r /etc/gentoo-release ] ; then
- JAVA_HOME=$(java-config --jre-home)
- fi
-fi
-
-# For Cygwin, ensure paths are in UNIX format before anything is touched
-if $cygwin ; then
- [ -n "$JAVA_HOME" ] &&
- JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
- [ -n "$CLASSPATH" ] &&
- CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
-fi
-
-# For Mingw, ensure paths are in UNIX format before anything is touched
-if $mingw ; then
- [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
- JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)"
-fi
-
-if [ -z "$JAVA_HOME" ]; then
- javaExecutable="$(which javac)"
- if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then
- # readlink(1) is not available as standard on Solaris 10.
- readLink=$(which readlink)
- if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then
- if $darwin ; then
- javaHome="$(dirname "\"$javaExecutable\"")"
- javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac"
- else
- javaExecutable="$(readlink -f "\"$javaExecutable\"")"
- fi
- javaHome="$(dirname "\"$javaExecutable\"")"
- javaHome=$(expr "$javaHome" : '\(.*\)/bin')
- JAVA_HOME="$javaHome"
- export JAVA_HOME
- fi
- fi
-fi
-
-if [ -z "$JAVACMD" ] ; then
- if [ -n "$JAVA_HOME" ] ; then
- if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+# set JAVACMD and JAVACCMD
+set_java_home() {
+ # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
+ if [ -n "${JAVA_HOME-}" ]; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ]; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
+ JAVACCMD="$JAVA_HOME/jre/sh/javac"
else
JAVACMD="$JAVA_HOME/bin/java"
+ JAVACCMD="$JAVA_HOME/bin/javac"
+
+ if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
+ echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
+ echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
+ return 1
+ fi
fi
else
- JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)"
- fi
-fi
-
-if [ ! -x "$JAVACMD" ] ; then
- echo "Error: JAVA_HOME is not defined correctly." >&2
- echo " We cannot execute $JAVACMD" >&2
- exit 1
-fi
+ JAVACMD="$(
+ 'set' +e
+ 'unset' -f command 2>/dev/null
+ 'command' -v java
+ )" || :
+ JAVACCMD="$(
+ 'set' +e
+ 'unset' -f command 2>/dev/null
+ 'command' -v javac
+ )" || :
-if [ -z "$JAVA_HOME" ] ; then
- echo "Warning: JAVA_HOME environment variable is not set."
-fi
-
-# traverses directory structure from process work directory to filesystem root
-# first directory with .mvn subdirectory is considered project base directory
-find_maven_basedir() {
- if [ -z "$1" ]
- then
- echo "Path not specified to find_maven_basedir"
- return 1
+ if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
+ echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
+ return 1
+ fi
fi
+}
- basedir="$1"
- wdir="$1"
- while [ "$wdir" != '/' ] ; do
- if [ -d "$wdir"/.mvn ] ; then
- basedir=$wdir
- break
- fi
- # workaround for JBEAP-8937 (on Solaris 10/Sparc)
- if [ -d "${wdir}" ]; then
- wdir=$(cd "$wdir/.." || exit 1; pwd)
- fi
- # end of workaround
+# hash string like Java String::hashCode
+hash_string() {
+ str="${1:-}" h=0
+ while [ -n "$str" ]; do
+ char="${str%"${str#?}"}"
+ h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
+ str="${str#?}"
done
- printf '%s' "$(cd "$basedir" || exit 1; pwd)"
+ printf %x\\n $h
}
-# concatenates all lines of a file
-concat_lines() {
- if [ -f "$1" ]; then
- # Remove \r in case we run on Windows within Git Bash
- # and check out the repository with auto CRLF management
- # enabled. Otherwise, we may read lines that are delimited with
- # \r\n and produce $'-Xarg\r' rather than -Xarg due to word
- # splitting rules.
- tr -s '\r\n' ' ' < "$1"
- fi
+verbose() { :; }
+[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
+
+die() {
+ printf %s\\n "$1" >&2
+ exit 1
}
-log() {
- if [ "$MVNW_VERBOSE" = true ]; then
- printf '%s\n' "$1"
- fi
+trim() {
+ # MWRAPPER-139:
+ # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
+ # Needed for removing poorly interpreted newline sequences when running in more
+ # exotic environments such as mingw bash on Windows.
+ printf "%s" "${1}" | tr -d '[:space:]'
+}
+
+# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
+while IFS="=" read -r key value; do
+ case "${key-}" in
+ distributionUrl) distributionUrl=$(trim "${value-}") ;;
+ distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
+ esac
+done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties"
+[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties"
+
+case "${distributionUrl##*/}" in
+maven-mvnd-*bin.*)
+ MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
+ case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
+ *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
+ :Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
+ :Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
+ :Linux*x86_64*) distributionPlatform=linux-amd64 ;;
+ *)
+ echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
+ distributionPlatform=linux-amd64
+ ;;
+ esac
+ distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
+ ;;
+maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
+*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
+esac
+
+# apply MVNW_REPOURL and calculate MAVEN_HOME
+# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
+[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
+distributionUrlName="${distributionUrl##*/}"
+distributionUrlNameMain="${distributionUrlName%.*}"
+distributionUrlNameMain="${distributionUrlNameMain%-bin}"
+MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
+MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
+
+exec_maven() {
+ unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
+ exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
}
-BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
-if [ -z "$BASE_DIR" ]; then
- exit 1;
+if [ -d "$MAVEN_HOME" ]; then
+ verbose "found existing MAVEN_HOME at $MAVEN_HOME"
+ exec_maven "$@"
fi
-MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
-log "$MAVEN_PROJECTBASEDIR"
+case "${distributionUrl-}" in
+*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
+*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
+esac
-##########################################################################################
-# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
-# This allows using the maven wrapper in projects that prohibit checking in binary data.
-##########################################################################################
-wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
-if [ -r "$wrapperJarPath" ]; then
- log "Found $wrapperJarPath"
+# prepare tmp dir
+if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
+ clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
+ trap clean HUP INT TERM EXIT
else
- log "Couldn't find $wrapperJarPath, downloading it ..."
+ die "cannot create temp dir"
+fi
- if [ -n "$MVNW_REPOURL" ]; then
- wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
- else
- wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
- fi
- while IFS="=" read -r key value; do
- # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
- safeValue=$(echo "$value" | tr -d '\r')
- case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;;
- esac
- done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
- log "Downloading from: $wrapperUrl"
+mkdir -p -- "${MAVEN_HOME%/*}"
- if $cygwin; then
- wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
- fi
+# Download and Install Apache Maven
+verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
+verbose "Downloading from: $distributionUrl"
+verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
- if command -v wget > /dev/null; then
- log "Found wget ... using wget"
- [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
- if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
- wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
- else
- wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
- fi
- elif command -v curl > /dev/null; then
- log "Found curl ... using curl"
- [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
- if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
- curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
- else
- curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
- fi
- else
- log "Falling back to using Java to download"
- javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
- javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
- # For Cygwin, switch paths to Windows format before running javac
- if $cygwin; then
- javaSource=$(cygpath --path --windows "$javaSource")
- javaClass=$(cygpath --path --windows "$javaClass")
- fi
- if [ -e "$javaSource" ]; then
- if [ ! -e "$javaClass" ]; then
- log " - Compiling MavenWrapperDownloader.java ..."
- ("$JAVA_HOME/bin/javac" "$javaSource")
- fi
- if [ -e "$javaClass" ]; then
- log " - Running MavenWrapperDownloader.java ..."
- ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
- fi
- fi
- fi
+# select .zip or .tar.gz
+if ! command -v unzip >/dev/null; then
+ distributionUrl="${distributionUrl%.zip}.tar.gz"
+ distributionUrlName="${distributionUrl##*/}"
fi
-##########################################################################################
-# End of extension
-##########################################################################################
-# If specified, validate the SHA-256 sum of the Maven wrapper jar file
-wrapperSha256Sum=""
-while IFS="=" read -r key value; do
- case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
- esac
-done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
-if [ -n "$wrapperSha256Sum" ]; then
- wrapperSha256Result=false
- if command -v sha256sum > /dev/null; then
- if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then
- wrapperSha256Result=true
+# verbose opt
+__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
+[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
+
+# normalize http auth
+case "${MVNW_PASSWORD:+has-password}" in
+'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
+has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
+esac
+
+if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
+ verbose "Found wget ... using wget"
+ wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
+elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
+ verbose "Found curl ... using curl"
+ curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
+elif set_java_home; then
+ verbose "Falling back to use Java to download"
+ javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
+ targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
+ cat >"$javaSource" <<-END
+ public class Downloader extends java.net.Authenticator
+ {
+ protected java.net.PasswordAuthentication getPasswordAuthentication()
+ {
+ return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
+ }
+ public static void main( String[] args ) throws Exception
+ {
+ setDefault( new Downloader() );
+ java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
+ }
+ }
+ END
+ # For Cygwin/MinGW, switch paths to Windows format before running javac and java
+ verbose " - Compiling Downloader.java ..."
+ "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
+ verbose " - Running Downloader.java ..."
+ "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
+fi
+
+# If specified, validate the SHA-256 sum of the Maven distribution zip file
+if [ -n "${distributionSha256Sum-}" ]; then
+ distributionSha256Result=false
+ if [ "$MVN_CMD" = mvnd.sh ]; then
+ echo "Checksum validation is not supported for maven-mvnd." >&2
+ echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
+ exit 1
+ elif command -v sha256sum >/dev/null; then
+ if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then
+ distributionSha256Result=true
fi
- elif command -v shasum > /dev/null; then
- if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
- wrapperSha256Result=true
+ elif command -v shasum >/dev/null; then
+ if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
+ distributionSha256Result=true
fi
else
- echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
- echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
+ echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
+ echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
exit 1
fi
- if [ $wrapperSha256Result = false ]; then
- echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
- echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
- echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
+ if [ $distributionSha256Result = false ]; then
+ echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
+ echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
exit 1
fi
fi
-MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
-
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin; then
- [ -n "$JAVA_HOME" ] &&
- JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
- [ -n "$CLASSPATH" ] &&
- CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
- [ -n "$MAVEN_PROJECTBASEDIR" ] &&
- MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
+# unzip and move
+if command -v unzip >/dev/null; then
+ unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
+else
+ tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
fi
+printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url"
+mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
-# Provide a "standardized" way to retrieve the CLI args that will
-# work with both Windows and non-Windows executions.
-MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*"
-export MAVEN_CMD_LINE_ARGS
-
-WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
-
-# shellcheck disable=SC2086 # safe args
-exec "$JAVACMD" \
- $MAVEN_OPTS \
- $MAVEN_DEBUG_OPTS \
- -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
- "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
- ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
+clean || :
+exec_maven "$@"
diff --git a/mvnw.cmd b/mvnw.cmd
index f80fbad3e7..b150b91ed5 100644
--- a/mvnw.cmd
+++ b/mvnw.cmd
@@ -1,3 +1,4 @@
+<# : batch portion
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@@ -18,188 +19,131 @@
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
-@REM Apache Maven Wrapper startup batch script, version 3.2.0
-@REM
-@REM Required ENV vars:
-@REM JAVA_HOME - location of a JDK home dir
+@REM Apache Maven Wrapper startup batch script, version 3.3.2
@REM
@REM Optional ENV vars
-@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
-@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
-@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
-@REM e.g. to debug Maven itself, use
-@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
-@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM MVNW_REPOURL - repo url base for downloading maven distribution
+@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
+@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
@REM ----------------------------------------------------------------------------
-@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
-@echo off
-@REM set title of command window
-title %0
-@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
-@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
-
-@REM set %HOME% to equivalent of $HOME
-if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
-
-@REM Execute a user defined script before this one
-if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
-@REM check for pre script, once with legacy .bat ending and once with .cmd ending
-if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
-if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
-:skipRcPre
-
-@setlocal
-
-set ERROR_CODE=0
-
-@REM To isolate internal variables from possible post scripts, we use another setlocal
-@setlocal
-
-@REM ==== START VALIDATION ====
-if not "%JAVA_HOME%" == "" goto OkJHome
-
-echo.
-echo Error: JAVA_HOME not found in your environment. >&2
-echo Please set the JAVA_HOME variable in your environment to match the >&2
-echo location of your Java installation. >&2
-echo.
-goto error
-
-:OkJHome
-if exist "%JAVA_HOME%\bin\java.exe" goto init
-
-echo.
-echo Error: JAVA_HOME is set to an invalid directory. >&2
-echo JAVA_HOME = "%JAVA_HOME%" >&2
-echo Please set the JAVA_HOME variable in your environment to match the >&2
-echo location of your Java installation. >&2
-echo.
-goto error
-
-@REM ==== END VALIDATION ====
-
-:init
-
-@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
-@REM Fallback to current working directory if not found.
-
-set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
-IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
-
-set EXEC_DIR=%CD%
-set WDIR=%EXEC_DIR%
-:findBaseDir
-IF EXIST "%WDIR%"\.mvn goto baseDirFound
-cd ..
-IF "%WDIR%"=="%CD%" goto baseDirNotFound
-set WDIR=%CD%
-goto findBaseDir
-
-:baseDirFound
-set MAVEN_PROJECTBASEDIR=%WDIR%
-cd "%EXEC_DIR%"
-goto endDetectBaseDir
-
-:baseDirNotFound
-set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
-cd "%EXEC_DIR%"
-
-:endDetectBaseDir
-
-IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
-
-@setlocal EnableExtensions EnableDelayedExpansion
-for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
-@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
-
-:endReadAdditionalConfig
-
-SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
-set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
-set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
-
-set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
-
-FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
- IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
-)
-
-@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
-@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
-if exist %WRAPPER_JAR% (
- if "%MVNW_VERBOSE%" == "true" (
- echo Found %WRAPPER_JAR%
- )
-) else (
- if not "%MVNW_REPOURL%" == "" (
- SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
- )
- if "%MVNW_VERBOSE%" == "true" (
- echo Couldn't find %WRAPPER_JAR%, downloading it ...
- echo Downloading from: %WRAPPER_URL%
- )
-
- powershell -Command "&{"^
- "$webclient = new-object System.Net.WebClient;"^
- "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
- "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
- "}"^
- "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
- "}"
- if "%MVNW_VERBOSE%" == "true" (
- echo Finished downloading %WRAPPER_JAR%
- )
-)
-@REM End of extension
-
-@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
-SET WRAPPER_SHA_256_SUM=""
-FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
- IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
+@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
+@SET __MVNW_CMD__=
+@SET __MVNW_ERROR__=
+@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
+@SET PSModulePath=
+@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
+ IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
)
-IF NOT %WRAPPER_SHA_256_SUM%=="" (
- powershell -Command "&{"^
- "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
- "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
- " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
- " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
- " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
- " exit 1;"^
- "}"^
- "}"
- if ERRORLEVEL 1 goto error
-)
-
-@REM Provide a "standardized" way to retrieve the CLI args that will
-@REM work with both Windows and non-Windows executions.
-set MAVEN_CMD_LINE_ARGS=%*
-
-%MAVEN_JAVA_EXE% ^
- %JVM_CONFIG_MAVEN_PROPS% ^
- %MAVEN_OPTS% ^
- %MAVEN_DEBUG_OPTS% ^
- -classpath %WRAPPER_JAR% ^
- "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
- %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
-if ERRORLEVEL 1 goto error
-goto end
-
-:error
-set ERROR_CODE=1
-
-:end
-@endlocal & set ERROR_CODE=%ERROR_CODE%
-
-if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
-@REM check for post script, once with legacy .bat ending and once with .cmd ending
-if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
-if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
-:skipRcPost
-
-@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
-if "%MAVEN_BATCH_PAUSE%"=="on" pause
-
-if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
-
-cmd /C exit /B %ERROR_CODE%
+@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
+@SET __MVNW_PSMODULEP_SAVE=
+@SET __MVNW_ARG0_NAME__=
+@SET MVNW_USERNAME=
+@SET MVNW_PASSWORD=
+@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*)
+@echo Cannot start maven from wrapper >&2 && exit /b 1
+@GOTO :EOF
+: end batch / begin powershell #>
+
+$ErrorActionPreference = "Stop"
+if ($env:MVNW_VERBOSE -eq "true") {
+ $VerbosePreference = "Continue"
+}
+
+# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
+$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
+if (!$distributionUrl) {
+ Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
+}
+
+switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
+ "maven-mvnd-*" {
+ $USE_MVND = $true
+ $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
+ $MVN_CMD = "mvnd.cmd"
+ break
+ }
+ default {
+ $USE_MVND = $false
+ $MVN_CMD = $script -replace '^mvnw','mvn'
+ break
+ }
+}
+
+# apply MVNW_REPOURL and calculate MAVEN_HOME
+# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
+if ($env:MVNW_REPOURL) {
+ $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" }
+ $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')"
+}
+$distributionUrlName = $distributionUrl -replace '^.*/',''
+$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
+$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
+if ($env:MAVEN_USER_HOME) {
+ $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain"
+}
+$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
+$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
+
+if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
+ Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
+ Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
+ exit $?
+}
+
+if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
+ Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
+}
+
+# prepare tmp dir
+$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
+$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
+$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
+trap {
+ if ($TMP_DOWNLOAD_DIR.Exists) {
+ try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
+ catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
+ }
+}
+
+New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
+
+# Download and Install Apache Maven
+Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
+Write-Verbose "Downloading from: $distributionUrl"
+Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
+
+$webclient = New-Object System.Net.WebClient
+if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
+ $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
+}
+[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
+$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
+
+# If specified, validate the SHA-256 sum of the Maven distribution zip file
+$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
+if ($distributionSha256Sum) {
+ if ($USE_MVND) {
+ Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
+ }
+ Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
+ if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
+ Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
+ }
+}
+
+# unzip and move
+Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
+Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null
+try {
+ Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
+} catch {
+ if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
+ Write-Error "fail to move MAVEN_HOME"
+ }
+} finally {
+ try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
+ catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
+}
+
+Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
diff --git a/pom.xml b/pom.xml
index 68488077d4..af14e426f2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,7 +24,7 @@
ratisorg.apache.ratis
- 3.1.0-SNAPSHOT
+ 3.2.0-SNAPSHOTApache Ratispom
@@ -51,6 +51,9 @@
${distMgmtSnapshotsId}${distMgmtSnapshotsName}${distMgmtSnapshotsUrl}
+
+ false
+ repository.jboss.org
@@ -91,13 +94,6 @@
ratis-assembly
-
-
- apache.snapshots
- https://repository.apache.org/snapshots/
-
-
-
scm:git:git://git.apache.org/ratis.gitscm:git:https://git-wip-us.apache.org/repos/asf/ratis.git
@@ -176,8 +172,12 @@
3.3.04.0.61.6.1
- 3.0.0
+ 3.3.0
+ 3.6.0
+ 3.0.0-M43.5.3
+ 3.4.0
+ 5.0.0.4389
@@ -188,7 +188,7 @@
2.2.00.6.1
- 1.0
+ 2.8.04.2.14.2.0
@@ -203,16 +203,16 @@
bash
- 1.8
+ 8${javac.version}3.3.9
- 1.0.5
+ 1.0.9
- 3.24.4
- 1.58.0
+ 3.25.5
+ 1.71.0true
@@ -221,8 +221,9 @@
42.0.7
- 5.10.1
- 0.8.11
+ 5.11.2
+ 0.8.12
+ flaky | org.apache.ratis.test.tag.FlakyTest
@@ -425,7 +426,7 @@
org.junitjunit-bom
- ${junit.jupiter.version}
+ ${junit-bom.version}pomimport
@@ -440,14 +441,6 @@
6.0.53provided
-
-
- com.github.spotbugs
- spotbugs-annotations
- ${spotbugs.version}
- provided
- true
-
@@ -462,6 +455,11 @@
+
+ org.apache.hadoop
+ hadoop-maven-plugins
+ ${hadoop-maven-plugins.version}
+ org.xolstice.maven.pluginsprotobuf-maven-plugin
@@ -477,11 +475,6 @@
license-maven-plugin${license-maven-plugin.version}
-
- com.coderplus.maven.plugins
- copy-rename-maven-plugin
- ${copy-rename-maven-plugin.version}
- org.apache.maven.plugins
@@ -499,6 +492,11 @@
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ ${maven-shade-plugin.version}
+ com.github.spotbugsspotbugs-maven-plugin
@@ -529,6 +527,7 @@
org.apache.maven.pluginsmaven-enforcer-plugin
+ 3.2.1
@@ -581,8 +580,6 @@
org.apache.maven.pluginsmaven-compiler-plugin
- ${javac.version}
- ${javac.version}true512m2048m
@@ -642,9 +639,10 @@
falsefalsefalse
+ all600
- -Xmx2048m -XX:+HeapDumpOnOutOfMemoryError @{argLine}
+ -Xmx2g -XX:+HeapDumpOnOutOfMemoryError @{argLine}${project.build.directory}/log${project.build.directory}/tmp
@@ -658,12 +656,6 @@
**/Test*$*.java${test.exclude.pattern}
-
-
- listener
- org.apache.ratis.JUnitRunListener
-
-
@@ -704,6 +696,11 @@
org.apache.maven.pluginsmaven-antrun-plugin
+
+ org.apache.maven.plugins
+ maven-remote-resources-plugin
+ ${maven-remote-resources-plugin.version}
+ org.apache.maven.pluginsmaven-site-plugin
@@ -731,6 +728,16 @@
jacoco-maven-plugin${jacoco.version}
+
+ org.cyclonedx
+ cyclonedx-maven-plugin
+ ${cyclonedx.version}
+
+
+ org.sonarsource.scanner.maven
+ sonar-maven-plugin
+ ${sonar-maven-plugin.version}
+
@@ -788,11 +795,31 @@
truetrue
+
+ org.apache.hadoop
+ hadoop-maven-plugins
+
+
+ version-info
+ generate-resources
+
+ version-info
+
+
+
+ ${project.basedir}
+
+
+
+
+ org.apache.maven.pluginsmaven-checkstyle-plugin${maven-checkstyle-plugin.version}
+
+
dev-support/checkstyle.xmltruefalse
@@ -807,24 +834,6 @@
false
-
- org.codehaus.mojo
- buildnumber-maven-plugin
- 1.4
-
-
- generate-resources
-
- create-metadata
-
-
- target/classes
- ratis-version.properties
- Unknown
-
-
-
- org.codehaus.mojobuild-helper-maven-plugin
@@ -870,9 +879,41 @@
+
+
+ ${project.basedir}/src/main/resources
+ false
+
+
+ ${project.basedir}/../src/main/resources
+
+ ratis-version.properties
+
+ true
+
+
+
+ java8
+
+ [,8]
+
+
+ ${javac.version}
+ ${javac.version}
+
+
+
+ java9-or-later
+
+ [9,]
+
+
+ ${javac.version}
+
+ experiments-build
@@ -939,9 +980,6 @@
jar
-
- ${project.build.directory}
-
@@ -1060,6 +1098,18 @@
+
+ org.cyclonedx
+ cyclonedx-maven-plugin
+
+
+ package
+
+ makeBom
+
+
+
+
@@ -1075,6 +1125,7 @@
org.apache.ratis.grpc.**
+ ${flaky-test-groups}
@@ -1092,6 +1143,7 @@
org.apache.ratis.datastream.**org.apache.ratis.server.**
+ ${flaky-test-groups}
@@ -1110,6 +1162,21 @@
org.apache.ratis.grpc.**org.apache.ratis.server.**
+ ${flaky-test-groups}
+
+
+
+
+
+
+ flaky-tests
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ ${flaky-test-groups}
diff --git a/ratis-assembly/pom.xml b/ratis-assembly/pom.xml
index 98826e142a..2b735fcb1e 100644
--- a/ratis-assembly/pom.xml
+++ b/ratis-assembly/pom.xml
@@ -17,7 +17,7 @@
ratisorg.apache.ratis
- 3.1.0-SNAPSHOT
+ 3.2.0-SNAPSHOTratis-assembly
@@ -33,6 +33,8 @@
${project.build.directory}/test-classes${project.build.directory}/test-classestrue
+
+ true
@@ -120,8 +122,6 @@
org.apache.maven.pluginsmaven-assembly-plugin
-
- apache-ratis-${project.version}falsetruegnu
@@ -137,25 +137,18 @@
src/main/assembly/src.xml
- apache-ratis-${project.version}-src
- false
- default-cli
+ binpackagesingle
- src/main/assembly/examples-bin.xml
- src/main/assembly/shell-bin.xmlsrc/main/assembly/bin.xml
- src/main/assembly/bin-pkg.xml
- apache-ratis-${project.version}-bin
- false
diff --git a/ratis-assembly/src/main/assembly/bin-pkg.xml b/ratis-assembly/src/main/assembly/bin-pkg.xml
deleted file mode 100644
index 4d89869578..0000000000
--- a/ratis-assembly/src/main/assembly/bin-pkg.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
- bin-pkg
-
- tar.gz
-
-
-
- ${project.basedir}/target/apache-ratis-${project.version}-bin/apache-ratis-${project.version}-bin
- ..
-
- bin/ratis
- libexec/*.sh
- examples/bin/*.sh
-
-
-
- ${project.basedir}/target/apache-ratis-${project.version}-bin/apache-ratis-${project.version}-bin
- ..
-
- bin/ratis
- libexec/*.sh
- examples/bin/*.sh
-
- 0755
-
-
-
diff --git a/ratis-assembly/src/main/assembly/bin.xml b/ratis-assembly/src/main/assembly/bin.xml
index 7be3d01d61..7fd3cc0df5 100644
--- a/ratis-assembly/src/main/assembly/bin.xml
+++ b/ratis-assembly/src/main/assembly/bin.xml
@@ -22,9 +22,18 @@
*/
-->
bin
+ apache-ratis-${project.version}-bin
- dir
+ tar.gz
+
+
+
+ org.apache.ratis:ratis-examples
+
+ examples/lib
+
+ true
@@ -42,6 +51,7 @@
org.apache.ratis:ratis-metrics-apiorg.apache.ratis:ratis-metrics-defaultorg.apache.ratis:ratis-metrics-dropwizard3
+ org.apache.ratis:ratis-shellorg.apache.ratis:ratis-toolsorg.apache.ratis:ratis-resource-bundle
@@ -63,6 +73,16 @@
0644
+
+
+ ${project.basedir}/../target
+ .
+
+ bom.json
+ bom.xml
+
+ 0644
+ ${project.basedir}/../ratis-docs/target/classes/docs
@@ -70,5 +90,50 @@
06440755
+
+ ${project.basedir}/../ratis-shell/src/main/bin
+ bin
+ 0755
+
+
+ ${project.basedir}/../ratis-shell/src/main/libexec
+ libexec
+ 0755
+ 0755
+
+
+ ${project.basedir}/../ratis-shell/src/main/conf
+ conf
+ 644
+
+
+ ${project.basedir}/../ratis-shell/target/lib/
+ jars
+
+
+ ${project.basedir}/../ratis-examples
+ examples
+
+ README.md
+
+ 0644
+
+
+ ${project.basedir}/../ratis-examples/src/main/bin
+ examples/bin
+
+ *.*
+
+ 0755
+
+
+ ${project.basedir}/../ratis-examples/src/main/resources
+ examples/conf
+
+ conf.properties
+ log4j.properties
+
+ 644
+
diff --git a/ratis-assembly/src/main/assembly/examples-bin.xml b/ratis-assembly/src/main/assembly/examples-bin.xml
deleted file mode 100644
index 21cc7eced2..0000000000
--- a/ratis-assembly/src/main/assembly/examples-bin.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-
-
-
- examples-bin
-
- dir
-
-
-
-
- org.apache.ratis:ratis-examples
-
- examples/lib
-
-
-
-
- ${project.basedir}/src/main/resources
- .
-
- README.md
- LICENSE
- NOTICE
-
- 0644
-
-
- ${project.basedir}/../ratis-examples
- examples
-
- README.md
-
- 0644
-
-
- ${project.basedir}/../ratis-examples/src/main/bin
- examples/bin
-
- *.*
-
- 0755
-
-
- ${project.basedir}/../ratis-examples/src/main/resources
- examples/conf
-
- conf.properties
- log4j.properties
-
- 644
-
-
-
diff --git a/ratis-assembly/src/main/assembly/shell-bin.xml b/ratis-assembly/src/main/assembly/shell-bin.xml
deleted file mode 100644
index 470870f41c..0000000000
--- a/ratis-assembly/src/main/assembly/shell-bin.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-
-
-
- shell
-
- dir
-
-
-
- ${project.basedir}/../ratis-shell/target/
- jars
-
- ratis-shell-${project.version}.jar
-
-
-
- ${project.basedir}/../ratis-shell/target/lib/
- jars
-
-
- ${project.basedir}/src/main/resources
- .
-
- README.md
- LICENSE
- NOTICE
-
- 0644
-
-
- ${project.basedir}/../ratis-shell/src/main/bin
- bin
- 0755
-
-
- ${project.basedir}/../ratis-shell/src/main/libexec
- libexec
- 0755
- 0755
-
-
- ${project.basedir}/../ratis-shell/src/main/conf
- conf
- 644
-
-
-
diff --git a/ratis-assembly/src/main/assembly/src.xml b/ratis-assembly/src/main/assembly/src.xml
index 98e06c5739..6aac4ec062 100644
--- a/ratis-assembly/src/main/assembly/src.xml
+++ b/ratis-assembly/src/main/assembly/src.xml
@@ -22,6 +22,7 @@
*/
-->
src
+ apache-ratis-${project.version}-srctar.gz
@@ -102,6 +103,7 @@
README.mdmvnw.cmdpom.xml
+ src/**start-build-env.sh0644
diff --git a/ratis-assembly/src/main/resources/NOTICE b/ratis-assembly/src/main/resources/NOTICE
index 9bc9242c6a..0e3c94434b 100644
--- a/ratis-assembly/src/main/resources/NOTICE
+++ b/ratis-assembly/src/main/resources/NOTICE
@@ -292,27 +292,5 @@ networking library, which can be obtained at:
* HOMEPAGE:
* https://netty.io
* LOCATION_IN_GRPC:
-* netty/third_party/netty
------------------------------------------------------------------------
-The JSR-305 reference implementation (jsr305.jar) is distributed under the terms of the New BSD:
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice,
-this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice,
-this list of conditions and the following disclaimer in the documentation and/or
-other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
-OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
-CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
+ * netty/third_party/netty
-----------------------------------------------------------------------
diff --git a/ratis-client/pom.xml b/ratis-client/pom.xml
index d1b08d5a6a..1456e682ae 100644
--- a/ratis-client/pom.xml
+++ b/ratis-client/pom.xml
@@ -17,12 +17,17 @@
ratisorg.apache.ratis
- 3.1.0-SNAPSHOT
+ 3.2.0-SNAPSHOTratis-clientApache Ratis Client
+
+
+ true
+
+
org.apache.ratis
diff --git a/ratis-client/src/main/java/org/apache/ratis/client/RaftClientConfigKeys.java b/ratis-client/src/main/java/org/apache/ratis/client/RaftClientConfigKeys.java
index 7360a9cadb..925324c21c 100644
--- a/ratis-client/src/main/java/org/apache/ratis/client/RaftClientConfigKeys.java
+++ b/ratis-client/src/main/java/org/apache/ratis/client/RaftClientConfigKeys.java
@@ -42,7 +42,7 @@ interface Rpc {
String PREFIX = RaftClientConfigKeys.PREFIX + ".rpc";
String REQUEST_TIMEOUT_KEY = PREFIX + ".request.timeout";
- TimeDuration REQUEST_TIMEOUT_DEFAULT = TimeDuration.valueOf(3000, TimeUnit.MILLISECONDS);
+ TimeDuration REQUEST_TIMEOUT_DEFAULT = TimeDuration.valueOf(3, TimeUnit.SECONDS);
static TimeDuration requestTimeout(RaftProperties properties) {
return getTimeDuration(properties.getTimeDuration(REQUEST_TIMEOUT_DEFAULT.getUnit()),
REQUEST_TIMEOUT_KEY, REQUEST_TIMEOUT_DEFAULT, getDefaultLog());
@@ -52,8 +52,7 @@ static void setRequestTimeout(RaftProperties properties, TimeDuration timeoutDur
}
String WATCH_REQUEST_TIMEOUT_KEY = PREFIX + ".watch.request.timeout";
- TimeDuration WATCH_REQUEST_TIMEOUT_DEFAULT =
- TimeDuration.valueOf(10000, TimeUnit.MILLISECONDS);
+ TimeDuration WATCH_REQUEST_TIMEOUT_DEFAULT = TimeDuration.valueOf(10, TimeUnit.SECONDS);
static TimeDuration watchRequestTimeout(RaftProperties properties) {
return getTimeDuration(properties.getTimeDuration(WATCH_REQUEST_TIMEOUT_DEFAULT.getUnit()),
WATCH_REQUEST_TIMEOUT_KEY, WATCH_REQUEST_TIMEOUT_DEFAULT, getDefaultLog());
@@ -125,7 +124,7 @@ static void setFlushRequestBytesMin(RaftProperties properties, SizeInBytes flush
}
String REQUEST_TIMEOUT_KEY = PREFIX + ".request.timeout";
- TimeDuration REQUEST_TIMEOUT_DEFAULT = TimeDuration.valueOf(10000, TimeUnit.MILLISECONDS);
+ TimeDuration REQUEST_TIMEOUT_DEFAULT = TimeDuration.valueOf(10, TimeUnit.SECONDS);
static TimeDuration requestTimeout(RaftProperties properties) {
return getTimeDuration(properties.getTimeDuration(REQUEST_TIMEOUT_DEFAULT.getUnit()),
REQUEST_TIMEOUT_KEY, REQUEST_TIMEOUT_DEFAULT, getDefaultLog());
diff --git a/ratis-client/src/main/java/org/apache/ratis/client/api/SnapshotManagementApi.java b/ratis-client/src/main/java/org/apache/ratis/client/api/SnapshotManagementApi.java
index edd0475442..f83d976040 100644
--- a/ratis-client/src/main/java/org/apache/ratis/client/api/SnapshotManagementApi.java
+++ b/ratis-client/src/main/java/org/apache/ratis/client/api/SnapshotManagementApi.java
@@ -27,6 +27,24 @@
*/
public interface SnapshotManagementApi {
- /** trigger create snapshot file. */
- RaftClientReply create(long timeoutMs) throws IOException;
+ /** The same as create(0, timeoutMs). */
+ default RaftClientReply create(long timeoutMs) throws IOException {
+ return create(0, timeoutMs);
+ }
+
+ /** The same as create(force? 1 : 0, timeoutMs). */
+ default RaftClientReply create(boolean force, long timeoutMs) throws IOException {
+ return create(force? 1 : 0, timeoutMs);
+ }
+
+ /**
+ * Trigger to create a snapshot.
+ *
+ * @param creationGap When (creationGap > 0) and (astAppliedIndex - lastSnapshotIndex < creationGap),
+ * return lastSnapshotIndex; otherwise, take a new snapshot and then return its index.
+ * When creationGap == 0, use the server configured value as the creationGap.
+ * @return a reply. When {@link RaftClientReply#isSuccess()} is true,
+ * {@link RaftClientReply#getLogIndex()} is the snapshot index fulfilling the operation.
+ */
+ RaftClientReply create(long creationGap, long timeoutMs) throws IOException;
}
diff --git a/ratis-client/src/main/java/org/apache/ratis/client/impl/BlockingImpl.java b/ratis-client/src/main/java/org/apache/ratis/client/impl/BlockingImpl.java
index 4be9fa3275..76987801ba 100644
--- a/ratis-client/src/main/java/org/apache/ratis/client/impl/BlockingImpl.java
+++ b/ratis-client/src/main/java/org/apache/ratis/client/impl/BlockingImpl.java
@@ -119,16 +119,18 @@ public RaftClientRequest newRequestImpl() {
ioe = e;
}
- pending.incrementExceptionCount(ioe);
- ClientRetryEvent event = new ClientRetryEvent(request, ioe, pending);
+ if (client.isClosed()) {
+ throw new AlreadyClosedException(this + " is closed.");
+ }
+
+ final ClientRetryEvent event = pending.newClientRetryEvent(request, ioe);
final RetryPolicy retryPolicy = client.getRetryPolicy();
final RetryPolicy.Action action = retryPolicy.handleAttemptFailure(event);
- TimeDuration sleepTime = client.getEffectiveSleepTime(ioe, action.getSleepTime());
-
if (!action.shouldRetry()) {
- throw (IOException)client.noMoreRetries(event);
+ throw client.noMoreRetries(event);
}
+ final TimeDuration sleepTime = client.getEffectiveSleepTime(ioe, action.getSleepTime());
try {
sleepTime.sleep();
} catch (InterruptedException e) {
diff --git a/ratis-client/src/main/java/org/apache/ratis/client/impl/ClientProtoUtils.java b/ratis-client/src/main/java/org/apache/ratis/client/impl/ClientProtoUtils.java
index db19831955..36c0b3937f 100644
--- a/ratis-client/src/main/java/org/apache/ratis/client/impl/ClientProtoUtils.java
+++ b/ratis-client/src/main/java/org/apache/ratis/client/impl/ClientProtoUtils.java
@@ -179,11 +179,11 @@ static RaftClientRequest toRaftClientRequest(RaftClientRequestProto p) {
final RaftClientRequest.Builder b = RaftClientRequest.newBuilder();
- final RaftPeerId perrId = RaftPeerId.valueOf(request.getReplyId());
+ final RaftPeerId peerId = RaftPeerId.valueOf(request.getReplyId());
if (request.getToLeader()) {
- b.setLeaderId(perrId);
+ b.setLeaderId(peerId);
} else {
- b.setServerId(perrId);
+ b.setServerId(peerId);
}
if (request.hasSlidingWindowEntry()) {
b.setSlidingWindowEntry(request.getSlidingWindowEntry());
@@ -204,9 +204,13 @@ static ByteBuffer toRaftClientRequestProtoByteBuffer(RaftClientRequest request)
}
static RaftClientRequestProto toRaftClientRequestProto(RaftClientRequest request) {
+ return toRaftClientRequestProto(request, true);
+ }
+
+ static RaftClientRequestProto toRaftClientRequestProto(RaftClientRequest request, boolean withMsg) {
final RaftClientRequestProto.Builder b = RaftClientRequestProto.newBuilder()
.setRpcRequest(toRaftRpcRequestProtoBuilder(request));
- if (request.getMessage() != null) {
+ if (withMsg && request.getMessage() != null) {
b.setMessage(toClientMessageEntryProtoBuilder(request.getMessage()));
}
@@ -364,6 +368,7 @@ static GroupInfoReplyProto toGroupInfoReplyProto(GroupInfoReply reply) {
b.setIsRaftStorageHealthy(reply.isRaftStorageHealthy());
b.setRole(reply.getRoleInfoProto());
b.addAllCommitInfos(reply.getCommitInfos());
+ b.setLogInfo(reply.getLogInfoProto());
}
}
return b.build();
@@ -397,7 +402,8 @@ static RaftClientReply toRaftClientReply(RaftClientReplyProto replyProto) {
e = new NotLeaderException(serverMemberId, suggestedLeader, peers);
} else if (replyProto.getExceptionDetailsCase() == NOTREPLICATEDEXCEPTION) {
final NotReplicatedExceptionProto nre = replyProto.getNotReplicatedException();
- e = new NotReplicatedException(nre.getCallId(), nre.getReplication(), nre.getLogIndex());
+ e = new NotReplicatedException(nre.getCallId(), nre.getReplication(), nre.getLogIndex(),
+ replyProto.getCommitInfosList());
} else if (replyProto.getExceptionDetailsCase().equals(STATEMACHINEEXCEPTION)) {
e = toStateMachineException(serverMemberId, replyProto.getStateMachineException());
} else if (replyProto.getExceptionDetailsCase().equals(DATASTREAMEXCEPTION)) {
@@ -506,7 +512,8 @@ static GroupInfoReply toGroupInfoReply(GroupInfoReplyProto replyProto) {
ProtoUtils.toRaftGroup(replyProto.getGroup()),
replyProto.getRole(),
replyProto.getIsRaftStorageHealthy(),
- replyProto.hasConf()? replyProto.getConf(): null);
+ replyProto.hasConf()? replyProto.getConf(): null,
+ replyProto.getLogInfo());
}
static Message toMessage(final ClientMessageEntryProto p) {
@@ -657,7 +664,8 @@ static SnapshotManagementRequest toSnapshotManagementRequest(SnapshotManagementR
switch(p.getOpCase()) {
case CREATE:
return SnapshotManagementRequest.newCreate(clientId, serverId,
- ProtoUtils.toRaftGroupId(m.getRaftGroupId()), m.getCallId(), m.getTimeoutMs());
+ ProtoUtils.toRaftGroupId(m.getRaftGroupId()), m.getCallId(), m.getTimeoutMs(),
+ p.getCreate().getCreationGap());
default:
throw new IllegalArgumentException("Unexpected op " + p.getOpCase() + " in " + p);
}
@@ -669,7 +677,7 @@ static SnapshotManagementRequestProto toSnapshotManagementRequestProto(
.setRpcRequest(toRaftRpcRequestProtoBuilder(request));
final SnapshotManagementRequest.Create create = request.getCreate();
if (create != null) {
- b.setCreate(SnapshotCreateRequestProto.newBuilder().build());
+ b.setCreate(SnapshotCreateRequestProto.newBuilder().setCreationGap(create.getCreationGap()).build());
}
return b.build();
}
diff --git a/ratis-client/src/main/java/org/apache/ratis/client/impl/DataStreamClientImpl.java b/ratis-client/src/main/java/org/apache/ratis/client/impl/DataStreamClientImpl.java
index 26d01c356f..313131cbda 100644
--- a/ratis-client/src/main/java/org/apache/ratis/client/impl/DataStreamClientImpl.java
+++ b/ratis-client/src/main/java/org/apache/ratis/client/impl/DataStreamClientImpl.java
@@ -40,6 +40,7 @@
import org.apache.ratis.protocol.RaftPeer;
import org.apache.ratis.protocol.exceptions.AlreadyClosedException;
import org.apache.ratis.rpc.CallId;
+import org.apache.ratis.thirdparty.io.netty.buffer.ByteBuf;
import org.apache.ratis.util.IOUtils;
import org.apache.ratis.protocol.*;
import org.apache.ratis.thirdparty.com.google.protobuf.ByteString;
@@ -152,6 +153,9 @@ private CompletableFuture combineHeader(CompletableFuture writeAsyncImpl(Object data, long length, Iterable options) {
if (isClosed()) {
+ if (data instanceof ByteBuf) {
+ ((ByteBuf) data).release();
+ }
return JavaUtils.completeExceptionally(new AlreadyClosedException(
clientId + ": stream already closed, request=" + header));
}
@@ -169,6 +173,10 @@ private CompletableFuture writeAsyncImpl(Object data, long leng
return f;
}
+ public CompletableFuture writeAsync(ByteBuf src, Iterable options) {
+ return writeAsyncImpl(src, src.readableBytes(), options);
+ }
+
@Override
public CompletableFuture writeAsync(ByteBuffer src, Iterable options) {
return writeAsyncImpl(src, src.remaining(), options);
@@ -235,7 +243,7 @@ public DataStreamClientRpc getClientRpc() {
}
@Override
- public DataStreamOutputRpc stream(RaftClientRequest request) {
+ public DataStreamOutputImpl stream(RaftClientRequest request) {
return new DataStreamOutputImpl(request);
}
diff --git a/ratis-client/src/main/java/org/apache/ratis/client/impl/OrderedAsync.java b/ratis-client/src/main/java/org/apache/ratis/client/impl/OrderedAsync.java
index a1aa58681c..1e21b171b3 100644
--- a/ratis-client/src/main/java/org/apache/ratis/client/impl/OrderedAsync.java
+++ b/ratis-client/src/main/java/org/apache/ratis/client/impl/OrderedAsync.java
@@ -32,6 +32,7 @@
import org.apache.ratis.protocol.RaftPeerId;
import org.apache.ratis.retry.RetryPolicy;
import org.apache.ratis.rpc.CallId;
+import org.apache.ratis.util.BatchLogger;
import org.apache.ratis.util.IOUtils;
import org.apache.ratis.util.JavaUtils;
import org.apache.ratis.util.Preconditions;
@@ -50,6 +51,7 @@
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.LongFunction;
@@ -57,6 +59,10 @@
public final class OrderedAsync {
public static final Logger LOG = LoggerFactory.getLogger(OrderedAsync.class);
+ private enum BatchLogKey implements BatchLogger.Key {
+ SEND_REQUEST_EXCEPTION
+ }
+
static class PendingOrderedRequest extends PendingClientRequest
implements SlidingWindow.ClientSideRequest {
private final long callId;
@@ -149,10 +155,6 @@ private void failAllAsyncRequests(RaftClientRequest request, Throwable t) {
getSlidingWindow(request).fail(request.getSlidingWindowEntry().getSeqNum(), t);
}
- private void handleAsyncRetryFailure(ClientRetryEvent event) {
- failAllAsyncRequests(event.getRequest(), client.noMoreRetries(event));
- }
-
CompletableFuture send(RaftClientRequest.Type type, Message message, RaftPeerId server) {
if (!type.is(TypeCase.WATCH) && !type.is(TypeCase.MESSAGESTREAM)) {
Objects.requireNonNull(message, "message == null");
@@ -187,85 +189,71 @@ private void sendRequestWithRetry(PendingOrderedRequest pending) {
if (pending == null) {
return;
}
-
- final CompletableFuture f = pending.getReplyFuture();
- if (f.isDone()) {
+ if (pending.getReplyFuture().isDone()) {
return;
}
- final RaftClientRequest request = pending.newRequestImpl();
+ final RaftClientRequest request = pending.newRequest();
if (request == null) { // already done
- LOG.debug("{} newRequestImpl returns null", pending);
+ LOG.debug("{} newRequest returns null", pending);
return;
}
- final RetryPolicy retryPolicy = client.getRetryPolicy();
- sendRequest(pending).exceptionally(e -> {
- if (e instanceof CompletionException) {
- e = JavaUtils.unwrapCompletionException(e);
- scheduleWithTimeout(pending, request, retryPolicy, e);
- return null;
- }
- f.completeExceptionally(e);
- return null;
- });
- }
-
- private void scheduleWithTimeout(PendingOrderedRequest pending,
- RaftClientRequest request, RetryPolicy retryPolicy, Throwable e) {
- final int attempt = pending.getAttemptCount();
- final ClientRetryEvent event = new ClientRetryEvent(request, e, pending);
- final TimeDuration sleepTime = client.getEffectiveSleepTime(e,
- retryPolicy.handleAttemptFailure(event).getSleepTime());
- LOG.debug("schedule* attempt #{} with sleep {} and policy {} for {}", attempt, sleepTime, retryPolicy, request);
- scheduleWithTimeout(pending, sleepTime, getSlidingWindow(request));
- }
-
- private void scheduleWithTimeout(PendingOrderedRequest pending, TimeDuration sleepTime,
- SlidingWindow.Client slidingWindow) {
- client.getScheduler().onTimeout(sleepTime,
- () -> slidingWindow.retry(pending, this::sendRequestWithRetry),
- LOG, () -> "Failed* to retry " + pending);
- }
-
- private CompletableFuture sendRequest(PendingOrderedRequest pending) {
- final RetryPolicy retryPolicy = client.getRetryPolicy();
- final RaftClientRequest request;
if (getSlidingWindow((RaftPeerId) null).isFirst(pending.getSeqNum())) {
pending.setFirstRequest();
}
- request = pending.newRequest();
LOG.debug("{}: send* {}", client.getId(), request);
- return client.getClientRpc().sendRequestAsync(request).thenApply(reply -> {
+ client.getClientRpc().sendRequestAsync(request).thenAccept(reply -> {
LOG.debug("{}: receive* {}", client.getId(), reply);
Objects.requireNonNull(reply, "reply == null");
client.handleReply(request, reply);
getSlidingWindow(request).receiveReply(
request.getSlidingWindowEntry().getSeqNum(), reply, this::sendRequestWithRetry);
- return reply;
}).exceptionally(e -> {
- LOG.error(client.getId() + ": Failed* " + request, e);
- e = JavaUtils.unwrapCompletionException(e);
- if (e instanceof IOException && !(e instanceof GroupMismatchException)) {
- pending.incrementExceptionCount(e);
- final ClientRetryEvent event = new ClientRetryEvent(request, e, pending);
- if (!retryPolicy.handleAttemptFailure(event).shouldRetry()) {
- handleAsyncRetryFailure(event);
- } else {
- if (e instanceof NotLeaderException) {
- NotLeaderException nle = (NotLeaderException)e;
- client.handleNotLeaderException(request, nle, this::resetSlidingWindow);
- } else {
- client.handleIOException(request, (IOException) e, null, this::resetSlidingWindow);
- }
- }
- throw new CompletionException(e);
- }
- failAllAsyncRequests(request, e);
+ final Throwable exception = e;
+ final String key = client.getId() + "-" + request.getCallId() + "-" + exception;
+ final Consumer op = suffix -> LOG.error("{} {}: Failed* {}", suffix, client.getId(), request, exception);
+ BatchLogger.print(BatchLogKey.SEND_REQUEST_EXCEPTION, key, op);
+ handleException(pending, request, e);
return null;
});
}
+ private void handleException(PendingOrderedRequest pending, RaftClientRequest request, Throwable e) {
+ final RetryPolicy retryPolicy = client.getRetryPolicy();
+ if (client.isClosed()) {
+ failAllAsyncRequests(request, new AlreadyClosedException(client + " is closed."));
+ return;
+ }
+
+ e = JavaUtils.unwrapCompletionException(e);
+ if (!(e instanceof IOException) || e instanceof GroupMismatchException) {
+ // non-retryable exceptions
+ failAllAsyncRequests(request, e);
+ return;
+ }
+
+ final ClientRetryEvent event = pending.newClientRetryEvent(request, e);
+ final RetryPolicy.Action action = retryPolicy.handleAttemptFailure(event);
+ if (!action.shouldRetry()) {
+ failAllAsyncRequests(request, client.noMoreRetries(event));
+ return;
+ }
+
+ if (e instanceof NotLeaderException) {
+ client.handleNotLeaderException(request, (NotLeaderException) e, this::resetSlidingWindow);
+ } else {
+ client.handleIOException(request, (IOException) e, null, this::resetSlidingWindow);
+ }
+ final TimeDuration sleepTime = client.getEffectiveSleepTime(e, action.getSleepTime());
+ LOG.debug("schedule* retry with sleep {} for attempt #{} of {}, {}",
+ sleepTime, event.getAttemptCount(), request, retryPolicy);
+ final SlidingWindow.Client slidingWindow = getSlidingWindow(request);
+ client.getScheduler().onTimeout(sleepTime,
+ () -> slidingWindow.retry(pending, this::sendRequestWithRetry),
+ LOG, () -> "Failed* to retry " + pending);
+ }
+
void assertRequestSemaphore(int expectedAvailablePermits, int expectedQueueLength) {
Preconditions.assertSame(expectedAvailablePermits, requestSemaphore.availablePermits(), "availablePermits");
Preconditions.assertSame(expectedQueueLength, requestSemaphore.getQueueLength(), "queueLength");
diff --git a/ratis-client/src/main/java/org/apache/ratis/client/impl/OrderedStreamAsync.java b/ratis-client/src/main/java/org/apache/ratis/client/impl/OrderedStreamAsync.java
index 989c00cbbc..275755514f 100644
--- a/ratis-client/src/main/java/org/apache/ratis/client/impl/OrderedStreamAsync.java
+++ b/ratis-client/src/main/java/org/apache/ratis/client/impl/OrderedStreamAsync.java
@@ -21,12 +21,14 @@
import org.apache.ratis.client.RaftClientConfigKeys;
import org.apache.ratis.conf.RaftProperties;
import org.apache.ratis.datastream.impl.DataStreamPacketByteBuffer;
+import org.apache.ratis.datastream.impl.DataStreamRequestByteBuf;
import org.apache.ratis.datastream.impl.DataStreamRequestByteBuffer;
import org.apache.ratis.datastream.impl.DataStreamRequestFilePositionCount;
import org.apache.ratis.io.FilePositionCount;
import org.apache.ratis.protocol.DataStreamReply;
import org.apache.ratis.protocol.DataStreamRequest;
import org.apache.ratis.protocol.DataStreamRequestHeader;
+import org.apache.ratis.thirdparty.io.netty.buffer.ByteBuf;
import org.apache.ratis.util.IOUtils;
import org.apache.ratis.util.JavaUtils;
import org.apache.ratis.util.SlidingWindow;
@@ -56,6 +58,8 @@ static class DataStreamWindowRequest implements SlidingWindow.ClientSideRequest<
DataStreamRequest getDataStreamRequest() {
if (header.getDataLength() == 0) {
return new DataStreamRequestByteBuffer(header, DataStreamPacketByteBuffer.EMPTY_BYTE_BUFFER);
+ } else if (data instanceof ByteBuf) {
+ return new DataStreamRequestByteBuf(header, (ByteBuf)data);
} else if (data instanceof ByteBuffer) {
return new DataStreamRequestByteBuffer(header, (ByteBuffer)data);
} else if (data instanceof FilePositionCount) {
diff --git a/ratis-client/src/main/java/org/apache/ratis/client/impl/RaftClientImpl.java b/ratis-client/src/main/java/org/apache/ratis/client/impl/RaftClientImpl.java
index ec16763c2c..db789aef2f 100644
--- a/ratis-client/src/main/java/org/apache/ratis/client/impl/RaftClientImpl.java
+++ b/ratis-client/src/main/java/org/apache/ratis/client/impl/RaftClientImpl.java
@@ -44,11 +44,13 @@
import org.apache.ratis.thirdparty.com.google.common.cache.Cache;
import org.apache.ratis.thirdparty.com.google.common.cache.CacheBuilder;
import org.apache.ratis.util.CollectionUtils;
+import org.apache.ratis.util.IOUtils;
import org.apache.ratis.util.JavaUtils;
import org.apache.ratis.util.MemoizedSupplier;
import org.apache.ratis.util.Preconditions;
import org.apache.ratis.util.TimeDuration;
import org.apache.ratis.util.TimeoutExecutor;
+import org.apache.ratis.util.Timestamp;
import java.io.IOException;
import java.util.ArrayList;
@@ -65,6 +67,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
@@ -79,16 +82,18 @@ public final class RaftClientImpl implements RaftClient {
.build();
public abstract static class PendingClientRequest {
- private final long creationTimeInMs = System.currentTimeMillis();
+ private final Timestamp creationTime = Timestamp.currentTime();
private final CompletableFuture replyFuture = new CompletableFuture<>();
private final AtomicInteger attemptCount = new AtomicInteger();
- private final Map, Integer> exceptionCount = new ConcurrentHashMap<>();
+ private final Map, Integer> exceptionCounts = new ConcurrentHashMap<>();
public abstract RaftClientRequest newRequestImpl();
final RaftClientRequest newRequest() {
- attemptCount.incrementAndGet();
- return newRequestImpl();
+ final int attempt = attemptCount.incrementAndGet();
+ final RaftClientRequest request = newRequestImpl();
+ LOG.debug("attempt #{}, newRequest {}", attempt, request);
+ return request;
}
CompletableFuture getReplyFuture() {
@@ -99,19 +104,10 @@ public int getAttemptCount() {
return attemptCount.get();
}
- int incrementExceptionCount(Throwable t) {
- return t != null ? exceptionCount.compute(t.getClass(), (k, v) -> v != null ? v + 1 : 1) : 0;
- }
-
- public int getExceptionCount(Throwable t) {
- return t != null ? Optional.ofNullable(exceptionCount.get(t.getClass())).orElse(0) : 0;
- }
-
- public boolean isRequestTimeout(TimeDuration timeout) {
- if (timeout == null) {
- return false;
- }
- return System.currentTimeMillis() - creationTimeInMs > timeout.toLong(TimeUnit.MILLISECONDS);
+ public ClientRetryEvent newClientRetryEvent(RaftClientRequest request, Throwable throwable) {
+ final int exceptionCount = throwable == null? 0
+ : exceptionCounts.compute(throwable.getClass(), (k, v) -> v == null? 1: v+1);
+ return new ClientRetryEvent(getAttemptCount(), request, exceptionCount, throwable, creationTime);
}
}
@@ -176,6 +172,7 @@ private synchronized Set getAndReset() {
private final RaftGroupId groupId;
private final RetryPolicy retryPolicy;
+ @SuppressWarnings({"squid:S3077"}) // Suppress volatile for generic type
private volatile RaftPeerId leaderId;
/** The callIds of the replied requests. */
private final RepliedCallIds repliedCallIds;
@@ -194,6 +191,8 @@ private synchronized Set getAndReset() {
private final ConcurrentMap
leaderElectionManagement = new ConcurrentHashMap<>();
+ private final AtomicBoolean closed = new AtomicBoolean();
+
@SuppressWarnings("checkstyle:ParameterNumber")
RaftClientImpl(ClientId clientId, RaftGroup group, RaftPeerId leaderId, RaftPeer primaryDataStreamServer,
RaftClientRpc clientRpc, RetryPolicy retryPolicy, RaftProperties properties, Parameters parameters) {
@@ -344,11 +343,11 @@ public DataStreamApi getDataStreamApi() {
return dataStreamApi.get();
}
- Throwable noMoreRetries(ClientRetryEvent event) {
+ IOException noMoreRetries(ClientRetryEvent event) {
final int attemptCount = event.getAttemptCount();
final Throwable throwable = event.getCause();
if (attemptCount == 1 && throwable != null) {
- return throwable;
+ return IOUtils.asIOException(throwable);
}
return new RaftRetryFailureException(event.getRequest(), attemptCount, retryPolicy, throwable);
}
@@ -416,8 +415,7 @@ void handleIOException(RaftClientRequest request, IOException ioe) {
void handleIOException(RaftClientRequest request, IOException ioe,
RaftPeerId newLeader, Consumer handler) {
- LOG.debug("{}: suggested new leader: {}. Failed {} with {}",
- clientId, newLeader, request, ioe);
+ LOG.debug("{}: suggested new leader: {}. Failed {}", clientId, newLeader, request, ioe);
if (LOG.isTraceEnabled()) {
LOG.trace("Stack trace", new Throwable("TRACE"));
}
@@ -454,8 +452,17 @@ public RaftClientRpc getClientRpc() {
return clientRpc;
}
+ boolean isClosed() {
+ return closed.get();
+ }
+
@Override
public void close() throws IOException {
+ if (!closed.compareAndSet(false, true)) {
+ return;
+ }
+
+ LOG.debug("close {}", getId());
clientRpc.close();
if (dataStreamApi.isInitialized()) {
dataStreamApi.get().close();
diff --git a/ratis-client/src/main/java/org/apache/ratis/client/impl/SnapshotManagementImpl.java b/ratis-client/src/main/java/org/apache/ratis/client/impl/SnapshotManagementImpl.java
index 1762dc0e49..65c54d0f21 100644
--- a/ratis-client/src/main/java/org/apache/ratis/client/impl/SnapshotManagementImpl.java
+++ b/ratis-client/src/main/java/org/apache/ratis/client/impl/SnapshotManagementImpl.java
@@ -37,9 +37,10 @@ class SnapshotManagementImpl implements SnapshotManagementApi {
}
@Override
- public RaftClientReply create(long timeoutMs) throws IOException {
+ public RaftClientReply create(long creationGap, long timeoutMs) throws IOException {
final long callId = CallId.getAndIncrement();
return client.io().sendRequestWithRetry(() -> SnapshotManagementRequest.newCreate(client.getId(),
- Optional.ofNullable(server).orElseGet(client::getLeaderId), client.getGroupId(), callId, timeoutMs));
+ Optional.ofNullable(server).orElseGet(client::getLeaderId),
+ client.getGroupId(), callId, timeoutMs, creationGap));
}
}
diff --git a/ratis-client/src/main/java/org/apache/ratis/client/impl/UnorderedAsync.java b/ratis-client/src/main/java/org/apache/ratis/client/impl/UnorderedAsync.java
index 84b817b581..eccda4dbdd 100644
--- a/ratis-client/src/main/java/org/apache/ratis/client/impl/UnorderedAsync.java
+++ b/ratis-client/src/main/java/org/apache/ratis/client/impl/UnorderedAsync.java
@@ -22,6 +22,7 @@
import org.apache.ratis.protocol.ClientId;
import org.apache.ratis.protocol.Message;
import org.apache.ratis.protocol.RaftPeerId;
+import org.apache.ratis.protocol.exceptions.AlreadyClosedException;
import org.apache.ratis.protocol.exceptions.GroupMismatchException;
import org.apache.ratis.protocol.exceptions.NotLeaderException;
import org.apache.ratis.protocol.RaftClientReply;
@@ -89,11 +90,14 @@ static void sendRequestWithRetry(PendingClientRequest pending, RaftClientImpl cl
}
final Throwable cause = replyException != null ? replyException : e;
- pending.incrementExceptionCount(cause);
- final ClientRetryEvent event = new ClientRetryEvent(request, cause, pending);
+ if (client.isClosed()) {
+ f.completeExceptionally(new AlreadyClosedException(client + " is closed"));
+ return;
+ }
+
+ final ClientRetryEvent event = pending.newClientRetryEvent(request, cause);
RetryPolicy retryPolicy = client.getRetryPolicy();
final RetryPolicy.Action action = retryPolicy.handleAttemptFailure(event);
- TimeDuration sleepTime = client.getEffectiveSleepTime(cause, action.getSleepTime());
if (!action.shouldRetry()) {
f.completeExceptionally(client.noMoreRetries(event));
return;
@@ -124,7 +128,9 @@ static void sendRequestWithRetry(PendingClientRequest pending, RaftClientImpl cl
}
}
- LOG.debug("schedule retry for attempt #{}, policy={}, request={}", attemptCount, retryPolicy, request);
+ final TimeDuration sleepTime = client.getEffectiveSleepTime(cause, action.getSleepTime());
+ LOG.debug("schedule~ attempt #{} with sleep {} and policy {} for {}",
+ attemptCount, sleepTime, retryPolicy, request);
client.getScheduler().onTimeout(sleepTime,
() -> sendRequestWithRetry(pending, client), LOG, () -> clientId + ": Failed~ to retry " + request);
} catch (Exception ex) {
diff --git a/ratis-client/src/main/java/org/apache/ratis/client/retry/ClientRetryEvent.java b/ratis-client/src/main/java/org/apache/ratis/client/retry/ClientRetryEvent.java
index f0c38efb96..c6a8beb06f 100644
--- a/ratis-client/src/main/java/org/apache/ratis/client/retry/ClientRetryEvent.java
+++ b/ratis-client/src/main/java/org/apache/ratis/client/retry/ClientRetryEvent.java
@@ -17,12 +17,11 @@
*/
package org.apache.ratis.client.retry;
-import org.apache.ratis.client.impl.RaftClientImpl.PendingClientRequest;
import org.apache.ratis.protocol.RaftClientRequest;
import org.apache.ratis.retry.RetryPolicy;
-import org.apache.ratis.thirdparty.com.google.common.annotations.VisibleForTesting;
import org.apache.ratis.util.JavaUtils;
import org.apache.ratis.util.TimeDuration;
+import org.apache.ratis.util.Timestamp;
/** An {@link RetryPolicy.Event} specific to client request failure. */
public class ClientRetryEvent implements RetryPolicy.Event {
@@ -30,23 +29,15 @@ public class ClientRetryEvent implements RetryPolicy.Event {
private final int causeCount;
private final RaftClientRequest request;
private final Throwable cause;
- private PendingClientRequest pending;
+ private final Timestamp pendingRequestCreationTime;
- @VisibleForTesting
- public ClientRetryEvent(int attemptCount, RaftClientRequest request, Throwable cause) {
- this(attemptCount, request, attemptCount, cause);
- }
-
- public ClientRetryEvent(RaftClientRequest request, Throwable t, PendingClientRequest pending) {
- this(pending.getAttemptCount(), request, pending.getExceptionCount(t), t);
- this.pending = pending;
- }
-
- private ClientRetryEvent(int attemptCount, RaftClientRequest request, int causeCount, Throwable cause) {
+ public ClientRetryEvent(int attemptCount, RaftClientRequest request, int causeCount, Throwable cause,
+ Timestamp pendingRequestCreationTime) {
this.attemptCount = attemptCount;
this.causeCount = causeCount;
this.request = request;
this.cause = cause;
+ this.pendingRequestCreationTime = pendingRequestCreationTime;
}
@Override
@@ -69,7 +60,7 @@ public Throwable getCause() {
}
boolean isRequestTimeout(TimeDuration timeout) {
- return pending != null && pending.isRequestTimeout(timeout);
+ return timeout != null && pendingRequestCreationTime.elapsedTime().compareTo(timeout) >= 0;
}
@Override
@@ -77,6 +68,7 @@ public String toString() {
return JavaUtils.getClassSimpleName(getClass())
+ ":attempt=" + attemptCount
+ ",request=" + request
- + ",cause=" + cause;
+ + ",cause=" + cause
+ + ",causeCount=" + causeCount;
}
}
diff --git a/ratis-common/pom.xml b/ratis-common/pom.xml
index 9bb36bcce5..b5676f2c9c 100644
--- a/ratis-common/pom.xml
+++ b/ratis-common/pom.xml
@@ -17,7 +17,7 @@
ratisorg.apache.ratis
- 3.1.0-SNAPSHOT
+ 3.2.0-SNAPSHOTratis-common
@@ -69,12 +69,5 @@
junit-jupiter-paramstest
-
-
- com.github.spotbugs
- spotbugs-annotations
- provided
- true
-
diff --git a/ratis-common/src/main/java/org/apache/ratis/conf/ConfUtils.java b/ratis-common/src/main/java/org/apache/ratis/conf/ConfUtils.java
index c1fb9268c9..43706faabc 100644
--- a/ratis-common/src/main/java/org/apache/ratis/conf/ConfUtils.java
+++ b/ratis-common/src/main/java/org/apache/ratis/conf/ConfUtils.java
@@ -18,7 +18,6 @@
package org.apache.ratis.conf;
import org.apache.ratis.security.TlsConf;
-import org.apache.ratis.thirdparty.com.google.common.base.Objects;
import org.apache.ratis.util.NetUtils;
import org.apache.ratis.util.SizeInBytes;
import org.apache.ratis.util.TimeDuration;
@@ -33,6 +32,9 @@
import java.net.InetSocketAddress;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
@@ -41,10 +43,24 @@
public interface ConfUtils {
Logger LOG = LoggerFactory.getLogger(ConfUtils.class);
+ class Utils {
+ private static final ConcurrentMap CACHE = new ConcurrentHashMap<>();
+
+ private static boolean isNew(String key, T value) {
+ if (value == null) {
+ final Object previous = CACHE.remove(key);
+ return previous != null;
+ } else {
+ final Object previous = CACHE.put(key, value);
+ return !value.equals(previous);
+ }
+ }
+ }
+
static void logGet(String key, T value, T defaultValue, Consumer logger) {
- if (logger != null) {
+ if (logger != null && Utils.isNew(key, value)) {
logger.accept(String.format("%s = %s (%s)", key, value,
- Objects.equal(value, defaultValue)? "default": "custom"));
+ Objects.equals(value, defaultValue)? "default": "custom"));
}
}
diff --git a/ratis-netty/src/main/java/org/apache/ratis/netty/server/DataStreamRequestByteBuf.java b/ratis-common/src/main/java/org/apache/ratis/datastream/impl/DataStreamRequestByteBuf.java
similarity index 96%
rename from ratis-netty/src/main/java/org/apache/ratis/netty/server/DataStreamRequestByteBuf.java
rename to ratis-common/src/main/java/org/apache/ratis/datastream/impl/DataStreamRequestByteBuf.java
index 2542b1ec6f..1873bec9b4 100644
--- a/ratis-netty/src/main/java/org/apache/ratis/netty/server/DataStreamRequestByteBuf.java
+++ b/ratis-common/src/main/java/org/apache/ratis/datastream/impl/DataStreamRequestByteBuf.java
@@ -16,9 +16,8 @@
* limitations under the License.
*/
-package org.apache.ratis.netty.server;
+package org.apache.ratis.datastream.impl;
-import org.apache.ratis.datastream.impl.DataStreamPacketImpl;
import org.apache.ratis.io.WriteOption;
import org.apache.ratis.proto.RaftProtos.DataStreamPacketHeaderProto.Type;
import org.apache.ratis.protocol.ClientId;
diff --git a/ratis-common/src/main/java/org/apache/ratis/io/MD5Hash.java b/ratis-common/src/main/java/org/apache/ratis/io/MD5Hash.java
index e60bef9652..0d19feb938 100644
--- a/ratis-common/src/main/java/org/apache/ratis/io/MD5Hash.java
+++ b/ratis-common/src/main/java/org/apache/ratis/io/MD5Hash.java
@@ -30,13 +30,15 @@ public class MD5Hash {
public static final int MD5_LEN = 16;
private static final ThreadLocal DIGESTER_FACTORY =
- ThreadLocal.withInitial(() -> {
+ ThreadLocal.withInitial(MD5Hash::newDigester);
+
+ public static MessageDigest newDigester() {
try {
return MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
- throw new RuntimeException(e);
+ throw new IllegalStateException("Failed to create MessageDigest for MD5", e);
}
- });
+ }
private byte[] digest;
diff --git a/ratis-common/src/main/java/org/apache/ratis/protocol/ClientId.java b/ratis-common/src/main/java/org/apache/ratis/protocol/ClientId.java
index 4de615730c..09b77e6e81 100644
--- a/ratis-common/src/main/java/org/apache/ratis/protocol/ClientId.java
+++ b/ratis-common/src/main/java/org/apache/ratis/protocol/ClientId.java
@@ -18,6 +18,7 @@
package org.apache.ratis.protocol;
import org.apache.ratis.thirdparty.com.google.protobuf.ByteString;
+import org.apache.ratis.util.WeakValueCache;
import java.util.UUID;
@@ -26,13 +27,17 @@
* to correctly identify retry requests from the same client.
*/
public final class ClientId extends RaftId {
- private static final Factory FACTORY = new Factory() {
+ private static final Factory FACTORY = new Factory(ClientId.class) {
@Override
ClientId newInstance(UUID uuid) {
return new ClientId(uuid);
}
};
+ static WeakValueCache getCache() {
+ return FACTORY.getCache();
+ }
+
public static ClientId emptyClientId() {
return FACTORY.emptyId();
}
diff --git a/ratis-common/src/main/java/org/apache/ratis/protocol/GroupInfoReply.java b/ratis-common/src/main/java/org/apache/ratis/protocol/GroupInfoReply.java
index 632fa65293..bfac81a2b0 100644
--- a/ratis-common/src/main/java/org/apache/ratis/protocol/GroupInfoReply.java
+++ b/ratis-common/src/main/java/org/apache/ratis/protocol/GroupInfoReply.java
@@ -19,6 +19,7 @@
import org.apache.ratis.proto.RaftProtos.RaftConfigurationProto;
import org.apache.ratis.proto.RaftProtos.CommitInfoProto;
+import org.apache.ratis.proto.RaftProtos.LogInfoProto;
import org.apache.ratis.proto.RaftProtos.RoleInfoProto;
import java.util.Collection;
@@ -33,25 +34,27 @@ public class GroupInfoReply extends RaftClientReply {
private final RoleInfoProto roleInfoProto;
private final boolean isRaftStorageHealthy;
private final RaftConfigurationProto conf;
+ private final LogInfoProto logInfoProto;
public GroupInfoReply(RaftClientRequest request, Collection commitInfos,
RaftGroup group, RoleInfoProto roleInfoProto, boolean isRaftStorageHealthy,
- RaftConfigurationProto conf) {
+ RaftConfigurationProto conf, LogInfoProto logInfoProto) {
this(request.getClientId(), request.getServerId(), request.getRaftGroupId(),
request.getCallId(), commitInfos,
- group, roleInfoProto, isRaftStorageHealthy, conf);
+ group, roleInfoProto, isRaftStorageHealthy, conf, logInfoProto);
}
@SuppressWarnings("parameternumber")
public GroupInfoReply(ClientId clientId, RaftPeerId serverId, RaftGroupId groupId, long callId,
Collection commitInfos,
RaftGroup group, RoleInfoProto roleInfoProto, boolean isRaftStorageHealthy,
- RaftConfigurationProto conf) {
+ RaftConfigurationProto conf, LogInfoProto logInfoProto) {
super(clientId, serverId, groupId, callId, true, null, null, 0L, commitInfos);
this.group = group;
this.roleInfoProto = roleInfoProto;
this.isRaftStorageHealthy = isRaftStorageHealthy;
this.conf = conf;
+ this.logInfoProto = logInfoProto;
}
public RaftGroup getGroup() {
@@ -69,4 +72,8 @@ public boolean isRaftStorageHealthy() {
public Optional getConf() {
return Optional.ofNullable(conf);
}
+
+ public LogInfoProto getLogInfoProto() {
+ return logInfoProto;
+ }
}
diff --git a/ratis-common/src/main/java/org/apache/ratis/protocol/RaftClientAsynchronousProtocol.java b/ratis-common/src/main/java/org/apache/ratis/protocol/RaftClientAsynchronousProtocol.java
index 1985bbe667..222ccff057 100644
--- a/ratis-common/src/main/java/org/apache/ratis/protocol/RaftClientAsynchronousProtocol.java
+++ b/ratis-common/src/main/java/org/apache/ratis/protocol/RaftClientAsynchronousProtocol.java
@@ -46,11 +46,11 @@ default CompletableFuture submitClientRequestAsync(
ReferenceCountedObject requestRef) {
try {
// for backward compatibility
- return submitClientRequestAsync(requestRef.retain())
- .whenComplete((r, e) -> requestRef.release());
+ return submitClientRequestAsync(requestRef.retain());
} catch (Exception e) {
- requestRef.release();
return JavaUtils.completeExceptionally(e);
+ } finally {
+ requestRef.release();
}
}
}
\ No newline at end of file
diff --git a/ratis-common/src/main/java/org/apache/ratis/protocol/RaftClientMessage.java b/ratis-common/src/main/java/org/apache/ratis/protocol/RaftClientMessage.java
index 8d3104a73d..92ae77ce21 100644
--- a/ratis-common/src/main/java/org/apache/ratis/protocol/RaftClientMessage.java
+++ b/ratis-common/src/main/java/org/apache/ratis/protocol/RaftClientMessage.java
@@ -18,7 +18,8 @@
package org.apache.ratis.protocol;
import org.apache.ratis.util.JavaUtils;
-import org.apache.ratis.util.Preconditions;
+
+import java.util.Objects;
public abstract class RaftClientMessage implements RaftRpcMessage {
private final ClientId clientId;
@@ -27,9 +28,9 @@ public abstract class RaftClientMessage implements RaftRpcMessage {
private final long callId;
RaftClientMessage(ClientId clientId, RaftPeerId serverId, RaftGroupId groupId, long callId) {
- this.clientId = Preconditions.assertNotNull(clientId, "clientId");
- this.serverId = Preconditions.assertNotNull(serverId, "serverId");
- this.groupId = Preconditions.assertNotNull(groupId, "groupId");
+ this.clientId = Objects.requireNonNull(clientId, "clientId == null");
+ this.serverId = Objects.requireNonNull(serverId, "serverId == null");
+ this.groupId = Objects.requireNonNull(groupId, "groupId == null");
this.callId = callId;
}
diff --git a/ratis-common/src/main/java/org/apache/ratis/protocol/RaftClientReply.java b/ratis-common/src/main/java/org/apache/ratis/protocol/RaftClientReply.java
index 64d667955d..8d9980a532 100644
--- a/ratis-common/src/main/java/org/apache/ratis/protocol/RaftClientReply.java
+++ b/ratis-common/src/main/java/org/apache/ratis/protocol/RaftClientReply.java
@@ -187,11 +187,6 @@ public Collection getCommitInfos() {
return commitInfos;
}
- @Override
- public final boolean isRequest() {
- return false;
- }
-
public long getLogIndex() {
return logIndex;
}
diff --git a/ratis-common/src/main/java/org/apache/ratis/protocol/RaftClientRequest.java b/ratis-common/src/main/java/org/apache/ratis/protocol/RaftClientRequest.java
index ed41f1ea2c..18c157130b 100644
--- a/ratis-common/src/main/java/org/apache/ratis/protocol/RaftClientRequest.java
+++ b/ratis-common/src/main/java/org/apache/ratis/protocol/RaftClientRequest.java
@@ -474,7 +474,13 @@ public long getTimeoutMs() {
@Override
public String toString() {
- return super.toString() + ", seq=" + ProtoUtils.toString(slidingWindowEntry) + ", "
- + type + ", " + getMessage();
+ return toStringShort() + ", " + getMessage();
+ }
+
+ /**
+ * @return a short string which does not include {@link #message}.
+ */
+ public String toStringShort() {
+ return super.toString() + ", seq=" + ProtoUtils.toString(slidingWindowEntry) + ", " + type;
}
}
diff --git a/ratis-common/src/main/java/org/apache/ratis/protocol/RaftGroup.java b/ratis-common/src/main/java/org/apache/ratis/protocol/RaftGroup.java
index 0612a16f9d..5cf970afc3 100644
--- a/ratis-common/src/main/java/org/apache/ratis/protocol/RaftGroup.java
+++ b/ratis-common/src/main/java/org/apache/ratis/protocol/RaftGroup.java
@@ -19,7 +19,13 @@
import org.apache.ratis.util.Preconditions;
-import java.util.*;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
/**
* Description of a raft group, which has a unique {@link RaftGroupId} and a collection of {@link RaftPeer}.
diff --git a/ratis-common/src/main/java/org/apache/ratis/protocol/RaftGroupId.java b/ratis-common/src/main/java/org/apache/ratis/protocol/RaftGroupId.java
index 9caedf7574..af40746918 100644
--- a/ratis-common/src/main/java/org/apache/ratis/protocol/RaftGroupId.java
+++ b/ratis-common/src/main/java/org/apache/ratis/protocol/RaftGroupId.java
@@ -18,6 +18,7 @@
package org.apache.ratis.protocol;
import org.apache.ratis.thirdparty.com.google.protobuf.ByteString;
+import org.apache.ratis.util.WeakValueCache;
import java.util.UUID;
@@ -27,13 +28,17 @@
* This is a value-based class.
*/
public final class RaftGroupId extends RaftId {
- private static final Factory FACTORY = new Factory() {
+ private static final Factory FACTORY = new Factory(RaftGroupId.class) {
@Override
RaftGroupId newInstance(UUID uuid) {
return new RaftGroupId(uuid);
}
};
+ static WeakValueCache getCache() {
+ return FACTORY.getCache();
+ }
+
public static RaftGroupId emptyGroupId() {
return FACTORY.emptyId();
}
diff --git a/ratis-common/src/main/java/org/apache/ratis/protocol/RaftId.java b/ratis-common/src/main/java/org/apache/ratis/protocol/RaftId.java
index 9c2a83ffa3..d089c7d3cb 100644
--- a/ratis-common/src/main/java/org/apache/ratis/protocol/RaftId.java
+++ b/ratis-common/src/main/java/org/apache/ratis/protocol/RaftId.java
@@ -17,17 +17,15 @@
*/
package org.apache.ratis.protocol;
-import org.apache.ratis.thirdparty.com.google.common.cache.Cache;
-import org.apache.ratis.thirdparty.com.google.common.cache.CacheBuilder;
import org.apache.ratis.thirdparty.com.google.protobuf.ByteString;
import org.apache.ratis.thirdparty.com.google.protobuf.UnsafeByteOperations;
import org.apache.ratis.util.JavaUtils;
import org.apache.ratis.util.Preconditions;
+import org.apache.ratis.util.WeakValueCache;
import java.nio.ByteBuffer;
import java.util.Objects;
import java.util.UUID;
-import java.util.concurrent.ExecutionException;
import java.util.function.Supplier;
/** Unique identifier implemented using {@link UUID}. */
@@ -53,18 +51,20 @@ static ByteString toByteString(UUID uuid) {
}
abstract static class Factory {
- private final Cache cache = CacheBuilder.newBuilder()
- .weakValues()
- .build();
+ private final WeakValueCache cache;
+
+ Factory(Class clazz) {
+ this.cache = new WeakValueCache<>(clazz.getSimpleName() + "_UUID", this::newInstance);
+ }
abstract ID newInstance(UUID uuid);
+ WeakValueCache getCache() {
+ return cache;
+ }
+
final ID valueOf(UUID uuid) {
- try {
- return cache.get(uuid, () -> newInstance(uuid));
- } catch (ExecutionException e) {
- throw new IllegalStateException("Failed to valueOf(" + uuid + ")", e);
- }
+ return cache.getOrCreate(uuid);
}
final ID valueOf(ByteString bytes) {
@@ -85,7 +85,7 @@ ID randomId() {
private final Supplier uuidString;
RaftId(UUID uuid) {
- this.uuid = Preconditions.assertNotNull(uuid, "uuid");
+ this.uuid = Objects.requireNonNull(uuid, "uuid == null");
this.uuidBytes = JavaUtils.memoize(() -> toByteString(uuid));
this.uuidString = JavaUtils.memoize(() -> createUuidString(uuid));
Preconditions.assertTrue(ZERO_UUID == uuid || !uuid.equals(ZERO_UUID),
diff --git a/ratis-common/src/main/java/org/apache/ratis/protocol/RaftPeerId.java b/ratis-common/src/main/java/org/apache/ratis/protocol/RaftPeerId.java
index 8098039d1b..8db842d734 100644
--- a/ratis-common/src/main/java/org/apache/ratis/protocol/RaftPeerId.java
+++ b/ratis-common/src/main/java/org/apache/ratis/protocol/RaftPeerId.java
@@ -17,7 +17,6 @@
*/
package org.apache.ratis.protocol;
-import javax.annotation.concurrent.Immutable;
import org.apache.ratis.proto.RaftProtos.RaftPeerIdProto;
import org.apache.ratis.thirdparty.com.google.protobuf.ByteString;
import org.apache.ratis.util.JavaUtils;
@@ -34,7 +33,6 @@
*
* This is a value-based class.
*/
-@Immutable
public final class RaftPeerId {
private static final Map BYTE_STRING_MAP = new ConcurrentHashMap<>();
private static final Map STRING_MAP = new ConcurrentHashMap<>();
diff --git a/ratis-common/src/main/java/org/apache/ratis/protocol/RaftRpcMessage.java b/ratis-common/src/main/java/org/apache/ratis/protocol/RaftRpcMessage.java
index 70727af2e2..7c497129d7 100644
--- a/ratis-common/src/main/java/org/apache/ratis/protocol/RaftRpcMessage.java
+++ b/ratis-common/src/main/java/org/apache/ratis/protocol/RaftRpcMessage.java
@@ -19,7 +19,9 @@
public interface RaftRpcMessage {
- boolean isRequest();
+ default boolean isRequest() {
+ return false;
+ }
String getRequestorId();
diff --git a/ratis-common/src/main/java/org/apache/ratis/protocol/SnapshotManagementRequest.java b/ratis-common/src/main/java/org/apache/ratis/protocol/SnapshotManagementRequest.java
index 2ea2059b51..269fdfc591 100644
--- a/ratis-common/src/main/java/org/apache/ratis/protocol/SnapshotManagementRequest.java
+++ b/ratis-common/src/main/java/org/apache/ratis/protocol/SnapshotManagementRequest.java
@@ -24,7 +24,16 @@ public final class SnapshotManagementRequest extends RaftClientRequest {
public abstract static class Op {
}
- public static class Create extends Op {
+
+ public static final class Create extends Op {
+ private final long creationGap;
+ private Create(long creationGap) {
+ this.creationGap = creationGap;
+ }
+
+ public long getCreationGap() {
+ return creationGap;
+ }
@Override
public String toString() {
@@ -35,8 +44,13 @@ public String toString() {
public static SnapshotManagementRequest newCreate(ClientId clientId,
RaftPeerId serverId, RaftGroupId groupId, long callId, long timeoutMs) {
+ return newCreate(clientId, serverId, groupId, callId, timeoutMs, 0);
+ }
+
+ public static SnapshotManagementRequest newCreate(ClientId clientId,
+ RaftPeerId serverId, RaftGroupId groupId, long callId, long timeoutMs, long creationGap) {
return new SnapshotManagementRequest(clientId,
- serverId, groupId, callId, timeoutMs,new SnapshotManagementRequest.Create());
+ serverId, groupId, callId, timeoutMs, new SnapshotManagementRequest.Create(creationGap));
}
private final Op op;
diff --git a/ratis-common/src/main/java/org/apache/ratis/protocol/exceptions/NotLeaderException.java b/ratis-common/src/main/java/org/apache/ratis/protocol/exceptions/NotLeaderException.java
index 8d5c2cb4e9..c7dc6a3961 100644
--- a/ratis-common/src/main/java/org/apache/ratis/protocol/exceptions/NotLeaderException.java
+++ b/ratis-common/src/main/java/org/apache/ratis/protocol/exceptions/NotLeaderException.java
@@ -30,7 +30,8 @@ public class NotLeaderException extends RaftException {
private final Collection peers;
public NotLeaderException(RaftGroupMemberId memberId, RaftPeer suggestedLeader, Collection peers) {
- super("Server " + memberId + " is not the leader" + (suggestedLeader != null? " " + suggestedLeader: ""));
+ super("Server " + memberId + " is not the leader" +
+ (suggestedLeader != null ? ", suggested leader is: " + suggestedLeader : ""));
this.suggestedLeader = suggestedLeader;
this.peers = peers != null? Collections.unmodifiableCollection(peers): Collections.emptyList();
Preconditions.assertUnique(this.peers);
diff --git a/ratis-common/src/main/java/org/apache/ratis/protocol/exceptions/NotReplicatedException.java b/ratis-common/src/main/java/org/apache/ratis/protocol/exceptions/NotReplicatedException.java
index 5f48654eec..37ff816245 100644
--- a/ratis-common/src/main/java/org/apache/ratis/protocol/exceptions/NotReplicatedException.java
+++ b/ratis-common/src/main/java/org/apache/ratis/protocol/exceptions/NotReplicatedException.java
@@ -17,12 +17,17 @@
*/
package org.apache.ratis.protocol.exceptions;
+import org.apache.ratis.proto.RaftProtos.CommitInfoProto;
import org.apache.ratis.proto.RaftProtos.ReplicationLevel;
+import java.util.Collection;
+
public class NotReplicatedException extends RaftException {
private final long callId;
private final ReplicationLevel requiredReplication;
private final long logIndex;
+ /** This is only populated on client-side since RaftClientReply already has commitInfos */
+ private Collection commitInfos;
public NotReplicatedException(long callId, ReplicationLevel requiredReplication, long logIndex) {
super("Request with call Id " + callId + " and log index " + logIndex
@@ -32,6 +37,12 @@ public NotReplicatedException(long callId, ReplicationLevel requiredReplication,
this.logIndex = logIndex;
}
+ public NotReplicatedException(long callId, ReplicationLevel requiredReplication, long logIndex,
+ Collection commitInfos) {
+ this(callId, requiredReplication, logIndex);
+ this.commitInfos = commitInfos;
+ }
+
public long getCallId() {
return callId;
}
@@ -43,4 +54,8 @@ public ReplicationLevel getRequiredReplication() {
public long getLogIndex() {
return logIndex;
}
+
+ public Collection getCommitInfos() {
+ return commitInfos;
+ }
}
diff --git a/ratis-common/src/main/java/org/apache/ratis/retry/ExponentialBackoffRetry.java b/ratis-common/src/main/java/org/apache/ratis/retry/ExponentialBackoffRetry.java
index bb2f50e43a..d506c85c80 100644
--- a/ratis-common/src/main/java/org/apache/ratis/retry/ExponentialBackoffRetry.java
+++ b/ratis-common/src/main/java/org/apache/ratis/retry/ExponentialBackoffRetry.java
@@ -17,9 +17,9 @@
*/
package org.apache.ratis.retry;
-import org.apache.ratis.util.Preconditions;
import org.apache.ratis.util.TimeDuration;
+import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;
/**
@@ -56,7 +56,7 @@ public Builder setMaxSleepTime(TimeDuration maxSleepTime) {
}
public ExponentialBackoffRetry build() {
- Preconditions.assertNotNull(baseSleepTime, "baseSleepTime");
+ Objects.requireNonNull(baseSleepTime, "baseSleepTime == null");
return new ExponentialBackoffRetry(baseSleepTime, maxSleepTime,
maxAttempts);
}
diff --git a/ratis-common/src/main/java/org/apache/ratis/util/AtomicFileOutputStream.java b/ratis-common/src/main/java/org/apache/ratis/util/AtomicFileOutputStream.java
index 530eb383c7..b54427066f 100644
--- a/ratis-common/src/main/java/org/apache/ratis/util/AtomicFileOutputStream.java
+++ b/ratis-common/src/main/java/org/apache/ratis/util/AtomicFileOutputStream.java
@@ -17,6 +17,10 @@
*/
package org.apache.ratis.util;
+import static java.nio.file.StandardOpenOption.CREATE;
+import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
+import static java.nio.file.StandardOpenOption.WRITE;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -24,7 +28,6 @@
import java.io.FilterOutputStream;
import java.io.IOException;
import java.nio.file.StandardCopyOption;
-import java.nio.file.StandardOpenOption;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -60,7 +63,7 @@ public AtomicFileOutputStream(File outFile) throws IOException {
}
public AtomicFileOutputStream(File outFile, File tmpFile) throws IOException {
- super(FileUtils.newOutputStreamForceAtClose(tmpFile, StandardOpenOption.CREATE, StandardOpenOption.WRITE));
+ super(FileUtils.newOutputStreamForceAtClose(tmpFile, CREATE, TRUNCATE_EXISTING, WRITE));
this.outFile = outFile.getAbsoluteFile();
this.tmpFile = tmpFile.getAbsoluteFile();
}
diff --git a/ratis-common/src/main/java/org/apache/ratis/util/AutoCloseableLock.java b/ratis-common/src/main/java/org/apache/ratis/util/AutoCloseableLock.java
index 8a5409bafe..9581e925a5 100644
--- a/ratis-common/src/main/java/org/apache/ratis/util/AutoCloseableLock.java
+++ b/ratis-common/src/main/java/org/apache/ratis/util/AutoCloseableLock.java
@@ -17,6 +17,7 @@
*/
package org.apache.ratis.util;
+import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
@@ -45,6 +46,13 @@ public static AutoCloseableLock acquire(final Lock lock, Runnable preUnlock) {
return new AutoCloseableLock(lock, preUnlock);
}
+ public static AutoCloseableLock tryAcquire(final Lock lock, Runnable preUnlock, TimeDuration timeout)
+ throws InterruptedException {
+ Objects.requireNonNull(timeout, "timeout == null");
+ final boolean locked = lock.tryLock(timeout.getDuration(), timeout.getUnit());
+ return locked? new AutoCloseableLock(lock, preUnlock): null;
+ }
+
private final Lock underlying;
private final AtomicBoolean closed = new AtomicBoolean(false);
private final Runnable preUnlock;
diff --git a/ratis-common/src/main/java/org/apache/ratis/util/BatchLogger.java b/ratis-common/src/main/java/org/apache/ratis/util/BatchLogger.java
index 9ccd66ad71..b57bed704c 100644
--- a/ratis-common/src/main/java/org/apache/ratis/util/BatchLogger.java
+++ b/ratis-common/src/main/java/org/apache/ratis/util/BatchLogger.java
@@ -35,13 +35,19 @@ public final class BatchLogger {
private BatchLogger() {
}
- public interface Key {}
+ public interface Key {
+ TimeDuration DEFAULT_DURATION = TimeDuration.valueOf(5, TimeUnit.SECONDS);
+
+ default TimeDuration getBatchDuration() {
+ return DEFAULT_DURATION;
+ }
+ }
private static final class UniqueId {
private final Key key;
- private final String name;
+ private final Object name;
- private UniqueId(Key key, String name) {
+ private UniqueId(Key key, Object name) {
this.key = Objects.requireNonNull(key, "key == null");
this.name = name;
}
@@ -93,11 +99,15 @@ private synchronized boolean tryStartBatch(Consumer op) {
private static final TimeoutExecutor SCHEDULER = TimeoutExecutor.getInstance();
private static final ConcurrentMap LOG_CACHE = new ConcurrentHashMap<>();
- public static void warn(Key key, String name, Consumer op, TimeDuration batchDuration) {
- warn(key, name, op, batchDuration, true);
+ public static void print(Key key, Object name, Consumer op) {
+ print(key, name, op, key.getBatchDuration(), true);
+ }
+
+ public static void print(Key key, Object name, Consumer op, TimeDuration batchDuration) {
+ print(key, name, op, batchDuration, true);
}
- public static void warn(Key key, String name, Consumer op, TimeDuration batchDuration, boolean shouldBatch) {
+ public static void print(Key key, Object name, Consumer op, TimeDuration batchDuration, boolean shouldBatch) {
if (!shouldBatch || batchDuration.isNonPositive()) {
op.accept("");
return;
diff --git a/ratis-common/src/main/java/org/apache/ratis/util/BiWeakValueCache.java b/ratis-common/src/main/java/org/apache/ratis/util/BiWeakValueCache.java
new file mode 100644
index 0000000000..d7eaf5744a
--- /dev/null
+++ b/ratis-common/src/main/java/org/apache/ratis/util/BiWeakValueCache.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ratis.util;
+
+import org.apache.ratis.thirdparty.com.google.common.collect.MapMaker;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+
+/**
+ * Weak Value Cache: ({@link OUTER}, {@link INNER}) -> {@link T}.
+ *
+ * Note that the cached values are weakly referenced.
+ * A cached value could be garage-collected (i.e. evicted from the cache)
+ * when there are no external (strong) references.
+ *
+ * For key types with a component, use {@link WeakValueCache}.
+ *
+ * @param the type of the outer keys.
+ * @param the type of the inner keys.
+ * @param the type to be cached.
+ */
+public final class BiWeakValueCache {
+ static ConcurrentMap newMap() {
+ return new MapMaker().weakValues().makeMap();
+ }
+
+ private final String outerName;
+ private final String innerName;
+ private final String name;
+
+ /** For constructing {@link T} values from ({@link OUTER}, {@link INNER}) keys. */
+ private final BiFunction constructor;
+ /** Count the number of {@link T} values constructed. */
+ private final AtomicInteger valueCount = new AtomicInteger(0);
+
+ /**
+ * Actual map {@link OUTER} -> ({@link INNER} -> {@link T})
+ * for the logical view ({@link OUTER}, {@link INNER}) -> {@link T}.
+ */
+ private final ConcurrentMap> map = new ConcurrentHashMap<>();
+
+ /**
+ * Create a cache for mapping ({@link OUTER}, {@link INNER}) keys to {@link T} values.
+ *
+ * @param outerName the name of the outer keys.
+ * @param innerName the name of the inner keys.
+ * @param constructor for constructing {@link T} values.
+ */
+ public BiWeakValueCache(String outerName, String innerName, BiFunction constructor) {
+ this.outerName = outerName;
+ this.innerName = innerName;
+ this.name = "(" + outerName + ", " + innerName + ")-cache";
+ this.constructor = constructor;
+ }
+
+ private T construct(OUTER outer, INNER inner) {
+ final T constructed = constructor.apply(outer, inner);
+ Objects.requireNonNull(constructed, "constructed == null");
+ valueCount.incrementAndGet();
+ return constructed;
+ }
+
+ /**
+ * If the key ({@link OUTER}, {@link INNER}) is in the cache, return the cached values.
+ * Otherwise, create a new value and then return it.
+ */
+ public T getOrCreate(OUTER outer, INNER inner) {
+ Objects.requireNonNull(outer, () -> outerName + " (outer) == null");
+ Objects.requireNonNull(inner, () -> innerName + " (inner) == null");
+ final ConcurrentMap innerMap = map.computeIfAbsent(outer, k -> newMap());
+ final T computed = innerMap.computeIfAbsent(inner, i -> construct(outer, i));
+ if ((valueCount.get() & 0xFFF) == 0) {
+ cleanupEmptyInnerMaps(); // cleanup empty maps once in a while
+ }
+ return computed;
+ }
+
+ /** @return the value count for the given outer key. */
+ int count(OUTER outer) {
+ final ConcurrentMap innerMap = map.get(outer);
+ if (innerMap == null) {
+ return 0;
+ }
+
+ // size() may return incorrect result; see Guava MapMaker javadoc
+ int n = 0;
+ for (INNER ignored : innerMap.keySet()) {
+ n++;
+ }
+ return n;
+ }
+
+ void cleanupEmptyInnerMaps() {
+ // isEmpty() may return incorrect result; see Guava MapMaker javadoc
+ map.values().removeIf(e -> !e.entrySet().iterator().hasNext());
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ /** The cache content for debugging. */
+ int dump(Consumer out) {
+ out.accept(name + ":\n");
+ int emptyCount = 0;
+ for (Map.Entry> entry : map.entrySet()) {
+ final OUTER outer = entry.getKey();
+ final ConcurrentMap innerMap = entry.getValue();
+ final int count = count(outer);
+ if (count == 0) {
+ emptyCount++;
+ }
+
+ out.accept(" " + outerName + ":" + outer);
+ out.accept(", " + innerName + ":" + innerMap.keySet());
+ out.accept(", count=" + count);
+ out.accept(", size=" + innerMap.size());
+ out.accept("\n");
+ }
+ out.accept(" emptyCount=" + emptyCount);
+ out.accept("\n");
+ return emptyCount;
+ }
+}
diff --git a/ratis-common/src/main/java/org/apache/ratis/util/CodeInjectionForTesting.java b/ratis-common/src/main/java/org/apache/ratis/util/CodeInjectionForTesting.java
index a7d36ac0eb..112f6bd250 100644
--- a/ratis-common/src/main/java/org/apache/ratis/util/CodeInjectionForTesting.java
+++ b/ratis-common/src/main/java/org/apache/ratis/util/CodeInjectionForTesting.java
@@ -68,4 +68,9 @@ public static boolean execute(String injectionPoint, Object localId,
}
return code.execute(localId, remoteId, args);
}
+
+ /** Remove an injection point. */
+ public static void remove(String injectionPoint) {
+ INJECTION_POINTS.remove(injectionPoint);
+ }
}
diff --git a/ratis-common/src/main/java/org/apache/ratis/util/CollectionUtils.java b/ratis-common/src/main/java/org/apache/ratis/util/CollectionUtils.java
index 11f484608a..2615c2659c 100644
--- a/ratis-common/src/main/java/org/apache/ratis/util/CollectionUtils.java
+++ b/ratis-common/src/main/java/org/apache/ratis/util/CollectionUtils.java
@@ -17,7 +17,14 @@
*/
package org.apache.ratis.util;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.BiFunction;
diff --git a/ratis-common/src/main/java/org/apache/ratis/util/DataBlockingQueue.java b/ratis-common/src/main/java/org/apache/ratis/util/DataBlockingQueue.java
index 842b8f1549..fb0f0715c5 100644
--- a/ratis-common/src/main/java/org/apache/ratis/util/DataBlockingQueue.java
+++ b/ratis-common/src/main/java/org/apache/ratis/util/DataBlockingQueue.java
@@ -29,6 +29,7 @@
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.Consumer;
import java.util.function.ToLongFunction;
/**
@@ -46,6 +47,8 @@ public class DataBlockingQueue extends DataQueue {
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
+ private boolean closed = false;
+
public DataBlockingQueue(Object name, SizeInBytes byteLimit, int elementLimit, ToLongFunction getNumBytes) {
super(name, byteLimit, elementLimit, getNumBytes);
}
@@ -72,10 +75,34 @@ public void clear() {
}
}
+ /** Apply the given handler to each element and then {@link #clear()}. */
+ public void clear(Consumer handler) {
+ try(AutoCloseableLock auto = AutoCloseableLock.acquire(lock)) {
+ for(E e : this) {
+ handler.accept(e);
+ }
+ super.clear();
+ }
+ }
+
+ /**
+ * Close this queue to stop accepting new elements, i.e. the offer(…) methods always return false.
+ * Note that closing the queue will not clear the existing elements.
+ * The existing elements can be peeked, polled or cleared after close.
+ */
+ public void close() {
+ try(AutoCloseableLock ignored = AutoCloseableLock.acquire(lock)) {
+ closed = true;
+ }
+ }
+
@Override
public boolean offer(E element) {
Objects.requireNonNull(element, "element == null");
try(AutoCloseableLock auto = AutoCloseableLock.acquire(lock)) {
+ if (closed) {
+ return false;
+ }
if (super.offer(element)) {
notEmpty.signal();
return true;
@@ -95,6 +122,9 @@ public boolean offer(E element, TimeDuration timeout) throws InterruptedExceptio
long nanos = timeout.toLong(TimeUnit.NANOSECONDS);
try(AutoCloseableLock auto = AutoCloseableLock.acquire(lock)) {
for(;;) {
+ if (closed) {
+ return false;
+ }
if (super.offer(element)) {
notEmpty.signal();
return true;
@@ -162,4 +192,11 @@ public List pollList(long timeoutM
return results;
}
}
+
+ @Override
+ public E peek() {
+ try(AutoCloseableLock auto = AutoCloseableLock.acquire(lock)) {
+ return super.peek();
+ }
+ }
}
diff --git a/ratis-common/src/main/java/org/apache/ratis/util/DataQueue.java b/ratis-common/src/main/java/org/apache/ratis/util/DataQueue.java
index 3db06f56e6..38762caa17 100644
--- a/ratis-common/src/main/java/org/apache/ratis/util/DataQueue.java
+++ b/ratis-common/src/main/java/org/apache/ratis/util/DataQueue.java
@@ -154,6 +154,11 @@ public E poll() {
return polled;
}
+ /** Peek the head element from this queue. */
+ public E peek() {
+ return q.peek();
+ }
+
/** The same as {@link java.util.Collection#remove(Object)}. */
public boolean remove(E e) {
final boolean removed = q.remove(e);
diff --git a/ratis-common/src/main/java/org/apache/ratis/util/FileUtils.java b/ratis-common/src/main/java/org/apache/ratis/util/FileUtils.java
index d5141e9171..4b9d9e3b28 100644
--- a/ratis-common/src/main/java/org/apache/ratis/util/FileUtils.java
+++ b/ratis-common/src/main/java/org/apache/ratis/util/FileUtils.java
@@ -27,7 +27,18 @@
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
-import java.nio.file.*;
+import java.nio.file.AtomicMoveNotSupportedException;
+import java.nio.file.CopyOption;
+import java.nio.file.FileAlreadyExistsException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Arrays;
import java.util.List;
@@ -201,8 +212,10 @@ static File move(File src, String suffix) throws IOException {
}
/** The same as passing f.toPath() to {@link #delete(Path)}. */
- static void deleteFile(File f) throws IOException {
- delete(f.toPath());
+ static Path deleteFile(File f) throws IOException {
+ final Path path = f.toPath();
+ delete(path);
+ return path;
}
/**
diff --git a/ratis-common/src/main/java/org/apache/ratis/util/JavaUtils.java b/ratis-common/src/main/java/org/apache/ratis/util/JavaUtils.java
index 00725903a7..958e88cee5 100644
--- a/ratis-common/src/main/java/org/apache/ratis/util/JavaUtils.java
+++ b/ratis-common/src/main/java/org/apache/ratis/util/JavaUtils.java
@@ -148,7 +148,7 @@ static T doPrivileged(Supplier action, Supplier name) {
* otherwise, return system property value.
*/
static String getSystemProperty(final String key) {
- Preconditions.assertNotNull(key, "key");
+ Objects.requireNonNull(key, "key == null");
Preconditions.assertTrue(!key.isEmpty(), "key is empty.");
return doPrivileged(() -> System.getProperty(key), () -> "get system property " + key);
}
@@ -166,9 +166,9 @@ static String getEnv(String variable) {
* When there is a {@link SecurityException}, this becomes a NOOP.
*/
static void setSystemProperty(String key, String value) {
- Preconditions.assertNotNull(key, "key");
+ Objects.requireNonNull(key, "key == null");
Preconditions.assertTrue(!key.isEmpty(), "key is empty.");
- Preconditions.assertNotNull(value, "value");
+ Objects.requireNonNull(value, "value == null");
doPrivileged(() -> System.setProperty(key, value), () -> "set system property " + key + " to " + value);
}
@@ -229,7 +229,7 @@ static RETURN attempt(
}
if (log != null && log.isWarnEnabled()) {
log.warn("FAILED \"" + name.get() + "\", attempt #" + i + "/" + numAttempts
- + ": " + t + ", sleep " + sleepTime + " and then retry.", t);
+ + ", sleep " + sleepTime + " and then retry: " + t);
}
}
@@ -257,7 +257,6 @@ static void attemptUntilTrue(
}, numAttempts, sleepTime, name, log);
}
-
static Timer runRepeatedly(Runnable runnable, long delay, long period, TimeUnit unit) {
final Timer timer = new Timer(true);
timer.schedule(new TimerTask() {
@@ -283,6 +282,10 @@ static CompletableFuture completeExceptionally(Throwable t) {
return future;
}
+ static boolean isCompletedNormally(CompletableFuture> future) {
+ return future.isDone() && !future.isCancelled() && !future.isCompletedExceptionally();
+ }
+
static Throwable unwrapCompletionException(Throwable t) {
Objects.requireNonNull(t, "t == null");
return t instanceof CompletionException && t.getCause() != null? t.getCause(): t;
diff --git a/ratis-common/src/main/java/org/apache/ratis/util/JmxRegister.java b/ratis-common/src/main/java/org/apache/ratis/util/JmxRegister.java
index 54f7989245..4554410488 100644
--- a/ratis-common/src/main/java/org/apache/ratis/util/JmxRegister.java
+++ b/ratis-common/src/main/java/org/apache/ratis/util/JmxRegister.java
@@ -46,15 +46,16 @@ static ObjectName tryRegister(String name, Object mBean) {
}
/**
- * Try registering the mBean with the names one by one.
+ * Try registering the mxBean with the names one by one.
* @return the registered name, or, if it fails, return null.
*/
- public synchronized String register(Object mBean, Iterable> names) {
+ public synchronized String register(Object mxBean, Iterable> names) {
if (registeredName == null) {
for (Supplier supplier : names) {
final String name = supplier.get();
- registeredName = tryRegister(name, mBean);
+ registeredName = tryRegister(name, mxBean);
if (registeredName != null) {
+ LOG.info("register mxBean {} as {}", mxBean.getClass(), name);
return name;
}
}
diff --git a/ratis-common/src/main/java/org/apache/ratis/util/LeakDetector.java b/ratis-common/src/main/java/org/apache/ratis/util/LeakDetector.java
new file mode 100644
index 0000000000..6f12205795
--- /dev/null
+++ b/ratis-common/src/main/java/org/apache/ratis/util/LeakDetector.java
@@ -0,0 +1,202 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ratis.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * Simple general resource leak detector using {@link ReferenceQueue} and {@link java.lang.ref.WeakReference} to
+ * observe resource object life-cycle and assert proper resource closure before they are GCed.
+ *
+ *
+ * Example usage:
+ *
+ *
{@code
+ * class MyResource implements AutoClosable {
+ * static final LeakDetector LEAK_DETECTOR = new LeakDetector("MyResource");
+ *
+ * private final UncheckedAutoCloseable leakTracker = LEAK_DETECTOR.track(this, () -> {
+ * // report leaks, don't refer to the original object (MyResource) here.
+ * System.out.println("MyResource is not closed before being discarded.");
+ * });
+ *
+ * @Override
+ * public void close() {
+ * // proper resources cleanup...
+ * // inform tracker that this object is closed properly.
+ * leakTracker.close();
+ * }
+ * }
+ *
+ * }
+ */
+public class LeakDetector {
+ private static final Logger LOG = LoggerFactory.getLogger(LeakDetector.class);
+
+ private static class LeakTrackerSet {
+ private final Set set = Collections.newSetFromMap(new HashMap<>());
+
+ synchronized boolean remove(LeakTracker tracker) {
+ return set.remove(tracker);
+ }
+
+ synchronized void removeExisting(LeakTracker tracker) {
+ final boolean removed = set.remove(tracker);
+ Preconditions.assertTrue(removed, () -> "Failed to remove existing " + tracker);
+ }
+
+ synchronized LeakTracker add(Object referent, ReferenceQueue
diff --git a/ratis-grpc/pom.xml b/ratis-grpc/pom.xml
index 3352ce16fb..c555b8e4c2 100644
--- a/ratis-grpc/pom.xml
+++ b/ratis-grpc/pom.xml
@@ -17,7 +17,7 @@
ratisorg.apache.ratis
- 3.1.0-SNAPSHOT
+ 3.2.0-SNAPSHOTratis-grpc
@@ -53,6 +53,10 @@
testtest-jar
+
+ org.apache.ratis
+ ratis-server-api
+ ratis-serverorg.apache.ratis
@@ -71,8 +75,8 @@
- junit
- junit
+ org.junit.jupiter
+ junit-jupiter-apitest
@@ -80,12 +84,5 @@
mockito-coretest
-
-
- com.github.spotbugs
- spotbugs-annotations
- provided
- true
-
diff --git a/ratis-grpc/src/main/java/org/apache/ratis/grpc/GrpcConfigKeys.java b/ratis-grpc/src/main/java/org/apache/ratis/grpc/GrpcConfigKeys.java
index 8caacfeeb5..2fcb9b6b0a 100644
--- a/ratis-grpc/src/main/java/org/apache/ratis/grpc/GrpcConfigKeys.java
+++ b/ratis-grpc/src/main/java/org/apache/ratis/grpc/GrpcConfigKeys.java
@@ -19,6 +19,7 @@
import org.apache.ratis.conf.Parameters;
import org.apache.ratis.conf.RaftProperties;
+import org.apache.ratis.grpc.server.GrpcServices;
import org.apache.ratis.server.RaftServerConfigKeys;
import org.apache.ratis.util.SizeInBytes;
import org.apache.ratis.util.TimeDuration;
@@ -230,15 +231,6 @@ static void setAsyncRequestThreadPoolSize(RaftProperties properties, int port) {
setInt(properties::setInt, ASYNC_REQUEST_THREAD_POOL_SIZE_KEY, port);
}
- String TLS_CONF_PARAMETER = PREFIX + ".tls.conf";
- Class TLS_CONF_CLASS = TLS.CONF_CLASS;
- static GrpcTlsConfig tlsConf(Parameters parameters) {
- return parameters != null ? parameters.get(TLS_CONF_PARAMETER, TLS_CONF_CLASS): null;
- }
- static void setTlsConf(Parameters parameters, GrpcTlsConfig conf) {
- parameters.put(TLS_CONF_PARAMETER, conf, TLS_CONF_CLASS);
- }
-
String LEADER_OUTSTANDING_APPENDS_MAX_KEY = PREFIX + ".leader.outstanding.appends.max";
int LEADER_OUTSTANDING_APPENDS_MAX_DEFAULT = 8;
static int leaderOutstandingAppendsMax(RaftProperties properties) {
@@ -292,6 +284,34 @@ static void setLogMessageBatchDuration(RaftProperties properties,
setTimeDuration(properties::setTimeDuration,
LOG_MESSAGE_BATCH_DURATION_KEY, logMessageBatchDuration);
}
+
+ String ZERO_COPY_ENABLED_KEY = PREFIX + ".zerocopy.enabled";
+ boolean ZERO_COPY_ENABLED_DEFAULT = false;
+ static boolean zeroCopyEnabled(RaftProperties properties) {
+ return getBoolean(properties::getBoolean, ZERO_COPY_ENABLED_KEY, ZERO_COPY_ENABLED_DEFAULT, getDefaultLog());
+ }
+ static void setZeroCopyEnabled(RaftProperties properties, boolean enabled) {
+ setBoolean(properties::setBoolean, ZERO_COPY_ENABLED_KEY, enabled);
+ }
+
+ String SERVICES_CUSTOMIZER_PARAMETER = PREFIX + ".services.customizer";
+ Class SERVICES_CUSTOMIZER_CLASS = GrpcServices.Customizer.class;
+ static GrpcServices.Customizer servicesCustomizer(Parameters parameters) {
+ return parameters == null ? null
+ : parameters.get(SERVICES_CUSTOMIZER_PARAMETER, SERVICES_CUSTOMIZER_CLASS);
+ }
+ static void setServicesCustomizer(Parameters parameters, GrpcServices.Customizer customizer) {
+ parameters.put(SERVICES_CUSTOMIZER_PARAMETER, customizer, SERVICES_CUSTOMIZER_CLASS);
+ }
+
+ String TLS_CONF_PARAMETER = PREFIX + ".tls.conf";
+ Class TLS_CONF_CLASS = TLS.CONF_CLASS;
+ static GrpcTlsConfig tlsConf(Parameters parameters) {
+ return parameters != null ? parameters.get(TLS_CONF_PARAMETER, TLS_CONF_CLASS): null;
+ }
+ static void setTlsConf(Parameters parameters, GrpcTlsConfig conf) {
+ parameters.put(TLS_CONF_PARAMETER, conf, TLS_CONF_CLASS);
+ }
}
String MESSAGE_SIZE_MAX_KEY = PREFIX + ".message.size.max";
diff --git a/ratis-grpc/src/main/java/org/apache/ratis/grpc/GrpcFactory.java b/ratis-grpc/src/main/java/org/apache/ratis/grpc/GrpcFactory.java
index 75eb34a2d1..331d1a8585 100644
--- a/ratis-grpc/src/main/java/org/apache/ratis/grpc/GrpcFactory.java
+++ b/ratis-grpc/src/main/java/org/apache/ratis/grpc/GrpcFactory.java
@@ -22,7 +22,8 @@
import org.apache.ratis.conf.RaftProperties;
import org.apache.ratis.grpc.client.GrpcClientRpc;
import org.apache.ratis.grpc.server.GrpcLogAppender;
-import org.apache.ratis.grpc.server.GrpcService;
+import org.apache.ratis.grpc.server.GrpcServices;
+import org.apache.ratis.grpc.server.GrpcServicesImpl;
import org.apache.ratis.protocol.ClientId;
import org.apache.ratis.rpc.SupportedRpcType;
import org.apache.ratis.server.RaftServer;
@@ -64,6 +65,8 @@ static boolean checkPooledByteBufAllocatorUseCacheForAllThreads(Consumer
return value;
}
+ private final GrpcServices.Customizer servicesCustomizer;
+
private final GrpcTlsConfig tlsConfig;
private final GrpcTlsConfig adminTlsConfig;
private final GrpcTlsConfig clientTlsConfig;
@@ -76,7 +79,7 @@ public static Parameters newRaftParameters(GrpcTlsConfig conf) {
}
public GrpcFactory(Parameters parameters) {
- this(
+ this(GrpcConfigKeys.Server.servicesCustomizer(parameters),
GrpcConfigKeys.TLS.conf(parameters),
GrpcConfigKeys.Admin.tlsConf(parameters),
GrpcConfigKeys.Client.tlsConf(parameters),
@@ -85,11 +88,14 @@ public GrpcFactory(Parameters parameters) {
}
public GrpcFactory(GrpcTlsConfig tlsConfig) {
- this(tlsConfig, null, null, null);
+ this(null, tlsConfig, null, null, null);
}
- private GrpcFactory(GrpcTlsConfig tlsConfig, GrpcTlsConfig adminTlsConfig,
+ private GrpcFactory(GrpcServices.Customizer servicesCustomizer,
+ GrpcTlsConfig tlsConfig, GrpcTlsConfig adminTlsConfig,
GrpcTlsConfig clientTlsConfig, GrpcTlsConfig serverTlsConfig) {
+ this.servicesCustomizer = servicesCustomizer;
+
this.tlsConfig = tlsConfig;
this.adminTlsConfig = adminTlsConfig;
this.clientTlsConfig = clientTlsConfig;
@@ -123,10 +129,11 @@ public LogAppender newLogAppender(RaftServer.Division server, LeaderState state,
}
@Override
- public GrpcService newRaftServerRpc(RaftServer server) {
+ public GrpcServices newRaftServerRpc(RaftServer server) {
checkPooledByteBufAllocatorUseCacheForAllThreads(LOG::info);
- return GrpcService.newBuilder()
+ return GrpcServicesImpl.newBuilder()
.setServer(server)
+ .setCustomizer(servicesCustomizer)
.setAdminTlsConfig(getAdminTlsConfig())
.setServerTlsConfig(getServerTlsConfig())
.setClientTlsConfig(getClientTlsConfig())
diff --git a/ratis-grpc/src/main/java/org/apache/ratis/grpc/GrpcUtil.java b/ratis-grpc/src/main/java/org/apache/ratis/grpc/GrpcUtil.java
index 22653b6efb..e17d17bff6 100644
--- a/ratis-grpc/src/main/java/org/apache/ratis/grpc/GrpcUtil.java
+++ b/ratis-grpc/src/main/java/org/apache/ratis/grpc/GrpcUtil.java
@@ -24,8 +24,12 @@
import org.apache.ratis.security.TlsConf.CertificatesConf;
import org.apache.ratis.security.TlsConf.PrivateKeyConf;
import org.apache.ratis.security.TlsConf.KeyManagerConf;
+import org.apache.ratis.thirdparty.com.google.protobuf.MessageLite;
import org.apache.ratis.thirdparty.io.grpc.ManagedChannel;
import org.apache.ratis.thirdparty.io.grpc.Metadata;
+import org.apache.ratis.thirdparty.io.grpc.MethodDescriptor;
+import org.apache.ratis.thirdparty.io.grpc.ServerCallHandler;
+import org.apache.ratis.thirdparty.io.grpc.ServerServiceDefinition;
import org.apache.ratis.thirdparty.io.grpc.Status;
import org.apache.ratis.thirdparty.io.grpc.StatusRuntimeException;
import org.apache.ratis.thirdparty.io.grpc.stub.StreamObserver;
@@ -59,14 +63,8 @@ public interface GrpcUtil {
Metadata.Key.of("heartbeat", Metadata.ASCII_STRING_MARSHALLER);
static StatusRuntimeException wrapException(Throwable t) {
- return wrapException(t, -1);
- }
-
- static StatusRuntimeException wrapException(Throwable t, long callId) {
t = JavaUtils.unwrapCompletionException(t);
- Metadata trailers = new StatusRuntimeExceptionMetadataBuilder(t)
- .addCallId(callId)
- .build();
+ Metadata trailers = new StatusRuntimeExceptionMetadataBuilder(t).build();
return wrapException(t, trailers);
}
@@ -163,13 +161,6 @@ static IOException unwrapIOException(Throwable t) {
return e;
}
- static void asyncCall(
- StreamObserver responseObserver,
- CheckedSupplier, IOException> supplier,
- Function toProto) {
- asyncCall(responseObserver, supplier, toProto, throwable -> {});
- }
-
static void asyncCall(
StreamObserver responseObserver,
CheckedSupplier, IOException> supplier,
@@ -304,4 +295,26 @@ static void setKeyManager(SslContextBuilder b, KeyManagerConf keyManagerConfig)
b.keyManager(privateKey.get(), certificates.get());
}
}
+
+ /**
+ * Used to add a method to Service definition with a custom request marshaller.
+ *
+ * @param orig original service definition.
+ * @param newServiceBuilder builder of the new service definition.
+ * @param origMethod the original method definition.
+ * @param customMarshaller custom marshaller to be set for the method.
+ * @param
+ * @param
+ */
+ static void addMethodWithCustomMarshaller(
+ ServerServiceDefinition orig, ServerServiceDefinition.Builder newServiceBuilder,
+ MethodDescriptor origMethod, MethodDescriptor.PrototypeMarshaller customMarshaller) {
+ MethodDescriptor newMethod = origMethod.toBuilder()
+ .setRequestMarshaller(customMarshaller)
+ .build();
+ @SuppressWarnings("unchecked")
+ ServerCallHandler serverCallHandler =
+ (ServerCallHandler) orig.getMethod(newMethod.getFullMethodName()).getServerCallHandler();
+ newServiceBuilder.addMethod(newMethod, serverCallHandler);
+ }
}
diff --git a/ratis-grpc/src/main/java/org/apache/ratis/grpc/client/GrpcClientProtocolClient.java b/ratis-grpc/src/main/java/org/apache/ratis/grpc/client/GrpcClientProtocolClient.java
index 08bacdb73b..3b9d512683 100644
--- a/ratis-grpc/src/main/java/org/apache/ratis/grpc/client/GrpcClientProtocolClient.java
+++ b/ratis-grpc/src/main/java/org/apache/ratis/grpc/client/GrpcClientProtocolClient.java
@@ -128,8 +128,11 @@ private ManagedChannel buildChannel(String address, GrpcTlsConfig tlsConf,
SizeInBytes flowControlWindow, SizeInBytes maxMessageSize) {
NettyChannelBuilder channelBuilder =
NettyChannelBuilder.forTarget(address);
+ // ignore any http proxy for grpc
+ channelBuilder.proxyDetector(uri -> null);
if (tlsConf != null) {
+ LOG.debug("Setting TLS for {}", address);
SslContextBuilder sslContextBuilder = GrpcSslContexts.forClient();
GrpcUtil.setTrustManager(sslContextBuilder, tlsConf.getTrustManager());
if (tlsConf.getMtlsEnabled()) {
diff --git a/ratis-grpc/src/main/java/org/apache/ratis/grpc/metrics/ZeroCopyMetrics.java b/ratis-grpc/src/main/java/org/apache/ratis/grpc/metrics/ZeroCopyMetrics.java
new file mode 100644
index 0000000000..1fcc317f9d
--- /dev/null
+++ b/ratis-grpc/src/main/java/org/apache/ratis/grpc/metrics/ZeroCopyMetrics.java
@@ -0,0 +1,79 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *