package org.infinispan.manager.impl;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.infinispan.manager.ClusterExecutionPolicy;
import org.infinispan.manager.ClusterExecutor;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.remoting.responses.ExceptionResponse;
import org.infinispan.remoting.responses.Response;
import org.infinispan.remoting.responses.SuccessfulResponse;
import org.infinispan.remoting.transport.Address;
import org.infinispan.remoting.transport.TopologyAwareAddress;
import org.infinispan.remoting.transport.jgroups.JGroupsAddress;
import org.infinispan.remoting.transport.jgroups.JGroupsAddressCache;
import org.infinispan.remoting.transport.jgroups.JGroupsTransport;
import org.infinispan.util.concurrent.TimeoutException;
import org.infinispan.util.logging.Log;
import org.jgroups.util.Rsp;
/**
* Abstract executor that contains code that should be shared by all
* @author wburns
* @since 9.0
*/
abstract class AbstractClusterExecutor<T extends ClusterExecutor> extends LocalClusterExecutor {
protected final JGroupsTransport transport;
protected final Address me;
AbstractClusterExecutor(Predicate<? super Address> predicate, EmbeddedCacheManager manager,
JGroupsTransport transport, long time, TimeUnit unit, Executor localExecutor,
ScheduledExecutorService timeoutExecutor) {
super(predicate, manager, localExecutor, time, unit, timeoutExecutor);
this.transport = transport;
this.me = Objects.requireNonNull(transport.getAddress(),
"Transport was not started before retrieving a ClusterExecutor!");
}
protected abstract T sameClusterExecutor(Predicate<? super Address> predicate,
long time, TimeUnit unit);
protected abstract Log getLog();
@Override
Address getMyAddress() {
return me;
}
static org.jgroups.Address convertToJGroupsAddress(Address address) {
return ((JGroupsAddress) address).getJGroupsAddress();
}
void consumeResponse(Rsp<Response> resp, org.jgroups.Address target, Consumer<? super Throwable> throwableEater) {
consumeResponse(resp, target, o -> {}, throwableEater, throwableEater);
}
void consumeResponse(Rsp<Response> resp, org.jgroups.Address target, Consumer<Object> resultsEater,
Consumer<? super Throwable> throwableEater, Consumer<? super TimeoutException> timeoutEater) {
if (resp.wasReceived()) {
if (resp.hasException()) {
throwableEater.accept(resp.getException());
}
Response ispnResponse = resp.getValue();
if (ispnResponse != null) {
if (ispnResponse instanceof ExceptionResponse) {
// We extract exception as it is always wrapped remotely
throwableEater.accept(((ExceptionResponse) ispnResponse).getException().getCause());
} else if (ispnResponse instanceof SuccessfulResponse) {
resultsEater.accept(((SuccessfulResponse) ispnResponse).getResponseValue());
} else {
throwableEater.accept(new IllegalStateException("Response was neither successful or an exception!"));
}
} else {
resultsEater.accept(null);
}
} else if (resp.wasSuspected()) {
throwableEater.accept(getLog().remoteNodeSuspected(JGroupsAddressCache.fromJGroupsAddress(target)));
} else {
timeoutEater.accept(getLog().remoteNodeTimedOut(JGroupsAddressCache.fromJGroupsAddress(target), time, unit));
}
}
/**
* @param includeMe whether or not the list returned should contain the address for the local node
* @return the targets we should use for JGroups. This excludes the local node if it is a target.
*/
List<org.jgroups.Address> getJGroupsTargets(boolean includeMe) {
List<org.jgroups.Address> list;
List<Address> ispnMembers = transport.getMembers();
int size = ispnMembers.size();
if (size == 0) {
list = Collections.emptyList();
} else {
if (predicate == null) {
if (size == 1) {
Address member = ispnMembers.get(0);
if (!includeMe && member.equals(me)) {
list = Collections.emptyList();
} else {
list = Collections.singletonList(convertToJGroupsAddress(member));
}
} else {
list = (includeMe ? ispnMembers.stream() : ispnMembers.stream().filter(a -> !a.equals(me)))
.map(AllClusterExecutor::convertToJGroupsAddress)
.collect(Collectors.toList());
}
} else {
list = (includeMe ? ispnMembers.stream() : ispnMembers.stream().filter(a -> !a.equals(me)))
.filter(predicate)
.map(AllClusterExecutor::convertToJGroupsAddress)
.collect(Collectors.toList());
}
}
return list;
}
@Override
public T filterTargets(Predicate<? super Address> predicate) {
return sameClusterExecutor(predicate, time, unit);
}
@Override
public T filterTargets(ClusterExecutionPolicy policy) throws IllegalStateException {
if (!manager.getCacheManagerConfiguration().transport().hasTopologyInfo()) {
throw new IllegalStateException("Topology information is not available!");
}
return sameClusterExecutor(a -> policy.include((TopologyAwareAddress) me,
(TopologyAwareAddress) a), time, unit);
}
@Override
public T filterTargets(ClusterExecutionPolicy policy, Predicate<? super Address> predicate) throws IllegalStateException {
if (!manager.getCacheManagerConfiguration().transport().hasTopologyInfo()) {
throw new IllegalStateException();
}
return sameClusterExecutor(a -> policy.include((TopologyAwareAddress) me,
(TopologyAwareAddress) a) && predicate.test(a), time, unit);
}
@Override
public T filterTargets(Collection<Address> addresses) {
return filterTargets(addresses::contains);
}
@Override
public T noFilter() {
if (predicate == null) {
return (T) this;
}
return sameClusterExecutor(null, time, unit);
}
@Override
public T timeout(long time, TimeUnit unit) {
if (time <= 0) {
throw new IllegalArgumentException("Time must be greater than 0!");
}
Objects.requireNonNull(unit, "TimeUnit must be non null!");
if (this.time == time && this.unit == unit) {
return (T) this;
}
return sameClusterExecutor(predicate, time, unit);
}
}