/*
* Copyright (c) 2008-2012, Hazel Bilisim Ltd. 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.impl;
import com.hazelcast.cluster.JoinInfo;
import com.hazelcast.config.TcpIpConfig;
import com.hazelcast.nio.Address;
import com.hazelcast.nio.Connection;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
public class MulticastJoiner extends AbstractJoiner {
final AtomicInteger currentTryCount = new AtomicInteger(0);
final AtomicInteger tryCount;
public MulticastJoiner(Node node) {
super(node);
tryCount = new AtomicInteger(calculateTryCount());
}
public void doJoin(AtomicBoolean joined) {
int tryCount = 0;
long joinStartTime = System.currentTimeMillis();
long maxJoinMillis = node.getGroupProperties().MAX_JOIN_SECONDS.getInteger() * 1000;
while (node.isActive() && !joined.get() && (System.currentTimeMillis() - joinStartTime < maxJoinMillis)) {
String msg = "Joining master " + node.getMasterAddress();
logger.log(Level.FINEST, msg);
systemLogService.logJoin(msg);
Address masterAddressNow = findMasterWithMulticast();
if (masterAddressNow != null && masterAddressNow.equals(node.getMasterAddress())) {
tryCount--;
}
node.setMasterAddress(masterAddressNow);
systemLogService.logJoin("Setting master " + masterAddressNow);
if (node.getMasterAddress() == null || node.address.equals(node.getMasterAddress())) {
TcpIpConfig tcpIpConfig = config.getNetworkConfig().getJoin().getTcpIpConfig();
if (tcpIpConfig != null && tcpIpConfig.isEnabled()) {
doTCP(joined);
} else {
systemLogService.logJoin("Setting as master");
node.setAsMaster();
}
return;
}
if (tryCount++ > 22) {
failedJoiningToMaster(true, tryCount);
}
if (!node.getMasterAddress().equals(node.address)) {
connectAndSendJoinRequest(node.getMasterAddress());
} else {
node.setMasterAddress(null);
tryCount = 0;
}
try {
//noinspection BusyWait
Thread.sleep(500L);
} catch (InterruptedException ignored) {
}
}
}
private void doTCP(AtomicBoolean joined) {
node.setMasterAddress(null);
logger.log(Level.FINEST, "Multicast couldn't find cluster. Trying TCP/IP");
new TcpIpJoiner(node).join(joined);
}
public void searchForOtherClusters(SplitBrainHandler splitBrainHandler) {
final BlockingQueue q = new LinkedBlockingQueue();
MulticastListener listener = new MulticastListener() {
public void onMessage(Object msg) {
systemLogService.logJoin("MulticastListener onMessage " + msg);
if (msg != null && msg instanceof JoinInfo) {
JoinInfo joinInfo = (JoinInfo) msg;
if (node.address != null && !node.address.equals(joinInfo.address)) {
q.offer(msg);
}
}
}
};
node.multicastService.addMulticastListener(listener);
node.multicastService.send(node.createJoinInfo());
systemLogService.logJoin("Sent multicast join request");
try {
JoinInfo joinInfo = (JoinInfo) q.poll(3, TimeUnit.SECONDS);
if (joinInfo != null) {
if (shouldMerge(joinInfo)) {
logger.log(Level.WARNING, node.address + " is merging [multicast] to " + joinInfo.address);
node.factory.restart();
return;
}
}
} catch (InterruptedException ignored) {
} catch (Exception e) {
if (logger != null) {
logger.log(Level.WARNING, e.getMessage(), e);
}
} finally {
node.multicastService.removeMulticastListener(listener);
}
}
private boolean connectAndSendJoinRequest(Address masterAddress) {
if (masterAddress == null || masterAddress.equals(node.address)) {
throw new IllegalArgumentException();
}
Connection conn = node.connectionManager.getOrConnect(masterAddress);
logger.log(Level.FINEST, "Master connection " + conn);
systemLogService.logJoin("Master connection " + conn);
if (conn != null) {
return node.clusterManager.sendJoinRequest(masterAddress, true);
}
return false;
}
private Address findMasterWithMulticast() {
try {
final String ip = System.getProperty("join.ip");
if (ip == null) {
JoinInfo joinInfo = node.createJoinInfo();
for (; node.isActive() && currentTryCount.incrementAndGet() <= tryCount.get(); ) {
joinInfo.setTryCount(currentTryCount.get());
node.multicastService.send(joinInfo);
if (node.getMasterAddress() == null) {
//noinspection BusyWait
Thread.sleep(10);
} else {
return node.getMasterAddress();
}
}
} else {
logger.log(Level.FINEST, "RETURNING join.ip");
return new Address(ip, config.getPort());
}
} catch (final Exception e) {
if (logger != null) {
logger.log(Level.WARNING, e.getMessage(), e);
}
} finally {
this.currentTryCount.set(0);
}
return null;
}
private int calculateTryCount() {
int timeoutSeconds = config.getNetworkConfig().getJoin().getMulticastConfig().getMulticastTimeoutSeconds();
int tryCount = timeoutSeconds * 100;
String host = node.address.getHost();
int lastDigits = 0;
try {
lastDigits = Integer.valueOf(host.substring(host.lastIndexOf(".") + 1));
} catch (NumberFormatException e) {
lastDigits = (int) (512 * Math.random());
}
lastDigits = lastDigits % 100;
tryCount += lastDigits + (node.address.getPort() - node.config.getPort()) * timeoutSeconds * 3;
return tryCount;
}
public void onReceivedJoinInfo(JoinInfo joinInfo) {
if (joinInfo.getTryCount() > this.currentTryCount.get() + 20) {
int timeoutSeconds = (config.getNetworkConfig().getJoin().getMulticastConfig().getMulticastTimeoutSeconds() + 4) * 100;
this.tryCount.set(timeoutSeconds);
}
}
}