URingPaxos-master/src/java/ch/usi/da/paxos/Util.java
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); 
	}

}
