
io.etcd.jetcd.Util Maven / Gradle / Ivy
The newest version!
/**
* Copyright 2017 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 static io.etcd.jetcd.common.exception.EtcdExceptionFactory.handleInterrupt;
import static io.etcd.jetcd.common.exception.EtcdExceptionFactory.newEtcdException;
import static io.etcd.jetcd.common.exception.EtcdExceptionFactory.toEtcdException;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import io.etcd.jetcd.common.exception.ErrorCode;
import io.grpc.Status;
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.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.slf4j.Logger;
public final class Util {
private Util() {
}
public static List toURIs(Collection 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 CompletableFuture toCompletableFuture(
ListenableFuture sourceFuture, Function resultConvert, Executor executor) {
CompletableFuture targetFuture = new CompletableFuture() {
// 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;
}
/**
* converts a ListenableFuture of Type S to a CompletableFuture of Type T with retry on
* ListenableFuture error.
*
* @param newSourceFuture a function that returns a new SourceFuture.
* @param resultConvert a function that converts Type S to Type T.
* @param executor a executor.
* @param Source type
* @param Converted Type.
* @return a CompletableFuture with type T.
*/
static CompletableFuture toCompletableFutureWithRetry(
Supplier> newSourceFuture,
Function resultConvert,
Executor executor) {
return toCompletableFutureWithRetry(newSourceFuture, resultConvert, Util::isRetriable, executor);
}
/**
* converts a ListenableFuture of Type S to a CompletableFuture of Type T with retry on
* ListenableFuture error.
*
* @param newSourceFuture 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 executor a executor.
* @param Source type
* @param Converted Type.
* @return a CompletableFuture with type T.
*/
static CompletableFuture toCompletableFutureWithRetry(
Supplier> newSourceFuture,
Function resultConvert,
Function doRetry,
Executor executor) {
AtomicReference> sourceFutureRef = new AtomicReference<>();
sourceFutureRef.lazySet(newSourceFuture.get());
CompletableFuture targetFuture = new CompletableFuture() {
// the cancel of targetFuture also cancels the sourceFuture.
@Override
public synchronized boolean cancel(boolean mayInterruptIfRunning) {
boolean cancelled = super.cancel(mayInterruptIfRunning);
ListenableFuture sourceFuture = sourceFutureRef.get();
if (sourceFuture != null) {
cancelled = sourceFuture.cancel(true);
}
return cancelled;
}
};
executor.execute(() -> {
// only retry 3 times.
int retryLimit = 3;
while (retryLimit-- > 0) {
try {
ListenableFuture f = sourceFutureRef.get();
targetFuture.complete(resultConvert.apply(f.get()));
return;
} catch (Exception e) {
if (doRetry.apply(e)) {
synchronized (targetFuture) {
if (targetFuture.isCancelled()) {
// don't retry if targetFuture has cancelled.
return;
}
sourceFutureRef.set(newSourceFuture.get());
}
try {
Thread.sleep(500);
} catch (InterruptedException e1) {
// raise interrupted exception to caller.
targetFuture.completeExceptionally(handleInterrupt(e1));
return;
}
continue;
}
targetFuture.completeExceptionally(e);
return;
}
}
// notify user that retry has failed.
targetFuture.completeExceptionally(
newEtcdException(
ErrorCode.ABORTED,
"maximum number of auto retries reached"
)
);
});
return targetFuture;
}
static boolean isRetriable(Exception 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 void applyIfNotNull(T target, Consumer consumer) {
if (target != null) {
consumer.accept(target);
}
}
static T supplyIfNull(T target, Supplier supplier) {
return target != null ? target : supplier.get();
}
static void addOnFailureLoggingCallback(
Executor executor,
ListenableFuture> listenableFuture,
Logger callerLogger,
String message) {
Futures.addCallback(
listenableFuture,
new FutureCallback
© 2015 - 2025 Weber Informatics LLC | Privacy Policy