aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-process-monitor/src/main/java/org
diff options
context:
space:
mode:
authorEric Hartmann <hartmann.eric@gmail.Com>2017-03-21 13:50:43 +0100
committerGitHub <noreply@github.com>2017-03-21 13:50:43 +0100
commit7e3819c67725d92e4078c16c4cdd3d6cb8629d5d (patch)
treebd8e11d4341151e6786c797c447954effaff3ff6 /server/sonar-process-monitor/src/main/java/org
parentff0efa8afb8e47ae7dea5f0a190b1772ba59ec0e (diff)
downloadsonarqube-7e3819c67725d92e4078c16c4cdd3d6cb8629d5d.tar.gz
sonarqube-7e3819c67725d92e4078c16c4cdd3d6cb8629d5d.zip
SONAR-8935 Add a log when joining a cluster
Diffstat (limited to 'server/sonar-process-monitor/src/main/java/org')
-rw-r--r--server/sonar-process-monitor/src/main/java/org/sonar/application/SchedulerImpl.java1
-rw-r--r--server/sonar-process-monitor/src/main/java/org/sonar/application/cluster/AppStateClusterImpl.java184
-rw-r--r--server/sonar-process-monitor/src/main/java/org/sonar/application/cluster/ClusterProperties.java1
-rw-r--r--server/sonar-process-monitor/src/main/java/org/sonar/application/cluster/HazelcastCluster.java234
4 files changed, 259 insertions, 161 deletions
diff --git a/server/sonar-process-monitor/src/main/java/org/sonar/application/SchedulerImpl.java b/server/sonar-process-monitor/src/main/java/org/sonar/application/SchedulerImpl.java
index 4cce5a17467..8359d66e08d 100644
--- a/server/sonar-process-monitor/src/main/java/org/sonar/application/SchedulerImpl.java
+++ b/server/sonar-process-monitor/src/main/java/org/sonar/application/SchedulerImpl.java
@@ -161,7 +161,6 @@ public class SchedulerImpl implements Scheduler, ProcessEventListener, ProcessLi
if (process != null) {
process.stop(1, TimeUnit.MINUTES);
}
-
}
/**
diff --git a/server/sonar-process-monitor/src/main/java/org/sonar/application/cluster/AppStateClusterImpl.java b/server/sonar-process-monitor/src/main/java/org/sonar/application/cluster/AppStateClusterImpl.java
index 3f25a987408..cae4d880572 100644
--- a/server/sonar-process-monitor/src/main/java/org/sonar/application/cluster/AppStateClusterImpl.java
+++ b/server/sonar-process-monitor/src/main/java/org/sonar/application/cluster/AppStateClusterImpl.java
@@ -20,41 +20,22 @@
package org.sonar.application.cluster;
-import com.hazelcast.config.Config;
-import com.hazelcast.config.JoinConfig;
-import com.hazelcast.config.NetworkConfig;
-import com.hazelcast.core.EntryEvent;
-import com.hazelcast.core.EntryListener;
-import com.hazelcast.core.Hazelcast;
-import com.hazelcast.core.HazelcastInstance;
-import com.hazelcast.core.IAtomicReference;
-import com.hazelcast.core.ILock;
-import com.hazelcast.core.MapEvent;
-import com.hazelcast.core.ReplicatedMap;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
import java.util.EnumMap;
-import java.util.List;
import java.util.Map;
+import java.util.stream.Collectors;
import javax.annotation.Nonnull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.sonar.application.AppState;
import org.sonar.application.AppStateListener;
import org.sonar.application.config.AppSettings;
import org.sonar.process.ProcessId;
public class AppStateClusterImpl implements AppState {
- static final String OPERATIONAL_PROCESSES = "OPERATIONAL_PROCESSES";
- static final String LEADER = "LEADER";
+ private static Logger LOGGER = LoggerFactory.getLogger(AppStateClusterImpl.class);
- static final String SONARQUBE_VERSION = "SONARQUBE_VERSION";
-
- private final List<AppStateListener> listeners = new ArrayList<>();
- private final ReplicatedMap<ClusterProcess, Boolean> operationalProcesses;
private final Map<ProcessId, Boolean> localProcesses = new EnumMap<>(ProcessId.class);
- private final String listenerUuid;
-
- final HazelcastInstance hzInstance;
+ private final HazelcastCluster hazelcastCluster;
public AppStateClusterImpl(AppSettings appSettings) {
ClusterProperties clusterProperties = new ClusterProperties(appSettings);
@@ -64,56 +45,15 @@ public class AppStateClusterImpl implements AppState {
throw new IllegalStateException("Cluster is not enabled on this instance");
}
- Config hzConfig = new Config();
- try {
- hzConfig.setInstanceName(InetAddress.getLocalHost().getHostName());
- } catch (UnknownHostException e) {
- // Ignore it
- }
-
- hzConfig.getGroupConfig().setName(clusterProperties.getName());
-
- // Configure the network instance
- NetworkConfig netConfig = hzConfig.getNetworkConfig();
- netConfig
- .setPort(clusterProperties.getPort())
- .setReuseAddress(true);
-
- if (!clusterProperties.getNetworkInterfaces().isEmpty()) {
- netConfig.getInterfaces()
- .setEnabled(true)
- .setInterfaces(clusterProperties.getNetworkInterfaces());
- }
+ hazelcastCluster = HazelcastCluster.create(clusterProperties);
- // Only allowing TCP/IP configuration
- JoinConfig joinConfig = netConfig.getJoin();
- joinConfig.getAwsConfig().setEnabled(false);
- joinConfig.getMulticastConfig().setEnabled(false);
- joinConfig.getTcpIpConfig().setEnabled(true);
- joinConfig.getTcpIpConfig().setMembers(clusterProperties.getHosts());
-
- // Tweak HazelCast configuration
- hzConfig
- // Increase the number of tries
- .setProperty("hazelcast.tcp.join.port.try.count", "10")
- // Don't bind on all interfaces
- .setProperty("hazelcast.socket.bind.any", "false")
- // Don't phone home
- .setProperty("hazelcast.phone.home.enabled", "false")
- // Use slf4j for logging
- .setProperty("hazelcast.logging.type", "slf4j");
-
- // We are not using the partition group of Hazelcast, so disabling it
- hzConfig.getPartitionGroupConfig().setEnabled(false);
-
- hzInstance = Hazelcast.newHazelcastInstance(hzConfig);
- operationalProcesses = hzInstance.getReplicatedMap(OPERATIONAL_PROCESSES);
- listenerUuid = operationalProcesses.addEntryListener(new OperationalProcessListener());
+ String members = hazelcastCluster.getMembers().stream().collect(Collectors.joining(","));
+ LOGGER.info("Joined the cluster [{}] that contains the following hosts : [{}]", hazelcastCluster.getName(), members);
}
@Override
public void addListener(@Nonnull AppStateListener listener) {
- listeners.add(listener);
+ hazelcastCluster.addListener(listener);
}
@Override
@@ -121,39 +61,18 @@ public class AppStateClusterImpl implements AppState {
if (local) {
return localProcesses.computeIfAbsent(processId, p -> false);
}
- for (Map.Entry<ClusterProcess, Boolean> entry : operationalProcesses.entrySet()) {
- if (entry.getKey().getProcessId().equals(processId) && entry.getValue()) {
- return true;
- }
- }
- return false;
+ return hazelcastCluster.isOperational(processId);
}
@Override
public void setOperational(@Nonnull ProcessId processId) {
localProcesses.put(processId, true);
- operationalProcesses.put(new ClusterProcess(getLocalUuid(), processId), Boolean.TRUE);
+ hazelcastCluster.setOperational(processId);
}
@Override
public boolean tryToLockWebLeader() {
- IAtomicReference<String> leader = hzInstance.getAtomicReference(LEADER);
- if (leader.get() == null) {
- ILock lock = hzInstance.getLock(LEADER);
- lock.lock();
- try {
- if (leader.get() == null) {
- leader.set(getLocalUuid());
- return true;
- } else {
- return false;
- }
- } finally {
- lock.unlock();
- }
- } else {
- return false;
- }
+ return hazelcastCluster.tryToLockWebLeader();
}
@Override
@@ -163,80 +82,25 @@ public class AppStateClusterImpl implements AppState {
@Override
public void close() {
- if (hzInstance != null) {
- operationalProcesses.removeEntryListener(listenerUuid);
- operationalProcesses.keySet().forEach(
- clusterNodeProcess -> {
- if (clusterNodeProcess.getNodeUuid().equals(getLocalUuid())) {
- operationalProcesses.remove(clusterNodeProcess);
- }
- });
- hzInstance.shutdown();
- }
+ hazelcastCluster.close();
}
@Override
public void registerSonarQubeVersion(String sonarqubeVersion) {
- IAtomicReference<String> sqVersion = hzInstance.getAtomicReference(SONARQUBE_VERSION);
- if (sqVersion.get() == null) {
- ILock lock = hzInstance.getLock(SONARQUBE_VERSION);
- lock.lock();
- try {
- if (sqVersion.get() == null) {
- sqVersion.set(sonarqubeVersion);
- }
- } finally {
- lock.unlock();
- }
- }
-
- String clusterVersion = sqVersion.get();
- if (!sqVersion.get().equals(sonarqubeVersion)) {
- hzInstance.shutdown();
- throw new IllegalStateException(
- String.format("The local version %s is not the same as the cluster %s", sonarqubeVersion, clusterVersion)
- );
- }
+ hazelcastCluster.registerSonarQubeVersion(sonarqubeVersion);
}
- private String getLocalUuid() {
- return hzInstance.getLocalEndpoint().getUuid();
+ HazelcastCluster getHazelcastCluster() {
+ return hazelcastCluster;
}
- private class OperationalProcessListener implements EntryListener<ClusterProcess, Boolean> {
-
- @Override
- public void entryAdded(EntryEvent<ClusterProcess, Boolean> event) {
- if (event.getValue()) {
- listeners.forEach(appStateListener -> appStateListener.onAppStateOperational(event.getKey().getProcessId()));
- }
- }
-
- @Override
- public void entryRemoved(EntryEvent<ClusterProcess, Boolean> event) {
- // Ignore it
- }
-
- @Override
- public void entryUpdated(EntryEvent<ClusterProcess, Boolean> event) {
- if (event.getValue()) {
- listeners.forEach(appStateListener -> appStateListener.onAppStateOperational(event.getKey().getProcessId()));
- }
- }
-
- @Override
- public void entryEvicted(EntryEvent<ClusterProcess, Boolean> event) {
- // Ignore it
- }
-
- @Override
- public void mapCleared(MapEvent event) {
- // Ignore it
- }
-
- @Override
- public void mapEvicted(MapEvent event) {
- // Ignore it
- }
+ /**
+ * Only used for testing purpose
+ *
+ * @param logger
+ */
+ static void setLogger(Logger logger) {
+ AppStateClusterImpl.LOGGER = logger;
}
+
}
diff --git a/server/sonar-process-monitor/src/main/java/org/sonar/application/cluster/ClusterProperties.java b/server/sonar-process-monitor/src/main/java/org/sonar/application/cluster/ClusterProperties.java
index e8287c24d74..f33877f4562 100644
--- a/server/sonar-process-monitor/src/main/java/org/sonar/application/cluster/ClusterProperties.java
+++ b/server/sonar-process-monitor/src/main/java/org/sonar/application/cluster/ClusterProperties.java
@@ -82,6 +82,7 @@ public final class ClusterProperties {
if (!enabled) {
return;
}
+
// Test validity of port
checkArgument(
port > 0 && port < 65_536,
diff --git a/server/sonar-process-monitor/src/main/java/org/sonar/application/cluster/HazelcastCluster.java b/server/sonar-process-monitor/src/main/java/org/sonar/application/cluster/HazelcastCluster.java
new file mode 100644
index 00000000000..39b4b18b98f
--- /dev/null
+++ b/server/sonar-process-monitor/src/main/java/org/sonar/application/cluster/HazelcastCluster.java
@@ -0,0 +1,234 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.application.cluster;
+
+import com.hazelcast.config.Config;
+import com.hazelcast.config.JoinConfig;
+import com.hazelcast.config.NetworkConfig;
+import com.hazelcast.core.EntryEvent;
+import com.hazelcast.core.EntryListener;
+import com.hazelcast.core.Hazelcast;
+import com.hazelcast.core.HazelcastInstance;
+import com.hazelcast.core.IAtomicReference;
+import com.hazelcast.core.ILock;
+import com.hazelcast.core.MapEvent;
+import com.hazelcast.core.ReplicatedMap;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import org.sonar.application.AppStateListener;
+import org.sonar.process.ProcessId;
+
+import static java.util.stream.Collectors.toList;
+import static org.sonar.process.NetworkUtils.getHostName;
+
+public class HazelcastCluster implements AutoCloseable {
+ static final String OPERATIONAL_PROCESSES = "OPERATIONAL_PROCESSES";
+ static final String LEADER = "LEADER";
+ static final String HOSTNAME = "HOSTNAME";
+ static final String SONARQUBE_VERSION = "SONARQUBE_VERSION";
+
+ private final List<AppStateListener> listeners = new ArrayList<>();
+ private final ReplicatedMap<ClusterProcess, Boolean> operationalProcesses;
+ private final String operationalProcessListenerUUID;
+
+ final HazelcastInstance hzInstance;
+
+ private HazelcastCluster(Config hzConfig) {
+ // Create the Hazelcast instance
+ hzInstance = Hazelcast.newHazelcastInstance(hzConfig);
+
+ // Get or create the replicated map
+ operationalProcesses = hzInstance.getReplicatedMap(OPERATIONAL_PROCESSES);
+ operationalProcessListenerUUID = operationalProcesses.addEntryListener(new OperationalProcessListener());
+ }
+
+ String getLocalUuid() {
+ return hzInstance.getLocalEndpoint().getUuid();
+ }
+
+ String getName() {
+ return hzInstance.getConfig().getGroupConfig().getName();
+ }
+
+ List<String> getMembers() {
+ return hzInstance.getCluster().getMembers().stream()
+ .filter(m -> !m.localMember())
+ .map(m -> m.getStringAttribute(HOSTNAME))
+ .collect(toList());
+ }
+
+ void addListener(AppStateListener listener) {
+ listeners.add(listener);
+ }
+
+ boolean isOperational(ProcessId processId) {
+ for (Map.Entry<ClusterProcess, Boolean> entry : operationalProcesses.entrySet()) {
+ if (entry.getKey().getProcessId().equals(processId) && entry.getValue()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void setOperational(ProcessId processId) {
+ operationalProcesses.put(new ClusterProcess(getLocalUuid(), processId), Boolean.TRUE);
+ }
+
+ boolean tryToLockWebLeader() {
+ IAtomicReference<String> leader = hzInstance.getAtomicReference(LEADER);
+ if (leader.get() == null) {
+ ILock lock = hzInstance.getLock(LEADER);
+ lock.lock();
+ try {
+ if (leader.get() == null) {
+ leader.set(getLocalUuid());
+ return true;
+ } else {
+ return false;
+ }
+ } finally {
+ lock.unlock();
+ }
+ } else {
+ return false;
+ }
+ }
+
+ public void registerSonarQubeVersion(String sonarqubeVersion) {
+ IAtomicReference<String> sqVersion = hzInstance.getAtomicReference(SONARQUBE_VERSION);
+ if (sqVersion.get() == null) {
+ ILock lock = hzInstance.getLock(SONARQUBE_VERSION);
+ lock.lock();
+ try {
+ if (sqVersion.get() == null) {
+ sqVersion.set(sonarqubeVersion);
+ }
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ String clusterVersion = sqVersion.get();
+ if (!sqVersion.get().equals(sonarqubeVersion)) {
+ throw new IllegalStateException(
+ String.format("The local version %s is not the same as the cluster %s", sonarqubeVersion, clusterVersion)
+ );
+ }
+ }
+
+ @Override
+ public void close() {
+ if (hzInstance != null) {
+ // Removing listeners
+ operationalProcesses.removeEntryListener(operationalProcessListenerUUID);
+
+ // Removing the operationalProcess from the replicated map
+ operationalProcesses.keySet().forEach(
+ clusterNodeProcess -> {
+ if (clusterNodeProcess.getNodeUuid().equals(getLocalUuid())) {
+ operationalProcesses.remove(clusterNodeProcess);
+ }
+ });
+
+ // Shutdown Hazelcast properly
+ hzInstance.shutdown();
+ }
+ }
+
+ public static HazelcastCluster create(ClusterProperties clusterProperties) {
+ Config hzConfig = new Config();
+ hzConfig.getGroupConfig().setName(clusterProperties.getName());
+
+ // Configure the network instance
+ NetworkConfig netConfig = hzConfig.getNetworkConfig();
+ netConfig
+ .setPort(clusterProperties.getPort())
+ .setReuseAddress(true);
+
+ if (!clusterProperties.getNetworkInterfaces().isEmpty()) {
+ netConfig.getInterfaces()
+ .setEnabled(true)
+ .setInterfaces(clusterProperties.getNetworkInterfaces());
+ }
+
+ // Only allowing TCP/IP configuration
+ JoinConfig joinConfig = netConfig.getJoin();
+ joinConfig.getAwsConfig().setEnabled(false);
+ joinConfig.getMulticastConfig().setEnabled(false);
+ joinConfig.getTcpIpConfig().setEnabled(true);
+ joinConfig.getTcpIpConfig().setMembers(clusterProperties.getHosts());
+
+ // Tweak HazelCast configuration
+ hzConfig
+ // Increase the number of tries
+ .setProperty("hazelcast.tcp.join.port.try.count", "10")
+ // Don't bind on all interfaces
+ .setProperty("hazelcast.socket.bind.any", "false")
+ // Don't phone home
+ .setProperty("hazelcast.phone.home.enabled", "false")
+ // Use slf4j for logging
+ .setProperty("hazelcast.logging.type", "slf4j");
+
+ // Trying to resolve the hostname
+ hzConfig.getMemberAttributeConfig().setStringAttribute(HOSTNAME, getHostName());
+
+ // We are not using the partition group of Hazelcast, so disabling it
+ hzConfig.getPartitionGroupConfig().setEnabled(false);
+ return new HazelcastCluster(hzConfig);
+ }
+
+ private class OperationalProcessListener implements EntryListener<ClusterProcess, Boolean> {
+ @Override
+ public void entryAdded(EntryEvent<ClusterProcess, Boolean> event) {
+ if (event.getValue()) {
+ listeners.forEach(appStateListener -> appStateListener.onAppStateOperational(event.getKey().getProcessId()));
+ }
+ }
+
+ @Override
+ public void entryRemoved(EntryEvent<ClusterProcess, Boolean> event) {
+ // Ignore it
+ }
+
+ @Override
+ public void entryUpdated(EntryEvent<ClusterProcess, Boolean> event) {
+ if (event.getValue()) {
+ listeners.forEach(appStateListener -> appStateListener.onAppStateOperational(event.getKey().getProcessId()));
+ }
+ }
+
+ @Override
+ public void entryEvicted(EntryEvent<ClusterProcess, Boolean> event) {
+ // Ignore it
+ }
+
+ @Override
+ public void mapCleared(MapEvent event) {
+ // Ignore it
+ }
+
+ @Override
+ public void mapEvicted(MapEvent event) {
+ // Ignore it
+ }
+ }
+}