package de.tutorials;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.Executors;
import static java.util.concurrent.TimeUnit.SECONDS;
public class NetworkChangeDetectorExample {
public static void main(String[] args) {
Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(new NetworkChangeDetector(), 1, 5, SECONDS);
}
static class NetworkChangeDetector implements Runnable {
private volatile Map<String, InterfaceEntry> knownInterfaces;
public void run() {
try {
detectNetworkChange();
} catch (Exception ex) {
ex.printStackTrace();
}
}
private void detectNetworkChange() {
Map<String, InterfaceEntry> detectedNetworkInterfaces = detectAvailableNetworkInterfaces();
if (knownInterfaces == null) {
knownInterfaces = detectedNetworkInterfaces;
//TODO notify listeners about initial discovered interfaces
} else {
NetworkChanges networkChanges = computeNetworkChanges(knownInterfaces, detectedNetworkInterfaces);
if (!networkChanges.isEmpty()) {
generateNetworkChangedEvents(networkChanges, detectedNetworkInterfaces);
knownInterfaces.clear();
knownInterfaces = detectedNetworkInterfaces;
}
}
}
protected void generateNetworkChangedEvents(NetworkChanges networkStatus, Map<String, InterfaceEntry> detectedNetworkInterfaces) {
//TODO notify listeners about network changes
}
protected Map<String, InterfaceEntry> detectAvailableNetworkInterfaces() {
Map<String, InterfaceEntry> detectedInterfaces = new TreeMap<String, InterfaceEntry>();
try {
for (NetworkInterface iface : Collections.list(NetworkInterface.getNetworkInterfaces())) {
detectedInterfaces.put(iface.getName(), new InterfaceEntry(iface));
}
} catch (SocketException e) {
e.printStackTrace();
}
return detectedInterfaces;
}
public NetworkChanges computeNetworkChanges(Map<String, InterfaceEntry> knownInterfaces, Map<String, InterfaceEntry> detectedNetworkInterfaces) {
NetworkChanges networkStatus = new NetworkChanges();
Set<String> newNetworkInterfaceNames = new TreeSet<String>(detectedNetworkInterfaces.keySet());
newNetworkInterfaceNames.removeAll(knownInterfaces.keySet());
Set<String> removedNetworkInterfaceNames = new TreeSet<String>(knownInterfaces.keySet());
removedNetworkInterfaceNames.removeAll(detectedNetworkInterfaces.keySet());
removedNetworkInterfaceNames.removeAll(newNetworkInterfaceNames);
Set<String> potentiallyChangedInterfaceNames = new TreeSet<String>(knownInterfaces.keySet());
potentiallyChangedInterfaceNames.addAll(detectedNetworkInterfaces.keySet());
potentiallyChangedInterfaceNames.removeAll(newNetworkInterfaceNames);
potentiallyChangedInterfaceNames.removeAll(removedNetworkInterfaceNames);
if (!removedNetworkInterfaceNames.isEmpty()) {
System.out.println("Removed network interfaces: " + removedNetworkInterfaceNames);
networkStatus.getRemovedNetworkInterfaces().addAll(removedNetworkInterfaceNames);
}
if (!newNetworkInterfaceNames.isEmpty()) {
System.out.println("new network interfaces: " + newNetworkInterfaceNames);
networkStatus.getNewNetworkInterfaces().addAll(newNetworkInterfaceNames);
}
for (String ifaceName : potentiallyChangedInterfaceNames) {
InterfaceEntry oldInterfaceEntry = knownInterfaces.get(ifaceName);
InterfaceEntry newInterfaceEntry = detectedNetworkInterfaces.get(ifaceName);
if (networkInterfaceChanged(oldInterfaceEntry, newInterfaceEntry)) {
Pair<InterfaceEntry, InterfaceEntry> oldNewEntry = new Pair<InterfaceEntry, InterfaceEntry>(oldInterfaceEntry, newInterfaceEntry);
networkStatus.getChangedNetworkInterfaces().put(ifaceName, oldNewEntry);
System.out.println("Network interface changed: Old: " + oldInterfaceEntry + " New: " + newInterfaceEntry);
}
}
return networkStatus;
}
public boolean networkInterfaceChanged(InterfaceEntry oldInterface, InterfaceEntry newInterface) {
InetAddress oldAddress = oldInterface.getPrimaryAddress();
InetAddress newAddress = newInterface.getPrimaryAddress();
if(oldAddress == newAddress){
return false;
}
if((oldAddress != null && newAddress == null)|| (newAddress != null && oldAddress == null)){
return true;
}
return !oldAddress.equals(newAddress);
}
}
static class NetworkChanges {
private final Set<String> newNetworkInterfaces = new TreeSet<String>();
private final Set<String> removedNetworkInterfaces = new TreeSet<String>();
private final Map<String, Pair<InterfaceEntry, InterfaceEntry>> changedNetworkInterfaces = new TreeMap<String, NetworkChangeDetectorExample.Pair<InterfaceEntry, InterfaceEntry>>();
public boolean isEmpty() {
return newNetworkInterfaces.isEmpty() && newNetworkInterfaces.isEmpty() && changedNetworkInterfaces.isEmpty();
}
public Map<String, Pair<InterfaceEntry, InterfaceEntry>> getChangedNetworkInterfaces() {
return changedNetworkInterfaces;
}
public Set<String> getNewNetworkInterfaces() {
return newNetworkInterfaces;
}
public Set<String> getRemovedNetworkInterfaces() {
return removedNetworkInterfaces;
}
}
static class InterfaceEntry {
final String name;
final InetAddress primaryAddress;
final NetworkInterface networkInterface;
public InterfaceEntry(NetworkInterface networkInterface) {
this.name = networkInterface.getName();
this.primaryAddress = getPrimaryIPV4Address(networkInterface); //TODO add support for IPV6
this.networkInterface = networkInterface;
}
protected InetAddress getPrimaryIPV4Address(NetworkInterface networkInterface) {
List<InetAddress> inetAddresses = Collections.list(networkInterface.getInetAddresses());
Inet4Address inet4address = null;
for (InetAddress adr : inetAddresses) {
if (adr instanceof Inet4Address) {
inet4address = (Inet4Address) adr;
break;
}
}
return inet4address;
}
public String getName() {
return name;
}
public InetAddress getPrimaryAddress() {
return primaryAddress;
}
public NetworkInterface getNetworkInterface() {
return networkInterface;
}
@Override
public String toString() {
return "NetInterfaceEntry [name=" + name + ", primaryAddress=" + primaryAddress + "]";
}
}
static class Pair<K, V> {
private final K key;
private final V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
}
}