/*
* Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved.
*
* 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 com.hazelcast.scheduledexecutor.impl;
import com.hazelcast.core.Member;
import com.hazelcast.logging.ILogger;
import com.hazelcast.spi.NodeEngine;
import com.hazelcast.spi.Operation;
import com.hazelcast.spi.OperationService;
import com.hazelcast.spi.annotation.PrivateApi;
import com.hazelcast.spi.serialization.SerializationService;
import com.hazelcast.util.function.Supplier;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
/**
* Executes an operation on a set of targets.
* Similar to {@link com.hazelcast.spi.impl.operationservice.impl.InvokeOnPartitions} but for members.
*/
@PrivateApi
public final class InvokeOnMembers {
private static final int TRY_COUNT = 10;
private static final int TRY_PAUSE_MILLIS = 300;
private final ILogger logger;
private final OperationService operationService;
private final SerializationService serializationService;
private final String serviceName;
private final Supplier<Operation> operationFactory;
private final Collection<Member> targets;
private final Map<Member, Future> futures;
private final Map<Member, Object> results;
public InvokeOnMembers(NodeEngine nodeEngine,
String serviceName, Supplier<Operation> operationFactory,
Collection<Member> targets) {
this.logger = nodeEngine.getLogger(getClass());
this.operationService = nodeEngine.getOperationService();
this.serializationService = nodeEngine.getSerializationService();
this.serviceName = serviceName;
this.operationFactory = operationFactory;
this.targets = targets;
this.futures = new HashMap<Member, Future>(targets.size());
this.results = new HashMap<Member, Object>(targets.size());
}
/**
* Executes the operation on all targets.
*/
public Map<Member, Object> invoke() throws Exception {
invokeOnAllTargets();
awaitCompletion();
retryFailedTargets();
return results;
}
private void invokeOnAllTargets() {
for (Member target : targets) {
Future future = operationService
.createInvocationBuilder(serviceName, operationFactory.get(), target.getAddress())
.setTryCount(TRY_COUNT)
.setTryPauseMillis(TRY_PAUSE_MILLIS)
.invoke();
futures.put(target, future);
}
}
private void awaitCompletion() {
for (Map.Entry<Member, Future> responseEntry : futures.entrySet()) {
try {
Future future = responseEntry.getValue();
results.put(responseEntry.getKey(), serializationService.toObject(future.get()));
} catch (Throwable t) {
if (logger.isFinestEnabled()) {
logger.finest(t);
} else {
logger.warning(t.getMessage());
}
results.put(responseEntry.getKey(), t);
}
}
}
private void retryFailedTargets() throws InterruptedException, ExecutionException {
List<Member> failedMembers = new LinkedList<Member>();
for (Map.Entry<Member, Object> memberResult : results.entrySet()) {
Member member = memberResult.getKey();
Object result = memberResult.getValue();
if (result instanceof Throwable) {
failedMembers.add(member);
}
}
for (Member failedMember : failedMembers) {
Operation operation = operationFactory.get();
Future future = operationService
.createInvocationBuilder(serviceName, operation, failedMember.getAddress())
.invoke();
results.put(failedMember, future);
}
for (Member failedMember : failedMembers) {
Future future = (Future) results.get(failedMember);
Object result = future.get();
results.put(failedMember, result);
}
}
}