/*_########################################################################## _## _## Copyright (C) 2011-2013 Kaito Yamada _## _########################################################################## */ package com.github.kaitoy.sneo.network.protocol; import java.net.InetAddress; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.pcap4j.util.MacAddress; import org.snmp4j.log.LogAdapter; import org.snmp4j.log.LogFactory; import com.github.kaitoy.sneo.network.MacAddressManager; import com.github.kaitoy.sneo.network.NetworkInterface; import com.github.kaitoy.sneo.network.Node; import com.github.kaitoy.sneo.util.FutureData; import com.github.kaitoy.sneo.util.NamedThreadFactory; abstract class NeighborDiscoveryHelper { private static final LogAdapter logger = LogFactory.getLogger(NeighborDiscoveryHelper.class.getPackage().getName()); protected static NeighborCache newNdCache(long cacheLife) { return new NeighborCache(cacheLife); } protected static MacAddress resolveVirtualAddress(InetAddress ipAddress) { return MacAddressManager.getInstance().resolveVirtualAddress(ipAddress); } protected static MacAddress resolveRealAddress( RequestSender reqSender, InetAddress targetIpAddr, Node node, NetworkInterface nif, NeighborCache neighborCache, long resolveTimeout, TimeUnit unit ) { FutureData<MacAddress> fd; synchronized (neighborCache) { fd = neighborCache.getEntry(targetIpAddr); if (fd != null) { logger.debug("Hit Neighbor cache" + fd); } else { fd = neighborCache.newEntry(targetIpAddr); } } if (!fd.isReady() && !fd.isWorking()) { fd.setWorking(true); reqSender.sendRequest( targetIpAddr, node, nif ); } MacAddress macAddress = null; try { macAddress = fd.get(resolveTimeout, unit); if (logger.isDebugEnabled()) { logger.debug(targetIpAddr + " was resolved to " + macAddress); } } catch (InterruptedException e) { logger.warn(e); fd.setWorking(false); } catch (TimeoutException e) { logger.warn(e); fd.setWorking(false); } return macAddress; } protected static void cache( NeighborCache neighborCache, InetAddress ipAddr, MacAddress macAddr ) { neighborCache.cache(ipAddr, macAddr); if (logger.isDebugEnabled()) { logger.debug( "Cached a neighbor: (" + ipAddr + ", " + macAddr + ")" ); } } protected static void clearCache(NeighborCache neighborCache) { neighborCache.clearCache(); } protected interface RequestSender { public void sendRequest( InetAddress targetIpAddr, Node node, NetworkInterface nif ); } protected static class NeighborCache { private final long cacheLife; // [s] private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor( new NamedThreadFactory( NeighborCacheInvalidater.class.getSimpleName(), true ) ); // TODO shutdown private final Map<InetAddress, FutureData<MacAddress>> table = new HashMap<InetAddress, FutureData<MacAddress>>(); private final Map<InetAddress, Future<?>> invalidateNeighborCacheFutures = new HashMap<InetAddress, Future<?>>(); protected NeighborCache(long cacheLife) { this.cacheLife = cacheLife; } private FutureData<MacAddress> newEntry(InetAddress ipAddress) { synchronized (table) { FutureData<MacAddress> fd = new FutureData<MacAddress>(); table.put(ipAddress, fd); return fd; } } private FutureData<MacAddress> getEntry(InetAddress ipAddr) { synchronized (table) { return table.get(ipAddr); } } private void cache(InetAddress ipAddr, MacAddress macAddr) { synchronized (table) { if (invalidateNeighborCacheFutures.containsKey(ipAddr)) { invalidateNeighborCacheFutures.get(ipAddr).cancel(true); } if (table.containsKey(ipAddr)) { FutureData<MacAddress> f = table.get(ipAddr); f.set(macAddr); } else { table.put(ipAddr, new FutureData<MacAddress>(macAddr)); } invalidateNeighborCacheFutures.put( ipAddr, scheduler.schedule( new NeighborCacheInvalidater(ipAddr), cacheLife, TimeUnit.MILLISECONDS ) ); } } private void clearCache() { synchronized (table) { table.clear(); for (Future<?> f: invalidateNeighborCacheFutures.values()) { f.cancel(false); } invalidateNeighborCacheFutures.clear(); } } private class NeighborCacheInvalidater implements Runnable { private InetAddress ipAddress; public NeighborCacheInvalidater(InetAddress ipAddress) { this.ipAddress = ipAddress; } public void run() { synchronized (table) { if (!Thread.interrupted()) { table.remove(ipAddress); } } } } } }