package ch.usi.da.paxos;
/*
* Copyright (c) 2013 Università della Svizzera italiana (USI)
*
* This file is part of URingPaxos.
*
* URingPaxos is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* URingPaxos 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with URingPaxos. If not, see <http://www.gnu.org/licenses/>.
*/
import java.beans.ExceptionListener;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.KeeperException.NodeExistsException;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.ACL;
import ch.usi.da.paxos.api.PaxosRole;
import ch.usi.da.paxos.ring.RingDescription;
public class Util {
/**
* Parse argument describing the ring into a List of RingDescription .
* The argument has the format:
* ringid:PAL (P/A/L are the roles in the ring).
*
* More than one ring can be specified:
* ring1id:PAL;ring2id:PAL
*
* @param ringsArg String of ring properties to parse
* @return list of ring descriptions that can be used to initialize a Node
*/
public static List<RingDescription> parseRingsArgument(String ringsArg) {
// process rings
List<RingDescription> rings = new ArrayList<RingDescription>();
for (String r : ringsArg.split(";")) {
int ringID = Integer.parseInt(r.split(":")[0]);
String roles = r.split(":")[1];
rings.add(new RingDescription(ringID, getPaxosRoles(roles)));
}
return rings;
}
private static List<PaxosRole> getPaxosRoles(String rs) {
List<PaxosRole> roles = new ArrayList<PaxosRole>();
if (rs.toLowerCase().contains("p")) {
roles.add(PaxosRole.Proposer);
}
if (rs.toLowerCase().contains("a")) {
roles.add(PaxosRole.Acceptor);
}
if (rs.toLowerCase().contains("l")) {
roles.add(PaxosRole.Learner);
}
return roles;
}
/**
* Get the host IP address
*
* Use env(IFACE) to select an interface or
* env(IP) to select a specific address
*
* to prefer IPv6 use: java.net.preferIPv6Stack=true
*
* @return return the host IP address or 127.0.0.1 (::1)
*/
public static InetAddress getHostAddress(){
boolean ipv6 = false;
String pv4 = System.getProperty("java.net.preferIPv4Stack");
String pv6 = System.getProperty("java.net.preferIPv6Stack");
if(pv4 != null && pv4.equals("false")){
ipv6 = true;
}
if(pv6 != null && pv6.equals("true")){
ipv6 = true;
}
try {
String iface = System.getenv("IFACE");
String public_ip = System.getenv("IP");
if(public_ip != null){
return InetAddress.getByName(public_ip);
}
Enumeration<NetworkInterface> ni = NetworkInterface.getNetworkInterfaces();
while (ni.hasMoreElements()){
NetworkInterface n = ni.nextElement();
if(iface == null || n.getDisplayName().equals(iface)){
Enumeration<InetAddress> ia = n.getInetAddresses();
while(ia.hasMoreElements()){
InetAddress addr = ia.nextElement();
if(!(addr.isLinkLocalAddress() || addr.isLoopbackAddress() || addr.toString().contains("172.17"))){
if(addr instanceof Inet6Address && ipv6){
return addr;
}else if (addr instanceof Inet4Address && !ipv6){
return addr;
}
}
}
}
}
return InetAddress.getLoopbackAddress();
} catch (SocketException | UnknownHostException e) {
return InetAddress.getLoopbackAddress();
}
}
/**
* This method fixes the race condition faced by the client when checking if
* a znode already exists before creating one. It basically ignores the
* <b>NodeExistsException</b>, leaving the existing znode untouched.
*
* @param path
* the path for the node
* @param data
* the initial data for the node
* @param acl
* the acl for the node
* @param createMode
* specifying whether the node to be created is ephemeral
* and/or sequential
* @param zooClient
* a ZooKeeper client object
* @return the actual path of the created node
* @throws KeeperException if the server returns a non-zero error code
* @throws KeeperException.InvalidACLException if the ACL is invalid, null, or empty
* @throws InterruptedException if the transaction is interrupted
* @throws IllegalArgumentException if an invalid path is specified
*/
public static String checkThenCreateZooNode(final String path, byte data[], List<ACL> acl, CreateMode createMode,
ZooKeeper zooClient) throws KeeperException, InterruptedException {
return checkThenCreateZooNode(path, data, acl, createMode, zooClient, null);
}
/**
* This method fixes the race condition faced by the client when checking if
* a znode already exists before creating one. It basically handles the
* <b>NodeExistsException</b>, if an exception handler was given, or ignore
* it, otherwise.
*
* @param path
* the path for the node
* @param data
* the initial data for the node
* @param acl
* the acl for the node
* @param createMode
* specifying whether the node to be created is ephemeral
* and/or sequential
* @param zooClient
* a ZooKeeper client object
* @param exceptionHandler
* the object that will handle the NodeExistsExcpetion; if this
* is null, the exception is ignored and the execution continues
* without replacing the already existing znode
* @return the actual path of the created node
* @throws KeeperException if the server returns a non-zero error code
* @throws KeeperException.InvalidACLException if the ACL is invalid, null, or empty
* @throws InterruptedException if the transaction is interrupted
* @throws IllegalArgumentException if an invalid path is specified
*/
public static String checkThenCreateZooNode(final String path, byte data[], List<ACL> acl, CreateMode createMode,
ZooKeeper zooClient, ExceptionListener exceptionHandler) throws KeeperException, InterruptedException {
String createReturn = null;
try {
createReturn = zooClient.create(path, data, acl, createMode);
} catch (NodeExistsException e) {
if (exceptionHandler != null){
exceptionHandler.exceptionThrown(e);
}
}
return createReturn;
}
/**
* @param value
* @return a byte[]
*/
public static synchronized final byte[] intToByte(int value) {
return new byte[] {
(byte)(value >>> 24),
(byte)(value >>> 16),
(byte)(value >>> 8),
(byte)value};
}
/**
* @param b
* @return the int
*/
public static synchronized final int byteToInt(byte [] b) {
return (b[0] << 24) + ((b[1] & 0xFF) << 16) + ((b[2] & 0xFF) << 8) + (b[3] & 0xFF);
}
/**
* @param value
* @return a byte[]
*/
public static synchronized final byte[] longToByte(long value) {
return new byte[] {
(byte)(value >>> 56),
(byte)(value >>> 48),
(byte)(value >>> 40),
(byte)(value >>> 32),
(byte)(value >>> 24),
(byte)(value >>> 16),
(byte)(value >>> 8),
(byte)value};
}
/**
* @param b
* @return the int
*/
public static synchronized final long byteToLong(byte [] b) {
return (b[0] << 56) + ((b[1] & 0xFF) << 48) + ((b[2] & 0xFF) << 40) + ((b[3] & 0xFF) << 32) + ((b[4] & 0xFF) << 24) + ((b[5] & 0xFF) << 16) + ((b[6] & 0xFF) << 8) + (b[7] & 0xFF);
}
}