mirror of https://github.com/etcd-io/jetcd.git
Compare commits
No commits in common. "main" and "jetcd-0.5.6" have entirely different histories.
main
...
jetcd-0.5.
|
@ -1,25 +0,0 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
**Versions**
|
||||
- etcd: _add the etcd version here_
|
||||
- jetcd: _add the jetcd version here_
|
||||
- java: _add the java version here_
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps or reproducer to reproduce the behavior in a form of a unit test.
|
||||
This section *must* be provided, if not, the issue may not get attention since the maintainers have very limited capacity.
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
|
@ -1,20 +0,0 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
|
@ -1,18 +1,20 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "19:30"
|
||||
timezone: Europe/Paris
|
||||
- package-ecosystem: "gradle"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "19:30"
|
||||
timezone: Europe/Paris
|
||||
labels:
|
||||
- area/dependencies
|
||||
ignore:
|
||||
- dependency-name: com.google.protobuf:protoc
|
||||
- package-ecosystem: maven
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 10
|
||||
ignore:
|
||||
- dependency-name: com.google.errorprone:error_prone_core
|
||||
versions:
|
||||
- "> 2.3.4, < 3"
|
||||
- dependency-name: junit:junit
|
||||
versions:
|
||||
- ">= 4.13.a, < 4.14"
|
||||
- dependency-name: org.codehaus.plexus:plexus-compiler-javac-errorprone
|
||||
versions:
|
||||
- "> 2.8.6, < 2.9"
|
||||
- dependency-name: net.revelc.code.formatter:formatter-maven-plugin
|
||||
versions:
|
||||
- 2.14.0
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
#
|
||||
# Copyright 2016-2023 The jetcd authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT 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 and Publish (Snapshot)
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.sha }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
jobs:
|
||||
checks:
|
||||
uses: ./.github/workflows/checks.yml
|
||||
|
||||
build:
|
||||
needs:
|
||||
- checks
|
||||
strategy:
|
||||
matrix:
|
||||
java-version:
|
||||
- 11
|
||||
- 17
|
||||
- 21
|
||||
etcd:
|
||||
- quay.io/coreos/etcd:v3.5.21
|
||||
- quay.io/coreos/etcd:v3.6.0
|
||||
uses: ./.github/workflows/build.yml
|
||||
with:
|
||||
javaVersion: "${{ matrix.java-version }}"
|
||||
etcdImage: "${{ matrix.etcd }}"
|
||||
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: 'Set Up Java'
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 11
|
||||
distribution: 'temurin'
|
||||
cache: 'gradle'
|
||||
- name: 'Collect Info'
|
||||
run: |
|
||||
./gradlew currentVersion
|
||||
- name: 'Publish Snapshot'
|
||||
if: github.event_name != 'schedule'
|
||||
env:
|
||||
NEXUS_USERNAME: ${{ secrets.OSSRH_USERNAME }}
|
||||
NEXUS_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
|
||||
run: |
|
||||
./gradlew publishToSonatype -Prelease.forceSnapshot
|
|
@ -0,0 +1,41 @@
|
|||
#
|
||||
# Copyright 2016-2020 The jetcd authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT 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: master
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set Up Java
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
- name: Build Project
|
||||
env:
|
||||
OSSRH_USERNAME: ${{ secrets. OSSRH_USERNAME }}
|
||||
OSSRH_PASSWORD: ${{ secrets. OSSRH_PASSWORD }}
|
||||
run: |
|
||||
./mvnw -V -ntp clean deploy -DskipTests -Pcheck-format --settings etc/mvn-deploy-settings.xml
|
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# Copyright 2016-2023 The jetcd authors
|
||||
# Copyright 2016-2020 The jetcd authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
@ -16,36 +16,24 @@
|
|||
|
||||
name: Build PR
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
checks:
|
||||
uses: ./.github/workflows/checks.yml
|
||||
|
||||
build:
|
||||
needs:
|
||||
- checks
|
||||
strategy:
|
||||
matrix:
|
||||
java-version:
|
||||
- 11
|
||||
- 17
|
||||
- 21
|
||||
etcd:
|
||||
- quay.io/coreos/etcd:v3.5.21
|
||||
- quay.io/coreos/etcd:v3.6.0
|
||||
uses: ./.github/workflows/build.yml
|
||||
with:
|
||||
javaVersion: "${{ matrix.java-version }}"
|
||||
etcdImage: "${{ matrix.etcd }}"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set Up Java
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
- name: Build Project
|
||||
run: |
|
||||
./mvnw -V -ntp -Pcheck-format clean install
|
||||
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
name: Build
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
javaVersion:
|
||||
required: true
|
||||
type: string
|
||||
etcdImage:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v4
|
||||
- name: 'Set Up Java'
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: ${{ inputs.javaVersion }}
|
||||
distribution: 'temurin'
|
||||
cache: 'gradle'
|
||||
- name: 'Build Project'
|
||||
env:
|
||||
ETCD_IMAGE: ${{ inputs.etcdImage }}
|
||||
run: |
|
||||
export TC_USER="$(id -u):$(id -g)"
|
||||
echo "tc user -> $TC_USER"
|
||||
|
||||
./gradlew test --info
|
|
@ -1,37 +0,0 @@
|
|||
name: Checks
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: 'Set Up Java'
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 11
|
||||
distribution: 'temurin'
|
||||
cache: 'gradle'
|
||||
- name: 'Run check task'
|
||||
run: |
|
||||
./gradlew check -x test
|
||||
- name: 'Run check script'
|
||||
run: |
|
||||
bash ./.github/workflows/scripts/precheck.sh
|
||||
shell: bash
|
||||
|
||||
deps:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'pull_request'
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v4
|
||||
- name: 'Dependency Review'
|
||||
uses: actions/dependency-review-action@v4
|
|
@ -1,55 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
failed=0
|
||||
|
||||
# Check trailing whitespace
|
||||
files=$(find . -type f \
|
||||
-not -path "./.git/*" \
|
||||
-not -path "*/.gradle/*" \
|
||||
-not -path "*/.idea/*" \
|
||||
-not -path "*/.vscode/*" \
|
||||
-not -path "*/build/*" \
|
||||
-not -path "*/out/*" \
|
||||
-not -path "*/bin/*" \
|
||||
-not -name "*.jar" \
|
||||
-not -name "*.java" \
|
||||
-exec grep -E -l " +$" {} \;)
|
||||
|
||||
count=0
|
||||
|
||||
for file in $files; do
|
||||
((count++))
|
||||
echo "$file"
|
||||
done
|
||||
|
||||
if [ $count -ne 0 ]; then
|
||||
failed=1
|
||||
echo "Error: trailing whitespace(s) in the above $count file(s)"
|
||||
fi
|
||||
|
||||
# Check newline
|
||||
files=$(find . -type f -size +0c \
|
||||
-not -path "./.git/*" \
|
||||
-not -path "*/.gradle/*" \
|
||||
-not -path "*/.idea/*" \
|
||||
-not -path "*/.vscode/*" \
|
||||
-not -path "*/build/*" \
|
||||
-not -path "*/out/*" \
|
||||
-not -path "*/bin/*" \
|
||||
-not -name "*.jar" \
|
||||
-not -name "*.java" \
|
||||
-exec bash -c 'if [[ $(tail -c1 "$0" | wc -l) -eq 0 ]]; then echo "$0"; fi' {} \;)
|
||||
|
||||
count=0
|
||||
|
||||
for file in $files; do
|
||||
((count++))
|
||||
echo "$file"
|
||||
done
|
||||
|
||||
if [ $count -ne 0 ]; then
|
||||
failed=1
|
||||
echo "Error: no newline in the above $count file(s)"
|
||||
fi
|
||||
|
||||
exit $failed
|
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# Copyright 2016-2023 The jetcd authors
|
||||
# Copyright 2016-2020 The jetcd authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
@ -24,19 +24,18 @@ jobs:
|
|||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
- uses: actions/stale@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 60
|
||||
days-before-close: 7
|
||||
only-labels: 'waiting-for-feedbacks'
|
||||
stale-issue-label: stale
|
||||
exempt-issue-labels: never-stale
|
||||
exempt-issue-label: never-stale
|
||||
stale-pr-label: stale
|
||||
exempt-pr-labels: never-stale
|
||||
exempt-pr-label: never-stale
|
||||
stale-issue-message: |
|
||||
This issue is stale because it has been open 60 days with no activity.
|
||||
This issue is stale because it has been open 60 days with no activity.
|
||||
Remove stale label or comment or this will be closed in 7 days.
|
||||
stale-pr-message: |
|
||||
This PR is stale because it has been open 60 days with no activity.
|
||||
Remove stale label or comment or this will be closed in 7 days.
|
||||
Remove stale label or comment or this will be closed in 7 days.
|
|
@ -1,7 +1,7 @@
|
|||
.*
|
||||
!.github
|
||||
!.gitignore
|
||||
!gradle/wrapper/gradle-wrapper.jar
|
||||
!.travis.yml
|
||||
target/
|
||||
target-ide/
|
||||
*.iml
|
||||
|
@ -13,6 +13,8 @@ logs/
|
|||
.project
|
||||
.env
|
||||
.sdkmanrc
|
||||
build/
|
||||
|
||||
# Created by './scripts/run_etcd.sh'
|
||||
/external/
|
||||
bin/
|
||||
out/
|
||||
/jetcd-all/dependency-reduced-pom.xml
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
public class MavenWrapperDownloader {
|
||||
|
||||
/**
|
||||
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
|
||||
*/
|
||||
private static final String DEFAULT_DOWNLOAD_URL =
|
||||
"https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.0/maven-wrapper-0.4.0.jar";
|
||||
|
||||
/**
|
||||
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
|
||||
* use instead of the default one.
|
||||
*/
|
||||
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
|
||||
".mvn/wrapper/maven-wrapper.properties";
|
||||
|
||||
/**
|
||||
* Path where the maven-wrapper.jar will be saved to.
|
||||
*/
|
||||
private static final String MAVEN_WRAPPER_JAR_PATH =
|
||||
".mvn/wrapper/maven-wrapper.jar";
|
||||
|
||||
/**
|
||||
* Name of the property which should be used to override the default download url for the wrapper.
|
||||
*/
|
||||
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
|
||||
|
||||
public static void main(String args[]) {
|
||||
System.out.println("- Downloader started");
|
||||
File baseDirectory = new File(args[0]);
|
||||
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
|
||||
|
||||
// If the maven-wrapper.properties exists, read it and check if it contains a custom
|
||||
// wrapperUrl parameter.
|
||||
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
|
||||
String url = DEFAULT_DOWNLOAD_URL;
|
||||
if(mavenWrapperPropertyFile.exists()) {
|
||||
FileInputStream mavenWrapperPropertyFileInputStream = null;
|
||||
try {
|
||||
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
|
||||
Properties mavenWrapperProperties = new Properties();
|
||||
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
|
||||
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
|
||||
} catch (IOException e) {
|
||||
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
|
||||
} finally {
|
||||
try {
|
||||
if(mavenWrapperPropertyFileInputStream != null) {
|
||||
mavenWrapperPropertyFileInputStream.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Ignore ...
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println("- Downloading from: : " + url);
|
||||
|
||||
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
|
||||
if(!outputFile.getParentFile().exists()) {
|
||||
if(!outputFile.getParentFile().mkdirs()) {
|
||||
System.out.println(
|
||||
"- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'");
|
||||
}
|
||||
}
|
||||
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
|
||||
try {
|
||||
downloadFileFromURL(url, outputFile);
|
||||
System.out.println("Done");
|
||||
System.exit(0);
|
||||
} catch (Throwable e) {
|
||||
System.out.println("- Error downloading");
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
|
||||
URL website = new URL(urlString);
|
||||
ReadableByteChannel rbc;
|
||||
rbc = Channels.newChannel(website.openStream());
|
||||
FileOutputStream fos = new FileOutputStream(destination);
|
||||
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
|
||||
fos.close();
|
||||
rbc.close();
|
||||
}
|
||||
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
|
|
@ -4,7 +4,7 @@ jetcd is Apache 2.0 licensed and accepts contributions via GitHub pull requests.
|
|||
|
||||
# Email and chat
|
||||
|
||||
- Email: [etcd-dev](https://groups.google.com/g/etcd-dev)
|
||||
- Email: [etcd-dev](https://groups.google.com/forum/?hl=en#!forum/etcd-dev)
|
||||
- IRC: #[etcd](irc://irc.freenode.org:6667/#etcd) IRC channel on freenode.org
|
||||
|
||||
## Getting started
|
||||
|
@ -25,7 +25,7 @@ This is a rough outline of what a contributor's workflow looks like:
|
|||
- Make sure commit messages are in the proper format (see below).
|
||||
- Push changes in a topic branch to a personal fork of the repository.
|
||||
- Submit a pull request to etcd-io/jetcd.
|
||||
- The PR must receive a LGTM from at least one maintainer found in the [OWNERS](https://github.com/etcd-io/jetcd/blob/main/OWNERS) file.
|
||||
- The PR must receive a LGTM from at least one maintainer found in the [MAINTAINERS](https://github.com/etcd-io/etcd/blob/master/MAINTAINERS) file.
|
||||
|
||||
Thanks for contributing!
|
||||
|
||||
|
@ -40,7 +40,7 @@ Please follow this style to make jetcd easy to review, maintain, and develop.
|
|||
To make sure CI checks would pass please run
|
||||
|
||||
```bash
|
||||
./gradlew spotlessApply
|
||||
./mvnw license:format
|
||||
```
|
||||
|
||||
and including any changes in PR before opening it.
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
Fanmin Shi <fanmin.shi@coreos.com> (@fanminshi) pkg:*
|
||||
Luca Burgazzoli <lburgazzoli@gmail.com> (@lburgazzoli) pkg:*
|
||||
Xiang Li <xiang.li@coreos.com> (@xiang90) pkg:*
|
||||
Anthony Romano <anthony.romano@coreos.com> (@heyitsanthony) pkg:*
|
||||
|
6
OWNERS
6
OWNERS
|
@ -1,6 +0,0 @@
|
|||
# See the OWNERS docs at https://go.k8s.io/owners
|
||||
|
||||
approvers:
|
||||
- fanminshi # Fanmin Shi <fanmin.shi@coreos.com>
|
||||
- lburgazzoli # Luca Burgazzoli <lburgazzoli@gmail.com>
|
||||
- vorburger # Michael Vorburger <mike@vorburger.ch>
|
77
README.md
77
README.md
|
@ -1,7 +1,8 @@
|
|||
# jetcd - A Java Client for etcd
|
||||
[](https://github.com/etcd-io/jetcd/actions)
|
||||
[](https://github.com/etcd-io/jetcd/actions)
|
||||
[](http://www.apache.org/licenses/LICENSE-2.0.html)
|
||||
[](https://search.maven.org/#search%7Cga%7C1%7Cio.etcd)
|
||||
[](https://github.com/etcd-io/jetcd/releases)
|
||||
[](https://javadoc.io/doc/io.etcd/jetcd-core)
|
||||
|
||||
jetcd is the official java client for [etcd](https://github.com/etcd-io/etcd) v3.
|
||||
|
@ -10,7 +11,7 @@ jetcd is the official java client for [etcd](https://github.com/etcd-io/etcd) v3
|
|||
|
||||
## Java Versions
|
||||
|
||||
Java 11 or above is required.
|
||||
Java 8 or above is required.
|
||||
|
||||
## Download
|
||||
|
||||
|
@ -23,31 +24,23 @@ Java 11 or above is required.
|
|||
</dependency>
|
||||
```
|
||||
|
||||
Development snapshots are available in [Sonatypes's snapshot repository](https://oss.sonatype.org/content/repositories/snapshots/io/etcd/).
|
||||
Development snapshots are available in [Sonatypes's snapshot repository](https://oss.sonatype.org/content/repositories/snapshots/io/etcd).
|
||||
|
||||
### Gradle
|
||||
|
||||
```
|
||||
dependencies {
|
||||
implementation "io.etcd:jetcd-core:$jetcd-version"
|
||||
compile "io.etcd:jetcd-core:$jetcd-version"
|
||||
}
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
```java
|
||||
// create client using endpoints
|
||||
Client client = Client.builder().endpoints("http://etcd0:2379", "http://etcd1:2379", "http://etcd2:2379").build();
|
||||
```
|
||||
|
||||
```java
|
||||
// create client using target which enable using any name resolution mechanism provided
|
||||
// by grpc-java (i.e. dns:///foo.bar.com:2379)
|
||||
Client client = Client.builder().target("ip:///etcd0:2379,etcd1:2379,etcd2:2379").build();
|
||||
```
|
||||
|
||||
```java
|
||||
// create client
|
||||
Client client = Client.builder().endpoints("http://localhost:2379").build();
|
||||
KV kvClient = client.getKVClient();
|
||||
|
||||
ByteSequence key = ByteSequence.from("test_key".getBytes());
|
||||
ByteSequence value = ByteSequence.from("test_value".getBytes());
|
||||
|
||||
|
@ -63,13 +56,12 @@ GetResponse response = getFuture.get();
|
|||
// delete the key
|
||||
kvClient.delete(key).get();
|
||||
```
|
||||
To build one ssl secured client, refer to [secured client config](docs/SslConfig.md).
|
||||
|
||||
For full etcd v3 API, plesase refer to the [official API documentation](https://etcd.io/docs/current/learning/api/).
|
||||
|
||||
### Examples
|
||||
|
||||
The [jetcd-ctl](https://github.com/etcd-io/jetcd/tree/master/jetcd-ctl) is a standalone projects that show usage of jetcd.
|
||||
The [examples](https://github.com/etcd-io/jetcd/tree/master/jetcd-examples) are standalone projects that show usage of jetcd.
|
||||
|
||||
## Launcher
|
||||
|
||||
|
@ -77,15 +69,12 @@ The `io.etcd:jetcd-test` offers a convenient utility to programmatically start &
|
|||
|
||||
```java
|
||||
import io.etcd.jetcd.Client;
|
||||
import io.etcd.jetcd.launcher.EtcdCluster;
|
||||
import io.etcd.jetcd.test.EtcdClusterExtension;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
@RegisterExtension
|
||||
public static final EtcdClusterExtension cluster = EtcdClusterExtension.builder()
|
||||
.withNodes(1)
|
||||
.build();
|
||||
|
||||
Client client = Client.builder().endpoints(cluster.clientEndpoints()).build();
|
||||
@RegisterExtension static final EtcdCluster etcd = new EtcdClusterExtension("test-etcd", 1);
|
||||
Client client = Client.builder().endpoints(etcd.getClientEndpoints()).build();
|
||||
```
|
||||
|
||||
This launcher uses the Testcontainers framework.
|
||||
|
@ -99,10 +88,10 @@ The current major version is zero (0.y.z). Anything may change at any time. The
|
|||
|
||||
## Build from source
|
||||
|
||||
The project can be built with [Gradle](https://gradle.org/):
|
||||
The project can be built with [Apache Maven](https://maven.apache.org/):
|
||||
|
||||
```
|
||||
./gradlew compileJava
|
||||
mvn clean install -DskipTests
|
||||
```
|
||||
|
||||
## Running tests
|
||||
|
@ -110,8 +99,33 @@ The project can be built with [Gradle](https://gradle.org/):
|
|||
The project is tested against a three node `etcd` setup started with the Launcher (above) :
|
||||
|
||||
```sh
|
||||
$ ./gradlew test
|
||||
```
|
||||
$ mvn test
|
||||
...
|
||||
|
||||
[INFO] T E S T S
|
||||
[INFO] -------------------------------------------------------
|
||||
[INFO] Running TestSuite
|
||||
[WARNING] Tests run: 104, Failures: 0, Errors: 0, Skipped: 3, Time elapsed: 31.308 s - in TestSuite
|
||||
[INFO]
|
||||
[INFO] Results:
|
||||
[INFO]
|
||||
[WARNING] Tests run: 104, Failures: 0, Errors: 0, Skipped: 3
|
||||
...
|
||||
[INFO] Reactor Summary:
|
||||
[INFO]
|
||||
[INFO] jetcd .............................................. SUCCESS [ 0.010 s]
|
||||
[INFO] jetcd-core ......................................... SUCCESS [ 55.480 s]
|
||||
[INFO] jetcd-discovery-dns-srv ............................ SUCCESS [ 3.225 s]
|
||||
[INFO] jetcd-watch-example ................................ SUCCESS [ 0.291 s]
|
||||
[INFO] jetcd-simple-ctl ................................... SUCCESS [ 0.028 s]
|
||||
[INFO] jetcd-examples ..................................... SUCCESS [ 0.000 s]
|
||||
[INFO] ------------------------------------------------------------------------
|
||||
[INFO] BUILD SUCCESS
|
||||
[INFO] ------------------------------------------------------------------------
|
||||
[INFO] Total time: 59.929 s
|
||||
[INFO] Finished at: 2018-02-13T12:51:13-08:00
|
||||
[INFO] Final Memory: 84M/443M
|
||||
````
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
|
@ -119,14 +133,17 @@ It recommmonds building the project before running tests so that you have artifa
|
|||
|
||||
## Contact
|
||||
|
||||
* Mailing list: [etcd-dev](https://groups.google.com/g/etcd-dev)
|
||||
* Mailing list: [etcd-dev](https://groups.google.com/forum/?hl=en#!forum/etcd-dev)
|
||||
* IRC: #[etcd](irc://irc.freenode.org:6667/#etcd) on freenode.org
|
||||
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING](https://github.com/etcd-io/jetcd/blob/master/CONTRIBUTING.md) for details on submitting patches and the contribution workflow.
|
||||
|
||||
## Reporting bugs
|
||||
|
||||
See [reporting bugs](https://github.com/etcd-io/etcd/blob/master/Documentation/reporting-bugs.md) for details about reporting any issues.
|
||||
|
||||
## License
|
||||
|
||||
jetcd is under the Apache 2.0 license. See the [LICENSE](https://github.com/etcd-io/jetcd/blob/master/LICENSE) file for details.
|
||||
|
||||
|
||||
|
|
100
build.gradle
100
build.gradle
|
@ -1,100 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016-2021 The jetcd authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
}
|
||||
dependencies {
|
||||
classpath "com.github.ben-manes:gradle-versions-plugin:${libs.versions.versionsPlugin.get()}"
|
||||
classpath "com.diffplug.spotless:spotless-plugin-gradle:${libs.versions.spotlessPlugin.get()}"
|
||||
classpath "net.ltgt.gradle:gradle-errorprone-plugin:${libs.versions.errorPronePlugin.get()}"
|
||||
classpath "io.github.gradle-nexus:publish-plugin:${libs.versions.nexusPublishPlugin.get()}"
|
||||
classpath "com.adarshr:gradle-test-logger-plugin:${libs.versions.testLoggerPlugin.get()}"
|
||||
classpath "pl.allegro.tech.build:axion-release-plugin:${libs.versions.axionReleasePlugin.get()}"
|
||||
classpath "org.gradle:test-retry-gradle-plugin:${libs.versions.testRetryPlugin.get()}"
|
||||
}
|
||||
}
|
||||
|
||||
apply from: "${rootProject.projectDir}/gradle/versions.gradle"
|
||||
apply from: "${rootProject.projectDir}/gradle/publish.gradle"
|
||||
apply from: "${rootProject.projectDir}/gradle/release.gradle"
|
||||
|
||||
group = 'io.etcd'
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
subprojects {
|
||||
apply from: "${rootProject.projectDir}/gradle/style.gradle"
|
||||
apply from: "${rootProject.projectDir}/gradle/quality.gradle"
|
||||
apply from: "${rootProject.projectDir}/gradle/publishing-release.gradle"
|
||||
apply from: "${rootProject.projectDir}/gradle/publishing-release-tasks.gradle"
|
||||
|
||||
apply plugin: 'java-library'
|
||||
apply plugin: 'com.adarshr.test-logger'
|
||||
apply plugin: 'org.gradle.test-retry'
|
||||
|
||||
configurations.api {
|
||||
exclude group: 'com.google.code.gson', module: 'gson'
|
||||
exclude group: 'com.google.android', module: 'annotations'
|
||||
exclude group: 'com.google.j2objc', module: 'j2objc-annotations'
|
||||
exclude group: 'org.codehaus.mojo', module: 'animal-sniffer-annotations'
|
||||
exclude group: 'com.google.errorprone', module: 'error_prone_annotations'
|
||||
exclude group: 'com.google.code.findbugs', module: 'jsr305'
|
||||
exclude group: 'org.checkerframework', module: 'checker-compat-qual'
|
||||
}
|
||||
|
||||
compileJava {
|
||||
options.release = 11
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
|
||||
maxParallelForks = Runtime.runtime.availableProcessors() ?: 1
|
||||
|
||||
retry {
|
||||
maxRetries = 1
|
||||
maxFailures = 5
|
||||
}
|
||||
}
|
||||
testing {
|
||||
suites {
|
||||
test {
|
||||
useJUnitJupiter()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testlogger {
|
||||
theme 'mocha-parallel'
|
||||
showStandardStreams false
|
||||
}
|
||||
|
||||
tasks.register('allDeps', DependencyReportTask)
|
||||
|
||||
tasks.withType(AbstractArchiveTask).configureEach {
|
||||
preserveFileTimestamps = false
|
||||
reproducibleFileOrder = true
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,3 +1,61 @@
|
|||
## etcd Community Code of Conduct
|
||||
## CoreOS Community Code of Conduct
|
||||
|
||||
etcd follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).
|
||||
### Contributor Code of Conduct
|
||||
|
||||
As contributors and maintainers of this project, and in the interest of
|
||||
fostering an open and welcoming community, we pledge to respect all people who
|
||||
contribute through reporting issues, posting feature requests, updating
|
||||
documentation, submitting pull requests or patches, and other activities.
|
||||
|
||||
We are committed to making participation in this project a harassment-free
|
||||
experience for everyone, regardless of level of experience, gender, gender
|
||||
identity and expression, sexual orientation, disability, personal appearance,
|
||||
body size, race, ethnicity, age, religion, or nationality.
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery
|
||||
* Personal attacks
|
||||
* Trolling or insulting/derogatory comments
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as physical or electronic addresses, without explicit permission
|
||||
* Other unethical or unprofessional conduct.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct. By adopting this Code of Conduct,
|
||||
project maintainers commit themselves to fairly and consistently applying these
|
||||
principles to every aspect of managing this project. Project maintainers who do
|
||||
not follow or enforce the Code of Conduct may be permanently removed from the
|
||||
project team.
|
||||
|
||||
This code of conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community.
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting a project maintainer, Brandon Philips
|
||||
<brandon.philips@coreos.com>, and/or Rithu John <rithu.john@coreos.com>.
|
||||
|
||||
This Code of Conduct is adapted from the Contributor Covenant
|
||||
(http://contributor-covenant.org), version 1.2.0, available at
|
||||
http://contributor-covenant.org/version/1/2/0/
|
||||
|
||||
### CoreOS Events Code of Conduct
|
||||
|
||||
CoreOS events are working conferences intended for professional networking and
|
||||
collaboration in the CoreOS community. Attendees are expected to behave
|
||||
according to professional standards and in accordance with their employer’s
|
||||
policies on appropriate workplace behavior.
|
||||
|
||||
While at CoreOS events or related social networking opportunities, attendees
|
||||
should not engage in discriminatory or offensive speech or actions including
|
||||
but not limited to gender, sexuality, race, age, disability, or religion.
|
||||
Speakers should be especially aware of these concerns.
|
||||
|
||||
CoreOS does not condone any statements by speakers contrary to these standards.
|
||||
CoreOS reserves the right to deny entrance and/or eject from an event (without
|
||||
refund) any individual found to be engaging in discriminatory or offensive
|
||||
speech or actions.
|
||||
|
||||
Please bring any concerns to the immediate attention of designated on-site
|
||||
staff, Brandon Philips <brandon.philips@coreos.com>, and/or Rithu John <rithu.john@coreos.com>.
|
||||
|
|
|
@ -26,3 +26,6 @@ for (KeyValue kv : response.getKvs()) {
|
|||
);
|
||||
}
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ The Lease interface provides methods to grant, revoke, and keepalive leases.
|
|||
1. This function is called periodically by `keepAliveSchedule`.
|
||||
2. The lease may expire as:
|
||||
* The etcd respond overtime.
|
||||
* The client fails to send request in time.
|
||||
* The client faile to send request in time.
|
||||
3. It will scan the keepAlives map and find the leases that requires keepAlive requests to send based on its nextKeepAliveTime.
|
||||
4. Send request to the StreamObserver for these leases.
|
||||
|
||||
|
@ -66,4 +66,4 @@ The Lease interface provides methods to grant, revoke, and keepalive leases.
|
|||
|
||||
1. The StreamObserver is the stream etcd uses to send responses.
|
||||
2. The gRPC client calls onNext when etcd server delivers a response.
|
||||
3. The onNext function will call `processKeepAliveRespond`.
|
||||
3. The onNext function will call `processKeepAliveRespond`.
|
|
@ -1,21 +0,0 @@
|
|||
= Release
|
||||
|
||||
+
|
||||
[source,shell]
|
||||
----
|
||||
$ ./gradlew currentVersion
|
||||
Project version: 0.7.5-SNAPSHOT
|
||||
|
||||
$ ./gradlew release
|
||||
|
||||
$ git tag
|
||||
jetcd-0.7.5
|
||||
|
||||
$ ./gradlew currentVersion
|
||||
Project version: 0.7.5
|
||||
|
||||
$ ./gradlew publish
|
||||
published jetcd-0.7.5 release version
|
||||
|
||||
$ ./gradlew markNextVersion -Prelease.version=0.7.6
|
||||
----
|
|
@ -1,36 +0,0 @@
|
|||
# How to Build Jectd Client for One TLS Secured Etcd Cluster
|
||||
|
||||
# prepare certification files
|
||||
|
||||
If your etcd cluster is installed using [etcdadm](https://github.com/kubernetes-sigs/etcdadm), you are likely to find
|
||||
certification files in the path `/etc/etcd/pki/`: `ca.crt`, `etcdctl-etcd-client.key`, `etcdctl-etcd-client.crt`.
|
||||
|
||||
If your etcd cluster is the builtin etcd in one kubernetes cluster(
|
||||
using [kubeadm](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/setup-ha-etcd-with-kubeadm/)),
|
||||
|
||||
you can find the same files in `/etc/kubernetes/pki/etcd/`: `ca.crt`, `healthcheck-client.crt`, `healthcheck-client.key`.
|
||||
|
||||
Because `SslContextBuilder` only support a PKCS#8 private key file in PEM format, convert `etcdctl-etcd-client.key`
|
||||
to `etcdctl-etcd-client.key.pem` according
|
||||
to [netty SslContextBuilder doc](https://netty.io/wiki/sslcontextbuilder-and-private-key.html).
|
||||
|
||||
# build jetcd client
|
||||
|
||||
```
|
||||
File cert = new File("ca.crt");
|
||||
File keyCertChainFile = new File("etcdctl-etcd-client.crt");
|
||||
File keyFile = new File("etcdctl-etcd-client.key.pem");
|
||||
SslContext context = GrpcSslContexts.forClient()
|
||||
.trustManager(cert)
|
||||
.keyManager(keyCertChainFile, keyFile)
|
||||
.build();
|
||||
Client client = Client.builder()
|
||||
.endpoints("https://10.168.168.66:2379")
|
||||
.sslContext(context)
|
||||
.build();
|
||||
|
||||
client.getClusterClient().listMember().get().getMembers().forEach(member -> {
|
||||
logger.info("member: {}", member);
|
||||
});
|
||||
|
||||
```
|
|
@ -12,15 +12,13 @@ The Watch provide methods to watch on a key interval and cancel a watcher. If th
|
|||
|
||||
4. Cancel watch request, the etcd client should process watch cancellation and filter all the notification after cancellation request.
|
||||
|
||||
5. The watch client should be able to make a progress notify request that propagates the latest revision number to all watches.
|
||||
|
||||
# Implementation
|
||||
|
||||
The etcd client process watch request with [watch function](#watch-function), process notification with [processEvents function](#processevents-function), process resume with [resume function](#resume-function), process cancel with [cancelWatch function](#cancelwatch-function) and request progress with [requestProgress function](#requestProgress-function).
|
||||
The etcd client process watch request with [watch function](#watch-function), process notification with [processEvents function](#processevents-function) , process resume with [resume function](#resume-function) and process cancel with [cancelWatch function](#cancelwatch-function).
|
||||
|
||||
## watch function
|
||||
|
||||
Watch watches on a key interval.
|
||||
Watch watches on a key interval.
|
||||
|
||||
1. Send create request to [requestStream](#requeststream-instance).
|
||||
2. If the watch is create successfully, the `onCreate` will be called and the ListenableFuture task will be completed.
|
||||
|
@ -46,13 +44,6 @@ Cancel the watch task with the watcher, the `onCanceled` will be called after su
|
|||
1. The watcher will be removed from [watchers](#watchers-instance) map.
|
||||
2. If the [watchers](#watchers-instance) map contain the watcher, it will be moved to [cancelWatchers](#cancelwatchers) and send cancel request to [requestStream](#requeststream-instance).
|
||||
|
||||
## requestProgress function
|
||||
|
||||
Send the latest revision processed to all active [watchers](#watchers-instance)
|
||||
|
||||
1. Send a progress request to [requestStream](#requeststream-instance).
|
||||
2. Working watchers will receive a WatchResponse containing the latest revision number. All future revision numbers are guaranteed to be greater than or equal to the received revision number.
|
||||
|
||||
## requestStream instance
|
||||
|
||||
StreamObserver instance
|
||||
|
@ -116,4 +107,4 @@ Process cancel response from etcd server.
|
|||
Resume all the the watchers on new requestStream.
|
||||
|
||||
1. Build new watch creation request for old watcher with last revision + 1.
|
||||
2. Call `watch` function with the new watch creation request.
|
||||
2. Call `watch` function with the new watch creation request.
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Copyright 2016-2023 The jetcd authors
|
||||
Copyright 2016-2021 The jetcd authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -271,3 +271,4 @@
|
|||
<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="CHECKSTYLE:ON"/>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
0=java
|
||||
1=javax
|
||||
2=org
|
||||
2=io
|
||||
4=com
|
||||
5=
|
||||
6=\#
|
|
@ -1,4 +1,4 @@
|
|||
Copyright $YEAR The jetcd authors
|
||||
Copyright ${license.git.copyrightYears} The jetcd authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -10,4 +10,4 @@ Unless required by applicable law or agreed to in writing, software
|
|||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
limitations under the License.
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Copyright 2016-2021 The jetcd authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
-->
|
||||
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://maven.apache.org/SETTINGS/1.0.0
|
||||
http://maven.apache.org/xsd/settings-1.0.0.xsd">
|
||||
|
||||
<servers>
|
||||
<server>
|
||||
<id>ossrh</id>
|
||||
<username>${env.OSSRH_USERNAME}</username>
|
||||
<password>${env.OSSRH_PASSWORD}</password>
|
||||
</server>
|
||||
</servers>
|
||||
|
||||
</settings>
|
|
@ -1,93 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Copyright 2016-2023 The jetcd authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
-->
|
||||
<ruleset name="PMD-Rules"
|
||||
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
|
||||
|
||||
<description>PMD Rules that govern static code analysis for jetcd</description>
|
||||
|
||||
<exclude-pattern>.*/io/etcd/jetcd/api/.*</exclude-pattern>
|
||||
|
||||
<rule ref="category/java/bestpractices.xml/AvoidUsingHardCodedIP"/>
|
||||
<rule ref="category/java/bestpractices.xml/UnusedFormalParameter"/>
|
||||
<rule ref="category/java/bestpractices.xml/UnusedLocalVariable"/>
|
||||
<rule ref="category/java/bestpractices.xml/UnusedPrivateField"/>
|
||||
<rule ref="category/java/bestpractices.xml/UnusedPrivateMethod"/>
|
||||
<rule ref="category/java/bestpractices.xml/OneDeclarationPerLine"/>
|
||||
<rule ref="category/java/bestpractices.xml/UseCollectionIsEmpty"/>
|
||||
<rule ref="category/java/bestpractices.xml/PreserveStackTrace"/>
|
||||
<rule ref="category/java/bestpractices.xml/SimplifiableTestAssertion" />
|
||||
|
||||
<rule ref="category/java/codestyle.xml/ExtendsObject"/>
|
||||
<rule ref="category/java/codestyle.xml/ForLoopShouldBeWhileLoop"/>
|
||||
<rule ref="category/java/codestyle.xml/UnnecessaryFullyQualifiedName"/>
|
||||
<rule ref="category/java/codestyle.xml/UnnecessaryModifier"/>
|
||||
<rule ref="category/java/codestyle.xml/UnnecessaryReturn"/>
|
||||
<rule ref="category/java/codestyle.xml/UselessParentheses"/>
|
||||
<rule ref="category/java/codestyle.xml/UselessQualifiedThis"/>
|
||||
<rule ref="category/java/codestyle.xml/ControlStatementBraces"/>
|
||||
<rule ref="category/java/codestyle.xml/UnnecessaryImport"/>
|
||||
<rule ref="category/java/codestyle.xml/EmptyControlStatement"/>
|
||||
|
||||
<rule ref="category/java/design.xml/FinalFieldCouldBeStatic">
|
||||
<priority>4</priority>
|
||||
</rule>
|
||||
<rule ref="category/java/design.xml/SimplifiedTernary">
|
||||
<priority>4</priority>
|
||||
</rule>
|
||||
<rule ref="category/java/design.xml/CollapsibleIfStatements">
|
||||
<priority>4</priority>
|
||||
</rule>
|
||||
<rule ref="category/java/design.xml/UselessOverridingMethod">
|
||||
<priority>4</priority>
|
||||
</rule>
|
||||
|
||||
<rule ref="category/java/errorprone.xml/AvoidBranchingStatementAsLastInLoop"/>
|
||||
<rule ref="category/java/errorprone.xml/AvoidMultipleUnaryOperators"/>
|
||||
<rule ref="category/java/errorprone.xml/AvoidUsingOctalValues"/>
|
||||
<rule ref="category/java/errorprone.xml/BrokenNullCheck"/>
|
||||
<rule ref="category/java/errorprone.xml/ClassCastExceptionWithToArray"/>
|
||||
<rule ref="category/java/errorprone.xml/DontUseFloatTypeForLoopIndices"/>
|
||||
|
||||
<rule ref="category/java/errorprone.xml/EmptyCatchBlock">
|
||||
<priority>4</priority>
|
||||
<properties>
|
||||
<property name="allowCommentedBlocks" value="true"/>
|
||||
<property name="allowExceptionNameRegex" value="ignored"/>
|
||||
</properties>
|
||||
</rule>
|
||||
|
||||
<rule ref="category/java/errorprone.xml/JumbledIncrementer"/>
|
||||
<rule ref="category/java/errorprone.xml/MisplacedNullCheck"/>
|
||||
<rule ref="category/java/errorprone.xml/OverrideBothEqualsAndHashcode"/>
|
||||
<rule ref="category/java/errorprone.xml/ReturnFromFinallyBlock"/>
|
||||
<rule ref="category/java/errorprone.xml/UnconditionalIfStatement"/>
|
||||
<rule ref="category/java/errorprone.xml/UnnecessaryConversionTemporary"/>
|
||||
<rule ref="category/java/errorprone.xml/UnusedNullCheckInEquals"/>
|
||||
<rule ref="category/java/errorprone.xml/UselessOperationOnImmutable"/>
|
||||
<rule ref="category/java/errorprone.xml/ConstructorCallsOverridableMethod"/>
|
||||
<rule ref="category/java/errorprone.xml/DoNotTerminateVM"/>
|
||||
|
||||
<rule ref="category/java/multithreading.xml/AvoidThreadGroup"/>
|
||||
<rule ref="category/java/multithreading.xml/DontCallThreadRun"/>
|
||||
|
||||
<rule ref="category/java/performance.xml/BigIntegerInstantiation"/>
|
||||
<rule ref="category/java/bestpractices.xml/PrimitiveWrapperInstantiation" />
|
||||
</ruleset>
|
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Copyright 2016-2021 The jetcd authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
-->
|
||||
<ruleset xmlns="http://mojo.codehaus.org/versions-maven-plugin/rule/2.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
comparisonMethod="maven"
|
||||
xsi:schemaLocation="http://mojo.codehaus.org/versions-maven-plugin/rule/2.0.0 http://mojo.codehaus.org/versions-maven-plugin/xsd/rule-2.0.0.xsd">
|
||||
<ignoreVersions>
|
||||
<!-- Ignore Alpha's, Beta's, release candidates and milestones -->
|
||||
<ignoreVersion type="regex">(?i).*Alpha(?:-?\d+)?</ignoreVersion>
|
||||
<ignoreVersion type="regex">(?i).*a(?:-?\d+)?</ignoreVersion>
|
||||
<ignoreVersion type="regex">(?i).*Beta(?:-?\d+)?</ignoreVersion>
|
||||
<ignoreVersion type="regex">(?i).*-B(?:-?\d+)?</ignoreVersion>
|
||||
<ignoreVersion type="regex">(?i).*RC(?:-?\d+)?</ignoreVersion>
|
||||
<ignoreVersion type="regex">(?i).*CR(?:-?\d+)?</ignoreVersion>
|
||||
<ignoreVersion type="regex">(?i).*M(?:-?\d+)?</ignoreVersion>
|
||||
</ignoreVersions>
|
||||
<rules>
|
||||
</rules>
|
||||
</ruleset>
|
|
@ -1,10 +0,0 @@
|
|||
#
|
||||
# project
|
||||
#
|
||||
gitProject = https://github.com/etcd-io/jetcd
|
||||
gitURL = git@github.com/etcd-io/jetcd.git
|
||||
|
||||
#
|
||||
# gradle
|
||||
#
|
||||
org.gradle.parallel = true
|
|
@ -1,86 +0,0 @@
|
|||
[versions]
|
||||
grpc = "1.74.0"
|
||||
log4j = "2.25.1"
|
||||
mockito = "5.18.0"
|
||||
slf4j = "2.0.17"
|
||||
guava = "33.4.8-jre"
|
||||
assertj = "3.27.3"
|
||||
junit = "5.13.4"
|
||||
testcontainers = "1.21.3"
|
||||
protoc = "3.25.1"
|
||||
failsafe = "3.3.2"
|
||||
awaitility = "4.3.0"
|
||||
commonsIo = "2.20.0"
|
||||
commonCompress = "1.28.0"
|
||||
autoService = "1.1.1"
|
||||
errorprone = "2.30.0"
|
||||
vertx = "5.0.1"
|
||||
picocli = "4.7.7"
|
||||
restAssured = "5.5.5"
|
||||
javaxAnnotation = "1.3.2"
|
||||
|
||||
versionsPlugin = "0.52.0"
|
||||
errorPronePlugin = "4.0.1"
|
||||
spotlessPlugin = "6.25.0"
|
||||
shadowPlugin = "8.1.1"
|
||||
testLoggerPlugin = "4.0.0"
|
||||
protobufPlugin = "0.9.5"
|
||||
nexusPublishPlugin = "2.0.0"
|
||||
axionReleasePlugin = "1.18.17"
|
||||
testRetryPlugin = "1.6.2"
|
||||
|
||||
|
||||
[libraries]
|
||||
slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
|
||||
guava = { module = "com.google.guava:guava", version.ref = "guava" }
|
||||
assertj = { module = "org.assertj:assertj-core", version.ref = "assertj" }
|
||||
junit = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }
|
||||
testcontainers = { module = "org.testcontainers:testcontainers", version.ref = "testcontainers" }
|
||||
protoc = { module = "com.google.protobuf:protoc", version.ref = "protoc" }
|
||||
failsafe = { module = "dev.failsafe:failsafe", version.ref = "failsafe" }
|
||||
awaitility = { module = "org.awaitility:awaitility", version.ref = "awaitility" }
|
||||
picocli = { module = "info.picocli:picocli", version.ref = "picocli"}
|
||||
restAssured = { module = "io.rest-assured:rest-assured", version.ref = "restAssured"}
|
||||
|
||||
commonsIo = { module = "commons-io:commons-io", version.ref = "commonsIo" }
|
||||
commonsCompress = { module = "org.apache.commons:commons-compress", version.ref = "commonCompress" }
|
||||
|
||||
mockitoCore = { module = "org.mockito:mockito-core", version.ref = "mockito" }
|
||||
mockitoJunit = { module = "org.mockito:mockito-junit-jupiter", version.ref = "mockito" }
|
||||
|
||||
grpcCore = { module = "io.grpc:grpc-core", version.ref = "grpc" }
|
||||
grpcNetty = { module = "io.grpc:grpc-netty", version.ref = "grpc" }
|
||||
grpcProtobuf = { module = "io.grpc:grpc-protobuf", version.ref = "grpc" }
|
||||
grpcStub = { module = "io.grpc:grpc-stub", version.ref = "grpc" }
|
||||
grpcGrpclb = { module = "io.grpc:grpc-grpclb", version.ref = "grpc" }
|
||||
grpcInprocess = { module = "io.grpc:grpc-inprocess", version.ref = "grpc" }
|
||||
grpcUtil = { module = "io.grpc:grpc-util", version.ref = "grpc" }
|
||||
|
||||
vertxGrpc = { module = "io.vertx:vertx-grpc", version.ref = "vertx" }
|
||||
|
||||
log4jApi = { module = "org.apache.logging.log4j:log4j-api", version.ref = "log4j" }
|
||||
log4jCore = { module = "org.apache.logging.log4j:log4j-core", version.ref = "log4j" }
|
||||
log4jSlf4j = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version.ref = "log4j" }
|
||||
log4j12 = { module = "org.apache.logging.log4j:log4j-1.2-api", version.ref = "log4j" }
|
||||
|
||||
autoServiceAnnotations = { module = "com.google.auto.service:auto-service-annotations", version.ref = "autoService"}
|
||||
autoServiceProcessor = { module = "com.google.auto.service:auto-service", version.ref = "autoService"}
|
||||
|
||||
javaxAnnotation = { module = "javax.annotation:javax.annotation-api", version.ref = "javaxAnnotation" }
|
||||
|
||||
errorprone = { module = "com.google.errorprone:error_prone_core", version.ref = "errorprone" }
|
||||
errorproneAnnotations = { module = "com.google.errorprone:error_prone_annotations", version.ref = "errorprone" }
|
||||
errorproneJavac = { module = "com.google.errorprone:javac", version = "9+" }
|
||||
|
||||
[bundles]
|
||||
grpc = [ "grpcCore", "grpcNetty", "grpcProtobuf", "grpcStub", "grpcGrpclb", "grpcUtil"]
|
||||
grpcTest = [ "grpcInprocess"]
|
||||
log4j = [ "log4jApi", "log4jCore", "log4jSlf4j", "log4j12" ]
|
||||
mockito = [ "mockitoCore", "mockitoJunit" ]
|
||||
testing = ["junit", "assertj", "mockitoCore", "mockitoJunit"]
|
||||
javax = [ "javaxAnnotation" ]
|
||||
|
||||
|
||||
[plugins]
|
||||
shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadowPlugin" }
|
||||
protobuf = { id = "com.google.protobuf", version.ref = "protobufPlugin" }
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016-2021 The jetcd authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
apply plugin: "signing"
|
||||
apply plugin: "maven-publish"
|
||||
apply plugin: "io.github.gradle-nexus.publish-plugin"
|
||||
|
||||
ext {
|
||||
if (!project.hasProperty('nexusUsername')) {
|
||||
nexusUsername = "$System.env.NEXUS_USERNAME"
|
||||
}
|
||||
if (!project.hasProperty('nexusPassword')) {
|
||||
nexusPassword = "$System.env.NEXUS_PASSWORD"
|
||||
}
|
||||
}
|
||||
|
||||
nexusPublishing {
|
||||
repositories {
|
||||
sonatype {
|
||||
username = nexusUsername
|
||||
password = nexusPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//afterReleaseBuild.dependsOn publishToSonatype
|
|
@ -1,25 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016-2023 The jetcd authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
task pubblications {
|
||||
doLast {
|
||||
project.publishing.publications.each { publication ->
|
||||
println "Publication $publication.name [$publication.groupId/$publication.artifactId/$publication.version]"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016-2023 The jetcd authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
apply plugin: 'java-library'
|
||||
apply plugin: 'maven-publish'
|
||||
apply plugin: 'signing'
|
||||
|
||||
java {
|
||||
withJavadocJar()
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
"${project.name}"(MavenPublication) {
|
||||
groupId = rootProject.group
|
||||
|
||||
from components.java
|
||||
|
||||
pom {
|
||||
name = project.name
|
||||
description = project.name
|
||||
url = "${gitProject}"
|
||||
|
||||
scm {
|
||||
url = "${gitProject}"
|
||||
connection = "scm:${gitProject}"
|
||||
developerConnection = "scm:${gitURL}"
|
||||
}
|
||||
|
||||
licenses {
|
||||
license {
|
||||
name = 'The Apache Software License, Version 2.0'
|
||||
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
|
||||
distribution = 'repo'
|
||||
}
|
||||
}
|
||||
|
||||
developers {
|
||||
developer {
|
||||
id = 'lburgazzoli'
|
||||
name = 'Luca Burgazzoli'
|
||||
organization = 'Red Hat'
|
||||
organizationUrl = 'http://redhat.com'
|
||||
}
|
||||
developer {
|
||||
name = 'Fanmin Shi'
|
||||
organization = 'CoreOS'
|
||||
organizationUrl = 'http://coreos.com'
|
||||
}
|
||||
developer {
|
||||
name = 'Xiang Li'
|
||||
organization = 'CoreOS'
|
||||
organizationUrl = 'http://coreos.com'
|
||||
}
|
||||
developer {
|
||||
name = 'Anthony Romano'
|
||||
organization = 'CoreOS'
|
||||
organizationUrl = 'http://coreos.com'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signing {
|
||||
required {
|
||||
!version.endsWith('SNAPSHOT')
|
||||
}
|
||||
|
||||
if (!version.endsWith('SNAPSHOT')) {
|
||||
useGpgCmd()
|
||||
}
|
||||
|
||||
sign publishing.publications."${project.name}"
|
||||
}
|
||||
|
||||
check.dependsOn javadoc
|
||||
|
||||
javadoc {
|
||||
if(JavaVersion.current().isJava9Compatible()) {
|
||||
options.addBooleanOption('html5', true)
|
||||
}
|
||||
|
||||
exclude "**/io/etcd/jetcd/api/**"
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016-2021 The jetcd authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
apply plugin: "net.ltgt.errorprone"
|
||||
apply plugin: "pmd"
|
||||
|
||||
dependencies {
|
||||
errorprone(libs.errorprone)
|
||||
errorprone(libs.errorproneAnnotations)
|
||||
errorproneJavac(libs.errorproneJavac)
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.errorprone {
|
||||
excludedPaths = '.*/generated/.*'
|
||||
disableWarningsInGeneratedCode = true
|
||||
}
|
||||
options.deprecation = true
|
||||
}
|
||||
|
||||
pmd {
|
||||
consoleOutput = true
|
||||
ruleSets = [ "${project.rootDir}/etc/pmd-ruleset.xml" ]
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016-2023 The jetcd authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
apply plugin: "pl.allegro.tech.build.axion-release"
|
||||
|
||||
|
||||
scmVersion {
|
||||
tag {
|
||||
prefix = 'jetcd-'
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
project.version = scmVersion.version
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016-2021 The jetcd authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
apply plugin: "com.diffplug.spotless"
|
||||
|
||||
spotless {
|
||||
java {
|
||||
removeUnusedImports()
|
||||
trimTrailingWhitespace()
|
||||
indentWithSpaces(4)
|
||||
endWithNewline()
|
||||
importOrderFile(rootProject.file('etc/eclipse.importorder'))
|
||||
eclipse().configFile(rootProject.file('etc/eclipse-formatter-config.xml'))
|
||||
targetExclude("build/generated/**/*.java")
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016-2021 The jetcd authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
apply plugin: 'com.github.ben-manes.versions'
|
||||
|
||||
dependencyUpdates.configure {
|
||||
|
||||
def isNonStable = { String version ->
|
||||
return ['alpha', 'beta', 'rc', 'cr', 'm', 'ea'].any { keyword -> version.toUpperCase().contains(keyword.toUpperCase())}
|
||||
}
|
||||
|
||||
rejectVersionIf {
|
||||
isNonStable(it.candidate.version)
|
||||
}
|
||||
}
|
||||
|
||||
task deps {
|
||||
dependsOn dependencyUpdates
|
||||
}
|
Binary file not shown.
|
@ -1,7 +0,0 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
|
@ -1,251 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH="\\\"\\\""
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
|
@ -1,94 +0,0 @@
|
|||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
|
@ -0,0 +1,135 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Copyright 2016-2021 The jetcd authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<parent>
|
||||
<groupId>io.etcd</groupId>
|
||||
<artifactId>jetcd-parent</artifactId>
|
||||
<version>0.5.6</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetcd-all</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.etcd</groupId>
|
||||
<artifactId>jetcd-common</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>animal-sniffer-annotations</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.checkerframework</groupId>
|
||||
<artifactId>checker-compat-qual</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.google.errorprone</groupId>
|
||||
<artifactId>error_prone_annotations</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.google.j2objc</groupId>
|
||||
<artifactId>j2objc-annotations</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.etcd</groupId>
|
||||
<artifactId>jetcd-core</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.checkerframework</groupId>
|
||||
<artifactId>checker-compat-qual</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.etcd</groupId>
|
||||
<artifactId>jetcd-extensions</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<createSourcesJar>true</createSourcesJar>
|
||||
<createDependencyReducedPom>true</createDependencyReducedPom>
|
||||
<shadedArtifactAttached>false</shadedArtifactAttached>
|
||||
<minimizeJar>false</minimizeJar>
|
||||
<artifactSet>
|
||||
<excludes>
|
||||
<exclude>org.slf4j:slf4j-api</exclude>
|
||||
<exclude>com.google.errorprone:error_prone_annotations</exclude>
|
||||
</excludes>
|
||||
</artifactSet>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>com.google</pattern>
|
||||
<shadedPattern>io.etcd.jetcd.shaded.com.google</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>io.grpc</pattern>
|
||||
<shadedPattern>io.etcd.jetcd.shaded.io.grpc</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>io.netty</pattern>
|
||||
<shadedPattern>io.etcd.jetcd.shaded.io.netty</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>javax.annotation</pattern>
|
||||
<shadedPattern>io.etcd.jetcd.shaded.javax.annotation</shadedPattern></relocation>
|
||||
<relocation>
|
||||
<pattern>io.opencensus</pattern>
|
||||
<shadedPattern>io.etcd.jetcd.shaded.io.opencensus</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>io.opencensus</pattern>
|
||||
<shadedPattern>io.etcd.jetcd.shaded.io.opencensus</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>net.jodah</pattern>
|
||||
<shadedPattern>io.etcd.jetcd.shaded.net.jodah</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -14,12 +14,15 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.etcd.jetcd.all;
|
||||
|
||||
dependencies {
|
||||
api libs.slf4j
|
||||
api libs.guava
|
||||
api libs.grpcCore
|
||||
|
||||
testImplementation libs.bundles.testing
|
||||
testRuntimeOnly libs.bundles.log4j
|
||||
/**
|
||||
* Placeholder.
|
||||
*/
|
||||
public final class JetcdAll {
|
||||
/**
|
||||
* Private c-tor.
|
||||
*/
|
||||
private JetcdAll() {
|
||||
}
|
||||
}
|
|
@ -14,7 +14,4 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
dependencies {
|
||||
api libs.slf4j
|
||||
}
|
||||
package io.etcd.jetcd.all;
|
|
@ -0,0 +1,69 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Copyright 2016-2021 The jetcd authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<parent>
|
||||
<groupId>io.etcd</groupId>
|
||||
<artifactId>jetcd-parent</artifactId>
|
||||
<version>0.5.6</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetcd-common</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>io/etcd/jetcd/api/**</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016-2021 The jetcd authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT 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 io.etcd.jetcd.common;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public abstract class Service implements AutoCloseable {
|
||||
private final AtomicBoolean running;
|
||||
|
||||
protected Service() {
|
||||
this.running = new AtomicBoolean();
|
||||
}
|
||||
|
||||
public void start() {
|
||||
if (this.running.compareAndSet(false, true)) {
|
||||
doStart();
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (this.running.compareAndSet(true, false)) {
|
||||
doStop();
|
||||
}
|
||||
}
|
||||
|
||||
public void restart() {
|
||||
stop();
|
||||
start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
stop();
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return running.get();
|
||||
}
|
||||
|
||||
protected abstract void doStart();
|
||||
|
||||
protected abstract void doStop();
|
||||
}
|
|
@ -31,22 +31,12 @@ import io.grpc.Status;
|
|||
*/
|
||||
public enum ErrorCode {
|
||||
|
||||
CANCELLED(Status.CANCELLED),
|
||||
UNKNOWN(Status.UNKNOWN),
|
||||
INVALID_ARGUMENT(Status.INVALID_ARGUMENT),
|
||||
DEADLINE_EXCEEDED(Status.DEADLINE_EXCEEDED),
|
||||
NOT_FOUND(Status.NOT_FOUND),
|
||||
ALREADY_EXISTS(Status.ALREADY_EXISTS),
|
||||
PERMISSION_DENIED(Status.PERMISSION_DENIED),
|
||||
UNAUTHENTICATED(Status.UNAUTHENTICATED),
|
||||
RESOURCE_EXHAUSTED(Status.RESOURCE_EXHAUSTED),
|
||||
FAILED_PRECONDITION(Status.FAILED_PRECONDITION),
|
||||
ABORTED(Status.ABORTED),
|
||||
OUT_OF_RANGE(Status.OUT_OF_RANGE),
|
||||
UNIMPLEMENTED(Status.UNIMPLEMENTED),
|
||||
INTERNAL(Status.INTERNAL),
|
||||
UNAVAILABLE(Status.UNAVAILABLE),
|
||||
DATA_LOSS(Status.DATA_LOSS),;
|
||||
CANCELLED(Status.CANCELLED), UNKNOWN(Status.UNKNOWN), INVALID_ARGUMENT(Status.INVALID_ARGUMENT),
|
||||
DEADLINE_EXCEEDED(Status.DEADLINE_EXCEEDED), NOT_FOUND(Status.NOT_FOUND), ALREADY_EXISTS(Status.ALREADY_EXISTS),
|
||||
PERMISSION_DENIED(Status.PERMISSION_DENIED), UNAUTHENTICATED(Status.UNAUTHENTICATED),
|
||||
RESOURCE_EXHAUSTED(Status.RESOURCE_EXHAUSTED), FAILED_PRECONDITION(Status.FAILED_PRECONDITION), ABORTED(Status.ABORTED),
|
||||
OUT_OF_RANGE(Status.OUT_OF_RANGE), UNIMPLEMENTED(Status.UNIMPLEMENTED), INTERNAL(Status.INTERNAL),
|
||||
UNAVAILABLE(Status.UNAVAILABLE), DATA_LOSS(Status.DATA_LOSS),;
|
||||
|
||||
private static final Map<Integer, ErrorCode> errorByRpcCode;
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
package io.etcd.jetcd.common.exception;
|
||||
|
||||
import java.util.Objects;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Base exception type for all exceptions produced by the etcd service.
|
||||
|
@ -27,13 +27,11 @@ public class EtcdException extends RuntimeException {
|
|||
|
||||
EtcdException(ErrorCode code, String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.code = Objects.requireNonNull(code, "Error code must not be null");
|
||||
this.code = checkNotNull(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the error code associated with this exception.
|
||||
*
|
||||
* @return a {@link ErrorCode}
|
||||
*/
|
||||
public ErrorCode getErrorCode() {
|
||||
return code;
|
||||
|
|
|
@ -16,10 +16,9 @@
|
|||
|
||||
package io.etcd.jetcd.common.exception;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import io.grpc.Status;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static io.grpc.Status.fromThrowable;
|
||||
|
||||
/**
|
||||
|
@ -66,7 +65,7 @@ public final class EtcdExceptionFactory {
|
|||
}
|
||||
|
||||
public static EtcdException toEtcdException(Throwable cause) {
|
||||
Objects.requireNonNull(cause, "cause can't be null");
|
||||
checkNotNull(cause, "cause can't be null");
|
||||
if (cause instanceof EtcdException) {
|
||||
return (EtcdException) cause;
|
||||
}
|
||||
|
@ -75,7 +74,7 @@ public final class EtcdExceptionFactory {
|
|||
}
|
||||
|
||||
public static EtcdException toEtcdException(Status status) {
|
||||
Objects.requireNonNull(status, "status can't be null");
|
||||
checkNotNull(status, "status can't be null");
|
||||
return fromStatus(status);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016-2021 The jetcd authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
dependencies {
|
||||
api project(':jetcd-grpc')
|
||||
api project(':jetcd-api')
|
||||
api project(':jetcd-common')
|
||||
|
||||
api libs.slf4j
|
||||
api libs.guava
|
||||
api libs.failsafe
|
||||
|
||||
//compileOnly libs.javaxAnnotation
|
||||
compileOnly libs.autoServiceAnnotations
|
||||
|
||||
annotationProcessor libs.autoServiceProcessor
|
||||
|
||||
testImplementation project(':jetcd-launcher')
|
||||
testImplementation project(':jetcd-test')
|
||||
|
||||
testImplementation libs.restAssured
|
||||
testImplementation libs.awaitility
|
||||
testImplementation libs.commonsIo
|
||||
testImplementation libs.bundles.testing
|
||||
|
||||
testRuntimeOnly libs.bundles.log4j
|
||||
}
|
|
@ -0,0 +1,232 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Copyright 2016-2021 The jetcd authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<parent>
|
||||
<groupId>io.etcd</groupId>
|
||||
<artifactId>jetcd-parent</artifactId>
|
||||
<version>0.5.6</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetcd-core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>jetcd-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-netty</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-protobuf</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-stub</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-grpclb</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.jodah</groupId>
|
||||
<artifactId>failsafe</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.annotation</groupId>
|
||||
<artifactId>javax.annotation-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.auto.service</groupId>
|
||||
<artifactId>auto-service-annotations</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-slf4j-impl</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-1.2-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-migrationsupport</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-junit-jupiter</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.awaitility</groupId>
|
||||
<artifactId>awaitility</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-testing</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-tcnative-boringssl-static</artifactId>
|
||||
<version>${netty-tcnative.version}</version>
|
||||
<classifier>${os.detected.classifier}</classifier>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>jetcd-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>kr.motd.maven</groupId>
|
||||
<artifactId>os-maven-plugin</artifactId>
|
||||
<version>${os-maven-plugin.version}</version>
|
||||
<extensions>true</extensions>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>detect</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.xolstice.maven.plugins</groupId>
|
||||
<artifactId>protobuf-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<protocArtifact>
|
||||
com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}
|
||||
</protocArtifact>
|
||||
<pluginId>grpc-java</pluginId>
|
||||
<pluginArtifact>
|
||||
io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}
|
||||
</pluginArtifact>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
<goal>compile-custom</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>test</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>add-source</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sources>
|
||||
<source>${basedir}/target/generated-sources/protobuf/grpc-java</source>
|
||||
<source>${basedir}/target/generated-sources/protobuf/java</source>
|
||||
</sources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>io/etcd/jetcd/api/**</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -14,9 +14,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.etcd.jetcd.impl;
|
||||
package io.etcd.jetcd;
|
||||
|
||||
import io.etcd.jetcd.Response;
|
||||
import io.etcd.jetcd.api.ResponseHeader;
|
||||
|
||||
public class AbstractResponse<R> implements Response {
|
|
@ -14,14 +14,13 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.etcd.jetcd.impl;
|
||||
package io.etcd.jetcd;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import io.etcd.jetcd.Auth;
|
||||
import io.etcd.jetcd.ByteSequence;
|
||||
import io.etcd.jetcd.api.AuthDisableRequest;
|
||||
import io.etcd.jetcd.api.AuthEnableRequest;
|
||||
import io.etcd.jetcd.api.AuthGrpc;
|
||||
import io.etcd.jetcd.api.AuthRoleAddRequest;
|
||||
import io.etcd.jetcd.api.AuthRoleDeleteRequest;
|
||||
import io.etcd.jetcd.api.AuthRoleGetRequest;
|
||||
|
@ -35,7 +34,7 @@ import io.etcd.jetcd.api.AuthUserGetRequest;
|
|||
import io.etcd.jetcd.api.AuthUserGrantRoleRequest;
|
||||
import io.etcd.jetcd.api.AuthUserListRequest;
|
||||
import io.etcd.jetcd.api.AuthUserRevokeRoleRequest;
|
||||
import io.etcd.jetcd.api.VertxAuthGrpc;
|
||||
import io.etcd.jetcd.api.Permission.Type;
|
||||
import io.etcd.jetcd.auth.AuthDisableResponse;
|
||||
import io.etcd.jetcd.auth.AuthEnableResponse;
|
||||
import io.etcd.jetcd.auth.AuthRoleAddResponse;
|
||||
|
@ -53,235 +52,180 @@ import io.etcd.jetcd.auth.AuthUserListResponse;
|
|||
import io.etcd.jetcd.auth.AuthUserRevokeRoleResponse;
|
||||
import io.etcd.jetcd.auth.Permission;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Implementation of etcd auth client.
|
||||
*/
|
||||
final class AuthImpl extends Impl implements Auth {
|
||||
final class AuthImpl implements Auth {
|
||||
|
||||
private final VertxAuthGrpc.AuthVertxStub stub;
|
||||
private final AuthGrpc.AuthFutureStub stub;
|
||||
private final ClientConnectionManager connectionManager;
|
||||
|
||||
AuthImpl(ClientConnectionManager connectionManager) {
|
||||
super(connectionManager);
|
||||
|
||||
this.stub = connectionManager.newStub(VertxAuthGrpc::newVertxStub);
|
||||
this.connectionManager = connectionManager;
|
||||
this.stub = connectionManager.newStub(AuthGrpc::newFutureStub);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<AuthEnableResponse> authEnable() {
|
||||
AuthEnableRequest enableRequest = AuthEnableRequest.getDefaultInstance();
|
||||
return completable(
|
||||
this.stub.authEnable(enableRequest),
|
||||
AuthEnableResponse::new);
|
||||
return Util.toCompletableFuture(this.stub.authEnable(enableRequest), AuthEnableResponse::new,
|
||||
this.connectionManager.getExecutorService());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<AuthDisableResponse> authDisable() {
|
||||
AuthDisableRequest disableRequest = AuthDisableRequest.getDefaultInstance();
|
||||
return completable(
|
||||
this.stub.authDisable(disableRequest),
|
||||
AuthDisableResponse::new);
|
||||
return Util.toCompletableFuture(this.stub.authDisable(disableRequest), AuthDisableResponse::new,
|
||||
this.connectionManager.getExecutorService());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<AuthUserAddResponse> userAdd(ByteSequence user, ByteSequence password) {
|
||||
requireNonNull(user, "user can't be null");
|
||||
requireNonNull(password, "password can't be null");
|
||||
checkNotNull(user, "user can't be null");
|
||||
checkNotNull(password, "password can't be null");
|
||||
|
||||
AuthUserAddRequest addRequest = AuthUserAddRequest.newBuilder()
|
||||
.setNameBytes(ByteString.copyFrom(user.getBytes()))
|
||||
.setPasswordBytes(ByteString.copyFrom(password.getBytes()))
|
||||
.build();
|
||||
|
||||
return completable(
|
||||
this.stub.userAdd(addRequest),
|
||||
AuthUserAddResponse::new);
|
||||
AuthUserAddRequest addRequest = AuthUserAddRequest.newBuilder().setNameBytes(user.getByteString())
|
||||
.setPasswordBytes(password.getByteString()).build();
|
||||
return Util.toCompletableFuture(this.stub.userAdd(addRequest), AuthUserAddResponse::new,
|
||||
this.connectionManager.getExecutorService());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<AuthUserDeleteResponse> userDelete(ByteSequence user) {
|
||||
requireNonNull(user, "user can't be null");
|
||||
checkNotNull(user, "user can't be null");
|
||||
|
||||
AuthUserDeleteRequest deleteRequest = AuthUserDeleteRequest.newBuilder()
|
||||
.setNameBytes(ByteString.copyFrom(user.getBytes()))
|
||||
.build();
|
||||
|
||||
return completable(
|
||||
this.stub.userDelete(deleteRequest),
|
||||
AuthUserDeleteResponse::new);
|
||||
AuthUserDeleteRequest deleteRequest = AuthUserDeleteRequest.newBuilder().setNameBytes(user.getByteString()).build();
|
||||
return Util.toCompletableFuture(this.stub.userDelete(deleteRequest), AuthUserDeleteResponse::new,
|
||||
this.connectionManager.getExecutorService());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<AuthUserChangePasswordResponse> userChangePassword(ByteSequence user, ByteSequence password) {
|
||||
requireNonNull(user, "user can't be null");
|
||||
requireNonNull(password, "password can't be null");
|
||||
checkNotNull(user, "user can't be null");
|
||||
checkNotNull(password, "password can't be null");
|
||||
|
||||
AuthUserChangePasswordRequest changePasswordRequest = AuthUserChangePasswordRequest.newBuilder()
|
||||
.setNameBytes(ByteString.copyFrom(user.getBytes()))
|
||||
.setPasswordBytes(ByteString.copyFrom(password.getBytes()))
|
||||
.build();
|
||||
|
||||
return completable(
|
||||
this.stub.userChangePassword(changePasswordRequest),
|
||||
AuthUserChangePasswordResponse::new);
|
||||
.setNameBytes(user.getByteString()).setPasswordBytes(password.getByteString()).build();
|
||||
return Util.toCompletableFuture(this.stub.userChangePassword(changePasswordRequest),
|
||||
AuthUserChangePasswordResponse::new, this.connectionManager.getExecutorService());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<AuthUserGetResponse> userGet(ByteSequence user) {
|
||||
requireNonNull(user, "user can't be null");
|
||||
checkNotNull(user, "user can't be null");
|
||||
|
||||
AuthUserGetRequest userGetRequest = AuthUserGetRequest.newBuilder()
|
||||
.setNameBytes(ByteString.copyFrom(user.getBytes()))
|
||||
.build();
|
||||
|
||||
return completable(
|
||||
this.stub.userGet(userGetRequest),
|
||||
AuthUserGetResponse::new);
|
||||
AuthUserGetRequest userGetRequest = AuthUserGetRequest.newBuilder().setNameBytes(user.getByteString()).build();
|
||||
return Util.toCompletableFuture(this.stub.userGet(userGetRequest), AuthUserGetResponse::new,
|
||||
this.connectionManager.getExecutorService());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<AuthUserListResponse> userList() {
|
||||
AuthUserListRequest userListRequest = AuthUserListRequest.getDefaultInstance();
|
||||
|
||||
return completable(
|
||||
this.stub.userList(userListRequest),
|
||||
AuthUserListResponse::new);
|
||||
return Util.toCompletableFuture(this.stub.userList(userListRequest), AuthUserListResponse::new,
|
||||
this.connectionManager.getExecutorService());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<AuthUserGrantRoleResponse> userGrantRole(ByteSequence user, ByteSequence role) {
|
||||
requireNonNull(user, "user can't be null");
|
||||
requireNonNull(role, "key can't be null");
|
||||
checkNotNull(user, "user can't be null");
|
||||
checkNotNull(role, "key can't be null");
|
||||
|
||||
AuthUserGrantRoleRequest userGrantRoleRequest = AuthUserGrantRoleRequest.newBuilder()
|
||||
.setUserBytes(ByteString.copyFrom(user.getBytes()))
|
||||
.setRoleBytes(ByteString.copyFrom(role.getBytes()))
|
||||
.build();
|
||||
|
||||
return completable(
|
||||
this.stub.userGrantRole(userGrantRoleRequest),
|
||||
AuthUserGrantRoleResponse::new);
|
||||
AuthUserGrantRoleRequest userGrantRoleRequest = AuthUserGrantRoleRequest.newBuilder().setUserBytes(user.getByteString())
|
||||
.setRoleBytes(role.getByteString()).build();
|
||||
return Util.toCompletableFuture(this.stub.userGrantRole(userGrantRoleRequest), AuthUserGrantRoleResponse::new,
|
||||
this.connectionManager.getExecutorService());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<AuthUserRevokeRoleResponse> userRevokeRole(ByteSequence user, ByteSequence role) {
|
||||
requireNonNull(user, "user can't be null");
|
||||
requireNonNull(role, "key can't be null");
|
||||
checkNotNull(user, "user can't be null");
|
||||
checkNotNull(role, "key can't be null");
|
||||
|
||||
AuthUserRevokeRoleRequest userRevokeRoleRequest = AuthUserRevokeRoleRequest.newBuilder()
|
||||
.setNameBytes(ByteString.copyFrom(user.getBytes()))
|
||||
.setRoleBytes(ByteString.copyFrom(role.getBytes()))
|
||||
.build();
|
||||
|
||||
return completable(
|
||||
this.stub.userRevokeRole(userRevokeRoleRequest),
|
||||
AuthUserRevokeRoleResponse::new);
|
||||
.setNameBytes(user.getByteString()).setRoleBytes(role.getByteString()).build();
|
||||
return Util.toCompletableFuture(this.stub.userRevokeRole(userRevokeRoleRequest), AuthUserRevokeRoleResponse::new,
|
||||
this.connectionManager.getExecutorService());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<AuthRoleAddResponse> roleAdd(ByteSequence user) {
|
||||
requireNonNull(user, "user can't be null");
|
||||
checkNotNull(user, "user can't be null");
|
||||
|
||||
AuthRoleAddRequest roleAddRequest = AuthRoleAddRequest.newBuilder().setNameBytes(ByteString.copyFrom(user.getBytes()))
|
||||
.build();
|
||||
|
||||
return completable(
|
||||
this.stub.roleAdd(roleAddRequest),
|
||||
AuthRoleAddResponse::new);
|
||||
AuthRoleAddRequest roleAddRequest = AuthRoleAddRequest.newBuilder().setNameBytes(user.getByteString()).build();
|
||||
return Util.toCompletableFuture(this.stub.roleAdd(roleAddRequest), AuthRoleAddResponse::new,
|
||||
this.connectionManager.getExecutorService());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<AuthRoleGrantPermissionResponse> roleGrantPermission(ByteSequence role, ByteSequence key,
|
||||
ByteSequence rangeEnd, Permission.Type permType) {
|
||||
requireNonNull(role, "role can't be null");
|
||||
requireNonNull(key, "key can't be null");
|
||||
requireNonNull(rangeEnd, "rangeEnd can't be null");
|
||||
requireNonNull(permType, "permType can't be null");
|
||||
checkNotNull(role, "role can't be null");
|
||||
checkNotNull(key, "key can't be null");
|
||||
checkNotNull(rangeEnd, "rangeEnd can't be null");
|
||||
checkNotNull(permType, "permType can't be null");
|
||||
|
||||
io.etcd.jetcd.api.Permission.Type type;
|
||||
switch (permType) {
|
||||
case WRITE:
|
||||
type = io.etcd.jetcd.api.Permission.Type.WRITE;
|
||||
type = Type.WRITE;
|
||||
break;
|
||||
case READWRITE:
|
||||
type = io.etcd.jetcd.api.Permission.Type.READWRITE;
|
||||
type = Type.READWRITE;
|
||||
break;
|
||||
case READ:
|
||||
type = io.etcd.jetcd.api.Permission.Type.READ;
|
||||
type = Type.READ;
|
||||
break;
|
||||
default:
|
||||
type = io.etcd.jetcd.api.Permission.Type.UNRECOGNIZED;
|
||||
type = Type.UNRECOGNIZED;
|
||||
break;
|
||||
}
|
||||
|
||||
io.etcd.jetcd.api.Permission perm = io.etcd.jetcd.api.Permission.newBuilder()
|
||||
.setKey(ByteString.copyFrom(key.getBytes()))
|
||||
.setRangeEnd(ByteString.copyFrom(rangeEnd.getBytes()))
|
||||
.setPermType(type)
|
||||
.build();
|
||||
|
||||
io.etcd.jetcd.api.Permission perm = io.etcd.jetcd.api.Permission.newBuilder().setKey(key.getByteString())
|
||||
.setRangeEnd(rangeEnd.getByteString()).setPermType(type).build();
|
||||
AuthRoleGrantPermissionRequest roleGrantPermissionRequest = AuthRoleGrantPermissionRequest.newBuilder()
|
||||
.setNameBytes(ByteString.copyFrom(role.getBytes()))
|
||||
.setPerm(perm)
|
||||
.build();
|
||||
|
||||
return completable(
|
||||
this.stub.roleGrantPermission(roleGrantPermissionRequest),
|
||||
AuthRoleGrantPermissionResponse::new);
|
||||
.setNameBytes(role.getByteString()).setPerm(perm).build();
|
||||
return Util.toCompletableFuture(this.stub.roleGrantPermission(roleGrantPermissionRequest),
|
||||
AuthRoleGrantPermissionResponse::new, this.connectionManager.getExecutorService());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<AuthRoleGetResponse> roleGet(ByteSequence role) {
|
||||
requireNonNull(role, "role can't be null");
|
||||
checkNotNull(role, "role can't be null");
|
||||
|
||||
AuthRoleGetRequest roleGetRequest = AuthRoleGetRequest.newBuilder()
|
||||
.setRoleBytes(ByteString.copyFrom(role.getBytes()))
|
||||
.build();
|
||||
|
||||
return completable(
|
||||
this.stub.roleGet(roleGetRequest),
|
||||
AuthRoleGetResponse::new);
|
||||
AuthRoleGetRequest roleGetRequest = AuthRoleGetRequest.newBuilder().setRoleBytes(role.getByteString()).build();
|
||||
return Util.toCompletableFuture(this.stub.roleGet(roleGetRequest), AuthRoleGetResponse::new,
|
||||
this.connectionManager.getExecutorService());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<AuthRoleListResponse> roleList() {
|
||||
AuthRoleListRequest roleListRequest = AuthRoleListRequest.getDefaultInstance();
|
||||
|
||||
return completable(
|
||||
this.stub.roleList(roleListRequest),
|
||||
AuthRoleListResponse::new);
|
||||
return Util.toCompletableFuture(this.stub.roleList(roleListRequest), AuthRoleListResponse::new,
|
||||
this.connectionManager.getExecutorService());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<AuthRoleRevokePermissionResponse> roleRevokePermission(ByteSequence role, ByteSequence key,
|
||||
ByteSequence rangeEnd) {
|
||||
requireNonNull(role, "role can't be null");
|
||||
requireNonNull(key, "key can't be null");
|
||||
requireNonNull(rangeEnd, "rangeEnd can't be null");
|
||||
checkNotNull(role, "role can't be null");
|
||||
checkNotNull(key, "key can't be null");
|
||||
checkNotNull(rangeEnd, "rangeEnd can't be null");
|
||||
|
||||
AuthRoleRevokePermissionRequest roleRevokePermissionRequest = AuthRoleRevokePermissionRequest.newBuilder()
|
||||
.setRoleBytes(ByteString.copyFrom(role.getBytes()))
|
||||
.setKey(ByteString.copyFrom(key.getBytes()))
|
||||
.setRangeEnd(ByteString.copyFrom(rangeEnd.getBytes()))
|
||||
.setRoleBytes(role.getByteString()).setKeyBytes(key.getByteString()).setRangeEndBytes(rangeEnd.getByteString())
|
||||
.build();
|
||||
|
||||
return completable(
|
||||
this.stub.roleRevokePermission(roleRevokePermissionRequest),
|
||||
AuthRoleRevokePermissionResponse::new);
|
||||
return Util.toCompletableFuture(this.stub.roleRevokePermission(roleRevokePermissionRequest),
|
||||
AuthRoleRevokePermissionResponse::new, this.connectionManager.getExecutorService());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<AuthRoleDeleteResponse> roleDelete(ByteSequence role) {
|
||||
requireNonNull(role, "role can't be null");
|
||||
AuthRoleDeleteRequest roleDeleteRequest = AuthRoleDeleteRequest.newBuilder()
|
||||
.setRoleBytes(ByteString.copyFrom(role.getBytes()))
|
||||
.build();
|
||||
|
||||
return completable(
|
||||
this.stub.roleDelete(roleDeleteRequest),
|
||||
AuthRoleDeleteResponse::new);
|
||||
checkNotNull(role, "role can't be null");
|
||||
AuthRoleDeleteRequest roleDeleteRequest = AuthRoleDeleteRequest.newBuilder().setRoleBytes(role.getByteString()).build();
|
||||
return Util.toCompletableFuture(this.stub.roleDelete(roleDeleteRequest), AuthRoleDeleteResponse::new,
|
||||
this.connectionManager.getExecutorService());
|
||||
}
|
||||
}
|
|
@ -17,7 +17,6 @@
|
|||
package io.etcd.jetcd;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
|
@ -32,7 +31,6 @@ public final class ByteSequence {
|
|||
private final ByteString byteString;
|
||||
|
||||
private ByteSequence(ByteString byteString) {
|
||||
Objects.requireNonNull(byteString, "byteString should not be null");
|
||||
this.byteString = byteString;
|
||||
this.hashVal = byteString.hashCode();
|
||||
}
|
||||
|
@ -45,10 +43,9 @@ public final class ByteSequence {
|
|||
* byte sequence represented by this string; <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean startsWith(ByteSequence prefix) {
|
||||
if (prefix == null) {
|
||||
return false;
|
||||
}
|
||||
return byteString.startsWith(prefix.byteString);
|
||||
ByteString baseByteString = this.getByteString();
|
||||
ByteString prefixByteString = prefix.getByteString();
|
||||
return baseByteString.startsWith(prefixByteString);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -58,19 +55,7 @@ public final class ByteSequence {
|
|||
* @return a new {@code ByteSequence} instance
|
||||
*/
|
||||
public ByteSequence concat(ByteSequence other) {
|
||||
Objects.requireNonNull(other, "other byteSequence should not be null");
|
||||
return new ByteSequence(this.byteString.concat(other.byteString));
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenate the given {@code ByteSequence} to this one.
|
||||
*
|
||||
* @param other string to concatenate
|
||||
* @return a new {@code ByteSequence} instance
|
||||
*/
|
||||
public ByteSequence concat(ByteString other) {
|
||||
Objects.requireNonNull(other, "other byteSequence should not be null");
|
||||
return new ByteSequence(this.byteString.concat(other));
|
||||
return new ByteSequence(this.byteString.concat(other.getByteString()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -122,6 +107,10 @@ public final class ByteSequence {
|
|||
return hashVal;
|
||||
}
|
||||
|
||||
ByteString getByteString() {
|
||||
return this.byteString;
|
||||
}
|
||||
|
||||
public String toString(Charset charset) {
|
||||
return byteString.toString(charset);
|
||||
}
|
||||
|
@ -138,11 +127,6 @@ public final class ByteSequence {
|
|||
return byteString.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return byteString.toStringUtf8();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new ByteSequence from a String.
|
||||
*
|
||||
|
@ -152,26 +136,16 @@ public final class ByteSequence {
|
|||
*/
|
||||
public static ByteSequence from(String source, Charset charset) {
|
||||
byte[] bytes = source.getBytes(charset);
|
||||
|
||||
return new ByteSequence(ByteString.copyFrom(bytes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new ByteSequence from a {@link ByteString}.
|
||||
*
|
||||
* @param source input {@link ByteString}
|
||||
* @return the ByteSequence
|
||||
*/
|
||||
public static ByteSequence from(ByteString source) {
|
||||
return new ByteSequence(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new ByteSequence from raw bytes.
|
||||
*
|
||||
* @param source input bytes
|
||||
* @return the ByteSequence
|
||||
*/
|
||||
public static ByteSequence from(byte[] source) {
|
||||
return new ByteSequence(ByteString.copyFrom(source));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,58 +26,42 @@ package io.etcd.jetcd;
|
|||
public interface Client extends AutoCloseable {
|
||||
|
||||
/**
|
||||
* Returns the {@link Auth} client.
|
||||
*
|
||||
* @return the client.
|
||||
* @return the {@link Auth} client.
|
||||
*/
|
||||
Auth getAuthClient();
|
||||
|
||||
/**
|
||||
* Returns the {@link KV} client.
|
||||
*
|
||||
* @return the client.
|
||||
* @return the {@link KV} client.
|
||||
*/
|
||||
KV getKVClient();
|
||||
|
||||
/**
|
||||
* Returns the {@link Cluster} client.
|
||||
*
|
||||
* @return the client.
|
||||
* @return the {@link Cluster} client.
|
||||
*/
|
||||
Cluster getClusterClient();
|
||||
|
||||
/**
|
||||
* Returns the {@link Maintenance} client.
|
||||
*
|
||||
* @return the client.
|
||||
* @return the {@link Maintenance} client.
|
||||
*/
|
||||
Maintenance getMaintenanceClient();
|
||||
|
||||
/**
|
||||
* Returns the {@link Lease} client.
|
||||
*
|
||||
* @return the client.
|
||||
* @return the {@link Lease} client.
|
||||
*/
|
||||
Lease getLeaseClient();
|
||||
|
||||
/**
|
||||
* Returns the {@link Watch} client.
|
||||
*
|
||||
* @return the client.
|
||||
* @return the {@link Watch} client.
|
||||
*/
|
||||
Watch getWatchClient();
|
||||
|
||||
/**
|
||||
* Returns the {@link Lock} client.
|
||||
*
|
||||
* @return the client.
|
||||
* @return the {@link Lock} client.
|
||||
*/
|
||||
Lock getLockClient();
|
||||
|
||||
/**
|
||||
* Returns the {@link Election} client.
|
||||
*
|
||||
* @return the client.
|
||||
* @return the {@link Election} client.
|
||||
*/
|
||||
Election getElectionClient();
|
||||
|
||||
|
@ -85,9 +69,7 @@ public interface Client extends AutoCloseable {
|
|||
void close();
|
||||
|
||||
/**
|
||||
* Returns a new {@link ClientBuilder}.
|
||||
*
|
||||
* @return the builder.
|
||||
* @return a new {@link ClientBuilder}.
|
||||
*/
|
||||
static ClientBuilder builder() {
|
||||
return new ClientBuilder();
|
||||
|
|
|
@ -19,34 +19,38 @@ package io.etcd.jetcd;
|
|||
import java.net.URI;
|
||||
import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
import io.etcd.jetcd.common.exception.EtcdException;
|
||||
import io.etcd.jetcd.common.exception.EtcdExceptionFactory;
|
||||
import io.etcd.jetcd.impl.ClientImpl;
|
||||
import io.etcd.jetcd.resolver.IPNameResolver;
|
||||
import io.grpc.ClientInterceptor;
|
||||
import io.grpc.Metadata;
|
||||
import io.grpc.netty.GrpcSslContexts;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
import io.vertx.core.Vertx;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
||||
/**
|
||||
* ClientBuilder knows how to create a Client instance.
|
||||
* ClientBuilder knows how to create an Client instance.
|
||||
*/
|
||||
public final class ClientBuilder implements Cloneable {
|
||||
|
||||
private String target;
|
||||
private final Set<URI> endpoints = new HashSet<>();
|
||||
private ByteSequence user;
|
||||
private ByteSequence password;
|
||||
private ExecutorService executorService;
|
||||
|
@ -55,64 +59,52 @@ public final class ClientBuilder implements Cloneable {
|
|||
private String authority;
|
||||
private Integer maxInboundMessageSize;
|
||||
private Map<Metadata.Key<?>, Object> headers;
|
||||
private Map<Metadata.Key<?>, Object> authHeaders;
|
||||
private List<ClientInterceptor> interceptors;
|
||||
private List<ClientInterceptor> authInterceptors;
|
||||
private ByteSequence namespace = ByteSequence.EMPTY;
|
||||
private long retryDelay = 500;
|
||||
private long retryMaxDelay = 2500;
|
||||
private int retryMaxAttempts = 2;
|
||||
private ChronoUnit retryChronoUnit = ChronoUnit.MILLIS;
|
||||
private Duration keepaliveTime = Duration.ofSeconds(30L);
|
||||
private Duration keepaliveTimeout = Duration.ofSeconds(10L);
|
||||
private Boolean keepaliveWithoutCalls = true;
|
||||
private Duration retryMaxDuration;
|
||||
private Duration connectTimeout;
|
||||
private boolean waitForReady = true;
|
||||
private Vertx vertx;
|
||||
private boolean discovery;
|
||||
|
||||
ClientBuilder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the etcd target.
|
||||
* gets the endpoints for the builder.
|
||||
*
|
||||
* @return the etcd target.
|
||||
* @return the list of endpoints configured for the builder
|
||||
*/
|
||||
public String target() {
|
||||
return target;
|
||||
public Collection<URI> endpoints() {
|
||||
return Collections.unmodifiableCollection(this.endpoints);
|
||||
}
|
||||
|
||||
/**
|
||||
* configure etcd server endpoints.
|
||||
*
|
||||
* @param target etcd server target
|
||||
* @return this builder to train
|
||||
* @throws NullPointerException if target is null or one of endpoint is null
|
||||
*/
|
||||
public ClientBuilder target(String target) {
|
||||
Preconditions.checkArgument(!Strings.isNullOrEmpty(target), "target can't be null or empty");
|
||||
|
||||
this.target = target;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* configure etcd server endpoints using the {@link IPNameResolver}.
|
||||
*
|
||||
* @param endpoints etcd server endpoints, at least one
|
||||
* @return this builder to train
|
||||
* @throws NullPointerException if endpoints is null or one of endpoint is null
|
||||
* @throws IllegalArgumentException if some endpoint is invalid
|
||||
*/
|
||||
public ClientBuilder endpoints(String... endpoints) {
|
||||
return endpoints(
|
||||
Stream.of(endpoints).map(URI::create).toArray(URI[]::new));
|
||||
public ClientBuilder endpoints(Collection<URI> endpoints) {
|
||||
checkNotNull(endpoints, "endpoints can't be null");
|
||||
|
||||
for (URI endpoint : endpoints) {
|
||||
checkNotNull(endpoint, "endpoint can't be null");
|
||||
checkArgument(endpoint.toString().trim().length() > 0, "invalid endpoint: endpoint=" + endpoint);
|
||||
this.endpoints.add(endpoint);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* configure etcd server endpoints using the {@link IPNameResolver}.
|
||||
* configure etcd server endpoints.
|
||||
*
|
||||
* @param endpoints etcd server endpoints, at least one
|
||||
* @return this builder to train
|
||||
|
@ -120,47 +112,25 @@ public final class ClientBuilder implements Cloneable {
|
|||
* @throws IllegalArgumentException if some endpoint is invalid
|
||||
*/
|
||||
public ClientBuilder endpoints(URI... endpoints) {
|
||||
checkNotNull(endpoints, "endpoints can't be null");
|
||||
|
||||
return endpoints(Arrays.asList(endpoints));
|
||||
}
|
||||
|
||||
/**
|
||||
* configure etcd server endpoints using the {@link IPNameResolver}.
|
||||
* configure etcd server endpoints.
|
||||
*
|
||||
* @param endpoints etcd server endpoints, at least one
|
||||
* @return this builder to train
|
||||
* @throws NullPointerException if endpoints is null or one of endpoint is null
|
||||
* @throws IllegalArgumentException if some endpoint is invalid
|
||||
*/
|
||||
public ClientBuilder endpoints(Iterable<URI> endpoints) {
|
||||
Objects.requireNonNull(endpoints, "endpoints can't be null");
|
||||
|
||||
endpoints.forEach(e -> {
|
||||
if (e.getHost() == null) {
|
||||
throw new IllegalArgumentException("Unable to compute target from endpoint: '" + e + "'");
|
||||
}
|
||||
});
|
||||
|
||||
final String target = StreamSupport.stream(endpoints.spliterator(), false)
|
||||
.map(e -> e.getHost() + (e.getPort() != -1 ? ":" + e.getPort() : ""))
|
||||
.distinct()
|
||||
.collect(Collectors.joining(","));
|
||||
|
||||
if (Strings.isNullOrEmpty(target)) {
|
||||
throw new IllegalArgumentException("Unable to compute target from endpoints: '" + endpoints + "'");
|
||||
}
|
||||
|
||||
return target(
|
||||
String.format(
|
||||
"%s://%s/%s",
|
||||
IPNameResolver.SCHEME,
|
||||
authority != null ? authority : "",
|
||||
target));
|
||||
public ClientBuilder endpoints(String... endpoints) {
|
||||
return endpoints(Util.toURIs(Arrays.asList(endpoints)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the auth user
|
||||
*
|
||||
* @return the user.
|
||||
* @return the auth user
|
||||
*/
|
||||
public ByteSequence user() {
|
||||
return user;
|
||||
|
@ -174,15 +144,13 @@ public final class ClientBuilder implements Cloneable {
|
|||
* @throws NullPointerException if user is <code>null</code>
|
||||
*/
|
||||
public ClientBuilder user(ByteSequence user) {
|
||||
Objects.requireNonNull(user, "user can't be null");
|
||||
checkNotNull(user, "user can't be null");
|
||||
this.user = user;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the auth password
|
||||
*
|
||||
* @return the password.
|
||||
* @return the auth password
|
||||
*/
|
||||
public ByteSequence password() {
|
||||
return password;
|
||||
|
@ -196,15 +164,13 @@ public final class ClientBuilder implements Cloneable {
|
|||
* @throws NullPointerException if password is <code>null</code>
|
||||
*/
|
||||
public ClientBuilder password(ByteSequence password) {
|
||||
Objects.requireNonNull(password, "password can't be null");
|
||||
checkNotNull(password, "password can't be null");
|
||||
this.password = password;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the namespace of each key used
|
||||
*
|
||||
* @return the namespace.
|
||||
* @return the namespace of each key used
|
||||
*/
|
||||
public ByteSequence namespace() {
|
||||
return namespace;
|
||||
|
@ -219,15 +185,13 @@ public final class ClientBuilder implements Cloneable {
|
|||
* @throws NullPointerException if namespace is <code>null</code>
|
||||
*/
|
||||
public ClientBuilder namespace(ByteSequence namespace) {
|
||||
Objects.requireNonNull(namespace, "namespace can't be null");
|
||||
checkNotNull(namespace, "namespace can't be null");
|
||||
this.namespace = namespace;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the executor service
|
||||
*
|
||||
* @return the executor service.
|
||||
* @return the executor service
|
||||
*/
|
||||
public ExecutorService executorService() {
|
||||
return executorService;
|
||||
|
@ -241,7 +205,7 @@ public final class ClientBuilder implements Cloneable {
|
|||
* @throws NullPointerException if executorService is <code>null</code>
|
||||
*/
|
||||
public ClientBuilder executorService(ExecutorService executorService) {
|
||||
Objects.requireNonNull(executorService, "executorService can't be null");
|
||||
checkNotNull(executorService, "executorService can't be null");
|
||||
this.executorService = executorService;
|
||||
return this;
|
||||
}
|
||||
|
@ -254,7 +218,7 @@ public final class ClientBuilder implements Cloneable {
|
|||
* @throws NullPointerException if loadBalancerPolicy is <code>null</code>
|
||||
*/
|
||||
public ClientBuilder loadBalancerPolicy(String loadBalancerPolicy) {
|
||||
Objects.requireNonNull(loadBalancerPolicy, "loadBalancerPolicy can't be null");
|
||||
checkNotNull(loadBalancerPolicy, "loadBalancerPolicy can't be null");
|
||||
this.loadBalancerPolicy = loadBalancerPolicy;
|
||||
return this;
|
||||
}
|
||||
|
@ -269,9 +233,7 @@ public final class ClientBuilder implements Cloneable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the ssl context
|
||||
*
|
||||
* @return the ssl context.
|
||||
* @return the ssl context
|
||||
*/
|
||||
public SslContext sslContext() {
|
||||
return sslContext;
|
||||
|
@ -304,17 +266,13 @@ public final class ClientBuilder implements Cloneable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns The authority used to authenticate connections to servers.
|
||||
*
|
||||
* @return the authority.
|
||||
* @return The authority used to authenticate connections to servers.
|
||||
*/
|
||||
public String authority() {
|
||||
return authority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the authority used to authenticate connections to servers.
|
||||
*
|
||||
* @param authority the authority used to authenticate connections to servers.
|
||||
* @return this builder
|
||||
*/
|
||||
|
@ -324,18 +282,14 @@ public final class ClientBuilder implements Cloneable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum message size allowed for a single gRPC frame.
|
||||
*
|
||||
* @return max inbound message size.
|
||||
* @return the maximum message size allowed for a single gRPC frame.
|
||||
*/
|
||||
public Integer maxInboundMessageSize() {
|
||||
return maxInboundMessageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum message size allowed for a single gRPC frame.
|
||||
*
|
||||
* @param maxInboundMessageSize the maximum message size allowed for a single gRPC frame.
|
||||
* @param maxInboundMessageSize Sets the maximum message size allowed for a single gRPC frame.
|
||||
* @return this builder
|
||||
*/
|
||||
public ClientBuilder maxInboundMessageSize(Integer maxInboundMessageSize) {
|
||||
|
@ -344,18 +298,14 @@ public final class ClientBuilder implements Cloneable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the headers to be added to http request headers
|
||||
*
|
||||
* @return headers.
|
||||
* @return the headers to be added to http request headers
|
||||
*/
|
||||
public Map<Metadata.Key<?>, Object> headers() {
|
||||
return headers == null ? Collections.emptyMap() : Collections.unmodifiableMap(headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets headers to be added to http request headers.
|
||||
*
|
||||
* @param headers headers to be added to http request headers.
|
||||
* @param headers Sets headers to be added to http request headers.
|
||||
* @return this builder
|
||||
*/
|
||||
public ClientBuilder headers(Map<Metadata.Key<?>, Object> headers) {
|
||||
|
@ -365,8 +315,6 @@ public final class ClientBuilder implements Cloneable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Set headers.
|
||||
*
|
||||
* @param key Sets an header key to be added to http request headers.
|
||||
* @param value Sets an header value to be added to http request headers.
|
||||
* @return this builder
|
||||
|
@ -382,56 +330,14 @@ public final class ClientBuilder implements Cloneable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the headers to be added to auth request headers
|
||||
*
|
||||
* @return auth headers.
|
||||
*/
|
||||
public Map<Metadata.Key<?>, Object> authHeaders() {
|
||||
return authHeaders == null ? Collections.emptyMap() : Collections.unmodifiableMap(authHeaders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the auth headers.
|
||||
*
|
||||
* @param authHeaders Sets headers to be added to auth request headers.
|
||||
* @return this builder
|
||||
*/
|
||||
public ClientBuilder authHeaders(Map<Metadata.Key<?>, Object> authHeaders) {
|
||||
this.authHeaders = new HashMap<>(authHeaders);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an auth header.
|
||||
*
|
||||
* @param key Sets an header key to be added to auth request headers.
|
||||
* @param value Sets an header value to be added to auth request headers.
|
||||
* @return this builder
|
||||
*/
|
||||
public ClientBuilder authHeader(String key, String value) {
|
||||
if (this.authHeaders == null) {
|
||||
this.authHeaders = new HashMap<>();
|
||||
}
|
||||
|
||||
this.authHeaders.put(Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER), value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the interceptors
|
||||
*
|
||||
* @return the interceptors.
|
||||
* @return the interceptors
|
||||
*/
|
||||
public List<ClientInterceptor> interceptors() {
|
||||
return interceptors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the interceptors.
|
||||
*
|
||||
* @param interceptors the interceptors.
|
||||
* @param interceptors Set the interceptors.
|
||||
* @return this builder
|
||||
*/
|
||||
public ClientBuilder interceptors(List<ClientInterceptor> interceptors) {
|
||||
|
@ -441,8 +347,6 @@ public final class ClientBuilder implements Cloneable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Add an interceptor.
|
||||
*
|
||||
* @param interceptor an interceptors to add
|
||||
* @param interceptors additional interceptors
|
||||
* @return this builder
|
||||
|
@ -459,56 +363,13 @@ public final class ClientBuilder implements Cloneable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the auth interceptors
|
||||
*
|
||||
* @return the interceptors.
|
||||
*/
|
||||
public List<ClientInterceptor> authInterceptors() {
|
||||
return authInterceptors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the auth interceptors.
|
||||
*
|
||||
* @param interceptors Set the interceptors to add to the auth chain
|
||||
* @return this builder
|
||||
*/
|
||||
public ClientBuilder authInterceptors(List<ClientInterceptor> interceptors) {
|
||||
this.authInterceptors = new ArrayList<>(interceptors);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an auth interceptor.
|
||||
*
|
||||
* @param interceptor an interceptors to add to the auth chain
|
||||
* @param interceptors additional interceptors to add to the auth chain
|
||||
* @return this builder
|
||||
*/
|
||||
public ClientBuilder authInterceptors(ClientInterceptor interceptor, ClientInterceptor... interceptors) {
|
||||
if (this.authInterceptors == null) {
|
||||
this.authInterceptors = new ArrayList<>();
|
||||
}
|
||||
|
||||
this.authInterceptors.add(interceptor);
|
||||
this.authInterceptors.addAll(Arrays.asList(interceptors));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns The delay between retries.
|
||||
*
|
||||
* @return the retry delay.
|
||||
* @return The delay between retries.
|
||||
*/
|
||||
public long retryDelay() {
|
||||
return retryDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* The delay between retries.
|
||||
*
|
||||
* @param retryDelay The delay between retries.
|
||||
* @return this builder
|
||||
*/
|
||||
|
@ -518,17 +379,13 @@ public final class ClientBuilder implements Cloneable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the max backing off delay between retries
|
||||
*
|
||||
* @return max retry delay.
|
||||
* @return the max backing off delay between retries
|
||||
*/
|
||||
public long retryMaxDelay() {
|
||||
return retryMaxDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the max backing off delay between retries.
|
||||
*
|
||||
* @param retryMaxDelay The max backing off delay between retries.
|
||||
* @return this builder
|
||||
*/
|
||||
|
@ -537,31 +394,6 @@ public final class ClientBuilder implements Cloneable {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the max number of retry attempts
|
||||
*
|
||||
* @return max retry attempts.
|
||||
*/
|
||||
public int retryMaxAttempts() {
|
||||
return retryMaxAttempts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the max number of retry attempts
|
||||
*
|
||||
* @param retryMaxAttempts The max retry attempts.
|
||||
* @return this builder
|
||||
*/
|
||||
public ClientBuilder retryMaxAttempts(int retryMaxAttempts) {
|
||||
this.retryMaxAttempts = retryMaxAttempts;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the keep alive time.
|
||||
*
|
||||
* @return keep alive time.
|
||||
*/
|
||||
public Duration keepaliveTime() {
|
||||
return keepaliveTime;
|
||||
}
|
||||
|
@ -570,8 +402,7 @@ public final class ClientBuilder implements Cloneable {
|
|||
* The interval for gRPC keepalives.
|
||||
* The current minimum allowed by gRPC is 10s
|
||||
*
|
||||
* @param keepaliveTime time between keepalives
|
||||
* @return this builder
|
||||
* @param keepaliveTime time between keepalives
|
||||
*/
|
||||
public ClientBuilder keepaliveTime(Duration keepaliveTime) {
|
||||
// gRPC uses a minimum keepalive time of 10s, if smaller values are given.
|
||||
|
@ -580,11 +411,6 @@ public final class ClientBuilder implements Cloneable {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the keep alive time out.
|
||||
*
|
||||
* @return keep alive time out.
|
||||
*/
|
||||
public Duration keepaliveTimeout() {
|
||||
return keepaliveTimeout;
|
||||
}
|
||||
|
@ -592,8 +418,7 @@ public final class ClientBuilder implements Cloneable {
|
|||
/**
|
||||
* The timeout for gRPC keepalives
|
||||
*
|
||||
* @param keepaliveTimeout the gRPC keep alive timeout.
|
||||
* @return this builder
|
||||
* @param keepaliveTimeout the gRPC keep alive timeout.
|
||||
*/
|
||||
public ClientBuilder keepaliveTimeout(Duration keepaliveTimeout) {
|
||||
this.keepaliveTimeout = keepaliveTimeout;
|
||||
|
@ -607,8 +432,7 @@ public final class ClientBuilder implements Cloneable {
|
|||
/**
|
||||
* Keepalive option for gRPC
|
||||
*
|
||||
* @param keepaliveWithoutCalls the gRPC keep alive without calls.
|
||||
* @return this builder
|
||||
* @param keepaliveWithoutCalls the gRPC keep alive without calls.
|
||||
*/
|
||||
public ClientBuilder keepaliveWithoutCalls(Boolean keepaliveWithoutCalls) {
|
||||
this.keepaliveWithoutCalls = keepaliveWithoutCalls;
|
||||
|
@ -616,17 +440,13 @@ public final class ClientBuilder implements Cloneable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns he retries period unit.
|
||||
*
|
||||
* @return the chrono unit.
|
||||
* @return he retries period unit.
|
||||
*/
|
||||
public ChronoUnit retryChronoUnit() {
|
||||
return retryChronoUnit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the retries period unit.
|
||||
*
|
||||
* @param retryChronoUnit the retries period unit.
|
||||
* @return this builder
|
||||
*/
|
||||
|
@ -636,26 +456,20 @@ public final class ClientBuilder implements Cloneable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the retries max duration.
|
||||
*
|
||||
* @return retry max duration.
|
||||
* @return the retries max duration.
|
||||
*/
|
||||
public Duration retryMaxDuration() {
|
||||
return retryMaxDuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the connect timeout.
|
||||
*
|
||||
* @return connect timeout.
|
||||
* @return the connect timeout.
|
||||
*/
|
||||
public Duration connectTimeout() {
|
||||
return connectTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the retries max duration.
|
||||
*
|
||||
* @param retryMaxDuration the retries max duration.
|
||||
* @return this builder
|
||||
*/
|
||||
|
@ -665,10 +479,8 @@ public final class ClientBuilder implements Cloneable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the connection timeout.
|
||||
*
|
||||
* @param connectTimeout Sets the connection timeout.
|
||||
* Clients connecting to fault tolerant etcd clusters (eg, clusters with more than 2 etcd server
|
||||
* Clients connecting to fault tolerant etcd clusters (eg, clusters with >= 3 etcd server
|
||||
* peers/endpoints)
|
||||
* should consider a value that will allow switching timely from a crashed/partitioned peer to
|
||||
* a consensus peer.
|
||||
|
@ -687,49 +499,18 @@ public final class ClientBuilder implements Cloneable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Enable gRPC's wait for ready semantics.
|
||||
*
|
||||
* @return if this client uses gRPC's wait for ready semantics.
|
||||
* @see <a href="https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md">gRPC Wait for Ready Semantics</a>
|
||||
* @return if the endpoint represent a discovery address using dns+srv.
|
||||
*/
|
||||
public boolean waitForReady() {
|
||||
return waitForReady;
|
||||
public boolean discovery() {
|
||||
return discovery;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the gRPC's wait for ready semantics.
|
||||
*
|
||||
* @param waitForReady if this client should use gRPC's wait for ready semantics. Enabled by default.
|
||||
* @return this builder.
|
||||
* @see <a href="https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md">gRPC Wait for Ready
|
||||
* Semantics</a>
|
||||
* @param discovery if the endpoint represent a discovery address using dns+srv.
|
||||
* @return this builder
|
||||
*/
|
||||
public ClientBuilder waitForReady(boolean waitForReady) {
|
||||
this.waitForReady = waitForReady;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Vertx instance.
|
||||
*
|
||||
* @return the vertx instance.
|
||||
*/
|
||||
public Vertx vertx() {
|
||||
return vertx;
|
||||
}
|
||||
|
||||
/**
|
||||
* configure Vertx instance.
|
||||
*
|
||||
* @param vertx Vertx instance to use.
|
||||
* @return this builder to train
|
||||
* @throws IllegalArgumentException if vertx is null
|
||||
*/
|
||||
public ClientBuilder vertx(Vertx vertx) {
|
||||
Preconditions.checkArgument(vertx != null, "vertx can't be null");
|
||||
|
||||
this.vertx = vertx;
|
||||
|
||||
public ClientBuilder discovery(boolean discovery) {
|
||||
this.discovery = discovery;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -740,15 +521,13 @@ public final class ClientBuilder implements Cloneable {
|
|||
* @throws EtcdException if client experiences build error.
|
||||
*/
|
||||
public Client build() {
|
||||
Preconditions.checkState(target != null, "please configure etcd server endpoints before build.");
|
||||
checkState(!endpoints.isEmpty(), "please configure etcd server endpoints before build.");
|
||||
|
||||
return new ClientImpl(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of this builder
|
||||
*
|
||||
* @return a copy of the builder.
|
||||
* @return a copy of this builder
|
||||
*/
|
||||
public ClientBuilder copy() {
|
||||
try {
|
||||
|
|
|
@ -0,0 +1,402 @@
|
|||
/*
|
||||
* Copyright 2016-2021 The jetcd authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT 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 io.etcd.jetcd;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.protobuf.ByteString;
|
||||
import io.etcd.jetcd.api.AuthGrpc;
|
||||
import io.etcd.jetcd.api.AuthenticateRequest;
|
||||
import io.etcd.jetcd.api.AuthenticateResponse;
|
||||
import io.etcd.jetcd.common.exception.EtcdExceptionFactory;
|
||||
import io.etcd.jetcd.resolver.DnsSrvNameResolver;
|
||||
import io.etcd.jetcd.resolver.IPNameResolver;
|
||||
import io.grpc.CallOptions;
|
||||
import io.grpc.Channel;
|
||||
import io.grpc.ClientCall;
|
||||
import io.grpc.ClientInterceptor;
|
||||
import io.grpc.ForwardingClientCall.SimpleForwardingClientCall;
|
||||
import io.grpc.ForwardingClientCallListener.SimpleForwardingClientCallListener;
|
||||
import io.grpc.ManagedChannel;
|
||||
import io.grpc.ManagedChannelBuilder;
|
||||
import io.grpc.Metadata;
|
||||
import io.grpc.MethodDescriptor;
|
||||
import io.grpc.Status;
|
||||
import io.grpc.netty.NegotiationType;
|
||||
import io.grpc.netty.NettyChannelBuilder;
|
||||
import io.grpc.stub.AbstractStub;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import net.jodah.failsafe.Failsafe;
|
||||
import net.jodah.failsafe.RetryPolicy;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static io.etcd.jetcd.Util.isInvalidTokenError;
|
||||
import static io.etcd.jetcd.common.exception.EtcdExceptionFactory.handleInterrupt;
|
||||
import static io.etcd.jetcd.common.exception.EtcdExceptionFactory.toEtcdException;
|
||||
|
||||
final class ClientConnectionManager {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ClientConnectionManager.class);
|
||||
private static final Metadata.Key<String> TOKEN = Metadata.Key.of("token", Metadata.ASCII_STRING_MARSHALLER);
|
||||
|
||||
private final Object lock;
|
||||
private final ClientBuilder builder;
|
||||
private final ExecutorService executorService;
|
||||
private volatile ManagedChannel managedChannel;
|
||||
private volatile String token;
|
||||
|
||||
ClientConnectionManager(ClientBuilder builder) {
|
||||
this(builder, null);
|
||||
}
|
||||
|
||||
ClientConnectionManager(ClientBuilder builder, ManagedChannel managedChannel) {
|
||||
this.lock = new Object();
|
||||
this.builder = builder;
|
||||
this.managedChannel = managedChannel;
|
||||
|
||||
if (builder.executorService() == null) {
|
||||
this.executorService = Executors.newCachedThreadPool();
|
||||
} else {
|
||||
this.executorService = builder.executorService();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get token from etcd with name and password.
|
||||
*
|
||||
* @param channel channel to etcd
|
||||
* @param username auth name
|
||||
* @param password auth password
|
||||
* @return authResp
|
||||
*/
|
||||
private static ListenableFuture<AuthenticateResponse> authenticate(@Nonnull Channel channel, @Nonnull ByteSequence username,
|
||||
@Nonnull ByteSequence password) {
|
||||
|
||||
final ByteString user = username.getByteString();
|
||||
final ByteString pass = password.getByteString();
|
||||
|
||||
checkArgument(!user.isEmpty(), "username can not be empty.");
|
||||
checkArgument(!pass.isEmpty(), "password can not be empty.");
|
||||
|
||||
return AuthGrpc.newFutureStub(channel)
|
||||
.authenticate(AuthenticateRequest.newBuilder().setNameBytes(user).setPasswordBytes(pass).build());
|
||||
}
|
||||
|
||||
ManagedChannel getChannel() {
|
||||
if (managedChannel == null) {
|
||||
synchronized (lock) {
|
||||
if (managedChannel == null) {
|
||||
managedChannel = defaultChannelBuilder().build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return managedChannel;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getToken(Channel channel) {
|
||||
if (token == null) {
|
||||
synchronized (lock) {
|
||||
if (token == null) {
|
||||
token = generateToken(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
void forceTokenRefresh() {
|
||||
synchronized (lock) {
|
||||
token = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshToken(Channel channel) {
|
||||
synchronized (lock) {
|
||||
token = generateToken(channel);
|
||||
}
|
||||
}
|
||||
|
||||
ByteSequence getNamespace() {
|
||||
return builder.namespace();
|
||||
}
|
||||
|
||||
ExecutorService getExecutorService() {
|
||||
return executorService;
|
||||
}
|
||||
|
||||
/**
|
||||
* create stub with saved channel.
|
||||
*
|
||||
* @param supplier the stub supplier
|
||||
* @param <T> the type of stub
|
||||
* @return the attached stub
|
||||
*/
|
||||
<T extends AbstractStub<T>> T newStub(Function<ManagedChannel, T> supplier) {
|
||||
return supplier.apply(getChannel()).withWaitForReady();
|
||||
}
|
||||
|
||||
void close() {
|
||||
synchronized (lock) {
|
||||
if (managedChannel != null) {
|
||||
managedChannel.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
if (builder.executorService() == null) {
|
||||
executorService.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
<T extends AbstractStub<T>, R> CompletableFuture<R> withNewChannel(URI endpoint, Function<ManagedChannel, T> stubCustomizer,
|
||||
Function<T, CompletableFuture<R>> stubConsumer) {
|
||||
|
||||
final ManagedChannel channel = defaultChannelBuilder(Collections.singletonList(endpoint)).build();
|
||||
final T stub = stubCustomizer.apply(channel).withWaitForReady();
|
||||
|
||||
try {
|
||||
return stubConsumer.apply(stub).whenComplete((r, t) -> channel.shutdown());
|
||||
} catch (Exception e) {
|
||||
channel.shutdown();
|
||||
throw EtcdExceptionFactory.toEtcdException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected ManagedChannelBuilder<?> defaultChannelBuilder() {
|
||||
return defaultChannelBuilder(builder.endpoints());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected ManagedChannelBuilder<?> defaultChannelBuilder(Collection<URI> endpoints) {
|
||||
if (endpoints.isEmpty()) {
|
||||
throw new IllegalArgumentException("At least one endpoint should be provided");
|
||||
}
|
||||
|
||||
final String target;
|
||||
|
||||
if (builder.discovery()) {
|
||||
if (endpoints.size() != 1) {
|
||||
throw new IllegalArgumentException("When configured for discovery, there should be only a single endpoint");
|
||||
}
|
||||
|
||||
target = String.format(
|
||||
"%s:///%s",
|
||||
DnsSrvNameResolver.SCHEME,
|
||||
Iterables.get(endpoints, 0));
|
||||
} else {
|
||||
target = String.format(
|
||||
"%s://%s/%s",
|
||||
IPNameResolver.SCHEME,
|
||||
builder.authority() != null ? builder.authority() : "",
|
||||
endpoints.stream().map(e -> e.getHost() + ":" + e.getPort()).collect(Collectors.joining(",")));
|
||||
}
|
||||
|
||||
final NettyChannelBuilder channelBuilder = NettyChannelBuilder.forTarget(target);
|
||||
|
||||
if (builder.authority() != null) {
|
||||
channelBuilder.overrideAuthority(builder.authority());
|
||||
}
|
||||
if (builder.maxInboundMessageSize() != null) {
|
||||
channelBuilder.maxInboundMessageSize(builder.maxInboundMessageSize());
|
||||
}
|
||||
if (builder.sslContext() != null) {
|
||||
channelBuilder.negotiationType(NegotiationType.TLS);
|
||||
channelBuilder.sslContext(builder.sslContext());
|
||||
} else {
|
||||
channelBuilder.negotiationType(NegotiationType.PLAINTEXT);
|
||||
}
|
||||
|
||||
if (builder.keepaliveTime() != null) {
|
||||
channelBuilder.keepAliveTime(builder.keepaliveTime().toMillis(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
if (builder.keepaliveTimeout() != null) {
|
||||
channelBuilder.keepAliveTimeout(builder.keepaliveTimeout().toMillis(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
if (builder.keepaliveWithoutCalls() != null) {
|
||||
channelBuilder.keepAliveWithoutCalls(builder.keepaliveWithoutCalls());
|
||||
}
|
||||
if (builder.connectTimeout() != null) {
|
||||
channelBuilder.withOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, (int) builder.connectTimeout().toMillis());
|
||||
}
|
||||
|
||||
if (builder.loadBalancerPolicy() != null) {
|
||||
channelBuilder.defaultLoadBalancingPolicy(builder.loadBalancerPolicy());
|
||||
} else {
|
||||
channelBuilder.defaultLoadBalancingPolicy("pick_first");
|
||||
}
|
||||
|
||||
channelBuilder.intercept(new AuthTokenInterceptor());
|
||||
|
||||
if (builder.headers() != null) {
|
||||
channelBuilder.intercept(new ClientInterceptor() {
|
||||
@Override
|
||||
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method,
|
||||
CallOptions callOptions, Channel next) {
|
||||
return new SimpleForwardingClientCall<ReqT, RespT>(next.newCall(method, callOptions)) {
|
||||
@Override
|
||||
public void start(Listener<RespT> responseListener, Metadata headers) {
|
||||
builder.headers().forEach((BiConsumer<Metadata.Key, Object>) headers::put);
|
||||
super.start(responseListener, headers);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (builder.interceptors() != null) {
|
||||
channelBuilder.intercept(builder.interceptors());
|
||||
}
|
||||
|
||||
return channelBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* get token with ClientBuilder.
|
||||
*
|
||||
* @return the auth token
|
||||
* @throws io.etcd.jetcd.common.exception.EtcdException a exception indicates failure reason.
|
||||
*/
|
||||
@Nullable
|
||||
private String generateToken(Channel channel) {
|
||||
if (builder.user() != null && builder.password() != null) {
|
||||
try {
|
||||
return authenticate(channel, builder.user(), builder.password()).get().getToken();
|
||||
} catch (InterruptedException ite) {
|
||||
throw handleInterrupt(ite);
|
||||
} catch (ExecutionException exee) {
|
||||
throw toEtcdException(exee);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* execute the task and retry it in case of failure.
|
||||
*
|
||||
* @param task a function that returns a new ListenableFuture.
|
||||
* @param resultConvert a function that converts Type S to Type T.
|
||||
* @param <S> Source type
|
||||
* @param <T> Converted Type.
|
||||
* @return a CompletableFuture with type T.
|
||||
*/
|
||||
public <S, T> CompletableFuture<T> execute(Callable<ListenableFuture<S>> task, Function<S, T> resultConvert) {
|
||||
return execute(task, resultConvert, Util::isRetryable);
|
||||
}
|
||||
|
||||
/**
|
||||
* execute the task and retry it in case of failure.
|
||||
*
|
||||
* @param task a function that returns a new SourceFuture.
|
||||
* @param resultConvert a function that converts Type S to Type T.
|
||||
* @param doRetry a function that determines the retry condition base on SourceFuture error.
|
||||
* @param <S> Source type
|
||||
* @param <T> Converted Type.
|
||||
* @return a CompletableFuture with type T.
|
||||
*/
|
||||
@SuppressWarnings("FutureReturnValueIgnored")
|
||||
public <S, T> CompletableFuture<T> execute(
|
||||
Callable<ListenableFuture<S>> task,
|
||||
Function<S, T> resultConvert,
|
||||
Predicate<Throwable> doRetry) {
|
||||
|
||||
RetryPolicy<CompletableFuture<S>> retryPolicy = new RetryPolicy<CompletableFuture<S>>().handleIf(doRetry)
|
||||
.onRetriesExceeded(e -> LOGGER.warn("maximum number of auto retries reached"))
|
||||
.withBackoff(builder.retryDelay(), builder.retryMaxDelay(), builder.retryChronoUnit());
|
||||
|
||||
if (builder.retryMaxDuration() != null) {
|
||||
retryPolicy = retryPolicy.withMaxDuration(builder.retryMaxDuration());
|
||||
}
|
||||
|
||||
return Failsafe.with(retryPolicy).with(executorService)
|
||||
.getAsyncExecution(execution -> {
|
||||
CompletableFuture<S> wrappedFuture = new CompletableFuture<>();
|
||||
ListenableFuture<S> future = task.call();
|
||||
future.addListener(() -> {
|
||||
try {
|
||||
wrappedFuture.complete(future.get());
|
||||
if (execution.complete(wrappedFuture)) {
|
||||
// success! but nothing to do here
|
||||
}
|
||||
} catch (Exception error) {
|
||||
if (!execution.retryOn(error)) {
|
||||
// permanent failure
|
||||
wrappedFuture.completeExceptionally(error);
|
||||
}
|
||||
}
|
||||
}, executorService);
|
||||
// note: the actual result value is supplied via execution.complete(..) above
|
||||
return wrappedFuture;
|
||||
}).thenCompose(f -> f.thenApply(resultConvert));
|
||||
}
|
||||
|
||||
/**
|
||||
* AuthTokenInterceptor fills header with Auth token of any rpc calls and
|
||||
* refreshes token if the rpc results an invalid Auth token error.
|
||||
*/
|
||||
private class AuthTokenInterceptor implements ClientInterceptor {
|
||||
|
||||
@Override
|
||||
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method,
|
||||
CallOptions callOptions, Channel next) {
|
||||
return new SimpleForwardingClientCall<ReqT, RespT>(next.newCall(method, callOptions)) {
|
||||
@Override
|
||||
public void start(Listener<RespT> responseListener, Metadata headers) {
|
||||
String token = getToken(next);
|
||||
if (token != null) {
|
||||
headers.put(TOKEN, token);
|
||||
}
|
||||
super.start(new SimpleForwardingClientCallListener<RespT>(responseListener) {
|
||||
@Override
|
||||
public void onClose(Status status, Metadata trailers) {
|
||||
if (isInvalidTokenError(status)) {
|
||||
try {
|
||||
refreshToken(next);
|
||||
} catch (Exception e) {
|
||||
// don't throw any error here.
|
||||
// rpc will retry on expired auth token.
|
||||
}
|
||||
}
|
||||
super.onClose(status, trailers);
|
||||
}
|
||||
}, headers);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,45 +14,35 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.etcd.jetcd.impl;
|
||||
package io.etcd.jetcd;
|
||||
|
||||
import io.etcd.jetcd.Auth;
|
||||
import io.etcd.jetcd.Client;
|
||||
import io.etcd.jetcd.ClientBuilder;
|
||||
import io.etcd.jetcd.Cluster;
|
||||
import io.etcd.jetcd.Election;
|
||||
import io.etcd.jetcd.KV;
|
||||
import io.etcd.jetcd.Lease;
|
||||
import io.etcd.jetcd.Lock;
|
||||
import io.etcd.jetcd.Maintenance;
|
||||
import io.etcd.jetcd.Watch;
|
||||
import io.etcd.jetcd.support.MemorizingClientSupplier;
|
||||
import io.etcd.jetcd.support.MemoizingClientSupplier;
|
||||
|
||||
/**
|
||||
* Etcd Client.
|
||||
*/
|
||||
public final class ClientImpl implements Client {
|
||||
final class ClientImpl implements Client {
|
||||
|
||||
private final ClientConnectionManager connectionManager;
|
||||
private final MemorizingClientSupplier<KV> kvClient;
|
||||
private final MemorizingClientSupplier<Auth> authClient;
|
||||
private final MemorizingClientSupplier<Maintenance> maintenanceClient;
|
||||
private final MemorizingClientSupplier<Cluster> clusterClient;
|
||||
private final MemorizingClientSupplier<Lease> leaseClient;
|
||||
private final MemorizingClientSupplier<Watch> watchClient;
|
||||
private final MemorizingClientSupplier<Lock> lockClient;
|
||||
private final MemorizingClientSupplier<Election> electionClient;
|
||||
private final MemoizingClientSupplier<KV> kvClient;
|
||||
private final MemoizingClientSupplier<Auth> authClient;
|
||||
private final MemoizingClientSupplier<Maintenance> maintenanceClient;
|
||||
private final MemoizingClientSupplier<Cluster> clusterClient;
|
||||
private final MemoizingClientSupplier<Lease> leaseClient;
|
||||
private final MemoizingClientSupplier<Watch> watchClient;
|
||||
private final MemoizingClientSupplier<Lock> lockClient;
|
||||
private final MemoizingClientSupplier<Election> electionClient;
|
||||
|
||||
public ClientImpl(ClientBuilder clientBuilder) {
|
||||
this.connectionManager = new ClientConnectionManager(clientBuilder.copy());
|
||||
this.kvClient = new MemorizingClientSupplier<>(() -> new KVImpl(this.connectionManager));
|
||||
this.authClient = new MemorizingClientSupplier<>(() -> new AuthImpl(this.connectionManager));
|
||||
this.maintenanceClient = new MemorizingClientSupplier<>(() -> new MaintenanceImpl(this.connectionManager));
|
||||
this.clusterClient = new MemorizingClientSupplier<>(() -> new ClusterImpl(this.connectionManager));
|
||||
this.leaseClient = new MemorizingClientSupplier<>(() -> new LeaseImpl(this.connectionManager));
|
||||
this.watchClient = new MemorizingClientSupplier<>(() -> new WatchImpl(this.connectionManager));
|
||||
this.lockClient = new MemorizingClientSupplier<>(() -> new LockImpl(this.connectionManager));
|
||||
this.electionClient = new MemorizingClientSupplier<>(() -> new ElectionImpl(this.connectionManager));
|
||||
this.kvClient = new MemoizingClientSupplier<>(() -> new KVImpl(this.connectionManager));
|
||||
this.authClient = new MemoizingClientSupplier<>(() -> new AuthImpl(this.connectionManager));
|
||||
this.maintenanceClient = new MemoizingClientSupplier<>(() -> new MaintenanceImpl(this.connectionManager));
|
||||
this.clusterClient = new MemoizingClientSupplier<>(() -> new ClusterImpl(this.connectionManager));
|
||||
this.leaseClient = new MemoizingClientSupplier<>(() -> new LeaseImpl(this.connectionManager));
|
||||
this.watchClient = new MemoizingClientSupplier<>(() -> new WatchImpl(this.connectionManager));
|
||||
this.lockClient = new MemoizingClientSupplier<>(() -> new LockImpl(this.connectionManager));
|
||||
this.electionClient = new MemoizingClientSupplier<>(() -> new ElectionImpl(this.connectionManager));
|
||||
}
|
||||
|
||||
@Override
|
|
@ -39,22 +39,13 @@ public interface Cluster extends CloseableClient {
|
|||
CompletableFuture<MemberListResponse> listMember();
|
||||
|
||||
/**
|
||||
* add a non-learner new member into the cluster.
|
||||
* add a new member into the cluster.
|
||||
*
|
||||
* @param peerAddrs the peer addresses of the new member
|
||||
* @return the response
|
||||
*/
|
||||
CompletableFuture<MemberAddResponse> addMember(List<URI> peerAddrs);
|
||||
|
||||
/**
|
||||
* add a new member into the cluster.
|
||||
*
|
||||
* @param peerAddrs the peer addresses of the new member
|
||||
* @param isLearner whether the member is raft learner
|
||||
* @return the response
|
||||
*/
|
||||
CompletableFuture<MemberAddResponse> addMember(List<URI> peerAddrs, boolean isLearner);
|
||||
|
||||
/**
|
||||
* removes an existing member from the cluster.
|
||||
*
|
||||
|
|
|
@ -14,19 +14,18 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.etcd.jetcd.impl;
|
||||
package io.etcd.jetcd;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.etcd.jetcd.Cluster;
|
||||
import io.etcd.jetcd.api.ClusterGrpc;
|
||||
import io.etcd.jetcd.api.MemberAddRequest;
|
||||
import io.etcd.jetcd.api.MemberListRequest;
|
||||
import io.etcd.jetcd.api.MemberRemoveRequest;
|
||||
import io.etcd.jetcd.api.MemberUpdateRequest;
|
||||
import io.etcd.jetcd.api.VertxClusterGrpc;
|
||||
import io.etcd.jetcd.cluster.MemberAddResponse;
|
||||
import io.etcd.jetcd.cluster.MemberListResponse;
|
||||
import io.etcd.jetcd.cluster.MemberRemoveResponse;
|
||||
|
@ -35,14 +34,14 @@ import io.etcd.jetcd.cluster.MemberUpdateResponse;
|
|||
/**
|
||||
* Implementation of cluster client.
|
||||
*/
|
||||
final class ClusterImpl extends Impl implements Cluster {
|
||||
final class ClusterImpl implements Cluster {
|
||||
|
||||
private final VertxClusterGrpc.ClusterVertxStub stub;
|
||||
private final ClusterGrpc.ClusterFutureStub stub;
|
||||
private final ClientConnectionManager connectionManager;
|
||||
|
||||
ClusterImpl(ClientConnectionManager connectionManager) {
|
||||
super(connectionManager);
|
||||
|
||||
this.stub = connectionManager.newStub(VertxClusterGrpc::newVertxStub);
|
||||
this.connectionManager = connectionManager;
|
||||
this.stub = connectionManager.newStub(ClusterGrpc::newFutureStub);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,37 +49,27 @@ final class ClusterImpl extends Impl implements Cluster {
|
|||
*/
|
||||
@Override
|
||||
public CompletableFuture<MemberListResponse> listMember() {
|
||||
return completable(
|
||||
return Util.toCompletableFuture(
|
||||
this.stub.memberList(MemberListRequest.getDefaultInstance()),
|
||||
MemberListResponse::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* add a non-learner new member into the cluster.
|
||||
*
|
||||
* @param peerAddrs the peer addresses of the new member
|
||||
*/
|
||||
@Override
|
||||
public CompletableFuture<MemberAddResponse> addMember(List<URI> peerAddrs) {
|
||||
return addMember(peerAddrs, false);
|
||||
MemberListResponse::new,
|
||||
this.connectionManager.getExecutorService());
|
||||
}
|
||||
|
||||
/**
|
||||
* add a new member into the cluster.
|
||||
*
|
||||
* @param peerAddrs the peer addresses of the new member
|
||||
* @param isLearner whether the member is raft learner
|
||||
*/
|
||||
@Override
|
||||
public CompletableFuture<MemberAddResponse> addMember(List<URI> peerAddrs, boolean isLearner) {
|
||||
public CompletableFuture<MemberAddResponse> addMember(List<URI> peerAddrs) {
|
||||
MemberAddRequest memberAddRequest = MemberAddRequest.newBuilder()
|
||||
.addAllPeerURLs(peerAddrs.stream().map(URI::toString).collect(Collectors.toList()))
|
||||
.setIsLearner(isLearner)
|
||||
.build();
|
||||
|
||||
return completable(
|
||||
return Util.toCompletableFuture(
|
||||
this.stub.memberAdd(memberAddRequest),
|
||||
MemberAddResponse::new);
|
||||
MemberAddResponse::new,
|
||||
this.connectionManager.getExecutorService());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -94,9 +83,10 @@ final class ClusterImpl extends Impl implements Cluster {
|
|||
.setID(memberID)
|
||||
.build();
|
||||
|
||||
return completable(
|
||||
return Util.toCompletableFuture(
|
||||
this.stub.memberRemove(memberRemoveRequest),
|
||||
MemberRemoveResponse::new);
|
||||
MemberRemoveResponse::new,
|
||||
this.connectionManager.getExecutorService());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -108,12 +98,12 @@ final class ClusterImpl extends Impl implements Cluster {
|
|||
@Override
|
||||
public CompletableFuture<MemberUpdateResponse> updateMember(long memberID, List<URI> peerAddrs) {
|
||||
MemberUpdateRequest memberUpdateRequest = MemberUpdateRequest.newBuilder()
|
||||
.addAllPeerURLs(peerAddrs.stream().map(URI::toString).collect(Collectors.toList()))
|
||||
.setID(memberID)
|
||||
.addAllPeerURLs(peerAddrs.stream().map(URI::toString).collect(Collectors.toList())).setID(memberID)
|
||||
.build();
|
||||
|
||||
return completable(
|
||||
return Util.toCompletableFuture(
|
||||
this.stub.memberUpdate(memberUpdateRequest),
|
||||
MemberUpdateResponse::new);
|
||||
MemberUpdateResponse::new,
|
||||
this.connectionManager.getExecutorService());
|
||||
}
|
||||
}
|
|
@ -22,11 +22,11 @@ import io.grpc.Metadata;
|
|||
* Constants of Etcd.
|
||||
*/
|
||||
public class Constants {
|
||||
|
||||
public static final String TOKEN = "token";
|
||||
public static final ByteSequence NULL_KEY = ByteSequence.from(new byte[] { '\0' });
|
||||
|
||||
public static final Metadata.Key<String> REQUIRE_LEADER_KEY = Metadata.Key.of(
|
||||
"hasleader",
|
||||
public static final Metadata.Key<String> REQUIRE_LEADER_KEY = Metadata.Key.of("hasleader",
|
||||
Metadata.ASCII_STRING_MARSHALLER);
|
||||
|
||||
public static final String REQUIRE_LEADER_VALUE = "true";
|
||||
}
|
||||
|
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* Copyright 2016-2021 The jetcd authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT 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 io.etcd.jetcd;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Function;
|
||||
|
||||
import io.etcd.jetcd.api.CampaignRequest;
|
||||
import io.etcd.jetcd.api.ElectionGrpc;
|
||||
import io.etcd.jetcd.api.LeaderRequest;
|
||||
import io.etcd.jetcd.api.ProclaimRequest;
|
||||
import io.etcd.jetcd.api.ResignRequest;
|
||||
import io.etcd.jetcd.common.exception.EtcdExceptionFactory;
|
||||
import io.etcd.jetcd.election.CampaignResponse;
|
||||
import io.etcd.jetcd.election.LeaderKey;
|
||||
import io.etcd.jetcd.election.LeaderResponse;
|
||||
import io.etcd.jetcd.election.NoLeaderException;
|
||||
import io.etcd.jetcd.election.NotLeaderException;
|
||||
import io.etcd.jetcd.election.ProclaimResponse;
|
||||
import io.etcd.jetcd.election.ResignResponse;
|
||||
import io.grpc.StatusRuntimeException;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
final class ElectionImpl implements Election {
|
||||
private final ElectionGrpc.ElectionStub stub;
|
||||
private final ByteSequence namespace;
|
||||
|
||||
ElectionImpl(ClientConnectionManager connectionManager) {
|
||||
this.stub = connectionManager.newStub(ElectionGrpc::newStub);
|
||||
this.namespace = connectionManager.getNamespace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CampaignResponse> campaign(ByteSequence electionName, long leaseId, ByteSequence proposal) {
|
||||
checkNotNull(electionName, "election name should not be null");
|
||||
checkNotNull(proposal, "proposal should not be null");
|
||||
|
||||
CampaignRequest request = CampaignRequest.newBuilder()
|
||||
.setName(Util.prefixNamespace(electionName.getByteString(), namespace))
|
||||
.setValue(proposal.getByteString())
|
||||
.setLease(leaseId)
|
||||
.build();
|
||||
|
||||
final StreamObserverDelegate<io.etcd.jetcd.api.CampaignResponse, CampaignResponse> observer = new StreamObserverDelegate<>(
|
||||
CampaignResponse::new);
|
||||
stub.campaign(request, observer);
|
||||
|
||||
return observer.getFuture();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ProclaimResponse> proclaim(LeaderKey leaderKey, ByteSequence proposal) {
|
||||
checkNotNull(leaderKey, "leader key should not be null");
|
||||
checkNotNull(proposal, "proposal should not be null");
|
||||
|
||||
io.etcd.jetcd.api.LeaderKey leader = io.etcd.jetcd.api.LeaderKey.newBuilder()
|
||||
.setKey(leaderKey.getKey()).setName(leaderKey.getName())
|
||||
.setLease(leaderKey.getLease()).setRev(leaderKey.getRevision())
|
||||
.build();
|
||||
ProclaimRequest request = ProclaimRequest.newBuilder()
|
||||
.setLeader(leader).setValue(proposal.getByteString())
|
||||
.build();
|
||||
|
||||
final StreamObserverDelegate<io.etcd.jetcd.api.ProclaimResponse, ProclaimResponse> observer = new StreamObserverDelegate<>(
|
||||
ProclaimResponse::new);
|
||||
stub.proclaim(request, observer);
|
||||
|
||||
return observer.getFuture();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<LeaderResponse> leader(ByteSequence electionName) {
|
||||
checkNotNull(electionName, "election name should not be null");
|
||||
|
||||
LeaderRequest request = LeaderRequest.newBuilder()
|
||||
.setName(Util.prefixNamespace(electionName.getByteString(), namespace))
|
||||
.build();
|
||||
|
||||
final StreamObserverDelegate<io.etcd.jetcd.api.LeaderResponse, LeaderResponse> observer = new StreamObserverDelegate<>(
|
||||
input -> new LeaderResponse(input, namespace));
|
||||
stub.leader(request, observer);
|
||||
|
||||
return observer.getFuture();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void observe(ByteSequence electionName, Listener listener) {
|
||||
checkNotNull(electionName, "election name should not be null");
|
||||
checkNotNull(listener, "listener should not be null");
|
||||
|
||||
LeaderRequest request = LeaderRequest.newBuilder().setName(electionName.getByteString()).build();
|
||||
|
||||
stub.observe(request, new StreamObserver<io.etcd.jetcd.api.LeaderResponse>() {
|
||||
@Override
|
||||
public void onNext(io.etcd.jetcd.api.LeaderResponse value) {
|
||||
listener.onNext(new LeaderResponse(value, namespace));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable error) {
|
||||
listener.onError(EtcdExceptionFactory.toEtcdException(error));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
listener.onCompleted();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ResignResponse> resign(LeaderKey leaderKey) {
|
||||
checkNotNull(leaderKey, "leader key should not be null");
|
||||
|
||||
io.etcd.jetcd.api.LeaderKey leader = io.etcd.jetcd.api.LeaderKey.newBuilder()
|
||||
.setKey(leaderKey.getKey()).setName(leaderKey.getName())
|
||||
.setLease(leaderKey.getLease()).setRev(leaderKey.getRevision())
|
||||
.build();
|
||||
ResignRequest request = ResignRequest.newBuilder().setLeader(leader).build();
|
||||
|
||||
final StreamObserverDelegate<io.etcd.jetcd.api.ResignResponse, ResignResponse> observer = new StreamObserverDelegate<>(
|
||||
ResignResponse::new);
|
||||
stub.resign(request, observer);
|
||||
|
||||
return observer.getFuture();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
private static class StreamObserverDelegate<S, T> implements StreamObserver<S> {
|
||||
private final CompletableFuture<T> future = new CompletableFuture<>();
|
||||
private final Function<S, T> converter;
|
||||
|
||||
public StreamObserverDelegate(Function<S, T> converter) {
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(S value) {
|
||||
future.complete(converter.apply(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable error) {
|
||||
if (error instanceof StatusRuntimeException) {
|
||||
StatusRuntimeException exception = (StatusRuntimeException) error;
|
||||
String description = exception.getStatus().getDescription();
|
||||
// different APIs use different messages. we cannot distinguish missing leader error otherwise,
|
||||
// because communicated status is always UNKNOWN
|
||||
if ("election: not leader".equals(description)) {
|
||||
future.completeExceptionally(NotLeaderException.INSTANCE);
|
||||
} else if ("election: no leader".equals(description)) {
|
||||
future.completeExceptionally(NoLeaderException.INSTANCE);
|
||||
}
|
||||
}
|
||||
future.completeExceptionally(EtcdExceptionFactory.toEtcdException(error));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
}
|
||||
|
||||
public CompletableFuture<T> getFuture() {
|
||||
return future;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -26,7 +26,6 @@ import io.etcd.jetcd.options.CompactOption;
|
|||
import io.etcd.jetcd.options.DeleteOption;
|
||||
import io.etcd.jetcd.options.GetOption;
|
||||
import io.etcd.jetcd.options.PutOption;
|
||||
import io.etcd.jetcd.options.TxnOption;
|
||||
import io.etcd.jetcd.support.CloseableClient;
|
||||
|
||||
/**
|
||||
|
@ -116,12 +115,4 @@ public interface KV extends CloseableClient {
|
|||
* @return a Txn
|
||||
*/
|
||||
Txn txn();
|
||||
|
||||
/**
|
||||
* creates a transaction.
|
||||
*
|
||||
* @param option TxnOption
|
||||
* @return a Txn
|
||||
*/
|
||||
Txn txn(TxnOption option);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* Copyright 2016-2021 The jetcd authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT 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 io.etcd.jetcd;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import io.etcd.jetcd.api.CompactionRequest;
|
||||
import io.etcd.jetcd.api.DeleteRangeRequest;
|
||||
import io.etcd.jetcd.api.KVGrpc;
|
||||
import io.etcd.jetcd.api.PutRequest;
|
||||
import io.etcd.jetcd.api.RangeRequest;
|
||||
import io.etcd.jetcd.kv.CompactResponse;
|
||||
import io.etcd.jetcd.kv.DeleteResponse;
|
||||
import io.etcd.jetcd.kv.GetResponse;
|
||||
import io.etcd.jetcd.kv.PutResponse;
|
||||
import io.etcd.jetcd.kv.TxnResponse;
|
||||
import io.etcd.jetcd.op.TxnImpl;
|
||||
import io.etcd.jetcd.options.CompactOption;
|
||||
import io.etcd.jetcd.options.DeleteOption;
|
||||
import io.etcd.jetcd.options.GetOption;
|
||||
import io.etcd.jetcd.options.PutOption;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static io.etcd.jetcd.options.OptionsUtil.toRangeRequestSortOrder;
|
||||
import static io.etcd.jetcd.options.OptionsUtil.toRangeRequestSortTarget;
|
||||
|
||||
/**
|
||||
* Implementation of etcd kv client.
|
||||
*/
|
||||
final class KVImpl implements KV {
|
||||
|
||||
private final ClientConnectionManager connectionManager;
|
||||
|
||||
private final KVGrpc.KVFutureStub stub;
|
||||
|
||||
private final ByteSequence namespace;
|
||||
|
||||
KVImpl(ClientConnectionManager connectionManager) {
|
||||
this.connectionManager = connectionManager;
|
||||
this.stub = connectionManager.newStub(KVGrpc::newFutureStub);
|
||||
this.namespace = connectionManager.getNamespace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<PutResponse> put(ByteSequence key, ByteSequence value) {
|
||||
return this.put(key, value, PutOption.DEFAULT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<PutResponse> put(ByteSequence key, ByteSequence value, PutOption option) {
|
||||
checkNotNull(key, "key should not be null");
|
||||
checkNotNull(value, "value should not be null");
|
||||
checkNotNull(option, "option should not be null");
|
||||
|
||||
PutRequest request = PutRequest.newBuilder()
|
||||
.setKey(Util.prefixNamespace(key.getByteString(), namespace))
|
||||
.setValue(value.getByteString())
|
||||
.setLease(option.getLeaseId())
|
||||
.setPrevKv(option.getPrevKV())
|
||||
.build();
|
||||
|
||||
return connectionManager.execute(
|
||||
() -> stub.put(request),
|
||||
response -> new PutResponse(response, namespace),
|
||||
Util::isRetryable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<GetResponse> get(ByteSequence key) {
|
||||
return this.get(key, GetOption.DEFAULT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<GetResponse> get(ByteSequence key, GetOption option) {
|
||||
checkNotNull(key, "key should not be null");
|
||||
checkNotNull(option, "option should not be null");
|
||||
|
||||
RangeRequest.Builder builder = RangeRequest.newBuilder()
|
||||
.setKey(Util.prefixNamespace(key.getByteString(), namespace))
|
||||
.setCountOnly(option.isCountOnly())
|
||||
.setLimit(option.getLimit())
|
||||
.setRevision(option.getRevision())
|
||||
.setKeysOnly(option.isKeysOnly())
|
||||
.setSerializable(option.isSerializable())
|
||||
.setSortOrder(toRangeRequestSortOrder(option.getSortOrder()))
|
||||
.setSortTarget(toRangeRequestSortTarget(option.getSortField()))
|
||||
.setMinCreateRevision(option.getMinCreateRevision())
|
||||
.setMaxCreateRevision(option.getMaxCreateRevision())
|
||||
.setMinModRevision(option.getMinModRevision())
|
||||
.setMaxModRevision(option.getMaxModRevision());
|
||||
|
||||
option.getEndKey().map(endKey -> Util.prefixNamespaceToRangeEnd(endKey.getByteString(), namespace))
|
||||
.ifPresent(builder::setRangeEnd);
|
||||
|
||||
RangeRequest request = builder.build();
|
||||
|
||||
return connectionManager.execute(
|
||||
() -> stub.range(request),
|
||||
response -> new GetResponse(response, namespace),
|
||||
Util::isRetryable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<DeleteResponse> delete(ByteSequence key) {
|
||||
return this.delete(key, DeleteOption.DEFAULT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<DeleteResponse> delete(ByteSequence key, DeleteOption option) {
|
||||
checkNotNull(key, "key should not be null");
|
||||
checkNotNull(option, "option should not be null");
|
||||
|
||||
DeleteRangeRequest.Builder builder = DeleteRangeRequest.newBuilder()
|
||||
.setKey(Util.prefixNamespace(key.getByteString(), namespace))
|
||||
.setPrevKv(option.isPrevKV());
|
||||
|
||||
option.getEndKey()
|
||||
.map(endKey -> Util.prefixNamespaceToRangeEnd(endKey.getByteString(), namespace))
|
||||
.ifPresent(builder::setRangeEnd);
|
||||
|
||||
DeleteRangeRequest request = builder.build();
|
||||
|
||||
return connectionManager.execute(
|
||||
() -> stub.deleteRange(request),
|
||||
response -> new DeleteResponse(response, namespace),
|
||||
Util::isRetryable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CompactResponse> compact(long rev) {
|
||||
return this.compact(rev, CompactOption.DEFAULT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CompactResponse> compact(long rev, CompactOption option) {
|
||||
checkNotNull(option, "option should not be null");
|
||||
|
||||
CompactionRequest request = CompactionRequest.newBuilder()
|
||||
.setRevision(rev).setPhysical(option.isPhysical())
|
||||
.build();
|
||||
|
||||
return connectionManager.execute(
|
||||
() -> stub.compact(request),
|
||||
CompactResponse::new,
|
||||
Util::isRetryable);
|
||||
}
|
||||
|
||||
public Txn txn() {
|
||||
return TxnImpl.newTxn(
|
||||
request -> connectionManager.execute(
|
||||
() -> stub.txn(request),
|
||||
response -> new TxnResponse(response, namespace), Util::isRetryable),
|
||||
namespace);
|
||||
}
|
||||
}
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
package io.etcd.jetcd;
|
||||
|
||||
import io.etcd.jetcd.support.Util;
|
||||
|
||||
/**
|
||||
* Etcd key value pair.
|
||||
*/
|
||||
|
@ -29,35 +27,26 @@ public class KeyValue {
|
|||
|
||||
public KeyValue(io.etcd.jetcd.api.KeyValue kv, ByteSequence namespace) {
|
||||
this.kv = kv;
|
||||
this.unprefixedKey = ByteSequence
|
||||
.from(kv.getKey().isEmpty() ? kv.getKey() : Util.unprefixNamespace(kv.getKey(), namespace));
|
||||
this.value = ByteSequence.from(kv.getValue());
|
||||
|
||||
this.unprefixedKey = ByteSequence.from(
|
||||
kv.getKey().isEmpty()
|
||||
? kv.getKey()
|
||||
: Util.unprefixNamespace(kv.getKey(), namespace));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key
|
||||
*
|
||||
* @return the key.
|
||||
* @return the key
|
||||
*/
|
||||
public ByteSequence getKey() {
|
||||
return unprefixedKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value
|
||||
*
|
||||
* @return the value.
|
||||
* @return the value
|
||||
*/
|
||||
public ByteSequence getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the create revision.
|
||||
*
|
||||
* @return the create revision.
|
||||
*/
|
||||
public long getCreateRevision() {
|
||||
|
@ -65,8 +54,6 @@ public class KeyValue {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the mod revision.
|
||||
*
|
||||
* @return the mod revision.
|
||||
*/
|
||||
public long getModRevision() {
|
||||
|
@ -74,8 +61,6 @@ public class KeyValue {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the version.
|
||||
*
|
||||
* @return the version.
|
||||
*/
|
||||
public long getVersion() {
|
||||
|
@ -83,8 +68,6 @@ public class KeyValue {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the lease.
|
||||
*
|
||||
* @return the lease.
|
||||
*/
|
||||
public long getLease() {
|
||||
|
|
|
@ -0,0 +1,352 @@
|
|||
/*
|
||||
* Copyright 2016-2021 The jetcd authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT 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 io.etcd.jetcd;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import io.etcd.jetcd.api.LeaseGrantRequest;
|
||||
import io.etcd.jetcd.api.LeaseGrpc;
|
||||
import io.etcd.jetcd.api.LeaseKeepAliveRequest;
|
||||
import io.etcd.jetcd.api.LeaseRevokeRequest;
|
||||
import io.etcd.jetcd.api.LeaseTimeToLiveRequest;
|
||||
import io.etcd.jetcd.common.exception.ErrorCode;
|
||||
import io.etcd.jetcd.lease.LeaseGrantResponse;
|
||||
import io.etcd.jetcd.lease.LeaseKeepAliveResponse;
|
||||
import io.etcd.jetcd.lease.LeaseRevokeResponse;
|
||||
import io.etcd.jetcd.lease.LeaseTimeToLiveResponse;
|
||||
import io.etcd.jetcd.options.LeaseOption;
|
||||
import io.etcd.jetcd.support.CloseableClient;
|
||||
import io.etcd.jetcd.support.Observers;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static io.etcd.jetcd.common.exception.EtcdExceptionFactory.newClosedLeaseClientException;
|
||||
import static io.etcd.jetcd.common.exception.EtcdExceptionFactory.newEtcdException;
|
||||
import static io.etcd.jetcd.common.exception.EtcdExceptionFactory.toEtcdException;
|
||||
|
||||
/**
|
||||
* Implementation of lease client.
|
||||
*/
|
||||
final class LeaseImpl implements Lease {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(LeaseImpl.class);
|
||||
|
||||
/**
|
||||
* FIRST_KEEPALIVE_TIMEOUT_MS is the timeout for the first keepalive request
|
||||
* before the actual TTL is known to the lease client.
|
||||
*/
|
||||
private static final int FIRST_KEEPALIVE_TIMEOUT_MS = 5000;
|
||||
|
||||
private final ClientConnectionManager connectionManager;
|
||||
private final LeaseGrpc.LeaseFutureStub stub;
|
||||
private final LeaseGrpc.LeaseStub leaseStub;
|
||||
private final Map<Long, KeepAlive> keepAlives;
|
||||
/**
|
||||
* Timer schedule to send keep alive request.
|
||||
*/
|
||||
private final ListeningScheduledExecutorService scheduledExecutorService;
|
||||
private ScheduledFuture<?> keepAliveFuture;
|
||||
private ScheduledFuture<?> deadlineFuture;
|
||||
/**
|
||||
* KeepAlive Request Stream, put request into this stream to keep the lease alive.
|
||||
*/
|
||||
private StreamObserver<LeaseKeepAliveRequest> keepAliveRequestObserver;
|
||||
|
||||
/**
|
||||
* KeepAlive Response Streamer, receive keep alive response from this stream and update the
|
||||
* nextKeepAliveTime and deadline of the leases.
|
||||
*/
|
||||
private StreamObserver<io.etcd.jetcd.api.LeaseKeepAliveResponse> keepAliveResponseObserver;
|
||||
|
||||
/**
|
||||
* hasKeepAliveServiceStarted indicates whether the background keep alive service has started.
|
||||
*/
|
||||
private volatile boolean hasKeepAliveServiceStarted;
|
||||
private volatile boolean closed;
|
||||
|
||||
LeaseImpl(ClientConnectionManager connectionManager) {
|
||||
this.connectionManager = connectionManager;
|
||||
this.stub = connectionManager.newStub(LeaseGrpc::newFutureStub);
|
||||
this.leaseStub = Util.applyRequireLeader(true, connectionManager.newStub(LeaseGrpc::newStub));
|
||||
this.keepAlives = new ConcurrentHashMap<>();
|
||||
this.scheduledExecutorService = MoreExecutors.listeningDecorator(Executors.newScheduledThreadPool(2));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<LeaseGrantResponse> grant(long ttl) {
|
||||
return connectionManager.execute(() -> this.stub.leaseGrant(LeaseGrantRequest.newBuilder().setTTL(ttl).build()),
|
||||
LeaseGrantResponse::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<LeaseGrantResponse> grant(long ttl, long timeout, TimeUnit unit) {
|
||||
return connectionManager.execute(
|
||||
() -> this.stub.withDeadlineAfter(timeout, unit).leaseGrant(LeaseGrantRequest.newBuilder().setTTL(ttl).build()),
|
||||
LeaseGrantResponse::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<LeaseRevokeResponse> revoke(long leaseId) {
|
||||
return connectionManager.execute(() -> this.stub.leaseRevoke(LeaseRevokeRequest.newBuilder().setID(leaseId).build()),
|
||||
LeaseRevokeResponse::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized CloseableClient keepAlive(long leaseId, StreamObserver<LeaseKeepAliveResponse> observer) {
|
||||
if (this.closed) {
|
||||
throw newClosedLeaseClientException();
|
||||
}
|
||||
|
||||
KeepAlive keepAlive = this.keepAlives.computeIfAbsent(leaseId, (key) -> new KeepAlive(leaseId));
|
||||
keepAlive.addObserver(observer);
|
||||
|
||||
if (!this.hasKeepAliveServiceStarted) {
|
||||
this.hasKeepAliveServiceStarted = true;
|
||||
this.start();
|
||||
}
|
||||
|
||||
return new CloseableClient() {
|
||||
@Override
|
||||
public void close() {
|
||||
keepAlive.removeObserver(observer);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
if (this.closed) {
|
||||
return;
|
||||
}
|
||||
this.closed = true;
|
||||
|
||||
if (!this.hasKeepAliveServiceStarted) { // hasKeepAliveServiceStarted hasn't started.
|
||||
return;
|
||||
}
|
||||
|
||||
this.keepAliveFuture.cancel(true);
|
||||
this.deadlineFuture.cancel(true);
|
||||
this.keepAliveRequestObserver.onCompleted();
|
||||
this.keepAliveResponseObserver.onCompleted();
|
||||
this.scheduledExecutorService.shutdownNow();
|
||||
|
||||
final Throwable errResp = newClosedLeaseClientException();
|
||||
|
||||
this.keepAlives.forEach((k, v) -> v.onError(errResp));
|
||||
this.keepAlives.clear();
|
||||
}
|
||||
|
||||
private synchronized void removeKeepAlive(long leaseId) {
|
||||
this.keepAlives.remove(leaseId);
|
||||
}
|
||||
|
||||
private void start() {
|
||||
this.sendKeepAliveExecutor();
|
||||
this.deadLineExecutor();
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
this.keepAliveFuture.cancel(true);
|
||||
this.keepAliveRequestObserver.onCompleted();
|
||||
this.keepAliveResponseObserver.onCompleted();
|
||||
this.sendKeepAliveExecutor();
|
||||
}
|
||||
|
||||
private void sendKeepAliveExecutor() {
|
||||
this.keepAliveResponseObserver = Observers.observer(this::processKeepAliveResponse, error -> processOnError());
|
||||
this.keepAliveRequestObserver = this.leaseStub.leaseKeepAlive(this.keepAliveResponseObserver);
|
||||
this.keepAliveFuture = scheduledExecutorService.scheduleAtFixedRate(() -> {
|
||||
// send keep alive req to the leases whose next keep alive is before now.
|
||||
this.keepAlives.entrySet().stream()
|
||||
.filter(entry -> entry.getValue().getNextKeepAlive() < System.currentTimeMillis()).map(Entry::getKey)
|
||||
.map(leaseId -> LeaseKeepAliveRequest.newBuilder().setID(leaseId).build())
|
||||
.forEach(keepAliveRequestObserver::onNext);
|
||||
|
||||
}, 0, 500, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
private synchronized void processOnError() {
|
||||
if (this.closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
Futures.addCallback(this.scheduledExecutorService.schedule(this::reset, 500, TimeUnit.MILLISECONDS),
|
||||
new FutureCallback<Object>() {
|
||||
@Override
|
||||
public void onFailure(Throwable throwable) {
|
||||
LOG.error("scheduled reset failed", throwable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Object result) {
|
||||
}
|
||||
}, this.scheduledExecutorService);
|
||||
}
|
||||
|
||||
private synchronized void processKeepAliveResponse(io.etcd.jetcd.api.LeaseKeepAliveResponse leaseKeepAliveResponse) {
|
||||
if (this.closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
final long leaseID = leaseKeepAliveResponse.getID();
|
||||
final long ttl = leaseKeepAliveResponse.getTTL();
|
||||
final KeepAlive ka = this.keepAlives.get(leaseID);
|
||||
|
||||
if (ka == null) {
|
||||
// return if the corresponding keep alive has closed.
|
||||
return;
|
||||
}
|
||||
|
||||
if (ttl > 0) {
|
||||
long nextKeepAlive = System.currentTimeMillis() + ttl * 1000 / 3;
|
||||
ka.setNextKeepAlive(nextKeepAlive);
|
||||
ka.setDeadLine(System.currentTimeMillis() + ttl * 1000);
|
||||
ka.onNext(leaseKeepAliveResponse);
|
||||
} else {
|
||||
// lease expired; close all keep alive
|
||||
this.removeKeepAlive(leaseID);
|
||||
ka.onError(newEtcdException(ErrorCode.NOT_FOUND, "etcdserver: requested lease not found"));
|
||||
}
|
||||
}
|
||||
|
||||
private void deadLineExecutor() {
|
||||
this.deadlineFuture = scheduledExecutorService.scheduleAtFixedRate(() -> {
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
this.keepAlives.values().removeIf(ka -> {
|
||||
if (ka.getDeadLine() < now) {
|
||||
ka.onCompleted();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}, 0, 1000, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<LeaseKeepAliveResponse> keepAliveOnce(long leaseId) {
|
||||
CompletableFuture<LeaseKeepAliveResponse> future = new CompletableFuture<>();
|
||||
|
||||
StreamObserver<LeaseKeepAliveRequest> requestObserver = Observers.observe(
|
||||
this.leaseStub::leaseKeepAlive,
|
||||
response -> future.complete(new LeaseKeepAliveResponse(response)),
|
||||
throwable -> future.completeExceptionally(toEtcdException(throwable)));
|
||||
|
||||
// cancel grpc stream when leaseKeepAliveResponseCompletableFuture completes.
|
||||
CompletableFuture<LeaseKeepAliveResponse> answer = future
|
||||
.whenCompleteAsync((val, throwable) -> requestObserver.onCompleted(), connectionManager.getExecutorService());
|
||||
|
||||
requestObserver.onNext(LeaseKeepAliveRequest.newBuilder().setID(leaseId).build());
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<LeaseTimeToLiveResponse> timeToLive(long leaseId, LeaseOption option) {
|
||||
checkNotNull(option, "LeaseOption should not be null");
|
||||
|
||||
LeaseTimeToLiveRequest leaseTimeToLiveRequest = LeaseTimeToLiveRequest.newBuilder()
|
||||
.setID(leaseId)
|
||||
.setKeys(option.isAttachedKeys())
|
||||
.build();
|
||||
|
||||
return connectionManager.execute(
|
||||
() -> this.stub.leaseTimeToLive(leaseTimeToLiveRequest),
|
||||
LeaseTimeToLiveResponse::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* The KeepAlive hold the keepAlive information for lease.
|
||||
*/
|
||||
private final class KeepAlive implements StreamObserver<io.etcd.jetcd.api.LeaseKeepAliveResponse> {
|
||||
private final List<StreamObserver<LeaseKeepAliveResponse>> observers;
|
||||
private final long leaseId;
|
||||
|
||||
private long deadLine;
|
||||
private long nextKeepAlive;
|
||||
|
||||
public KeepAlive(long leaseId) {
|
||||
this.nextKeepAlive = System.currentTimeMillis();
|
||||
this.deadLine = nextKeepAlive + FIRST_KEEPALIVE_TIMEOUT_MS;
|
||||
|
||||
this.observers = new CopyOnWriteArrayList<>();
|
||||
this.leaseId = leaseId;
|
||||
}
|
||||
|
||||
public long getDeadLine() {
|
||||
return deadLine;
|
||||
}
|
||||
|
||||
public void setDeadLine(long deadLine) {
|
||||
this.deadLine = deadLine;
|
||||
}
|
||||
|
||||
public void addObserver(StreamObserver<LeaseKeepAliveResponse> observer) {
|
||||
this.observers.add(observer);
|
||||
}
|
||||
|
||||
//removeObserver only would be called synchronously by close in KeepAliveListener, no need to get lock here
|
||||
public void removeObserver(StreamObserver<LeaseKeepAliveResponse> listener) {
|
||||
this.observers.remove(listener);
|
||||
if (this.observers.isEmpty()) {
|
||||
removeKeepAlive(leaseId);
|
||||
}
|
||||
}
|
||||
|
||||
public long getNextKeepAlive() {
|
||||
return nextKeepAlive;
|
||||
}
|
||||
|
||||
public void setNextKeepAlive(long nextKeepAlive) {
|
||||
this.nextKeepAlive = nextKeepAlive;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(io.etcd.jetcd.api.LeaseKeepAliveResponse response) {
|
||||
for (StreamObserver<LeaseKeepAliveResponse> observer : observers) {
|
||||
observer.onNext(new LeaseKeepAliveResponse(response));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable throwable) {
|
||||
for (StreamObserver<LeaseKeepAliveResponse> observer : observers) {
|
||||
observer.onError(toEtcdException(throwable));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
this.observers.forEach(StreamObserver::onCompleted);
|
||||
this.observers.clear();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright 2016-2021 The jetcd authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT 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 io.etcd.jetcd;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import io.etcd.jetcd.api.lock.LockGrpc;
|
||||
import io.etcd.jetcd.api.lock.LockRequest;
|
||||
import io.etcd.jetcd.api.lock.UnlockRequest;
|
||||
import io.etcd.jetcd.lock.LockResponse;
|
||||
import io.etcd.jetcd.lock.UnlockResponse;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
final class LockImpl implements Lock {
|
||||
|
||||
private final ClientConnectionManager connectionManager;
|
||||
|
||||
private final LockGrpc.LockFutureStub stub;
|
||||
|
||||
private final ByteSequence namespace;
|
||||
|
||||
LockImpl(ClientConnectionManager connectionManager) {
|
||||
this.connectionManager = connectionManager;
|
||||
this.stub = connectionManager.newStub(LockGrpc::newFutureStub);
|
||||
this.namespace = connectionManager.getNamespace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<LockResponse> lock(ByteSequence name, long leaseId) {
|
||||
checkNotNull(name);
|
||||
|
||||
LockRequest request = LockRequest.newBuilder()
|
||||
.setName(Util.prefixNamespace(name.getByteString(), namespace))
|
||||
.setLease(leaseId)
|
||||
.build();
|
||||
|
||||
return connectionManager.execute(
|
||||
() -> stub.lock(request),
|
||||
response -> new LockResponse(response, namespace),
|
||||
Util::isRetryable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<UnlockResponse> unlock(ByteSequence lockKey) {
|
||||
checkNotNull(lockKey);
|
||||
|
||||
UnlockRequest request = UnlockRequest.newBuilder()
|
||||
.setKey(Util.prefixNamespace(lockKey.getByteString(), namespace))
|
||||
.build();
|
||||
|
||||
return connectionManager.execute(
|
||||
() -> stub.unlock(request),
|
||||
UnlockResponse::new,
|
||||
Util::isRetryable);
|
||||
}
|
||||
}
|
|
@ -17,9 +17,16 @@
|
|||
package io.etcd.jetcd;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import io.etcd.jetcd.maintenance.*;
|
||||
import io.etcd.jetcd.maintenance.AlarmMember;
|
||||
import io.etcd.jetcd.maintenance.AlarmResponse;
|
||||
import io.etcd.jetcd.maintenance.DefragmentResponse;
|
||||
import io.etcd.jetcd.maintenance.HashKVResponse;
|
||||
import io.etcd.jetcd.maintenance.MoveLeaderResponse;
|
||||
import io.etcd.jetcd.maintenance.SnapshotResponse;
|
||||
import io.etcd.jetcd.maintenance.StatusResponse;
|
||||
import io.etcd.jetcd.support.CloseableClient;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
|
||||
|
@ -48,7 +55,7 @@ public interface Maintenance extends CloseableClient {
|
|||
|
||||
/**
|
||||
* get all active keyspace alarm.
|
||||
*
|
||||
*
|
||||
* @return the response
|
||||
*/
|
||||
CompletableFuture<AlarmResponse> listAlarms();
|
||||
|
@ -62,7 +69,7 @@ public interface Maintenance extends CloseableClient {
|
|||
CompletableFuture<AlarmResponse> alarmDisarm(AlarmMember member);
|
||||
|
||||
/**
|
||||
* Defragment one member of the cluster by its endpoint.
|
||||
* defragment one member of the cluster by its endpoint.
|
||||
*
|
||||
* <p>
|
||||
* After compacting the keyspace, the backend database may exhibit internal
|
||||
|
@ -78,29 +85,29 @@ public interface Maintenance extends CloseableClient {
|
|||
* To defragment multiple members in the cluster, user need to call defragment
|
||||
* multiple times with different endpoints.
|
||||
*
|
||||
* @param target the etcd server endpoint.
|
||||
* @return the response result
|
||||
* @param endpoint the etcd server endpoint.
|
||||
* @return the response result
|
||||
*/
|
||||
CompletableFuture<DefragmentResponse> defragmentMember(String target);
|
||||
CompletableFuture<DefragmentResponse> defragmentMember(URI endpoint);
|
||||
|
||||
/**
|
||||
* get the status of a member by its endpoint.
|
||||
*
|
||||
* @param target the etcd server endpoint.
|
||||
* @return the response result
|
||||
* @param endpoint the etcd server endpoint.
|
||||
* @return the response result
|
||||
*/
|
||||
CompletableFuture<StatusResponse> statusMember(String target);
|
||||
CompletableFuture<StatusResponse> statusMember(URI endpoint);
|
||||
|
||||
/**
|
||||
* returns a hash of the KV state at the time of the RPC.
|
||||
* If revision is zero, the hash is computed on all keys. If the revision
|
||||
* is non-zero, the hash is computed on all keys at or below the given revision.
|
||||
*
|
||||
* @param target the etcd server endpoint.
|
||||
* @param rev the revision
|
||||
* @return the response result
|
||||
* @param endpoint the etcd server endpoint.
|
||||
* @param rev the revision
|
||||
* @return the response result
|
||||
*/
|
||||
CompletableFuture<HashKVResponse> hashKV(String target, long rev);
|
||||
CompletableFuture<HashKVResponse> hashKV(URI endpoint, long rev);
|
||||
|
||||
/**
|
||||
* retrieves backend snapshot.
|
||||
|
|
|
@ -0,0 +1,208 @@
|
|||
/*
|
||||
* Copyright 2016-2021 The jetcd authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT 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 io.etcd.jetcd;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import io.etcd.jetcd.api.AlarmRequest;
|
||||
import io.etcd.jetcd.api.AlarmType;
|
||||
import io.etcd.jetcd.api.DefragmentRequest;
|
||||
import io.etcd.jetcd.api.HashKVRequest;
|
||||
import io.etcd.jetcd.api.MaintenanceGrpc;
|
||||
import io.etcd.jetcd.api.MoveLeaderRequest;
|
||||
import io.etcd.jetcd.api.SnapshotRequest;
|
||||
import io.etcd.jetcd.api.SnapshotResponse;
|
||||
import io.etcd.jetcd.api.StatusRequest;
|
||||
import io.etcd.jetcd.maintenance.AlarmResponse;
|
||||
import io.etcd.jetcd.maintenance.DefragmentResponse;
|
||||
import io.etcd.jetcd.maintenance.HashKVResponse;
|
||||
import io.etcd.jetcd.maintenance.MoveLeaderResponse;
|
||||
import io.etcd.jetcd.maintenance.StatusResponse;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static io.etcd.jetcd.common.exception.EtcdExceptionFactory.toEtcdException;
|
||||
|
||||
/**
|
||||
* Implementation of maintenance client.
|
||||
*/
|
||||
final class MaintenanceImpl implements Maintenance {
|
||||
|
||||
private final ClientConnectionManager connectionManager;
|
||||
private final MaintenanceGrpc.MaintenanceFutureStub stub;
|
||||
private final MaintenanceGrpc.MaintenanceStub streamStub;
|
||||
|
||||
MaintenanceImpl(ClientConnectionManager connectionManager) {
|
||||
this.connectionManager = connectionManager;
|
||||
this.stub = connectionManager.newStub(MaintenanceGrpc::newFutureStub);
|
||||
this.streamStub = connectionManager.newStub(MaintenanceGrpc::newStub);
|
||||
}
|
||||
|
||||
/**
|
||||
* get all active keyspace alarm.
|
||||
*
|
||||
* @return alarm list
|
||||
*/
|
||||
@Override
|
||||
public CompletableFuture<AlarmResponse> listAlarms() {
|
||||
AlarmRequest alarmRequest = AlarmRequest.newBuilder()
|
||||
.setAlarm(AlarmType.NONE)
|
||||
.setAction(AlarmRequest.AlarmAction.GET)
|
||||
.setMemberID(0)
|
||||
.build();
|
||||
|
||||
return Util.toCompletableFuture(this.stub.alarm(alarmRequest), AlarmResponse::new,
|
||||
this.connectionManager.getExecutorService());
|
||||
}
|
||||
|
||||
/**
|
||||
* disarms a given alarm.
|
||||
*
|
||||
* @param member the alarm
|
||||
* @return the response result
|
||||
*/
|
||||
@Override
|
||||
public CompletableFuture<AlarmResponse> alarmDisarm(io.etcd.jetcd.maintenance.AlarmMember member) {
|
||||
checkArgument(member.getMemberId() != 0, "the member id can not be 0");
|
||||
checkArgument(member.getAlarmType() != io.etcd.jetcd.maintenance.AlarmType.NONE, "alarm type can not be NONE");
|
||||
|
||||
AlarmRequest alarmRequest = AlarmRequest.newBuilder()
|
||||
.setAlarm(AlarmType.NOSPACE)
|
||||
.setAction(AlarmRequest.AlarmAction.DEACTIVATE)
|
||||
.setMemberID(member.getMemberId())
|
||||
.build();
|
||||
|
||||
return Util.toCompletableFuture(this.stub.alarm(alarmRequest), AlarmResponse::new,
|
||||
this.connectionManager.getExecutorService());
|
||||
}
|
||||
|
||||
/**
|
||||
* defragment one member of the cluster.
|
||||
*
|
||||
* <p>
|
||||
* After compacting the keyspace, the backend database may exhibit internal
|
||||
* fragmentation. Any internal fragmentation is space that is free to use
|
||||
* by the backend but still consumes storage space. The process of
|
||||
* defragmentation releases this storage space back to the file system.
|
||||
* Defragmentation is issued on a per-member so that cluster-wide latency
|
||||
* spikes may be avoided.
|
||||
*
|
||||
* <p>
|
||||
* Defragment is an expensive operation. User should avoid defragmenting
|
||||
* multiple members at the same time.
|
||||
* To defragment multiple members in the cluster, user need to call defragment
|
||||
* multiple times with different endpoints.
|
||||
*/
|
||||
@Override
|
||||
public CompletableFuture<DefragmentResponse> defragmentMember(URI endpoint) {
|
||||
return this.connectionManager.withNewChannel(
|
||||
endpoint,
|
||||
MaintenanceGrpc::newFutureStub,
|
||||
stub -> Util.toCompletableFuture(
|
||||
stub.defragment(DefragmentRequest.getDefaultInstance()),
|
||||
DefragmentResponse::new,
|
||||
this.connectionManager.getExecutorService()));
|
||||
}
|
||||
|
||||
/**
|
||||
* get the status of one member.
|
||||
*/
|
||||
@Override
|
||||
public CompletableFuture<StatusResponse> statusMember(URI endpoint) {
|
||||
return this.connectionManager.withNewChannel(
|
||||
endpoint,
|
||||
MaintenanceGrpc::newFutureStub,
|
||||
stub -> Util.toCompletableFuture(
|
||||
stub.status(StatusRequest.getDefaultInstance()),
|
||||
StatusResponse::new,
|
||||
this.connectionManager.getExecutorService()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<MoveLeaderResponse> moveLeader(long transfereeID) {
|
||||
return Util.toCompletableFuture(
|
||||
this.stub.moveLeader(MoveLeaderRequest.newBuilder().setTargetID(transfereeID).build()),
|
||||
MoveLeaderResponse::new,
|
||||
this.connectionManager.getExecutorService());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<HashKVResponse> hashKV(URI endpoint, long rev) {
|
||||
return this.connectionManager.withNewChannel(
|
||||
endpoint,
|
||||
MaintenanceGrpc::newFutureStub,
|
||||
stub -> Util.toCompletableFuture(
|
||||
stub.hashKV(HashKVRequest.newBuilder().setRevision(rev).build()),
|
||||
HashKVResponse::new,
|
||||
this.connectionManager.getExecutorService()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Long> snapshot(OutputStream outputStream) {
|
||||
final CompletableFuture<Long> answer = new CompletableFuture<>();
|
||||
final AtomicLong bytes = new AtomicLong(0);
|
||||
|
||||
this.streamStub.snapshot(SnapshotRequest.getDefaultInstance(), new StreamObserver<SnapshotResponse>() {
|
||||
@Override
|
||||
public void onNext(SnapshotResponse snapshotResponse) {
|
||||
try {
|
||||
snapshotResponse.getBlob().writeTo(outputStream);
|
||||
|
||||
bytes.addAndGet(snapshotResponse.getBlob().size());
|
||||
} catch (IOException e) {
|
||||
answer.completeExceptionally(toEtcdException(e));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable throwable) {
|
||||
answer.completeExceptionally(toEtcdException(throwable));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
answer.complete(bytes.get());
|
||||
}
|
||||
});
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void snapshot(StreamObserver<io.etcd.jetcd.maintenance.SnapshotResponse> observer) {
|
||||
this.streamStub.snapshot(SnapshotRequest.getDefaultInstance(), new StreamObserver<SnapshotResponse>() {
|
||||
@Override
|
||||
public void onNext(SnapshotResponse snapshotResponse) {
|
||||
observer.onNext(new io.etcd.jetcd.maintenance.SnapshotResponse(snapshotResponse));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable throwable) {
|
||||
observer.onError(toEtcdException(throwable));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
observer.onCompleted();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016-2023 The jetcd authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT 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 io.etcd.jetcd;
|
||||
|
||||
public class Preconditions {
|
||||
|
||||
public static void checkArgument(boolean expression, String errorMessage) {
|
||||
if (!expression) {
|
||||
throw new IllegalArgumentException(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkState(boolean expression, String errorMessage) {
|
||||
if (!expression) {
|
||||
throw new IllegalStateException(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,39 +22,29 @@ package io.etcd.jetcd;
|
|||
public interface Response {
|
||||
|
||||
/**
|
||||
* Returns the response header
|
||||
*
|
||||
* @return the header.
|
||||
* @return the response header
|
||||
*/
|
||||
Header getHeader();
|
||||
|
||||
interface Header {
|
||||
|
||||
/**
|
||||
* Returns the cluster id
|
||||
*
|
||||
* @return the cluster id.
|
||||
* @return the cluster id
|
||||
*/
|
||||
long getClusterId();
|
||||
|
||||
/**
|
||||
* Returns the member id
|
||||
*
|
||||
* @return the member id.
|
||||
* @return the member id
|
||||
*/
|
||||
long getMemberId();
|
||||
|
||||
/**
|
||||
* Returns the revision id
|
||||
*
|
||||
* @return the revision.
|
||||
* @return the revision id
|
||||
*/
|
||||
long getRevision();
|
||||
|
||||
/**
|
||||
* Returns the raft term
|
||||
*
|
||||
* @return theraft term.
|
||||
* @return the raft term
|
||||
*/
|
||||
long getRaftTerm();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2016-2023 The jetcd authors
|
||||
* Copyright 2016-2021 The jetcd authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -25,19 +25,20 @@ import io.etcd.jetcd.op.Op;
|
|||
/**
|
||||
* Txn is the interface that wraps mini-transactions.
|
||||
*
|
||||
* <h2>Usage examples</h2>
|
||||
* <h3>Usage examples</h3>
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* txn.If(
|
||||
* new Cmp(KEY, Cmp.Op.GREATER, CmpTarget.value(VALUE)),
|
||||
* new Cmp(KEY, cmp.Op.EQUAL, CmpTarget.version(2))).Then(
|
||||
* Op.put(KEY2, VALUE2, PutOption.DEFAULT),
|
||||
* Op.put(KEY3, VALUE3, PutOption.DEFAULT))
|
||||
* .Else(
|
||||
* Op.put(KEY4, VALUE4, PutOption.DEFAULT),
|
||||
* Op.put(KEY4, VALUE4, PutOption.DEFAULT))
|
||||
* .commit();
|
||||
* new Cmp(KEY, Cmp.Op.GREATER, CmpTarget.value(VALUE)),
|
||||
* new Cmp(KEY, cmp.Op.EQUAL, CmpTarget.version(2))
|
||||
* ).Then(
|
||||
* Op.put(KEY2, VALUE2, PutOption.DEFAULT),
|
||||
* Op.put(KEY3, VALUE3, PutOption.DEFAULT)
|
||||
* ).Else(
|
||||
* Op.put(KEY4, VALUE4, PutOption.DEFAULT),
|
||||
* Op.put(KEY4, VALUE4, PutOption.DEFAULT)
|
||||
* ).commit();
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
|
@ -47,17 +48,18 @@ import io.etcd.jetcd.op.Op;
|
|||
* <pre>
|
||||
* {@code
|
||||
* txn.If(
|
||||
* new Cmp(KEY, Cmp.Op.GREATER, CmpTarget.value(VALUE))).If(
|
||||
* new Cmp(KEY, cmp.Op.EQUAL, CmpTarget.version(VERSION)))
|
||||
* .Then(
|
||||
* Op.put(KEY2, VALUE2, PutOption.DEFAULT))
|
||||
* .Then(
|
||||
* Op.put(KEY3, VALUE3, PutOption.DEFAULT))
|
||||
* .Else(
|
||||
* Op.put(KEY4, VALUE4, PutOption.DEFAULT))
|
||||
* .Else(
|
||||
* Op.put(KEY4, VALUE4, PutOption.DEFAULT))
|
||||
* .commit();
|
||||
* new Cmp(KEY, Cmp.Op.GREATER, CmpTarget.value(VALUE))
|
||||
* ).If(
|
||||
* new Cmp(KEY, cmp.Op.EQUAL, CmpTarget.version(VERSION))
|
||||
* ).Then(
|
||||
* Op.put(KEY2, VALUE2, PutOption.DEFAULT)
|
||||
* ).Then(
|
||||
* Op.put(KEY3, VALUE3, PutOption.DEFAULT)
|
||||
* ).Else(
|
||||
* Op.put(KEY4, VALUE4, PutOption.DEFAULT)
|
||||
* ).Else(
|
||||
* Op.put(KEY4, VALUE4, PutOption.DEFAULT)
|
||||
* ).commit();
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* Copyright 2016-2021 The jetcd authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT 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 io.etcd.jetcd;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.protobuf.ByteString;
|
||||
import io.grpc.Metadata;
|
||||
import io.grpc.Status;
|
||||
import io.grpc.stub.AbstractStub;
|
||||
import io.grpc.stub.MetadataUtils;
|
||||
|
||||
import static io.etcd.jetcd.common.exception.EtcdExceptionFactory.toEtcdException;
|
||||
|
||||
public final class Util {
|
||||
|
||||
private Util() {
|
||||
}
|
||||
|
||||
public static List<URI> toURIs(Collection<String> uris) {
|
||||
return uris.stream().map(uri -> {
|
||||
try {
|
||||
return new URI(uri);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new IllegalArgumentException("Invalid endpoint URI: " + uri, e);
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* convert ListenableFuture of Type S to CompletableFuture of Type T.
|
||||
*/
|
||||
static <S, T> CompletableFuture<T> toCompletableFuture(ListenableFuture<S> sourceFuture, Function<S, T> resultConvert,
|
||||
Executor executor) {
|
||||
|
||||
CompletableFuture<T> targetFuture = new CompletableFuture<T>() {
|
||||
// the cancel of targetFuture also cancels the sourceFuture.
|
||||
@Override
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
super.cancel(mayInterruptIfRunning);
|
||||
return sourceFuture.cancel(mayInterruptIfRunning);
|
||||
}
|
||||
};
|
||||
|
||||
sourceFuture.addListener(() -> {
|
||||
try {
|
||||
targetFuture.complete(resultConvert.apply(sourceFuture.get()));
|
||||
} catch (Exception e) {
|
||||
targetFuture.completeExceptionally(toEtcdException(e));
|
||||
}
|
||||
}, executor);
|
||||
|
||||
return targetFuture;
|
||||
}
|
||||
|
||||
static boolean isRetryable(Throwable e) {
|
||||
return isInvalidTokenError(Status.fromThrowable(e));
|
||||
}
|
||||
|
||||
static boolean isInvalidTokenError(Status status) {
|
||||
return status.getCode() == Status.Code.UNAUTHENTICATED
|
||||
&& "etcdserver: invalid auth token".equals(status.getDescription());
|
||||
}
|
||||
|
||||
static <T> T supplyIfNull(T target, Supplier<T> supplier) {
|
||||
return target != null ? target : supplier.get();
|
||||
}
|
||||
|
||||
public static ByteString prefixNamespace(ByteString key, ByteSequence namespace) {
|
||||
return namespace.isEmpty() ? key : namespace.getByteString().concat(key);
|
||||
}
|
||||
|
||||
public static ByteString prefixNamespaceToRangeEnd(ByteString end, ByteSequence namespace) {
|
||||
if (namespace.isEmpty()) {
|
||||
return end;
|
||||
}
|
||||
|
||||
if (end.size() == 1 && end.toByteArray()[0] == 0) {
|
||||
// range end is '\0', calculate the prefixed range end by (key + 1)
|
||||
byte[] prefixedEndArray = namespace.getByteString().toByteArray();
|
||||
boolean ok = false;
|
||||
for (int i = (prefixedEndArray.length - 1); i >= 0; i--) {
|
||||
prefixedEndArray[i] = (byte) (prefixedEndArray[i] + 1);
|
||||
if (prefixedEndArray[i] != 0) {
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ok) {
|
||||
// 0xff..ff => 0x00
|
||||
prefixedEndArray = new byte[] { 0 };
|
||||
}
|
||||
return ByteString.copyFrom(prefixedEndArray);
|
||||
} else {
|
||||
return namespace.getByteString().concat(end);
|
||||
}
|
||||
}
|
||||
|
||||
public static ByteString unprefixNamespace(ByteString key, ByteSequence namespace) {
|
||||
return namespace.isEmpty() ? key : key.substring(namespace.size());
|
||||
}
|
||||
|
||||
static <T extends AbstractStub<T>> T applyRequireLeader(boolean requireLeader, T stub) {
|
||||
if (!requireLeader) {
|
||||
return stub;
|
||||
}
|
||||
final Metadata md = new Metadata();
|
||||
md.put(Constants.REQUIRE_LEADER_KEY, Constants.REQUIRE_LEADER_VALUE);
|
||||
return MetadataUtils.attachHeaders(stub, md);
|
||||
}
|
||||
|
||||
public static boolean isHaltError(final Status status) {
|
||||
return status.getCode() != Status.Code.UNAVAILABLE && status.getCode() != Status.Code.INTERNAL;
|
||||
}
|
||||
|
||||
static final String NO_LEADER_ERROR_MESSAGE = "etcdserver: no leader";
|
||||
|
||||
public static boolean isNoLeaderError(final Status status) {
|
||||
return status.getCode() == Status.Code.UNAVAILABLE && NO_LEADER_ERROR_MESSAGE.equals(status.getDescription());
|
||||
}
|
||||
}
|
|
@ -53,8 +53,6 @@ public interface Watch extends CloseableClient {
|
|||
}
|
||||
|
||||
/**
|
||||
* Watch key.
|
||||
*
|
||||
* @param key key to be watched on.
|
||||
* @param onNext the on next consumer
|
||||
* @return this watcher
|
||||
|
@ -64,8 +62,6 @@ public interface Watch extends CloseableClient {
|
|||
}
|
||||
|
||||
/**
|
||||
* Watch key.
|
||||
*
|
||||
* @param key key to be watched on.
|
||||
* @param onNext the on next consumer
|
||||
* @param onError the on error consumer
|
||||
|
@ -76,8 +72,6 @@ public interface Watch extends CloseableClient {
|
|||
}
|
||||
|
||||
/**
|
||||
* Watch key.
|
||||
*
|
||||
* @param key key to be watched on.
|
||||
* @param onNext the on next consumer
|
||||
* @param onError the on error consumer
|
||||
|
@ -89,8 +83,6 @@ public interface Watch extends CloseableClient {
|
|||
}
|
||||
|
||||
/**
|
||||
* Watch key.
|
||||
*
|
||||
* @param key key to be watched on.
|
||||
* @param onNext the on next consumer
|
||||
* @param onCompleted the on completion consumer
|
||||
|
@ -102,7 +94,6 @@ public interface Watch extends CloseableClient {
|
|||
}
|
||||
|
||||
/**
|
||||
* Watch key with option.
|
||||
*
|
||||
* @param key key to be watched on.
|
||||
* @param option the options
|
||||
|
@ -114,8 +105,6 @@ public interface Watch extends CloseableClient {
|
|||
}
|
||||
|
||||
/**
|
||||
* Watch key with option.
|
||||
*
|
||||
* @param key key to be watched on.
|
||||
* @param option the options
|
||||
* @param onNext the on next consumer
|
||||
|
@ -127,8 +116,6 @@ public interface Watch extends CloseableClient {
|
|||
}
|
||||
|
||||
/**
|
||||
* Watch key with option.
|
||||
*
|
||||
* @param key key to be watched on.
|
||||
* @param option the options
|
||||
* @param onNext the on next consumer
|
||||
|
@ -141,8 +128,6 @@ public interface Watch extends CloseableClient {
|
|||
}
|
||||
|
||||
/**
|
||||
* Watch key with option.
|
||||
*
|
||||
* @param key key to be watched on.
|
||||
* @param option the options
|
||||
* @param onNext the on next consumer
|
||||
|
@ -155,11 +140,6 @@ public interface Watch extends CloseableClient {
|
|||
return watch(key, option, listener(onNext, onError, onCompleted));
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests the latest revision processed for all watcher instances
|
||||
*/
|
||||
void requestProgress();
|
||||
|
||||
static Listener listener(Consumer<WatchResponse> onNext) {
|
||||
return listener(onNext, t -> {
|
||||
}, () -> {
|
||||
|
@ -225,15 +205,5 @@ public interface Watch extends CloseableClient {
|
|||
*/
|
||||
@Override
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Returns if watcher is already closed
|
||||
*/
|
||||
boolean isClosed();
|
||||
|
||||
/**
|
||||
* Requests the latest revision processed and propagates it to listeners
|
||||
*/
|
||||
void requestProgress();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,40 +14,31 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.etcd.jetcd.impl;
|
||||
package io.etcd.jetcd;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import io.etcd.jetcd.ByteSequence;
|
||||
import io.etcd.jetcd.Watch;
|
||||
import io.etcd.jetcd.api.VertxWatchGrpc;
|
||||
import io.etcd.jetcd.api.WatchCancelRequest;
|
||||
import io.etcd.jetcd.api.WatchCreateRequest;
|
||||
import io.etcd.jetcd.api.WatchProgressRequest;
|
||||
import io.etcd.jetcd.api.WatchRequest;
|
||||
import io.etcd.jetcd.api.WatchResponse;
|
||||
import io.etcd.jetcd.common.exception.ErrorCode;
|
||||
import io.etcd.jetcd.common.exception.EtcdException;
|
||||
import io.etcd.jetcd.options.OptionsUtil;
|
||||
import io.etcd.jetcd.options.WatchOption;
|
||||
import io.etcd.jetcd.support.Errors;
|
||||
import io.etcd.jetcd.support.Util;
|
||||
import io.grpc.Status;
|
||||
import io.vertx.core.streams.WriteStream;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import io.etcd.jetcd.api.WatchCancelRequest;
|
||||
import io.etcd.jetcd.api.WatchCreateRequest;
|
||||
import io.etcd.jetcd.api.WatchGrpc;
|
||||
import io.etcd.jetcd.api.WatchRequest;
|
||||
import io.etcd.jetcd.api.WatchResponse;
|
||||
import io.etcd.jetcd.common.exception.ErrorCode;
|
||||
import io.etcd.jetcd.common.exception.EtcdException;
|
||||
import io.etcd.jetcd.options.WatchOption;
|
||||
import io.grpc.Status;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static io.etcd.jetcd.common.exception.EtcdExceptionFactory.newClosedWatchClientException;
|
||||
import static io.etcd.jetcd.common.exception.EtcdExceptionFactory.newCompactedException;
|
||||
|
@ -57,24 +48,22 @@ import static io.etcd.jetcd.common.exception.EtcdExceptionFactory.toEtcdExceptio
|
|||
/**
|
||||
* watch Implementation.
|
||||
*/
|
||||
final class WatchImpl extends Impl implements Watch {
|
||||
final class WatchImpl implements Watch {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(WatchImpl.class);
|
||||
|
||||
private final Object lock;
|
||||
private final VertxWatchGrpc.WatchVertxStub stub;
|
||||
private final ClientConnectionManager connectionManager;
|
||||
private final WatchGrpc.WatchStub stub;
|
||||
private final ListeningScheduledExecutorService executor;
|
||||
private final AtomicBoolean closed;
|
||||
private final List<WatcherImpl> watchers;
|
||||
private final ByteSequence namespace;
|
||||
|
||||
WatchImpl(ClientConnectionManager connectionManager) {
|
||||
super(connectionManager);
|
||||
|
||||
this.lock = new Object();
|
||||
this.stub = connectionManager.newStub(VertxWatchGrpc::newVertxStub);
|
||||
// set it to daemon as there is no way for users to create this thread pool by their own
|
||||
this.executor = MoreExecutors.listeningDecorator(
|
||||
Executors.newScheduledThreadPool(1, Util.createThreadFactory("jetcd-watch-", true)));
|
||||
this.connectionManager = connectionManager;
|
||||
this.stub = connectionManager.newStub(WatchGrpc::newStub);
|
||||
this.executor = MoreExecutors.listeningDecorator(Executors.newScheduledThreadPool(1));
|
||||
this.closed = new AtomicBoolean();
|
||||
this.watchers = new CopyOnWriteArrayList<>();
|
||||
this.namespace = connectionManager.getNamespace();
|
||||
|
@ -108,23 +97,13 @@ final class WatchImpl extends Impl implements Watch {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestProgress() {
|
||||
if (!closed.get()) {
|
||||
synchronized (this.lock) {
|
||||
watchers.forEach(Watcher::requestProgress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class WatcherImpl implements Watcher {
|
||||
final class WatcherImpl implements Watcher, StreamObserver<WatchResponse> {
|
||||
private final ByteSequence key;
|
||||
private final WatchOption option;
|
||||
private final Listener listener;
|
||||
private final AtomicBoolean closed;
|
||||
|
||||
private final AtomicReference<WriteStream<WatchRequest>> wstream;
|
||||
private final AtomicBoolean started;
|
||||
private StreamObserver<WatchRequest> stream;
|
||||
private long revision;
|
||||
private long id;
|
||||
|
||||
|
@ -134,8 +113,7 @@ final class WatchImpl extends Impl implements Watch {
|
|||
this.listener = listener;
|
||||
this.closed = new AtomicBoolean();
|
||||
|
||||
this.started = new AtomicBoolean();
|
||||
this.wstream = new AtomicReference<>();
|
||||
this.stream = null;
|
||||
this.id = -1;
|
||||
this.revision = this.option.getRevision();
|
||||
}
|
||||
|
@ -146,8 +124,7 @@ final class WatchImpl extends Impl implements Watch {
|
|||
//
|
||||
// ************************
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
boolean isClosed() {
|
||||
return this.closed.get() || WatchImpl.this.closed.get();
|
||||
}
|
||||
|
||||
|
@ -156,24 +133,17 @@ final class WatchImpl extends Impl implements Watch {
|
|||
return;
|
||||
}
|
||||
|
||||
if (started.compareAndSet(false, true)) {
|
||||
// id is not really useful today, but it may be in etcd 3.4
|
||||
if (stream == null) {
|
||||
// id is not really useful today but it may be in etcd 3.4
|
||||
id = -1;
|
||||
|
||||
WatchCreateRequest.Builder builder = WatchCreateRequest.newBuilder()
|
||||
.setKey(Util.prefixNamespace(this.key, namespace))
|
||||
.setPrevKv(this.option.isPrevKV())
|
||||
.setKey(Util.prefixNamespace(this.key.getByteString(), namespace)).setPrevKv(this.option.isPrevKV())
|
||||
.setProgressNotify(option.isProgressNotify()).setStartRevision(this.revision);
|
||||
|
||||
option.getEndKey()
|
||||
.map(endKey -> Util.prefixNamespaceToRangeEnd(endKey, namespace))
|
||||
option.getEndKey().map(endKey -> Util.prefixNamespaceToRangeEnd(endKey.getByteString(), namespace))
|
||||
.ifPresent(builder::setRangeEnd);
|
||||
|
||||
if (option.getEndKey().isEmpty() && option.isPrefix()) {
|
||||
ByteSequence endKey = OptionsUtil.prefixEndOf(key);
|
||||
builder.setRangeEnd(Util.prefixNamespaceToRangeEnd(endKey, namespace));
|
||||
}
|
||||
|
||||
if (option.isNoDelete()) {
|
||||
builder.addFilters(WatchCreateRequest.FilterType.NODELETE);
|
||||
}
|
||||
|
@ -182,15 +152,8 @@ final class WatchImpl extends Impl implements Watch {
|
|||
builder.addFilters(WatchCreateRequest.FilterType.NOPUT);
|
||||
}
|
||||
|
||||
var ignored = Util.applyRequireLeader(option.withRequireLeader(), stub)
|
||||
.watchWithHandler(
|
||||
stream -> {
|
||||
wstream.set(stream);
|
||||
stream.write(WatchRequest.newBuilder().setCreateRequest(builder).build());
|
||||
},
|
||||
this::onNext,
|
||||
event -> onCompleted(),
|
||||
this::onError);
|
||||
stream = Util.applyRequireLeader(option.withRequireLeader(), stub).watch(this);
|
||||
stream.onNext(WatchRequest.newBuilder().setCreateRequest(builder).build());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -200,15 +163,16 @@ final class WatchImpl extends Impl implements Watch {
|
|||
// sync with onError()
|
||||
synchronized (WatchImpl.this.lock) {
|
||||
if (closed.compareAndSet(false, true)) {
|
||||
if (wstream.get() != null) {
|
||||
if (id != -1) {
|
||||
final WatchCancelRequest watchCancelRequest = WatchCancelRequest.newBuilder().setWatchId(this.id)
|
||||
.build();
|
||||
final WatchRequest request = WatchRequest.newBuilder().setCancelRequest(watchCancelRequest).build();
|
||||
if (stream != null) {
|
||||
WatchCancelRequest watchCancelRequest = WatchCancelRequest.newBuilder().setWatchId(this.id).build();
|
||||
|
||||
wstream.get().end(request);
|
||||
} else {
|
||||
wstream.get().end();
|
||||
if (id != -1) {
|
||||
stream.onNext(WatchRequest.newBuilder().setCancelRequest(watchCancelRequest).build());
|
||||
}
|
||||
|
||||
if (stream != null) {
|
||||
stream.onError(Status.CANCELLED.withDescription("shutdown").asException());
|
||||
stream = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,21 +186,14 @@ final class WatchImpl extends Impl implements Watch {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestProgress() {
|
||||
if (!closed.get() && wstream.get() != null) {
|
||||
WatchProgressRequest watchProgressRequest = WatchProgressRequest.newBuilder().build();
|
||||
wstream.get().write(WatchRequest.newBuilder().setProgressRequest(watchProgressRequest).build());
|
||||
}
|
||||
}
|
||||
|
||||
// ************************
|
||||
//
|
||||
// StreamObserver
|
||||
//
|
||||
// ************************
|
||||
|
||||
private void onNext(WatchResponse response) {
|
||||
@Override
|
||||
public void onNext(WatchResponse response) {
|
||||
if (closed.get()) {
|
||||
// events eventually received when the client is closed should
|
||||
// not be propagated to the listener
|
||||
|
@ -245,11 +202,9 @@ final class WatchImpl extends Impl implements Watch {
|
|||
|
||||
// handle a special case when watch has been created and closed at the same time
|
||||
if (response.getCreated() && response.getCanceled() && response.getCancelReason() != null
|
||||
&& (response.getCancelReason().contains("etcdserver: permission denied") ||
|
||||
response.getCancelReason().contains("etcdserver: invalid auth token"))) {
|
||||
|
||||
&& response.getCancelReason().contains("etcdserver: permission denied")) {
|
||||
// potentially access token expired
|
||||
connectionManager().authCredential().refresh();
|
||||
connectionManager.forceTokenRefresh();
|
||||
Status error = Status.Code.CANCELLED.toStatus().withDescription(response.getCancelReason());
|
||||
handleError(toEtcdException(error), true);
|
||||
} else if (response.getCreated()) {
|
||||
|
@ -263,11 +218,8 @@ final class WatchImpl extends Impl implements Watch {
|
|||
return;
|
||||
}
|
||||
|
||||
revision = Math.max(revision, response.getHeader().getRevision());
|
||||
revision = response.getHeader().getRevision();
|
||||
id = response.getWatchId();
|
||||
if (option.isCreatedNotify()) {
|
||||
listener.onNext(new io.etcd.jetcd.watch.WatchResponse(response));
|
||||
}
|
||||
} else if (response.getCanceled()) {
|
||||
|
||||
//
|
||||
|
@ -286,10 +238,7 @@ final class WatchImpl extends Impl implements Watch {
|
|||
error = newEtcdException(ErrorCode.FAILED_PRECONDITION, reason);
|
||||
}
|
||||
|
||||
handleError(toEtcdException(error), false);
|
||||
} else if (io.etcd.jetcd.watch.WatchResponse.isProgressNotify(response)) {
|
||||
listener.onNext(new io.etcd.jetcd.watch.WatchResponse(response));
|
||||
revision = Math.max(revision, response.getHeader().getRevision());
|
||||
listener.onError(error);
|
||||
} else if (response.getEventsCount() == 0 && option.isProgressNotify()) {
|
||||
|
||||
//
|
||||
|
@ -315,11 +264,8 @@ final class WatchImpl extends Impl implements Watch {
|
|||
}
|
||||
}
|
||||
|
||||
private void onCompleted() {
|
||||
listener.onCompleted();
|
||||
}
|
||||
|
||||
private void onError(Throwable t) {
|
||||
@Override
|
||||
public void onError(Throwable t) {
|
||||
handleError(toEtcdException(t), shouldReschedule(Status.fromThrowable(t)));
|
||||
}
|
||||
|
||||
|
@ -331,18 +277,12 @@ final class WatchImpl extends Impl implements Watch {
|
|||
}
|
||||
|
||||
listener.onError(etcdException);
|
||||
if (wstream.get() != null) {
|
||||
wstream.get().end();
|
||||
if (stream != null) {
|
||||
stream.onCompleted();
|
||||
}
|
||||
wstream.set(null);
|
||||
started.set(false);
|
||||
stream = null;
|
||||
}
|
||||
if (shouldReschedule) {
|
||||
if (etcdException.getMessage().contains("etcdserver: permission denied")) {
|
||||
// potentially access token expired
|
||||
connectionManager().authCredential().refresh();
|
||||
}
|
||||
|
||||
reschedule();
|
||||
return;
|
||||
}
|
||||
|
@ -350,14 +290,14 @@ final class WatchImpl extends Impl implements Watch {
|
|||
}
|
||||
|
||||
private boolean shouldReschedule(final Status status) {
|
||||
return !Errors.isHaltError(status) && !Errors.isNoLeaderError(status);
|
||||
return !Util.isHaltError(status) && !Util.isNoLeaderError(status);
|
||||
}
|
||||
|
||||
private void reschedule() {
|
||||
Futures.addCallback(executor.schedule(this::resume, 500, TimeUnit.MILLISECONDS), new FutureCallback<Object>() {
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
LOG.warn("scheduled resume failed", t);
|
||||
public void onFailure(Throwable throwable) {
|
||||
LOG.error("scheduled resume failed", throwable);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -365,5 +305,9 @@ final class WatchImpl extends Impl implements Watch {
|
|||
}
|
||||
}, executor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,11 +16,10 @@
|
|||
|
||||
package io.etcd.jetcd.auth;
|
||||
|
||||
import io.etcd.jetcd.Auth;
|
||||
import io.etcd.jetcd.impl.AbstractResponse;
|
||||
import io.etcd.jetcd.AbstractResponse;
|
||||
|
||||
/**
|
||||
* AuthDisableResponse returned by {@link Auth#authDisable()} contains a header.
|
||||
* AuthDisableResponse returned by {@link io.etcd.jetcd.Auth#authDisable()} contains a header.
|
||||
*/
|
||||
public class AuthDisableResponse extends AbstractResponse<io.etcd.jetcd.api.AuthDisableResponse> {
|
||||
|
||||
|
|
|
@ -16,11 +16,10 @@
|
|||
|
||||
package io.etcd.jetcd.auth;
|
||||
|
||||
import io.etcd.jetcd.Auth;
|
||||
import io.etcd.jetcd.impl.AbstractResponse;
|
||||
import io.etcd.jetcd.AbstractResponse;
|
||||
|
||||
/**
|
||||
* AuthEnableResponse returned by {@link Auth#authEnable()} call contains a header.
|
||||
* AuthEnableResponse returned by {@link io.etcd.jetcd.Auth#authEnable()} call contains a header.
|
||||
*/
|
||||
public class AuthEnableResponse extends AbstractResponse<io.etcd.jetcd.api.AuthEnableResponse> {
|
||||
|
||||
|
|
|
@ -16,12 +16,11 @@
|
|||
|
||||
package io.etcd.jetcd.auth;
|
||||
|
||||
import io.etcd.jetcd.Auth;
|
||||
import io.etcd.jetcd.AbstractResponse;
|
||||
import io.etcd.jetcd.ByteSequence;
|
||||
import io.etcd.jetcd.impl.AbstractResponse;
|
||||
|
||||
/**
|
||||
* AuthRoleAddResponse returned by {@link Auth#roleAdd(ByteSequence)} contains
|
||||
* AuthRoleAddResponse returned by {@link io.etcd.jetcd.Auth#roleAdd(ByteSequence)} contains
|
||||
* a header.
|
||||
*/
|
||||
public class AuthRoleAddResponse extends AbstractResponse<io.etcd.jetcd.api.AuthRoleAddResponse> {
|
||||
|
|
|
@ -16,12 +16,11 @@
|
|||
|
||||
package io.etcd.jetcd.auth;
|
||||
|
||||
import io.etcd.jetcd.Auth;
|
||||
import io.etcd.jetcd.AbstractResponse;
|
||||
import io.etcd.jetcd.ByteSequence;
|
||||
import io.etcd.jetcd.impl.AbstractResponse;
|
||||
|
||||
/**
|
||||
* AuthRoleDeleteResponse returned by {@link Auth#roleDelete(ByteSequence)}
|
||||
* AuthRoleDeleteResponse returned by {@link io.etcd.jetcd.Auth#roleDelete(ByteSequence)}
|
||||
* contains a header.
|
||||
*/
|
||||
public class AuthRoleDeleteResponse extends AbstractResponse<io.etcd.jetcd.api.AuthRoleDeleteResponse> {
|
||||
|
|
|
@ -19,12 +19,12 @@ package io.etcd.jetcd.auth;
|
|||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.etcd.jetcd.Auth;
|
||||
import io.etcd.jetcd.AbstractResponse;
|
||||
import io.etcd.jetcd.ByteSequence;
|
||||
import io.etcd.jetcd.impl.AbstractResponse;
|
||||
import io.etcd.jetcd.auth.Permission.Type;
|
||||
|
||||
/**
|
||||
* AuthRoleGetResponse returned by {@link Auth#roleGet(ByteSequence)} contains
|
||||
* AuthRoleGetResponse returned by {@link io.etcd.jetcd.Auth#roleGet(ByteSequence)} contains
|
||||
* a header and a list of permissions.
|
||||
*/
|
||||
public class AuthRoleGetResponse extends AbstractResponse<io.etcd.jetcd.api.AuthRoleGetResponse> {
|
||||
|
@ -42,16 +42,16 @@ public class AuthRoleGetResponse extends AbstractResponse<io.etcd.jetcd.api.Auth
|
|||
Permission.Type type;
|
||||
switch (perm.getPermType()) {
|
||||
case READ:
|
||||
type = Permission.Type.READ;
|
||||
type = Type.READ;
|
||||
break;
|
||||
case WRITE:
|
||||
type = Permission.Type.WRITE;
|
||||
type = Type.WRITE;
|
||||
break;
|
||||
case READWRITE:
|
||||
type = Permission.Type.READWRITE;
|
||||
type = Type.READWRITE;
|
||||
break;
|
||||
default:
|
||||
type = Permission.Type.UNRECOGNIZED;
|
||||
type = Type.UNRECOGNIZED;
|
||||
}
|
||||
|
||||
return new Permission(type, key, rangeEnd);
|
||||
|
|
|
@ -16,13 +16,12 @@
|
|||
|
||||
package io.etcd.jetcd.auth;
|
||||
|
||||
import io.etcd.jetcd.Auth;
|
||||
import io.etcd.jetcd.AbstractResponse;
|
||||
import io.etcd.jetcd.ByteSequence;
|
||||
import io.etcd.jetcd.impl.AbstractResponse;
|
||||
|
||||
/**
|
||||
* AuthRoleGrantPermissionResponse returned by
|
||||
* {@link Auth#roleGrantPermission(ByteSequence, ByteSequence, ByteSequence, Permission.Type)} contains a
|
||||
* {@link io.etcd.jetcd.Auth#roleGrantPermission(ByteSequence, ByteSequence, ByteSequence, Permission.Type)} contains a
|
||||
* header.
|
||||
*/
|
||||
public class AuthRoleGrantPermissionResponse extends AbstractResponse<io.etcd.jetcd.api.AuthRoleGrantPermissionResponse> {
|
||||
|
|
|
@ -18,8 +18,8 @@ package io.etcd.jetcd.auth;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import io.etcd.jetcd.AbstractResponse;
|
||||
import io.etcd.jetcd.Auth;
|
||||
import io.etcd.jetcd.impl.AbstractResponse;
|
||||
|
||||
/**
|
||||
* AuthRoleListResponse returned by {@link Auth#roleList()} contains a header and
|
||||
|
@ -32,9 +32,7 @@ public class AuthRoleListResponse extends AbstractResponse<io.etcd.jetcd.api.Aut
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a list of roles.
|
||||
*
|
||||
* @return the roles.
|
||||
* @return a list of roles.
|
||||
*/
|
||||
public List<String> getRoles() {
|
||||
return getResponse().getRolesList();
|
||||
|
|
|
@ -16,10 +16,12 @@
|
|||
|
||||
package io.etcd.jetcd.auth;
|
||||
|
||||
import io.etcd.jetcd.impl.AbstractResponse;
|
||||
import io.etcd.jetcd.AbstractResponse;
|
||||
import io.etcd.jetcd.ByteSequence;
|
||||
|
||||
/**
|
||||
* AuthRoleRevokePermissionResponse
|
||||
* AuthRoleRevokePermissionResponse returned by {@link io.etcd.jetcd.Auth#roleRevokePermission(ByteSequence,
|
||||
* ByteSequence, ByteSequence)} contains a header.
|
||||
*/
|
||||
public class AuthRoleRevokePermissionResponse extends AbstractResponse<io.etcd.jetcd.api.AuthRoleRevokePermissionResponse> {
|
||||
|
||||
|
|
|
@ -16,12 +16,11 @@
|
|||
|
||||
package io.etcd.jetcd.auth;
|
||||
|
||||
import io.etcd.jetcd.Auth;
|
||||
import io.etcd.jetcd.AbstractResponse;
|
||||
import io.etcd.jetcd.ByteSequence;
|
||||
import io.etcd.jetcd.impl.AbstractResponse;
|
||||
|
||||
/**
|
||||
* AuthUserAddResponse returned by {@link Auth#userAdd(ByteSequence, ByteSequence)} contains a
|
||||
* AuthUserAddResponse returned by {@link io.etcd.jetcd.Auth#userAdd(ByteSequence, ByteSequence)} contains a
|
||||
* header.
|
||||
*/
|
||||
public class AuthUserAddResponse extends AbstractResponse<io.etcd.jetcd.api.AuthUserAddResponse> {
|
||||
|
|
|
@ -16,10 +16,12 @@
|
|||
|
||||
package io.etcd.jetcd.auth;
|
||||
|
||||
import io.etcd.jetcd.impl.AbstractResponse;
|
||||
import io.etcd.jetcd.AbstractResponse;
|
||||
import io.etcd.jetcd.ByteSequence;
|
||||
|
||||
/**
|
||||
* AuthUserChangePasswordResponse
|
||||
* AuthUserChangePasswordResponse returned by {@link io.etcd.jetcd.Auth#userChangePassword(ByteSequence,
|
||||
* ByteSequence)} contains a header.
|
||||
*/
|
||||
public class AuthUserChangePasswordResponse extends AbstractResponse<io.etcd.jetcd.api.AuthUserChangePasswordResponse> {
|
||||
|
||||
|
|
|
@ -16,12 +16,11 @@
|
|||
|
||||
package io.etcd.jetcd.auth;
|
||||
|
||||
import io.etcd.jetcd.Auth;
|
||||
import io.etcd.jetcd.AbstractResponse;
|
||||
import io.etcd.jetcd.ByteSequence;
|
||||
import io.etcd.jetcd.impl.AbstractResponse;
|
||||
|
||||
/**
|
||||
* AuthUserDeleteResponse returned by {@link Auth#userDelete(ByteSequence)} contains a header.
|
||||
* AuthUserDeleteResponse returned by {@link io.etcd.jetcd.Auth#userDelete(ByteSequence)} contains a header.
|
||||
*/
|
||||
public class AuthUserDeleteResponse extends AbstractResponse<io.etcd.jetcd.api.AuthUserDeleteResponse> {
|
||||
|
||||
|
|
|
@ -18,12 +18,11 @@ package io.etcd.jetcd.auth;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import io.etcd.jetcd.Auth;
|
||||
import io.etcd.jetcd.AbstractResponse;
|
||||
import io.etcd.jetcd.ByteSequence;
|
||||
import io.etcd.jetcd.impl.AbstractResponse;
|
||||
|
||||
/**
|
||||
* AuthUserGetResponse returned by {@link Auth#userGet(ByteSequence)} contains a header and
|
||||
* AuthUserGetResponse returned by {@link io.etcd.jetcd.Auth#userGet(ByteSequence)} contains a header and
|
||||
* a list of roles associated with the user.
|
||||
*/
|
||||
public class AuthUserGetResponse extends AbstractResponse<io.etcd.jetcd.api.AuthUserGetResponse> {
|
||||
|
@ -33,9 +32,7 @@ public class AuthUserGetResponse extends AbstractResponse<io.etcd.jetcd.api.Auth
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a list of roles.
|
||||
*
|
||||
* @return the roles.
|
||||
* @return a list of roles.
|
||||
*/
|
||||
public List<String> getRoles() {
|
||||
return getResponse().getRolesList();
|
||||
|
|
|
@ -16,10 +16,12 @@
|
|||
|
||||
package io.etcd.jetcd.auth;
|
||||
|
||||
import io.etcd.jetcd.impl.AbstractResponse;
|
||||
import io.etcd.jetcd.AbstractResponse;
|
||||
import io.etcd.jetcd.ByteSequence;
|
||||
|
||||
/**
|
||||
* AuthUserGrantRoleResponse
|
||||
* AuthUserGrantRoleResponse returned by {@link io.etcd.jetcd.Auth#userGrantRole(ByteSequence,
|
||||
* ByteSequence)} contains a header.
|
||||
*/
|
||||
public class AuthUserGrantRoleResponse extends AbstractResponse<io.etcd.jetcd.api.AuthUserGrantRoleResponse> {
|
||||
|
||||
|
|
|
@ -18,11 +18,10 @@ package io.etcd.jetcd.auth;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import io.etcd.jetcd.Auth;
|
||||
import io.etcd.jetcd.impl.AbstractResponse;
|
||||
import io.etcd.jetcd.AbstractResponse;
|
||||
|
||||
/**
|
||||
* AuthUserListResponse returned by {@link Auth#userList()} contains a header and
|
||||
* AuthUserListResponse returned by {@link io.etcd.jetcd.Auth#userList()} contains a header and
|
||||
* a list of users.
|
||||
*/
|
||||
public class AuthUserListResponse extends AbstractResponse<io.etcd.jetcd.api.AuthUserListResponse> {
|
||||
|
@ -32,9 +31,7 @@ public class AuthUserListResponse extends AbstractResponse<io.etcd.jetcd.api.Aut
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a list of users.
|
||||
*
|
||||
* @return the users.
|
||||
* @return a list of users.
|
||||
*/
|
||||
public List<String> getUsers() {
|
||||
return getResponse().getUsersList();
|
||||
|
|
|
@ -16,12 +16,11 @@
|
|||
|
||||
package io.etcd.jetcd.auth;
|
||||
|
||||
import io.etcd.jetcd.Auth;
|
||||
import io.etcd.jetcd.AbstractResponse;
|
||||
import io.etcd.jetcd.ByteSequence;
|
||||
import io.etcd.jetcd.impl.AbstractResponse;
|
||||
|
||||
/**
|
||||
* AuthUserRevokeRoleResponse returned by {@link Auth#userRevokeRole(ByteSequence, ByteSequence)}
|
||||
* AuthUserRevokeRoleResponse returned by {@link io.etcd.jetcd.Auth#userRevokeRole(ByteSequence, ByteSequence)}
|
||||
* contains a header.
|
||||
*/
|
||||
public class AuthUserRevokeRoleResponse extends AbstractResponse<io.etcd.jetcd.api.AuthUserRevokeRoleResponse> {
|
||||
|
|
|
@ -38,9 +38,7 @@ public class Permission {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the type of Permission: READ, WRITE, READWRITE, or UNRECOGNIZED.
|
||||
*
|
||||
* @return the permission type.
|
||||
* @return the type of Permission: READ, WRITE, READWRITE, or UNRECOGNIZED.
|
||||
*/
|
||||
public Type getPermType() {
|
||||
return permType;
|
||||
|
|
|
@ -19,7 +19,7 @@ package io.etcd.jetcd.cluster;
|
|||
import java.net.URI;
|
||||
import java.util.List;
|
||||
|
||||
import io.etcd.jetcd.support.Util;
|
||||
import io.etcd.jetcd.Util;
|
||||
|
||||
public class Member {
|
||||
|
||||
|
@ -30,48 +30,31 @@ public class Member {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the member ID for this member.
|
||||
*
|
||||
* @return the id.
|
||||
* @return the member ID for this member.
|
||||
*/
|
||||
public long getId() {
|
||||
return member.getID();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the human-readable name of the member, ff the member is not started, the name will be an empty string.
|
||||
*
|
||||
* @return the name.
|
||||
* @return the human-readable name of the member, ff the member is not started, the name will be an empty string.
|
||||
*/
|
||||
public String getName() {
|
||||
return member.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of URLs the member exposes to the cluster for communication.
|
||||
*
|
||||
* @return the peer url
|
||||
* @return the list of URLs the member exposes to the cluster for communication.
|
||||
*/
|
||||
public List<URI> getPeerURIs() {
|
||||
return Util.toURIs(member.getPeerURLsList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of URLs the member exposes to clients for communication, if the member is not started, clientURLs will
|
||||
* be empty.
|
||||
*
|
||||
* @return the client URIs.
|
||||
* @return list of URLs the member exposes to clients for communication, if the member is not started, clientURLs will
|
||||
* be empty.
|
||||
*/
|
||||
public List<URI> getClientURIs() {
|
||||
return Util.toURIs(member.getClientURLsList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the member is raft learner
|
||||
*
|
||||
* @return if the member is raft learner
|
||||
*/
|
||||
public boolean isLearner() {
|
||||
return member.getIsLearner();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,11 +18,10 @@ package io.etcd.jetcd.cluster;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import io.etcd.jetcd.Cluster;
|
||||
import io.etcd.jetcd.impl.AbstractResponse;
|
||||
import io.etcd.jetcd.AbstractResponse;
|
||||
|
||||
/**
|
||||
* MemberAddResponse returned by {@link Cluster#addMember(List, boolean)}
|
||||
* MemberAddResponse returned by {@link io.etcd.jetcd.Cluster#addMember(List)}
|
||||
* contains a header, added member, and list of members after adding the new member.
|
||||
*/
|
||||
public class MemberAddResponse extends AbstractResponse<io.etcd.jetcd.api.MemberAddResponse> {
|
||||
|
@ -36,18 +35,14 @@ public class MemberAddResponse extends AbstractResponse<io.etcd.jetcd.api.Member
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the member information for the added member.
|
||||
*
|
||||
* @return the member information.
|
||||
* @return the member information for the added member.
|
||||
*/
|
||||
public Member getMember() {
|
||||
return member;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all members after adding the new member.
|
||||
*
|
||||
* @return the list of members.
|
||||
* @return a list of all members after adding the new member.
|
||||
*/
|
||||
public synchronized List<Member> getMembers() {
|
||||
if (members == null) {
|
||||
|
|
|
@ -18,8 +18,8 @@ package io.etcd.jetcd.cluster;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import io.etcd.jetcd.AbstractResponse;
|
||||
import io.etcd.jetcd.Cluster;
|
||||
import io.etcd.jetcd.impl.AbstractResponse;
|
||||
|
||||
/**
|
||||
* MemberListResponse returned by {@link Cluster#listMember()}
|
||||
|
@ -34,9 +34,7 @@ public class MemberListResponse extends AbstractResponse<io.etcd.jetcd.api.Membe
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a list of members. empty list if none.
|
||||
*
|
||||
* @return the list of members.
|
||||
* @return a list of members. empty list if none.
|
||||
*/
|
||||
public synchronized List<Member> getMembers() {
|
||||
if (members == null) {
|
||||
|
|
|
@ -18,11 +18,10 @@ package io.etcd.jetcd.cluster;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import io.etcd.jetcd.Cluster;
|
||||
import io.etcd.jetcd.impl.AbstractResponse;
|
||||
import io.etcd.jetcd.AbstractResponse;
|
||||
|
||||
/**
|
||||
* MemberRemoveResponse returned by {@link Cluster#removeMember(long)}
|
||||
* MemberRemoveResponse returned by {@link io.etcd.jetcd.Cluster#removeMember(long)}
|
||||
* contains a header and a list of member the removal of the member.
|
||||
*/
|
||||
public class MemberRemoveResponse extends AbstractResponse<io.etcd.jetcd.api.MemberRemoveResponse> {
|
||||
|
@ -34,9 +33,7 @@ public class MemberRemoveResponse extends AbstractResponse<io.etcd.jetcd.api.Mem
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all members after removing the member.
|
||||
*
|
||||
* @return the list of members.
|
||||
* @return a list of all members after removing the member.
|
||||
*/
|
||||
public synchronized List<Member> getMembers() {
|
||||
if (members == null) {
|
||||
|
|
|
@ -18,11 +18,10 @@ package io.etcd.jetcd.cluster;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import io.etcd.jetcd.Cluster;
|
||||
import io.etcd.jetcd.impl.AbstractResponse;
|
||||
import io.etcd.jetcd.AbstractResponse;
|
||||
|
||||
/**
|
||||
* MemberUpdateResponse returned by {@link Cluster#updateMember(long, List)}
|
||||
* MemberUpdateResponse returned by {@link io.etcd.jetcd.Cluster#updateMember(long, List)}
|
||||
* contains a header and a list of members after the member update.
|
||||
*/
|
||||
public class MemberUpdateResponse extends AbstractResponse<io.etcd.jetcd.api.MemberUpdateResponse> {
|
||||
|
@ -34,9 +33,7 @@ public class MemberUpdateResponse extends AbstractResponse<io.etcd.jetcd.api.Mem
|
|||
}
|
||||
|
||||
/**
|
||||
* Return a list of all members after updating the member.
|
||||
*
|
||||
* @return the list of members.
|
||||
* @return a list of all members after updating the member.
|
||||
*/
|
||||
public synchronized List<Member> getMembers() {
|
||||
if (members == null) {
|
||||
|
|
|
@ -22,14 +22,13 @@ import java.util.stream.Collectors;
|
|||
/**
|
||||
* Util class for Cluster models.
|
||||
*/
|
||||
final class Util {
|
||||
private Util() {
|
||||
}
|
||||
public class Util {
|
||||
|
||||
/**
|
||||
* Converts a list of API member to a List of client side member.
|
||||
*/
|
||||
static List<Member> toMembers(List<io.etcd.jetcd.api.Member> members) {
|
||||
return members.stream().map(Member::new).collect(Collectors.toList());
|
||||
static List<io.etcd.jetcd.cluster.Member> toMembers(List<io.etcd.jetcd.api.Member> members) {
|
||||
|
||||
return members.stream().map(io.etcd.jetcd.cluster.Member::new).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue