/*
* 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.test.mocknetwork;
import com.hazelcast.instance.Node;
import com.hazelcast.internal.cluster.impl.AbstractJoiner;
import com.hazelcast.internal.cluster.impl.SplitBrainJoinMessage;
import com.hazelcast.nio.Address;
import com.hazelcast.util.Clock;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
class MockJoiner extends AbstractJoiner {
private static final long JOIN_ADDRESS_TIMEOUT_IN_MILLIS = 5000;
// blacklisted addresses
private final Set<Address> blacklist;
private final TestNodeRegistry registry;
MockJoiner(Node node, TestNodeRegistry registry, Set<Address> initiallyBlockedAddresses) {
super(node);
this.registry = registry;
this.blacklist = new CopyOnWriteArraySet<Address>(initiallyBlockedAddresses);
}
public void doJoin() {
registry.registerNode(node);
final long joinStartTime = Clock.currentTimeMillis();
final long maxJoinMillis = getMaxJoinMillis();
Address previousJoinAddress = null;
long joinAddressTimeout = 0;
while (shouldRetry() && (Clock.currentTimeMillis() - joinStartTime < maxJoinMillis)) {
synchronized (registry) {
Address joinAddress = getJoinAddress();
verifyInvariant(joinAddress != null, "joinAddress should not be null");
if (!joinAddress.equals(previousJoinAddress)) {
previousJoinAddress = joinAddress;
joinAddressTimeout = Clock.currentTimeMillis() + JOIN_ADDRESS_TIMEOUT_IN_MILLIS;
}
if (node.getThisAddress().equals(joinAddress)) {
logger.fine("This node is found as master, no need to join.");
clusterJoinManager.setThisMemberAsMaster();
break;
}
logger.fine("Sending join request to " + joinAddress);
if (!clusterJoinManager.sendJoinRequest(joinAddress, true)) {
logger.fine("Could not send join request to " + joinAddress);
clusterService.setMasterAddressToJoin(null);
}
if (Clock.currentTimeMillis() > joinAddressTimeout) {
logger.warning("Resetting master address because join address timeout");
previousJoinAddress = null;
joinAddressTimeout = 0;
clusterService.setMasterAddressToJoin(null);
}
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
}
private Address getJoinAddress() {
Address joinAddress = node.getMasterAddress();
logger.fine("Known master address is: " + joinAddress);
if (joinAddress == null) {
joinAddress = lookupJoinAddress();
if (!node.getThisAddress().equals(joinAddress)) {
clusterJoinManager.sendMasterQuestion(joinAddress);
}
}
return joinAddress;
}
private Address lookupJoinAddress() {
Node foundNode = findAliveNode();
if (foundNode == null) {
logger.fine("Picking this node as master, no other running node has been detected.");
return node.getThisAddress();
}
logger.fine("Found alive node. Will try to connect to " + foundNode.getThisAddress());
return foundNode.getThisAddress();
}
private Node findAliveNode() {
Collection<Address> joinAddresses = registry.getJoinAddresses();
logger.fine("Searching possible addresses for master " + joinAddresses);
for (Address address : joinAddresses) {
Node foundNode = registry.getNode(address);
if (foundNode == null) {
logger.fine("Node for " + address + " is null.");
continue;
}
verifyInvariant(address.equals(foundNode.getThisAddress()), "The address should be equal to the one in the found node");
if (foundNode.getThisAddress().equals(node.getThisAddress())) {
continue;
}
if (!foundNode.isRunning()) {
logger.fine("Node for " + address + " is not running. -> " + foundNode.getState());
continue;
}
if (!foundNode.getClusterService().isJoined()) {
logger.fine("Node for " + address + " is not joined yet.");
continue;
}
if (isBlacklisted(address)) {
logger.fine("Node for " + address + " is blacklisted and should not be joined.");
continue;
}
logger.fine("Found an alive node. Will ask master of " + address);
return foundNode;
}
return null;
}
public void searchForOtherClusters() {
Collection<Address> possibleAddresses = new ArrayList<Address>(registry.getJoinAddresses());
possibleAddresses.remove(node.getThisAddress());
possibleAddresses.removeAll(node.getClusterService().getMemberAddresses());
for (Address address : possibleAddresses) {
SplitBrainJoinMessage response = sendSplitBrainJoinMessage(address);
if (shouldMerge(response)) {
startClusterMerge(address);
}
}
}
@Override
public String getType() {
return "mock";
}
@Override
public String toString() {
return "MockJoiner";
}
@Override
public void blacklist(Address address, boolean permanent) {
// blacklist is always temporary in MockJoiner
blacklist.add(address);
}
@Override
public boolean unblacklist(Address address) {
return blacklist.remove(address);
}
@Override
public boolean isBlacklisted(Address address) {
return blacklist.contains(address);
}
private static void verifyInvariant(boolean check, String msg) {
if (!check) {
throw new AssertionError(msg);
}
}
}