package lsr.common;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Holds the configuration of the system. It consists of
* <ul>
* <li>the list of replicas {@link PID}
* <li>an optional list of configuration properties.
* </ul>
*
* <p>
* The configuration file is a standard java properties file (readable with the
* class {@link Properties}). The PIDs of the replicas must be specified as in
* the example below:
*
* <pre>
* # Mandatory properties
* process.0 = <host0>:<clientPort0>:<replicaPort0>
* process.1 = <host1>:<clientPort1>:<replicaPort1>
* process.2 = <host2>:<clientPort2>:<replicaPort2>
*
* # Optional properties
* propkey = propvalue
* ...
* </pre>
*
* It's possible to specify any number of replicas, as long as they are numbered
* sequentially starting at 0, in the form of <code>process.x</code>.
*
* @author Nuno Santos (LSR)
*
*/
public final class Configuration {
/*---------------------------------------------
* The following properties are compile time constants.
*---------------------------------------------*/
public static final int UDP_RECEIVE_BUFFER_SIZE = 64 * 1024;
public static final int UDP_SEND_BUFFER_SIZE = 64 * 1024;
/** for re-sending catch-up query we use a separate, self-adjusting timeout */
public static final long CATCHUP_MIN_RESEND_TIMEOUT = 50;
private final List<PID> processes;
private final Properties configuration = new Properties();
/**
* Loads the configuration from the default file
* <code>paxos.properties</code>
*
* @throws IOException
*/
public Configuration() throws IOException {
this("paxos.properties");
}
/**
* Loads the configuration from the given file.
*
* @param confFile
* @throws IOException
*/
public Configuration(String confFile) throws IOException {
// Load property from file there is one
FileInputStream fis = new FileInputStream(confFile);
configuration.load(fis);
fis.close();
logger.warn("Configuration loaded from file: {}", confFile);
this.processes = Collections.unmodifiableList(loadProcessList());
}
/**
* Creates a configuration with the process list, and an empty set of
* optional properties.
*
* @param processes
*/
public Configuration(List<PID> processes) {
this.processes = processes;
}
public int getN() {
return processes.size();
}
public List<PID> getProcesses() {
return processes;
}
public PID getProcess(int id) {
return processes.get(id);
}
public boolean containsKey(String key) {
return configuration.containsKey(key);
}
/**
* Returns a given property, converting the value to an integer.
*
* @param key - the key identifying the property
* @param defValue - the default value to use in case the key is not found.
* @return the value of key property or defValue if key not found
*/
public int getIntProperty(String key, int defValue) {
String str = configuration.getProperty(key);
if (str == null) {
logger.debug("Property not found: {}. Using default value: {}", key, defValue);
return defValue;
}
return Integer.parseInt(str);
}
/**
* Returns a given property, converting the value to a boolean.
*
* @param key - the key identifying the property
* @param defValue - the default value to use in case the key is not found.
* @return the value of key property or defValue if key not found
*/
public boolean getBooleanProperty(String key, boolean defValue) {
String str = configuration.getProperty(key);
if (str == null) {
logger.debug("Property not found: {}. Using default value: {}", key, defValue);
return defValue;
}
return Boolean.parseBoolean(str);
}
/**
*
* @param key - the key identifying the property
* @param defValue - the default value to use in case the key is not found.
*
* @return the value of key property or defValue if key not found
*/
public String getProperty(String key, String defValue) {
String str = configuration.getProperty(key);
if (str == null) {
logger.debug("Property not found: {}. Using default value: {}", key, defValue);
return defValue;
}
return str;
}
private List<PID> loadProcessList() {
List<PID> processes = new ArrayList<PID>();
int i = 0;
while (true) {
String line = configuration.getProperty("process." + i);
if (line == null) {
break;
}
StringTokenizer st = new StringTokenizer(line, ":");
PID pid = new PID(i, st.nextToken(), Integer.parseInt(st.nextToken()),
Integer.parseInt(st.nextToken()));
processes.add(pid);
i++;
}
logger.warn("Processes: {}", processes);
return processes;
}
public String toString() {
StringBuilder sb = new StringBuilder(256);
sb.append("Processes:\n");
for (PID p : processes) {
sb.append(" ").append(p).append("\n");
}
sb.append("Properties:\n");
Object[] keys = configuration.keySet().toArray();
Arrays.sort(keys);
for (Object key : keys) {
sb.append(" ").append(key).append("=").append(configuration.get(key)).append("\n");
}
// Remove the trailing '\n'
return sb.substring(0, sb.length() - 1);
}
public double getDoubleProperty(String key, double defultValue) {
String str = configuration.getProperty(key);
if (str == null) {
logger.debug("Property not found: {}. Using default value: {}", key, defultValue);
return defultValue;
}
return Double.parseDouble(str);
}
public long getLongProperty(String key, long defultValue) {
String str = configuration.getProperty(key);
if (str == null) {
logger.debug("Property not found: {}. Using default value: {}", key, defultValue);
return defultValue;
}
return Long.parseLong(str);
}
private final static Logger logger = LoggerFactory.getLogger(Configuration.class);
}