/*
* 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 java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import javax.annotation.Nullable;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.application.config.AppSettings;
import org.sonar.process.ProcessProperties;
/**
* Properties of the cluster configuration
*/
public final class ClusterProperties {
static final String DEFAULT_PORT = "9003";
private static final Logger LOGGER = LoggerFactory.getLogger(ClusterProperties.class);
private final int port;
private final boolean enabled;
private final List<String> hosts;
private final List<String> networkInterfaces;
private final String name;
ClusterProperties(AppSettings appSettings) {
port = appSettings.getProps().valueAsInt(ProcessProperties.CLUSTER_PORT);
enabled = appSettings.getProps().valueAsBoolean(ProcessProperties.CLUSTER_ENABLED);
networkInterfaces = extractNetworkInterfaces(
appSettings.getProps().value(ProcessProperties.CLUSTER_NETWORK_INTERFACES, "")
);
name = appSettings.getProps().nonNullValue(ProcessProperties.CLUSTER_NAME);
hosts = extractHosts(
appSettings.getProps().value(ProcessProperties.CLUSTER_HOSTS, "")
);
}
int getPort() {
return port;
}
boolean isEnabled() {
return enabled;
}
List<String> getHosts() {
return hosts;
}
List<String> getNetworkInterfaces() {
return networkInterfaces;
}
String getName() {
return name;
}
void validate() {
if (!enabled) {
return;
}
// Test validity of port
checkArgument(
port > 0 && port < 65_536,
"Cluster port have been set to %d which is outside the range [1-65535].",
port
);
// Test the networkInterfaces parameter
try {
List<String> localInterfaces = findAllLocalIPs();
networkInterfaces.forEach(
inet -> checkArgument(
StringUtils.isEmpty(inet) || localInterfaces.contains(inet),
"Interface %s is not available on this machine.",
inet
)
);
} catch (SocketException e) {
LOGGER.warn("Unable to retrieve network networkInterfaces. Interfaces won't be checked", e);
}
}
private static List<String> extractHosts(final String hosts) {
List<String> result = new ArrayList<>();
for (String host : hosts.split(",")) {
if (StringUtils.isNotEmpty(host)) {
if (!host.contains(":")) {
result.add(
String.format("%s:%s", host, DEFAULT_PORT)
);
} else {
result.add(host);
}
}
}
return result;
}
private static List<String> extractNetworkInterfaces(final String networkInterfaces) {
List<String> result = new ArrayList<>();
for (String iface : networkInterfaces.split(",")) {
if (StringUtils.isNotEmpty(iface)) {
result.add(iface);
}
}
return result;
}
private static List<String> findAllLocalIPs() throws SocketException {
Enumeration<NetworkInterface> netInterfaces = NetworkInterface.getNetworkInterfaces();
List<String> localInterfaces = new ArrayList<>();
while (netInterfaces.hasMoreElements()) {
NetworkInterface networkInterface = netInterfaces.nextElement();
Enumeration<InetAddress> ips = networkInterface.getInetAddresses();
while (ips.hasMoreElements()) {
InetAddress ip = ips.nextElement();
localInterfaces.add(ip.getHostAddress());
}
}
return localInterfaces;
}
private static void checkArgument(boolean expression,
@Nullable String messageTemplate,
@Nullable Object... args) {
if (!expression) {
throw new IllegalArgumentException(String.format(messageTemplate, args));
}
}
}