/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.pieShare.pieShareApp.service.networkService;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import org.pieShare.pieTools.pieUtilities.service.pieLogger.PieLogger;
/**
*
* @author Svetoslav
*/
public class NetworkService implements INetworkService {
private int minPort = 1024;
private int maxPort = 49151;
private InetAddress address = null;
private String nicDisplayName;
private ConcurrentHashMap<Integer, ServerSocket> reservedSockets;
public NetworkService() {
this.reservedSockets = new ConcurrentHashMap<>();
}
@Override
public void setNicDisplayName(String nicDisplayName) {
this.nicDisplayName = nicDisplayName;
System.setProperty("jgroups.bind_addr", this.getLocalHost(true).getHostAddress());
}
@Override
public int getAvailablePort() {
return this.getAvailablePortStartingFrom(this.minPort);
}
@Override
public synchronized int getAvailablePortStartingFrom(int port) {
for (int p = port; p <= this.maxPort; p++) {
PieLogger.trace(this.getClass(), "Checking port {}!", p);
if (this.checkPort(p)) {
PieLogger.trace(this.getClass(), "Found port {}!", p);
return p;
}
}
//todo: throw exception
return -1;
}
private synchronized boolean checkPort(int p) {
try {
ServerSocket tmpSocket = new ServerSocket();
tmpSocket.setReuseAddress(true);
tmpSocket.bind(new InetSocketAddress(p));
tmpSocket.close();
} catch (IOException ex) {
PieLogger.info(this.getClass(), "Port in use: {}", p, ex);
return false;
}
try {
Socket s = new Socket("localhost", p);
PieLogger.info(this.getClass(), "Port in use: {}", p);
s.close();
} catch (IOException ex) {
return true;
}
return false;
}
@Override
public synchronized InetAddress getLocalHost() {
return this.getLocalHost(false);
}
private boolean useFixedNic() {
return this.nicDisplayName != null && !this.nicDisplayName.isEmpty();
}
private boolean checkFixedNic(NetworkInterface nic) {
if (nic.getDisplayName().equals(this.nicDisplayName)) {
return true;
}
return false;
}
private synchronized InetAddress checkReachableAddress(InetAddress ad) {
if (this.useFixedNic()) {
return ad;
} else {
//test internet connection
try (SocketChannel socket = SocketChannel.open()) {
socket.socket().setSoTimeout(5000);
int freePort = this.getAvailablePort();
socket.bind(new InetSocketAddress(ad, freePort));
//this has to become way better
//socket.connect(new InetSocketAddress("google.com", 80));
//if everything passes the InetAddress should be okay.
socket.close();
this.address = ad;
PieLogger.info(this.getClass(), "Found internet!");
return this.address;
} catch (IOException ex) {
PieLogger.info(this.getClass(), "No internet here!", ex);
}
}
return null;
}
private synchronized List<InetAddress> checkAddresses(NetworkInterface nic) {
List<InetAddress> possibleAds = new ArrayList<>();
Enumeration<InetAddress> ads = nic.getInetAddresses();
while (ads.hasMoreElements()) {
InetAddress ad = ads.nextElement();
try {
if (ad instanceof Inet4Address) {
if (ad.isReachable(5000)) {
if (this.checkReachableAddress(ad) == ad) {
possibleAds = new ArrayList<>();
possibleAds.add(ad);
return possibleAds;
}
possibleAds.add(ad);
}
}
} catch (IOException ex) {
PieLogger.info(this.getClass(), "Well looks bad for internet!", ex);
}
}
return possibleAds;
}
@Override
public synchronized InetAddress getLocalHost(boolean invalidate) {
//todo-sv: try to get local host out of cloud service
if (invalidate) {
this.address = null;
}
if (this.address != null) {
return this.address;
}
List<InetAddress> possibleAds = new ArrayList<>();
try {
Enumeration<NetworkInterface> nics = NetworkInterface.getNetworkInterfaces();
while (nics.hasMoreElements()) {
NetworkInterface nic = nics.nextElement();
if (this.useFixedNic()) {
if (this.checkFixedNic(nic)) {
List<InetAddress> ads = this.checkAddresses(nic);
if (!ads.isEmpty()) {
return ads.get(0);
}
}
} else {
if (!nic.isLoopback() && !nic.isVirtual() && nic.isUp()) {
List<InetAddress> ads = this.checkAddresses(nic);
if (ads.size() == 1) {
return ads.get(0);
} else {
possibleAds.addAll(ads);
}
}
}
}
} catch (SocketException ex) {
PieLogger.info(this.getClass(), "God damit! Give me internet", ex);
}
if (possibleAds.size() == 0) {
//todo: throw exception
return InetAddress.getLoopbackAddress();
}
//todo-sv: checkout 0.0.0.0 adress for adressing all adapters
this.address = possibleAds.get(0);
return this.address;
}
@Override
public synchronized int getNumberOfAvailablePorts(int firstPort, int lastPort) {
int count = 0;
for (int i = firstPort; i <= lastPort; i++) {
if (this.checkPort(i)) {
count++;
}
}
return count;
}
@Override
public synchronized int reserveAvailablePortStartingFrom(int port) throws IOException {
port = this.getAvailablePortStartingFrom(port);
ServerSocket socket = new ServerSocket();
socket.setReuseAddress(true);
socket.bind(new InetSocketAddress(port));
this.reservedSockets.put(port, socket);
return port;
}
@Override
public synchronized void freeReservedPort(int port) throws IOException {
ServerSocket socket = this.reservedSockets.get(port);
socket.close();
}
}