package org.dcache.util;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterators;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.List;
/**
* This class provides an unmodifiable, serializable snapshot of a
* {@link NetworkInterface}. It provides much the same functionality
* as NetworkInterface; however, {@link #getParent} and {@link #getSubInterfaces}
* return different types and the public methods will never throw a
* {@code SocketException}.
* <p>
* {@link #getInterfaceAddresses} returns a list of {@link InterfaceAddressView} objects.
*/
public class NetworkInterfaceView implements Serializable {
private static final long serialVersionUID = 5589652882519395824L;
private static final Logger _log = LoggerFactory.getLogger(NetworkInterfaceView.class);
private final String _displayName;
private final byte[] _hardwareAddress;
private final List<InterfaceAddressView> _interfaceAddresses;
private final List<InetAddress> _inetAddresses;
private final int _mtu;
private final String _name;
private final List<NetworkInterfaceView> _children;
private final boolean _isLoopback;
private final boolean _isPointToPoint;
private final boolean _isUp;
private final boolean _isVirtual;
private final boolean _hasMulticastSupport;
private final NetworkInterfaceView _parent;
/**
* Create a {@link NetworkInterfaceView} snapshot of a source
* {@link NetworkInterface} object. {@code source} not be a
* sub-interface; i.e., a call to {@link NetworkInterface#getParent}
* on {@code source} must return {@code null}.
* <p>
* When creating a {@code NetworkInterfaceView} object from a
* {@code NetworkInterface} object, a {@code NetworkInterfaceView} object is created
* for each sub-interface. These {@code NetworkInterfaceView} objects are
* accessible via {@link #getSubInterfaces}.
* <p>
* If, when creating the list of {@code NetworkInterfaceView} objects for the
* sub-interfaces, a {@code NetworkInterfaceView} object cannot be created then
* that sub-interface is silently skipped.
* <p>
* @param source the {@code NetworkInterface} from which to create a snapshot.
* @throws SocketException if any of the {@code getHardwareAddress}, {@code getMTU},
* {@code isLoopback}, {@code isPointToPoint}, {@code isUp} and
* {@code supportsMulticast} methods of {@code source} throws a {@code SocketException}.
*/
public NetworkInterfaceView(NetworkInterface source) throws SocketException {
this(source, null);
Preconditions.checkArgument(source.getParent() == null,
"Cannot create NetworkInterfaceView from interface %s",
source.getName());
}
private NetworkInterfaceView(NetworkInterface ni,
NetworkInterfaceView parent) throws SocketException {
_displayName = ni.getDisplayName();
_hardwareAddress = ni.getHardwareAddress();
_mtu = ni.getMTU();
_name = ni.getName();
_isLoopback = ni.isLoopback();
_isPointToPoint = ni.isPointToPoint();
_isUp = ni.isUp();
_isVirtual = ni.isVirtual();
_hasMulticastSupport = ni.supportsMulticast();
_parent = parent;
_interfaceAddresses = viewOfInterfaceAddresses(ni);
_inetAddresses = viewOfInetAddresses(ni);
_children = viewOfChildren(ni);
}
private List<InterfaceAddressView> viewOfInterfaceAddresses(NetworkInterface ni) {
ImmutableList.Builder<InterfaceAddressView> builder = ImmutableList.builder();
for(InterfaceAddress address : ni.getInterfaceAddresses()) {
builder.add(new InterfaceAddressView(address));
}
return builder.build();
}
private List<InetAddress> viewOfInetAddresses(NetworkInterface ni) {
ImmutableList.Builder<InetAddress> builder = ImmutableList.builder();
Enumeration<InetAddress> addresses = ni.getInetAddresses();
while(addresses.hasMoreElements()) {
InetAddress address = addresses.nextElement();
builder.add(address);
}
return builder.build();
}
private List<NetworkInterfaceView> viewOfChildren(NetworkInterface ni) {
ImmutableList.Builder<NetworkInterfaceView> builder = ImmutableList.builder();
Enumeration<NetworkInterface> subInterfaces = ni.getSubInterfaces();
while( subInterfaces.hasMoreElements()) {
NetworkInterface child = subInterfaces.nextElement();
try {
NetworkInterfaceView childView = new NetworkInterfaceView(child, this);
builder.add(childView);
} catch (SocketException e) {
_log.debug("Unable to add child {} of interface {}: {}", child.getName(), ni.getName(), e.getMessage());
}
}
return builder.build();
}
public List<NetworkInterfaceView> getSubInterfaces() {
return _children;
}
public Enumeration<InetAddress> getInetAddresses() {
return Iterators.asEnumeration(_inetAddresses.iterator());
}
public List<InetAddress> getInetAddressList() {
return _inetAddresses;
}
public boolean hasSubInterfaces() {
return !_children.isEmpty();
}
public String getDisplayName() {
return _displayName;
}
public byte[] getHardwareAddress() {
return _hardwareAddress;
}
public List<InterfaceAddressView> getInterfaceAddresses() {
return _interfaceAddresses;
}
public int getMTU() {
return _mtu;
}
public String getName() {
return _name;
}
public NetworkInterfaceView getParent() {
return _parent;
}
public boolean isLoopback() {
return _isLoopback;
}
public boolean isPointToPoint() {
return _isPointToPoint;
}
public boolean isUp() {
return _isUp;
}
public boolean isVirtual() {
return _isVirtual;
}
public boolean supportsMulticast() {
return _hasMulticastSupport;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("name:");
if( getName() != null) {
sb.append(getName());
} else {
sb.append("null");
}
if(getDisplayName() != null) {
sb.append(" (").append(getDisplayName()).append(")");
}
List<InterfaceAddressView> addresses = getInterfaceAddresses();
/*
* NetworkInterface provides an "index" field, which isn't exposed
* by any other method; because of this, NetworkInterfaceView
* doesn't record the index and that part of the toString output
* is skipped.
*/
sb.append(" addresses:\n");
for(InterfaceAddressView address : addresses) {
sb.append(address.getAddress()).append(";\n");
}
return sb.toString();
}
/**
* This class provides the same functionality as InterfaceAddress
* but is serializable.
*/
public static class InterfaceAddressView implements Serializable {
private static final long serialVersionUID = 467290761384687925L;
private final InetAddress _address;
private final InetAddress _broadcast;
private final short _maskLength;
public InterfaceAddressView( InterfaceAddress ifAddress) {
_address = ifAddress.getAddress();
_broadcast = ifAddress.getBroadcast();
_maskLength = ifAddress.getNetworkPrefixLength();
}
public InetAddress getAddress() {
return _address;
}
public InetAddress getBroadcast() {
return _broadcast;
}
public short getNetworkPrefixLength() {
return _maskLength;
}
@Override
public int hashCode() {
return Objects.hashCode(getNetworkPrefixLength(), getAddress(), getBroadcast());
}
@Override
public boolean equals(Object rawOther) {
if( rawOther == this) {
return true;
}
if (rawOther == null){
return false;
}
if(rawOther.getClass().equals(InterfaceAddressView.class)) {
InterfaceAddressView other = (InterfaceAddressView) rawOther;
return _maskLength == other.getNetworkPrefixLength() &&
Objects.equal(_address, other.getAddress()) &&
Objects.equal(_broadcast, other.getBroadcast());
}
return false;
}
@Override
public String toString() {
return _address.toString() + "/" + _maskLength + " [" + _broadcast + "]";
}
}
}