/*
* NetworkUtils.java
*
* Created on Jan 12, 2010, 9:17:04 AM
*
* Description: NetworkUtils utilities.
*
* Copyright (C) Jan 12, 2010 reed.
*
* This program 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.
*
* This program 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 this program;
* if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.texai.util;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.NetworkInterface;
import java.net.Socket;
import java.net.SocketException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import net.jcip.annotations.NotThreadSafe;
import org.apache.log4j.Logger;
/** NetworkUtils utilities.
*
* @author reed
*/
@NotThreadSafe
public final class NetworkUtils {
/** the logger */
private static final Logger LOGGER = Logger.getLogger(NetworkUtils.class);
/** the lower bound of dynamic TCP ports */
static final int LOWER_PORT_BOUND = 49152;
/** the upper bound of dynamic TCP ports */
static final int UPPER_PORT_BOUND = 65535;
/** the server port number file path */
static final String SERVER_PORT_PATH = "data/server-port.txt";
/** the IANA registered service port for Texai: http://www.iana.org/assignments/port-numbers */
public static final int TEXAI_PORT = 5048;
/** the launcher port */
public static final int LAUNCHER_PORT = 5049;
/** the socket connection timeout */
public static final int CONNECTION_TIMEOUT = 10000;
/** Private constructor because this class is never instantiated. */
private NetworkUtils() {
}
/** Returns whether this set of nodes is running as a cloud service.
*
* @return whether this set of nodes is running as a cloud service
*/
public static boolean isCloudService() {
return true;
}
/** Returns whether this set of nodes is running as an Internet-distributed Texai instance.
*
* @return whether this set of nodes is running as an Internet-distributed Texai instance
*/
public static boolean isDistributedTexaiInstance() {
return !isCloudService();
}
/** Gets the host name.
*
* @return the host name
*/
public static String getHostName() {
final String hostName = getLocalHostAddress().getHostName();
if (hostName.endsWith(".local")) {
return hostName.substring(0, hostName.length() - 6);
} else {
return hostName;
}
}
/** Obtains the local host address in situations where it cannot be obtained solely from the InetAddress class.
*
* @return the local host address
*/
public static InetAddress getLocalHostAddress() {
Enumeration<NetworkInterface> networkInterfaces = null;
try {
networkInterfaces = NetworkInterface.getNetworkInterfaces();
} catch (SocketException ex) {
throw new TexaiException(ex);
}
while (networkInterfaces.hasMoreElements()) {
final NetworkInterface networkInterface = networkInterfaces.nextElement();
final Enumeration<InetAddress> address = networkInterface.getInetAddresses();
while (address.hasMoreElements()) {
final InetAddress inetAddress = address.nextElement();
if (!inetAddress.isLoopbackAddress()
&& !inetAddress.getHostAddress().contains(":")) {
return inetAddress;
}
}
}
try {
return InetAddress.getLocalHost();
} catch (UnknownHostException ex) {
throw new TexaiException(ex);
}
}
/** Returns whether the given internet address is reserved for private networks, such as those behind a NAT router.
*
* @param inetAddress the given internet address
* @return whether the given internet address is reserved for private networks
*/
public static boolean isPrivateNetworkAddress(final InetAddress inetAddress) {
//Preconditions
assert inetAddress != null : "inetAddress must not be null";
final String inetAddressString = inetAddress.getHostAddress();
return inetAddressString.startsWith("192.168.")
|| inetAddressString.startsWith("10.")
|| inetAddressString.startsWith("172.");
}
/** Return the MAC address of the current network interface for this computer.
*
* @return the MAC address of the current network interface for this computer
*/
public static List<Byte> getMACAddress() {
final List<Byte> macAddress = new ArrayList<>();
try {
final InetAddress inetAddress = getLocalHostAddress();
// get NetworkInterface for the current host and then read the hardware address.
final NetworkInterface networkInterface = NetworkInterface.getByInetAddress(inetAddress);
if (networkInterface == null) {
throw new TexaiException("Network Interface for the address " + inetAddress + " is not found.");
} else {
final byte[] macAddressBytes = networkInterface.getHardwareAddress();
if (macAddressBytes == null) {
throw new TexaiException("Address doesn't exist or is not accessible.");
// extract each byte of mac address and convert it to hex with the following format 08-00-27-DC-4A-9E.
} else {
final int macAddressBytes_len = macAddressBytes.length;
System.out.println("macAddressBytes_len: " + macAddressBytes_len);
for (int i = 0; i < macAddressBytes_len; i++) {
final byte macAddressByte = macAddressBytes[i];
macAddress.add(macAddressByte);
}
}
}
} catch (SocketException ex) {
throw new TexaiException(ex);
}
return macAddress;
}
/** Returns the string representation of the MAC address of the current network interface for this computer.
*
* @return the string representation of the MAC address of the current network interface for this computer
*/
public static String getMACAddressString() {
final StringBuilder stringBuilder = new StringBuilder();
final List<Byte> macAddress = getMACAddress();
boolean isFirst = true;
for (final Byte macAddressByte : macAddress) {
if (isFirst) {
isFirst = false;
} else {
stringBuilder.append('-');
}
stringBuilder.append(ByteUtils.toHex(macAddressByte));
}
return stringBuilder.toString();
}
/** Returns the SSL server port number, which is allocated from the
* dynamic range: 49152–65535, as described in
* http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers
*
* @return the SSL server port number
*/
public static int getDynamicServerPort() {
final File serverPortFile = new File(SERVER_PORT_PATH);
int serverPort = 0;
if (serverPortFile.exists()) {
// subsequent times use the recorded server port number
try {
try (BufferedReader bufferedReader = new BufferedReader(new FileReader(serverPortFile))) {
final String line = bufferedReader.readLine();
if (line == null) {
throw new TexaiException("missing SSL server port in: " + SERVER_PORT_PATH);
}
serverPort = Integer.parseInt(line.trim());
}
} catch (IOException ex) {
throw new TexaiException(ex);
}
} else {
try {
final File dataDirectory = new File("data");
if (!dataDirectory.exists()) {
LOGGER.info("creating data directory");
final boolean isDirectoryCreated = dataDirectory.mkdir();
if (!isDirectoryCreated) {
throw new TexaiException("cannot create data directory");
}
}
// first time allocate in the dynamic range
serverPort = getRandomDynamicServerPort();
try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(serverPortFile))) {
bufferedWriter.append(String.valueOf(serverPort));
bufferedWriter.newLine();
}
} catch (IOException ex) {
throw new TexaiException(ex);
}
}
return serverPort;
}
/** Returns a dynamic server port number, which is allocated from the
* dynamic range: 49152–65535, as described in
* http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers
*
* @return the SSL server port number
*/
public static int getRandomDynamicServerPort() {
return (int) (LOWER_PORT_BOUND + Math.random() * (UPPER_PORT_BOUND - LOWER_PORT_BOUND));
}
/** Returns whether the given host is accepting connections on the given port.
*
* @param host the given host
* @param port the given port
* @return whether the given host is accepting connections on the given port
*/
public static boolean isHostAvailable(final String host, final int port) {
//Preconditions
assert host != null : "the host must not be null";
assert !host.isEmpty() : "the host must not be empty";
assert port >= 0 && port <= UPPER_PORT_BOUND : "the port be within the range 0 - 65535";
Socket socket = new Socket();
try {
socket.connect(new InetSocketAddress(host, port), CONNECTION_TIMEOUT);
} catch (IOException ex) {
try {
socket.close();
} catch (IOException ex1) {
}
return false;
}
try {
socket.close();
} catch (IOException ex1) {
return false;
}
return true;
}
/** Returns a socket address formed from the given URL string.
*
* @param urlString the given URL string
* @return a socket address formed from the given URL string
*/
public static InetSocketAddress makeInetSocketAddress(final String urlString) {
//Preconditions
assert urlString != null : "the urlString must not be null";
assert !urlString.isEmpty() : "the urlString must not be empty";
final URL url;
try {
url = new URL(urlString);
} catch (MalformedURLException ex) {
throw new TexaiException(ex);
}
return new InetSocketAddress(url.getHost(), url.getPort());
}
}