|
@@ -19,15 +19,15 @@
|
|
|
package org.apache.zookeeper.server.quorum;
|
|
|
|
|
|
import java.io.BufferedReader;
|
|
|
+import java.io.BufferedWriter;
|
|
|
import java.io.File;
|
|
|
import java.io.FileInputStream;
|
|
|
+import java.io.FileOutputStream;
|
|
|
import java.io.FileReader;
|
|
|
+import java.io.FileWriter;
|
|
|
import java.io.IOException;
|
|
|
import java.net.InetAddress;
|
|
|
import java.net.InetSocketAddress;
|
|
|
-import java.util.Collections;
|
|
|
-import java.util.HashMap;
|
|
|
-import java.util.Map;
|
|
|
import java.util.Properties;
|
|
|
import java.util.Map.Entry;
|
|
|
|
|
@@ -35,6 +35,7 @@ import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
import org.slf4j.MDC;
|
|
|
|
|
|
+
|
|
|
import org.apache.zookeeper.server.ZooKeeperServer;
|
|
|
import org.apache.zookeeper.server.quorum.QuorumPeer.LearnerType;
|
|
|
import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer;
|
|
@@ -43,12 +44,16 @@ import org.apache.zookeeper.server.quorum.flexible.QuorumMaj;
|
|
|
import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier;
|
|
|
import org.apache.zookeeper.server.util.VerifyingFileFactory;
|
|
|
|
|
|
+
|
|
|
public class QuorumPeerConfig {
|
|
|
private static final Logger LOG = LoggerFactory.getLogger(QuorumPeerConfig.class);
|
|
|
|
|
|
protected InetSocketAddress clientPortAddress;
|
|
|
protected File dataDir;
|
|
|
protected File dataLogDir;
|
|
|
+ protected boolean configBackwardCompatibilityMode = false;
|
|
|
+ protected String dynamicConfigFileStr = null;
|
|
|
+ protected String configFileStr = null;
|
|
|
protected int tickTime = ZooKeeperServer.DEFAULT_TICK_TIME;
|
|
|
protected int maxClientCnxns = 60;
|
|
|
/** defaults to -1 if not set explicitly */
|
|
@@ -60,16 +65,10 @@ public class QuorumPeerConfig {
|
|
|
protected int syncLimit;
|
|
|
protected int electionAlg = 3;
|
|
|
protected int electionPort = 2182;
|
|
|
- protected final HashMap<Long,QuorumServer> servers =
|
|
|
- new HashMap<Long, QuorumServer>();
|
|
|
- protected final HashMap<Long,QuorumServer> observers =
|
|
|
- new HashMap<Long, QuorumServer>();
|
|
|
|
|
|
protected long serverId;
|
|
|
- protected HashMap<Long, Long> serverWeight = new HashMap<Long, Long>();
|
|
|
- protected HashMap<Long, Long> serverGroup = new HashMap<Long, Long>();
|
|
|
- protected int numGroups = 0;
|
|
|
- protected QuorumVerifier quorumVerifier;
|
|
|
+
|
|
|
+ protected QuorumVerifier quorumVerifier = null;
|
|
|
protected int snapRetainCount = 3;
|
|
|
protected int purgeInterval = 0;
|
|
|
|
|
@@ -98,13 +97,13 @@ public class QuorumPeerConfig {
|
|
|
*/
|
|
|
public void parse(String path) throws ConfigException {
|
|
|
LOG.info("Reading configuration from: " + path);
|
|
|
-
|
|
|
+
|
|
|
try {
|
|
|
File configFile = (new VerifyingFileFactory.Builder(LOG)
|
|
|
.warnForRelativePath()
|
|
|
.failForNonExistingPath()
|
|
|
.build()).create(path);
|
|
|
-
|
|
|
+
|
|
|
Properties cfg = new Properties();
|
|
|
FileInputStream in = new FileInputStream(configFile);
|
|
|
try {
|
|
@@ -112,12 +111,41 @@ public class QuorumPeerConfig {
|
|
|
} finally {
|
|
|
in.close();
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
parseProperties(cfg);
|
|
|
+
|
|
|
+ // backward compatibility - dynamic configuration in the same file as static configuration params
|
|
|
+ // see writeDynamicConfig() - we change the config file to new format if reconfig happens
|
|
|
+ if (dynamicConfigFileStr == null) {
|
|
|
+ configBackwardCompatibilityMode = true;
|
|
|
+ configFileStr = path;
|
|
|
+ parseDynamicConfig(cfg, electionAlg);
|
|
|
+ checkValidity();
|
|
|
+ }
|
|
|
+
|
|
|
} catch (IOException e) {
|
|
|
throw new ConfigException("Error processing " + path, e);
|
|
|
} catch (IllegalArgumentException e) {
|
|
|
throw new ConfigException("Error processing " + path, e);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (dynamicConfigFileStr!=null) {
|
|
|
+ try {
|
|
|
+ Properties dynamicCfg = new Properties();
|
|
|
+ FileInputStream inConfig = new FileInputStream(dynamicConfigFileStr);
|
|
|
+ try {
|
|
|
+ dynamicCfg.load(inConfig);
|
|
|
+ } finally {
|
|
|
+ inConfig.close();
|
|
|
+ }
|
|
|
+ parseDynamicConfig(dynamicCfg, electionAlg);
|
|
|
+ checkValidity();
|
|
|
+
|
|
|
+ } catch (IOException e) {
|
|
|
+ throw new ConfigException("Error processing " + dynamicConfigFileStr, e);
|
|
|
+ } catch (IllegalArgumentException e) {
|
|
|
+ throw new ConfigException("Error processing " + dynamicConfigFileStr, e);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -166,63 +194,14 @@ public class QuorumPeerConfig {
|
|
|
{
|
|
|
throw new ConfigException("Unrecognised peertype: " + value);
|
|
|
}
|
|
|
+ } else if (key.equals("dynamicConfigFile")){
|
|
|
+ dynamicConfigFileStr = value;
|
|
|
} else if (key.equals("autopurge.snapRetainCount")) {
|
|
|
snapRetainCount = Integer.parseInt(value);
|
|
|
} else if (key.equals("autopurge.purgeInterval")) {
|
|
|
purgeInterval = Integer.parseInt(value);
|
|
|
- } else if (key.startsWith("server.")) {
|
|
|
- int dot = key.indexOf('.');
|
|
|
- long sid = Long.parseLong(key.substring(dot + 1));
|
|
|
- String parts[] = value.split(":");
|
|
|
- if ((parts.length != 2) && (parts.length != 3) && (parts.length !=4)) {
|
|
|
- LOG.error(value
|
|
|
- + " does not have the form host:port or host:port:port " +
|
|
|
- " or host:port:port:type");
|
|
|
- }
|
|
|
- InetSocketAddress addr = new InetSocketAddress(parts[0],
|
|
|
- Integer.parseInt(parts[1]));
|
|
|
- if (parts.length == 2) {
|
|
|
- servers.put(Long.valueOf(sid), new QuorumServer(sid, addr));
|
|
|
- } else if (parts.length == 3) {
|
|
|
- InetSocketAddress electionAddr = new InetSocketAddress(
|
|
|
- parts[0], Integer.parseInt(parts[2]));
|
|
|
- servers.put(Long.valueOf(sid), new QuorumServer(sid, addr,
|
|
|
- electionAddr));
|
|
|
- } else if (parts.length == 4) {
|
|
|
- InetSocketAddress electionAddr = new InetSocketAddress(
|
|
|
- parts[0], Integer.parseInt(parts[2]));
|
|
|
- LearnerType type = LearnerType.PARTICIPANT;
|
|
|
- if (parts[3].equalsIgnoreCase("observer")) {
|
|
|
- type = LearnerType.OBSERVER;
|
|
|
- observers.put(Long.valueOf(sid), new QuorumServer(sid, addr,
|
|
|
- electionAddr,type));
|
|
|
- } else if (parts[3].equalsIgnoreCase("participant")) {
|
|
|
- type = LearnerType.PARTICIPANT;
|
|
|
- servers.put(Long.valueOf(sid), new QuorumServer(sid, addr,
|
|
|
- electionAddr,type));
|
|
|
- } else {
|
|
|
- throw new ConfigException("Unrecognised peertype: " + value);
|
|
|
- }
|
|
|
- }
|
|
|
- } else if (key.startsWith("group")) {
|
|
|
- int dot = key.indexOf('.');
|
|
|
- long gid = Long.parseLong(key.substring(dot + 1));
|
|
|
-
|
|
|
- numGroups++;
|
|
|
-
|
|
|
- String parts[] = value.split(":");
|
|
|
- for(String s : parts){
|
|
|
- long sid = Long.parseLong(s);
|
|
|
- if(serverGroup.containsKey(sid))
|
|
|
- throw new ConfigException("Server " + sid + "is in multiple groups");
|
|
|
- else
|
|
|
- serverGroup.put(sid, gid);
|
|
|
- }
|
|
|
-
|
|
|
- } else if(key.startsWith("weight")) {
|
|
|
- int dot = key.indexOf('.');
|
|
|
- long sid = Long.parseLong(key.substring(dot + 1));
|
|
|
- serverWeight.put(sid, Long.parseLong(value));
|
|
|
+ } else if ((key.startsWith("server.") || key.startsWith("group") || key.startsWith("weight")) && zkProp.entrySet().contains("dynamicConfigFile")){
|
|
|
+ throw new ConfigException("parameter: " + key + " must be in a separate dynamic config file");
|
|
|
} else {
|
|
|
System.setProperty("zookeeper." + key, value);
|
|
|
}
|
|
@@ -248,97 +227,197 @@ public class QuorumPeerConfig {
|
|
|
+ " is missing.");
|
|
|
}
|
|
|
}
|
|
|
- if (clientPort == 0) {
|
|
|
- throw new IllegalArgumentException("clientPort is not set");
|
|
|
- }
|
|
|
- if (clientPortAddress == null) {
|
|
|
- this.clientPortAddress = new InetSocketAddress(clientPort);
|
|
|
- } else {
|
|
|
- this.clientPortAddress = new InetSocketAddress(
|
|
|
- InetAddress.getByName(clientPortAddress), clientPort);
|
|
|
+ if (clientPortAddress != null) {
|
|
|
+ if (clientPort == 0) {
|
|
|
+ throw new IllegalArgumentException("clientPortAddress is set but clientPort is not set");
|
|
|
}
|
|
|
-
|
|
|
+ this.clientPortAddress = new InetSocketAddress(
|
|
|
+ InetAddress.getByName(clientPortAddress), clientPort);
|
|
|
+ } else if (clientPort!=0){
|
|
|
+ this.clientPortAddress = new InetSocketAddress(clientPort);
|
|
|
+ }
|
|
|
if (tickTime == 0) {
|
|
|
throw new IllegalArgumentException("tickTime is not set");
|
|
|
}
|
|
|
if (minSessionTimeout > maxSessionTimeout) {
|
|
|
throw new IllegalArgumentException(
|
|
|
"minSessionTimeout must not be larger than maxSessionTimeout");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Writes dynamic configuration file, updates static config file if needed.
|
|
|
+ * @param dynamicConfigFilename
|
|
|
+ * @param configFileStr
|
|
|
+ * @param configBackwardCompatibilityMode
|
|
|
+ * @param qv
|
|
|
+ */
|
|
|
+ public static void writeDynamicConfig(String dynamicConfigFilename, String configFileStr,
|
|
|
+ boolean configBackwardCompatibilityMode, QuorumVerifier qv) throws IOException {
|
|
|
+ FileOutputStream outConfig = null;
|
|
|
+ try {
|
|
|
+ byte b[] = qv.toByteArray();
|
|
|
+ if (configBackwardCompatibilityMode) {
|
|
|
+ dynamicConfigFilename = configFileStr + ".dynamic";
|
|
|
+ }
|
|
|
+ String tmpFilename = dynamicConfigFilename + ".tmp";
|
|
|
+ outConfig = new FileOutputStream(tmpFilename);
|
|
|
+
|
|
|
+ outConfig.write(b);
|
|
|
+ outConfig.close();
|
|
|
+ File curFile = new File(dynamicConfigFilename);
|
|
|
+ File tmpFile = new File(tmpFilename);
|
|
|
+ if (!tmpFile.renameTo(curFile)) {
|
|
|
+ throw new IOException("renaming " + tmpFile.toString() + " to " + curFile.toString() + " failed!");
|
|
|
+ }
|
|
|
+ } finally{
|
|
|
+ if (outConfig!=null) {
|
|
|
+ outConfig.close();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // the following is for users who run without a dynamic config file (old config file)
|
|
|
+ // if the configuration changes (reconfiguration executes), we create a dynamic config
|
|
|
+ // file, remove all the dynamic definitions from the config file and add a pointer
|
|
|
+ // to the config file. The dynamic config file's name will be the same as the config file's
|
|
|
+ // with ".dynamic" appended to it
|
|
|
+
|
|
|
+ if (configBackwardCompatibilityMode) {
|
|
|
+ BufferedWriter out = null;
|
|
|
+ try {
|
|
|
+ File configFile = (new VerifyingFileFactory.Builder(LOG)
|
|
|
+ .warnForRelativePath()
|
|
|
+ .failForNonExistingPath()
|
|
|
+ .build()).create(configFileStr);
|
|
|
+
|
|
|
+ Properties cfg = new Properties();
|
|
|
+ FileInputStream in = new FileInputStream(configFile);
|
|
|
+ try {
|
|
|
+ cfg.load(in);
|
|
|
+ } finally {
|
|
|
+ in.close();
|
|
|
+ }
|
|
|
+ String tmpFilename = configFileStr + ".tmp";
|
|
|
+ FileWriter fstream = new FileWriter(tmpFilename);
|
|
|
+ out = new BufferedWriter(fstream);
|
|
|
+
|
|
|
+ for (Entry<Object, Object> entry : cfg.entrySet()) {
|
|
|
+ String key = entry.getKey().toString().trim();
|
|
|
+ String value = entry.getValue().toString().trim();
|
|
|
+ if (!key.startsWith("server.") && !key.startsWith("group")
|
|
|
+ && !key.startsWith("weight") && !key.equals("clientPort") && !key.equals("clientPortAddress")){
|
|
|
+ out.write(key.concat("=").concat(value).concat("\n"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ out.write("dynamicConfigFile=".concat(dynamicConfigFilename).concat("\n"));
|
|
|
+ out.close();
|
|
|
+ File tmpFile = new File(tmpFilename);
|
|
|
+ if (!tmpFile.renameTo(configFile)) {
|
|
|
+ throw new IOException("renaming " + tmpFile.toString() + " to " + configFile.toString() + " failed!");
|
|
|
+ }
|
|
|
+ } finally{
|
|
|
+ if (out!=null) {
|
|
|
+ out.close();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ public static void deleteFile(String filename){
|
|
|
+ File f = new File(filename);
|
|
|
+ if (f.exists()) {
|
|
|
+ try{
|
|
|
+ f.delete();
|
|
|
+ } catch (Exception e) {
|
|
|
+ LOG.warn("deleting " + filename + " failed");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private QuorumVerifier createQuorumVerifier(Properties dynamicConfigProp, boolean isHierarchical) throws ConfigException{
|
|
|
+ if(isHierarchical){
|
|
|
+ return new QuorumHierarchical(dynamicConfigProp);
|
|
|
+ } else {
|
|
|
+ /*
|
|
|
+ * The default QuorumVerifier is QuorumMaj
|
|
|
+ */
|
|
|
+ //LOG.info("Defaulting to majority quorums");
|
|
|
+ return new QuorumMaj(dynamicConfigProp);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Parse dynamic configuration file.
|
|
|
+ * @param zkProp Properties to parse from.
|
|
|
+ * @throws IOException
|
|
|
+ * @throws ConfigException
|
|
|
+ */
|
|
|
+ public void parseDynamicConfig(Properties dynamicConfigProp, int eAlg)
|
|
|
+ throws IOException, ConfigException {
|
|
|
+ boolean isHierarchical = false;
|
|
|
+ for (Entry<Object, Object> entry : dynamicConfigProp.entrySet()) {
|
|
|
+ String key = entry.getKey().toString().trim();
|
|
|
+ if (key.startsWith("group") || key.startsWith("weight")) {
|
|
|
+ isHierarchical = true;
|
|
|
+ } else if (!configBackwardCompatibilityMode && !key.startsWith("server.") && !key.equals("version")){
|
|
|
+ LOG.info(dynamicConfigProp.toString());
|
|
|
+ throw new ConfigException("Unrecognised parameter: " + key);
|
|
|
+ }
|
|
|
}
|
|
|
- if (servers.size() == 0) {
|
|
|
- if (observers.size() > 0) {
|
|
|
+
|
|
|
+ quorumVerifier = createQuorumVerifier(dynamicConfigProp, isHierarchical);
|
|
|
+
|
|
|
+ int numParticipators = quorumVerifier.getVotingMembers().size();
|
|
|
+ int numObservers = quorumVerifier.getObservingMembers().size();
|
|
|
+ if (numParticipators == 0) {
|
|
|
+ if (numObservers > 0) {
|
|
|
throw new IllegalArgumentException("Observers w/o participants is an invalid configuration");
|
|
|
}
|
|
|
// Not a quorum configuration so return immediately - not an error
|
|
|
// case (for b/w compatibility), server will default to standalone
|
|
|
// mode.
|
|
|
return;
|
|
|
- } else if (servers.size() == 1) {
|
|
|
- if (observers.size() > 0) {
|
|
|
+ } else if (numParticipators == 1) {
|
|
|
+ if (numObservers > 0) {
|
|
|
throw new IllegalArgumentException("Observers w/o quorum is an invalid configuration");
|
|
|
}
|
|
|
|
|
|
// HBase currently adds a single server line to the config, for
|
|
|
// b/w compatibility reasons we need to keep this here.
|
|
|
LOG.error("Invalid configuration, only one server specified (ignoring)");
|
|
|
- servers.clear();
|
|
|
- } else if (servers.size() > 1) {
|
|
|
- if (servers.size() == 2) {
|
|
|
+ //servers.clear();
|
|
|
+ } else if (numParticipators > 1) {
|
|
|
+ if (numParticipators == 2) {
|
|
|
LOG.warn("No server failure will be tolerated. " +
|
|
|
"You need at least 3 servers.");
|
|
|
- } else if (servers.size() % 2 == 0) {
|
|
|
+ } else if (numParticipators % 2 == 0) {
|
|
|
LOG.warn("Non-optimial configuration, consider an odd number of servers.");
|
|
|
}
|
|
|
- if (initLimit == 0) {
|
|
|
- throw new IllegalArgumentException("initLimit is not set");
|
|
|
- }
|
|
|
- if (syncLimit == 0) {
|
|
|
- throw new IllegalArgumentException("syncLimit is not set");
|
|
|
- }
|
|
|
/*
|
|
|
* If using FLE, then every server requires a separate election
|
|
|
* port.
|
|
|
- */
|
|
|
- if (electionAlg != 0) {
|
|
|
- for (QuorumServer s : servers.values()) {
|
|
|
- if (s.electionAddr == null)
|
|
|
- throw new IllegalArgumentException(
|
|
|
- "Missing election port for server: " + s.id);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * Default of quorum config is majority
|
|
|
- */
|
|
|
- if(serverGroup.size() > 0){
|
|
|
- if(servers.size() != serverGroup.size())
|
|
|
- throw new ConfigException("Every server must be in exactly one group");
|
|
|
- /*
|
|
|
- * The deafult weight of a server is 1
|
|
|
- */
|
|
|
- for(QuorumServer s : servers.values()){
|
|
|
- if(!serverWeight.containsKey(s.id))
|
|
|
- serverWeight.put(s.id, (long) 1);
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * Set the quorumVerifier to be QuorumHierarchical
|
|
|
- */
|
|
|
- quorumVerifier = new QuorumHierarchical(numGroups,
|
|
|
- serverWeight, serverGroup);
|
|
|
- } else {
|
|
|
- /*
|
|
|
- * The default QuorumVerifier is QuorumMaj
|
|
|
- */
|
|
|
-
|
|
|
- LOG.info("Defaulting to majority quorums");
|
|
|
- quorumVerifier = new QuorumMaj(servers.size());
|
|
|
- }
|
|
|
-
|
|
|
- // Now add observers to servers, once the quorums have been
|
|
|
- // figured out
|
|
|
- servers.putAll(observers);
|
|
|
+ */
|
|
|
+ if (eAlg != 0) {
|
|
|
+ for (QuorumServer s : quorumVerifier.getVotingMembers().values()) {
|
|
|
+ if (s.electionAddr == null)
|
|
|
+ throw new IllegalArgumentException(
|
|
|
+ "Missing election port for server: " + s.id);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
|
|
|
+ public void checkValidity() throws IOException, ConfigException{
|
|
|
+
|
|
|
+ if (quorumVerifier.getVotingMembers().size() > 1) {
|
|
|
+ if (initLimit == 0) {
|
|
|
+ throw new IllegalArgumentException("initLimit is not set");
|
|
|
+ }
|
|
|
+ if (syncLimit == 0) {
|
|
|
+ throw new IllegalArgumentException("syncLimit is not set");
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
File myIdFile = new File(dataDir, "myid");
|
|
|
if (!myIdFile.exists()) {
|
|
|
throw new IllegalArgumentException(myIdFile.toString()
|
|
@@ -359,8 +438,18 @@ public class QuorumPeerConfig {
|
|
|
+ " is not a number");
|
|
|
}
|
|
|
|
|
|
+ QuorumServer qs = quorumVerifier.getAllMembers().get(serverId);
|
|
|
+ if (clientPortAddress!=null && qs!=null && qs.clientAddr!=null){
|
|
|
+ if ((!clientPortAddress.getAddress().isAnyLocalAddress()
|
|
|
+ && !clientPortAddress.equals(qs.clientAddr)) ||
|
|
|
+ (clientPortAddress.getAddress().isAnyLocalAddress()
|
|
|
+ && clientPortAddress.getPort()!=qs.clientAddr.getPort()))
|
|
|
+ throw new ConfigException("client address for this server (id = " + serverId + ") in static config file is " + clientPortAddress + " is different from client address found in dynamic file: " + qs.clientAddr);
|
|
|
+ }
|
|
|
+ if (qs!=null) clientPortAddress = qs.clientAddr;
|
|
|
+
|
|
|
// Warn about inconsistent peer type
|
|
|
- LearnerType roleByServersList = observers.containsKey(serverId) ? LearnerType.OBSERVER
|
|
|
+ LearnerType roleByServersList = quorumVerifier.getObservingMembers().containsKey(serverId) ? LearnerType.OBSERVER
|
|
|
: LearnerType.PARTICIPANT;
|
|
|
if (roleByServersList != peerType) {
|
|
|
LOG.warn("Peer type from servers list (" + roleByServersList
|
|
@@ -369,9 +458,11 @@ public class QuorumPeerConfig {
|
|
|
|
|
|
peerType = roleByServersList;
|
|
|
}
|
|
|
- }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
public InetSocketAddress getClientPortAddress() { return clientPortAddress; }
|
|
|
public File getDataDir() { return dataDir; }
|
|
|
public File getDataLogDir() { return dataLogDir; }
|
|
@@ -397,15 +488,24 @@ public class QuorumPeerConfig {
|
|
|
return quorumVerifier;
|
|
|
}
|
|
|
|
|
|
- public Map<Long,QuorumServer> getServers() {
|
|
|
- return Collections.unmodifiableMap(servers);
|
|
|
- }
|
|
|
-
|
|
|
public long getServerId() { return serverId; }
|
|
|
|
|
|
- public boolean isDistributed() { return servers.size() > 1; }
|
|
|
+ public boolean isDistributed() { return (quorumVerifier!=null && quorumVerifier.getVotingMembers().size() > 1); }
|
|
|
|
|
|
public LearnerType getPeerType() {
|
|
|
return peerType;
|
|
|
}
|
|
|
+
|
|
|
+ public String getDynamicConfigFilename() {
|
|
|
+ return dynamicConfigFileStr;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getConfigFilename(){
|
|
|
+ return configFileStr;
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean getConfigBackwardCompatibility(){
|
|
|
+ return configBackwardCompatibilityMode;
|
|
|
+ }
|
|
|
+
|
|
|
}
|