Friday, March 04, 2011

IP change notifier



package ca.digitalrapids.net;

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

import ca.digitalrapids.util.concurrent.DRThreadFactory;

import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

/** Monitors the local system's IP, and informs of any changes.
*/
public class IpChangeNotifier {

static public class IpChangeEvent {
private final IpChangeEventType type;
private final NetworkInterface networkInterface;
private final Set oldAddresses;
private final Set newAddresses;
public IpChangeEvent(IpChangeEventType type,
NetworkInterface networkInterface,
Set oldAddresses, Set newAddresses) {
super();
this.type = type;
this.networkInterface = networkInterface;
this.oldAddresses = oldAddresses == null ? null :
Collections.unmodifiableSet(oldAddresses);
this.newAddresses = newAddresses == null ? null :
Collections.unmodifiableSet(newAddresses);
}
public IpChangeEventType getType() {
return type;
}
public NetworkInterface getNetworkInterface() {
return networkInterface;
}
public Set getOldAddresses() {
return oldAddresses;
}
public Set getNewAddresses() {
return newAddresses;
}
@Override
public String toString() {
return "Event [type=" + type + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((type == null) ? 0 : type.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
IpChangeEvent other = (IpChangeEvent) obj;
if (type != other.type)
return false;
return true;
}
}
static public enum IpChangeEventType {
INTERFACE_ADDED,
INTERFACE_REMOVED,
ADDRESSES_CHANGED
}
static private class InterfaceInfo {
public final NetworkInterface networkInterface;
public final Set lastAddresses;
public InterfaceInfo(NetworkInterface networkInterface,
Set lastAddresses) {
super();
this.networkInterface = networkInterface;
this.lastAddresses = lastAddresses;
}
}
public interface Listener {
void handleIpChange(IpChangeEvent event);
}
interface NetworkInterfacesFactory {
List get() throws SocketException;
}

static private final transient Logger logger = Logger
.getLogger(IpChangeNotifier.class.getName());

private final List listeners =
new CopyOnWriteArrayList();
private final ScheduledExecutorService executor =
Executors.newSingleThreadScheduledExecutor(
new DRThreadFactory("IpChangeNotifier", true));
private final long delayMillis;
private NetworkInterfacesFactory networkInterfacesFactory =
new NetworkInterfacesFactory()
{
@Override
public List get() throws SocketException {
return Collections.list(NetworkInterface.getNetworkInterfaces());
}
};
private Map interfaceInfosByName =
Maps.newHashMap();

public IpChangeNotifier(long delayMillis) {
super();
this.delayMillis = delayMillis;
}

public void setNetworkInterfacesFactory(
NetworkInterfacesFactory networkInterfacesFactory) {
this.networkInterfacesFactory = networkInterfacesFactory;
}

public void addListener(Listener listener) {
this.listeners.add(listener);
}

public void removeListener(Listener listener) {
this.listeners.remove(listener);
}

/**
* Remove all listeners
*/
public void clear() {
this.listeners.clear();
}

public void start() {
setBaseline();
executor.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
try {
check();
} catch (Throwable e) {
logger.log(Level.SEVERE, e.getMessage(), e);
}
}
}, 0, delayMillis, TimeUnit.MILLISECONDS);
}

public void stop() {
executor.shutdown();
}

public void check()
{
check(true);
}

private void check(boolean fire) {
try {
Collection removedInterfaces =
Sets.newHashSet(interfaceInfosByName.values());
for (NetworkInterface networkInterface : networkInterfacesFactory.get())
{
String interfaceName = networkInterface.getName();
Set currentAddresses =
getInetAddresses(networkInterface);
InterfaceInfo interfaceInfo =
interfaceInfosByName.get(interfaceName);
if ( interfaceInfo == null )
{
interfaceInfosByName.put(interfaceName,
new InterfaceInfo(networkInterface, currentAddresses));
if ( fire )
fireIpChanged(new IpChangeEvent(
IpChangeEventType.INTERFACE_ADDED,
networkInterface, null, currentAddresses));
} else {
removedInterfaces.remove(interfaceInfo);
if ( !interfaceInfo.lastAddresses.equals(currentAddresses) ) {
logger.info("interface "+interfaceName+" addresses " +
"changed from "+interfaceInfo.lastAddresses+" to "+
currentAddresses);
interfaceInfosByName.put(interfaceName,
new InterfaceInfo(networkInterface, currentAddresses));
if ( fire )
fireIpChanged(new IpChangeEvent(
IpChangeEventType.ADDRESSES_CHANGED,
networkInterface, interfaceInfo.lastAddresses,
currentAddresses));
}
}
}
handleInterfacesRemoved(removedInterfaces);
} catch (SocketException e) {
logger.log(Level.WARNING, "IP check failed. Could not get " +
"network interfaces", e);
}
}

