package com.limegroup.gnutella; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.limewire.io.GUID; import com.google.inject.Inject; import com.google.inject.Singleton; import com.google.inject.name.Named; /** * A factory to manage GuidMaps. * This will ensure that the guids are expired in an appropriate timeframe. */ @Singleton class GuidMapManagerImpl implements GuidMapManager { /* The time which expired GUIDs will be purged. */ private static long EXPIRE_POLL_TIME = 2 * 60 * 1000; /** The default lifetime of the GUID (10 minutes). */ private static long TIMED_GUID_LIFETIME = 10 * 60 * 1000; /** A listing of all GuidMaps that have atleast one GUID that needs expiry. */ private List<GuidMapImpl> toExpire = new LinkedList<GuidMapImpl>(); /** Whether or not we've scheduled our cleaner. */ private boolean scheduled = false; private final ScheduledExecutorService backgroundExecutor; @Inject public GuidMapManagerImpl(@Named("backgroundExecutor") ScheduledExecutorService backgroundExecutor) { this.backgroundExecutor = backgroundExecutor; } /* (non-Javadoc) * @see com.limegroup.gnutella.GuidMapFactory#getMap() */ public GuidMap getMap() { return new GuidMapImpl(); } /* (non-Javadoc) * @see com.limegroup.gnutella.GuidMapFactory#removeMap(com.limegroup.gnutella.GuidMap) */ public synchronized void removeMap(GuidMap expiree) { toExpire.remove(expiree); } /** Adds the GuidMapImpl to the list of maps that need to be expired. */ private synchronized void addMapToExpire(GuidMapImpl expiree) { // schedule it on demand if (!scheduled) { backgroundExecutor.scheduleWithFixedDelay(new GuidExpirer(), 0, EXPIRE_POLL_TIME, TimeUnit.MILLISECONDS); scheduled = true; } toExpire.add(expiree); } /** Runnable that iterates through potential expirations and expires them. */ private class GuidExpirer implements Runnable { public void run() { synchronized (GuidMapManagerImpl.this) { // iterator through all the maps.... for(Iterator<GuidMapImpl> i = toExpire.iterator(); i.hasNext(); ) { GuidMapImpl next = i.next(); synchronized (next) { long now = System.currentTimeMillis(); Map<GUID.TimedGUID, GUID> currMap = next.getMap(); // and expire as many entries as possible.... for(Iterator<GUID.TimedGUID> j = currMap.keySet().iterator(); j.hasNext(); ) { if (j.next().shouldExpire(now)) j.remove(); } } } } } } /** * Implementation of GuidMap that delegates to the factory to expire things. */ private class GuidMapImpl implements GuidMap { /** Mapping between new & old GUID. Lazily constructed. */ private Map<GUID.TimedGUID, GUID> map; @Override public String toString() { return "impl, map: " + map; } /** Returns the mapping between the two GUIDs. */ Map<GUID.TimedGUID, GUID> getMap() { return map; } /** Adds a mapping from origGUID to newGUID. The default lifetime of 10 mintues is used. */ public void addMapping(byte[] origGUID, byte[] newGUID) { addMapping(origGUID, newGUID, TIMED_GUID_LIFETIME); } public void addMapping(byte[] origGUID, byte[] newGUID, long lifetime) { boolean created = false; synchronized(this) { if(map == null) { map = new HashMap<GUID.TimedGUID, GUID>(); created = true; } GUID.TimedGUID tGuid = new GUID.TimedGUID(new GUID(newGUID), lifetime); map.put(tGuid, new GUID(origGUID)); } if(created) addMapToExpire(this); } public synchronized byte[] getOriginalGUID(byte[] newGUID) { if(map != null) { GUID.TimedGUID wrapper = new GUID.TimedGUID(new GUID(newGUID), 0); GUID orig = map.get(wrapper); if(orig != null) return orig.bytes(); } return null; } public synchronized GUID getNewGUID(GUID origGUID) { if(map != null) { for(Iterator<Map.Entry<GUID.TimedGUID, GUID>> i = map.entrySet().iterator(); i.hasNext(); ) { Map.Entry<GUID.TimedGUID, GUID> next = i.next(); if(next.getValue().equals(origGUID)) return next.getKey().getGUID(); } } return null; } } }