package org.infinispan.manager.impl;
import java.util.Collection;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Predicate;
import org.infinispan.manager.ClusterExecutionPolicy;
import org.infinispan.manager.ClusterExecutor;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.remoting.transport.Address;
import org.infinispan.util.concurrent.TimeoutException;
import org.infinispan.util.function.TriConsumer;
/**
* @author wburns
* @since 9.0
*/
class LocalClusterExecutor implements ClusterExecutor {
protected final Predicate<? super Address> predicate;
protected final EmbeddedCacheManager manager;
protected final long time;
protected final TimeUnit unit;
protected final Executor localExecutor;
protected final ScheduledExecutorService timeoutExecutor;
LocalClusterExecutor(Predicate<? super Address> predicate, EmbeddedCacheManager manager, Executor localExecutor,
long time, TimeUnit unit, ScheduledExecutorService timeoutExecutor) {
this.predicate = predicate;
this.manager = Objects.requireNonNull(manager);
this.localExecutor = Objects.requireNonNull(localExecutor);
if (time <= 0) {
throw new IllegalArgumentException("time must be greater than 0");
}
this.time = time;
this.unit = Objects.requireNonNull(unit);
this.timeoutExecutor = Objects.requireNonNull(timeoutExecutor);
}
Address getMyAddress() {
return null;
}
@Override
public void execute(Runnable command) {
// We ignore time out since user can't ever even respond to it, so no reason to create extra fluff
localExecutor.execute(command);
}
@Override
public CompletableFuture<Void> submit(Runnable command) {
CompletableFuture<Void> future = new CompletableFuture<>();
localExecutor.execute(() -> {
try {
command.run();
future.complete(null);
} catch (Throwable t) {
future.completeExceptionally(t);
}
});
ScheduledFuture<Boolean> scheduledFuture = timeoutExecutor.schedule(
() -> future.completeExceptionally(new TimeoutException()), time, unit);
future.whenComplete((v, t) -> scheduledFuture.cancel(true));
return future;
}
@Override
public <V> CompletableFuture<Void> submitConsumer(Function<? super EmbeddedCacheManager, ? extends V> callable,
TriConsumer<? super Address, ? super V, ? super Throwable> triConsumer) {
CompletableFuture<Void> future = new CompletableFuture<>();
localInvocation(callable).whenComplete((r, t) -> {
try {
triConsumer.accept(getMyAddress(), r, t);
future.complete(null);
} catch (Throwable throwable) {
future.completeExceptionally(throwable);
}
});
ScheduledFuture<Boolean> scheduledFuture = timeoutExecutor.schedule(() ->
future.completeExceptionally(new TimeoutException()), time, unit);
future.whenComplete((v, t) -> scheduledFuture.cancel(true));
return future;
}
<T> CompletableFuture<T> localInvocation(Function<? super EmbeddedCacheManager, ? extends T> function) {
CompletableFuture<T> future = new CompletableFuture<>();
localExecutor.execute(() -> {
try {
T result = function.apply(manager);
future.complete(result);
} catch (Throwable t) {
future.completeExceptionally(t);
}
});
return future;
}
protected ClusterExecutor sameClusterExecutor(Predicate<? super Address> predicate,
long time, TimeUnit unit) {
return new LocalClusterExecutor(predicate, manager, localExecutor, time, unit, timeoutExecutor);
}
@Override
public ClusterExecutor timeout(long time, TimeUnit unit) {
return this;
}
@Override
public ClusterExecutor filterTargets(Predicate<? super Address> predicate) {
return sameClusterExecutor(predicate, 0, null);
}
@Override
public ClusterExecutor filterTargets(ClusterExecutionPolicy policy) throws IllegalStateException {
throw new IllegalStateException();
}
@Override
public ClusterExecutor filterTargets(ClusterExecutionPolicy policy, Predicate<? super Address> predicate) throws IllegalStateException {
throw new IllegalStateException();
}
@Override
public ClusterExecutor filterTargets(Collection<Address> addresses) {
return filterTargets(addresses::contains);
}
@Override
public ClusterExecutor noFilter() {
if (predicate == null) {
return this;
}
return sameClusterExecutor(null, 0, null);
}
@Override
public ClusterExecutor singleNodeSubmission() {
return this;
}
@Override
public ClusterExecutor singleNodeSubmission(int failOverCount) {
return new FailOverClusterExecutor(this, failOverCount);
}
@Override
public ClusterExecutor allNodeSubmission() {
return this;
}
}