private void handleInterfacesRemoved(
Collection removedInterfaces)
{
for (InterfaceInfo interfaceInfo : removedInterfaces) {
interfaceInfosByName.remove(interfaceInfo.networkInterface.getName());
fireIpChanged(new IpChangeEvent(IpChangeEventType.INTERFACE_REMOVED,
interfaceInfo.networkInterface, interfaceInfo.lastAddresses,
null));
}
}

private HashSet getInetAddresses(
NetworkInterface networkInterface) {
return Sets.newHashSet(Collections.list(
networkInterface.getInetAddresses()));
}

private void fireIpChanged(IpChangeEvent event) {
for (Listener listener : listeners) {
listener.handleIpChange(event);
}
}

public static void main(String[] args) {
IpChangeNotifier ipChangeNotifier = new IpChangeNotifier(1000);
ipChangeNotifier.addListener(new Listener() {
@Override
public void handleIpChange(IpChangeEvent event) {
System.out.println("ip changed "+event);
}
});
ipChangeNotifier.start();
System.out.println("kevin");
}

public void setBaseline() {
check(false);
}
}

package ca.digitalrapids.net;


import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;

import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.util.Collections;
import java.util.List;

import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import ca.digitalrapids.net.IpChangeNotifier.IpChangeEvent;
import ca.digitalrapids.net.IpChangeNotifier.IpChangeEventType;
import ca.digitalrapids.net.IpChangeNotifier.Listener;
import ca.digitalrapids.net.IpChangeNotifier.NetworkInterfacesFactory;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

public class IpChangeNotifierTest {

private IpChangeNotifier subject;
@Mock
private NetworkInterfacesFactory interfacesFactory;
@Mock
private Listener listener;
@Mock
private InterfaceAddress interfaceAddress;

@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
subject = new IpChangeNotifier(500);
subject.setNetworkInterfacesFactory(interfacesFactory);
subject.addListener(listener);
}

@Test
public void testGeneral() throws Exception {
List interfaces = Lists.newArrayList();
when(interfacesFactory.get()).thenReturn(interfaces);

/* NetworkInterface is final, so we can't mock it. Need to think of
* another way to test this class. */
NetworkInterface networkInterface0 =
NetworkInterface.getNetworkInterfaces().nextElement();

subject.check();
verifyZeroInteractions(listener);
interfaces.add(networkInterface0);
subject.check();

ArgumentCaptor captor =
ArgumentCaptor.forClass(IpChangeEvent.class);
verify(listener).handleIpChange(captor.capture());
IpChangeEvent resultEvent = captor.getValue();
assertEquals(IpChangeEventType.INTERFACE_ADDED, resultEvent.getType());
assertEquals(networkInterface0, resultEvent.getNetworkInterface());
assertNull(resultEvent.getOldAddresses());
assertEquals(Sets.newHashSet(Collections.list(
networkInterface0.getInetAddresses())),
resultEvent.getNewAddresses());

subject.check();
verifyZeroInteractions(listener);
}

@Test
public void testSetBaseline() throws Exception {
List interfaces = Lists.newArrayList();
when(interfacesFactory.get()).thenReturn(interfaces);

/* NetworkInterface is final, so we can't mock it. Need to think of
* another way to test this class. */
NetworkInterface networkInterface0 =
NetworkInterface.getNetworkInterfaces().nextElement();

interfaces.add(networkInterface0);
subject.setBaseline();
subject.check();
verifyZeroInteractions(listener);
}

@Test
public void testInterfaceRemoved() throws Exception {
List interfaces = Lists.newArrayList();
when(interfacesFactory.get()).thenReturn(interfaces);

/* NetworkInterface is final, so we can't mock it. Need to think of
* another way to test this class. */
NetworkInterface networkInterface0 =
NetworkInterface.getNetworkInterfaces().nextElement();

interfaces.add(networkInterface0);
subject.setBaseline();
interfaces.clear();
subject.check();

ArgumentCaptor captor =
ArgumentCaptor.forClass(IpChangeEvent.class);
verify(listener).handleIpChange(captor.capture());
IpChangeEvent resultEvent = captor.getValue();
assertEquals(IpChangeEventType.INTERFACE_REMOVED, resultEvent.getType());
assertEquals(networkInterface0, resultEvent.getNetworkInterface());
assertNull(resultEvent.getNewAddresses());
assertEquals(Sets.newHashSet(Collections.list(
networkInterface0.getInetAddresses())),
resultEvent.getOldAddresses());

subject.check();
verifyZeroInteractions(listener);
}

}

0 Comments:

Post a Comment

<< Home