package edu.uw.zookeeper; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.google.common.base.CaseFormat; import com.google.common.base.Joiner; import com.google.common.base.Objects; public abstract class Cfg { public static Properties load(String path) throws IOException { Properties cfg = new Properties(); load(cfg, path); String dynamicPath = Key.DYNAMIC_CONFIG_FILE.getValue(cfg); if (dynamicPath != null) { Properties dynamicCfg = new Properties(cfg); return load(dynamicCfg, dynamicPath); } else { return cfg; } } public static Properties load(Properties properties, String path) throws IOException { FileInputStream file = new FileInputStream(new File(path)); try { properties.load(file); return properties; } finally { file.close(); } } public enum Key { CLIENT_PORT, CLIENT_PORT_ADDRESS, DYNAMIC_CONFIG_FILE; public String key() { return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, name()); } public String getValue(Properties properties) { return properties.getProperty(key()); } public boolean hasValue(Properties properties) { return getValue(properties) != null; } } public static final class Server { public static final String SERVER = "server"; public static enum Group { ID { @Override public String expr() { return "\\d+"; } }; public abstract String expr(); public String key() { return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, name()); } public String value(Matcher m) { return m.group(key()); } @Override public String toString() { return "(?<" + key() +">" + expr() + ")"; } } public static final Pattern PATTERN = Pattern.compile(SERVER + "\\." + Group.ID.toString()); public static final String FORMAT = SERVER + ".%d"; public static Matcher matcher(String input) { return PATTERN.matcher(input); } public static Server valueOf(String input) { Matcher m = matcher(input); if (!m.matches()) { throw new IllegalArgumentException(input); } return valueOf(Integer.parseInt(Group.ID.value(m))); } public static Server valueOf(int id) { return new Server(id); } private final int id; protected Server(int id) { this.id = id; } public int getId() { return id; } @Override public String toString() { return String.format(FORMAT, id); } @Override public int hashCode() { return id; } public boolean equals(Object obj) { if (this == obj) { return true; } if (! (obj instanceof Server)) { return false; } Server other = (Server) obj; return id == other.id; } } // note that these patterns are underspecified public static final String PORT_RE = "\\d+"; public static final String ADDRESS_RE = "[^:]+"; // < 3.5.0 public static final class StaticServer { public static enum Group { HOSTNAME { @Override public String expr() { return ADDRESS_RE; } }, LEADER_PORT { @Override public String expr() { return PORT_RE; } }, ELECTION_PORT { @Override public String expr() { return PORT_RE; } }; public abstract String expr(); public String key() { return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, name()); } public String value(Matcher m) { return m.group(key()); } @Override public String toString() { return "(?<" + key() +">" + expr() + ")"; } } public static final String DEFAULT_HOSTNAME = ""; public static final int DEFAULT_ELECTION_PORT = -1; public static final Pattern PATTERN = Pattern.compile( "(?:" + Group.HOSTNAME.toString() + ")?" + ":"+ Group.LEADER_PORT.toString() + "(?::" + Group.ELECTION_PORT.toString() + ")?"); public static final String FORMAT = "%s:%d:%d"; public static final String FORMAT_NO_HOSTNAME = ":%d:%d"; public static final String FORMAT_NO_ELECTION_PORT = "%s:%d"; public static final String FORMAT_MIN = ":%d"; public static Matcher matcher(String input) { return PATTERN.matcher(input); } public static StaticServer valueOf(String input) { Matcher m = matcher(input); if (!m.matches()) { throw new IllegalArgumentException(input); } return valueOf(m); } public static StaticServer valueOf(Matcher m) { String hostname = Group.HOSTNAME.value(m); if (hostname == null) { hostname = DEFAULT_HOSTNAME; } int leaderPort = Integer.parseInt(Group.LEADER_PORT.value(m)); String electionPort = Group.ELECTION_PORT.value(m); if (electionPort == null) { return valueOf(hostname, leaderPort); } else { return valueOf(hostname, leaderPort, Integer.parseInt(electionPort)); } } public static StaticServer valueOf(String hostname, int leaderPort, int electionPort) { return new StaticServer( hostname, leaderPort, electionPort); } public static StaticServer valueOf(String hostname, int leaderPort) { return valueOf( hostname, leaderPort, DEFAULT_ELECTION_PORT); } public static StaticServer valueOf( int leaderPort, int electionPort) { return valueOf( DEFAULT_HOSTNAME, leaderPort, electionPort); } public static StaticServer valueOf( int leaderPort) { return valueOf( DEFAULT_HOSTNAME, leaderPort, DEFAULT_ELECTION_PORT); } public static String toString(String hostname, int leaderPort, int electionPort) { return String.format(FORMAT, hostname, leaderPort, electionPort); } public static String toString(String hostname, int leaderPort) { return String.format(FORMAT_NO_ELECTION_PORT, hostname, leaderPort); } public static String toString( int leaderPort, int electionPort) { return String.format(FORMAT_NO_HOSTNAME, leaderPort, electionPort); } public static String toString( int leaderPort) { return String.format(FORMAT_MIN, leaderPort); } private final String hostname; private final int leaderPort; private final int electionPort; protected StaticServer(String hostname, int leaderPort, int electionPort) { this.hostname = hostname; this.leaderPort = leaderPort; this.electionPort = electionPort; } public String getHostname() { return hostname; } public int getLeaderPort() { return leaderPort; } public int getElectionPort() { return electionPort; } @Override public String toString() { if (hostname.equals(DEFAULT_HOSTNAME)) { if (electionPort == DEFAULT_ELECTION_PORT) { return toString( leaderPort); } else { return toString( leaderPort, electionPort); } } else { if (electionPort == DEFAULT_ELECTION_PORT) { return toString( hostname, leaderPort); } else { return toString( hostname, leaderPort, electionPort); } } } @Override public int hashCode() { return Objects.hashCode(hostname); } public boolean equals(Object obj) { if (this == obj) { return true; } if (! (obj instanceof StaticServer)) { return false; } StaticServer other = (StaticServer) obj; return Objects.equal(hostname, other.hostname) && (leaderPort == other.leaderPort) && (electionPort == other.electionPort); } } // >= 3.5.0 public static final class DynamicServer { public static enum Role { PARTICIPANT, OBSERVER; public static Role fromString(String input) { for (Role v: values()) { if (input.equals(v.toString())) { return v; } } throw new IllegalArgumentException(input); } @Override public String toString() { return name().toLowerCase(); } } public static enum Group { ADDRESS { @Override public String expr() { return ADDRESS_RE; } }, LEADER_PORT { @Override public String expr() { return PORT_RE; } }, ELECTION_PORT { @Override public String expr() { return PORT_RE; } }, ROLE { @Override public String expr() { return Joiner.on('|').join(Role.values()); } }, CLIENT_ADDRESS { @Override public String expr() { return ADDRESS_RE; } }, CLIENT_PORT { @Override public String expr() { return PORT_RE; } }; public abstract String expr(); public String key() { return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, name()); } public String value(Matcher m) { return m.group(key()); } @Override public String toString() { return "(?<" + key() +">" + expr() + ")"; } } public static final Role DEFAULT_ROLE = Role.PARTICIPANT; public static final String DEFAULT_CLIENT_ADDRESS = "0.0.0.0"; public static final Pattern PATTERN = Pattern.compile( Group.ADDRESS.toString() + ":"+ Group.LEADER_PORT.toString() + ":" + Group.ELECTION_PORT.toString() + "(?::" + Group.ROLE.toString() + ")?" + ";" + "(?:" + Group.CLIENT_ADDRESS.toString() + ":)?" + Group.CLIENT_PORT.toString()); public static final String FORMAT = "%s:%d:%d:%s;%s:%d"; public static final String FORMAT_NO_ROLE = "%s:%d:%d;%s:%d"; public static final String FORMAT_NO_ADDRESS = "%s:%d:%d:%s;%d"; public static final String FORMAT_MIN = "%s:%d:%d;%d"; public static Matcher matcher(String input) { return PATTERN.matcher(input); } public static DynamicServer valueOf(String input) { Matcher m = matcher(input); if (!m.matches()) { throw new IllegalArgumentException(input); } return valueOf(m); } public static DynamicServer valueOf(Matcher m) { String address = Group.ADDRESS.value(m); int leaderPort = Integer.parseInt(Group.LEADER_PORT.value(m)); int electionPort = Integer.parseInt(Group.ELECTION_PORT.value(m)); String role = Group.ROLE.value(m); String clientAddress = Group.CLIENT_ADDRESS.value(m); if (clientAddress == null) { clientAddress = DEFAULT_CLIENT_ADDRESS; } int clientPort = Integer.parseInt(Group.CLIENT_PORT.value(m)); if (role == null) { return valueOf(address, leaderPort, electionPort, clientAddress, clientPort); } else { return valueOf(address, leaderPort, electionPort, Role.fromString(role), clientAddress, clientPort); } } public static DynamicServer valueOf(String address, int leaderPort, int electionPort, Role role, String clientAddress, int clientPort) { return new DynamicServer( address, leaderPort, electionPort, role, clientAddress, clientPort); } public static DynamicServer valueOf(String address, int leaderPort, int electionPort, String clientAddress, int clientPort) { return valueOf( address, leaderPort, electionPort, DEFAULT_ROLE, clientAddress, clientPort); } public static DynamicServer valueOf(String address, int leaderPort, int electionPort, Role role, int clientPort) { return valueOf( address, leaderPort, electionPort, role, DEFAULT_CLIENT_ADDRESS, clientPort); } public static DynamicServer valueOf(String address, int leaderPort, int electionPort, int clientPort) { return valueOf( address, leaderPort, electionPort, DEFAULT_ROLE, DEFAULT_CLIENT_ADDRESS, clientPort); } public static String toString(String address, int leaderPort, int electionPort, Role role, String clientAddress, int clientPort) { return String.format(FORMAT, address, leaderPort, electionPort, role, clientAddress, clientPort); } public static String toString(String address, int leaderPort, int electionPort, String clientAddress, int clientPort) { return String.format(FORMAT_NO_ROLE, address, leaderPort, electionPort, clientAddress, clientPort); } public static String toString(String address, int leaderPort, int electionPort, Role role, int clientPort) { return String.format(FORMAT_NO_ADDRESS, address, leaderPort, electionPort, role, clientPort); } public static String toString(String address, int leaderPort, int electionPort, int clientPort) { return String.format(FORMAT_MIN, address, leaderPort, electionPort, clientPort); } private final String address; private final int leaderPort; private final int electionPort; private final Role role; private final String clientAddress; private final int clientPort; protected DynamicServer(String address, int leaderPort, int electionPort, Role role, String clientAddress, int clientPort) { this.address = address; this.leaderPort = leaderPort; this.electionPort = electionPort; this.role = role; this.clientAddress = clientAddress; this.clientPort = clientPort; } public String getAddress() { return address; } public int getLeaderPort() { return leaderPort; } public int getElectionPort() { return electionPort; } public Role getRole() { return role; } public String getClientAddress() { return clientAddress; } public int getClientPort() { return clientPort; } @Override public String toString() { if (clientAddress.equals(DEFAULT_CLIENT_ADDRESS)) { return toString( address, leaderPort, electionPort, role, clientPort); } else { return toString( address, leaderPort, electionPort, role, clientAddress, clientPort); } } @Override public int hashCode() { return Objects.hashCode(address, clientPort); } public boolean equals(Object obj) { if (this == obj) { return true; } if (! (obj instanceof DynamicServer)) { return false; } DynamicServer other = (DynamicServer) obj; return Objects.equal(address, other.address) && (leaderPort == other.leaderPort) && (electionPort == other.electionPort) && Objects.equal(role, other.role) && Objects.equal(clientAddress, other.clientAddress) && (clientPort == other.clientPort); } } }